///|
priv suberror ParseFailure {
  ParseFailure(String, Array[String?])
}

///|
priv struct Cursor {
  chars : Array[Char]
  mut index : Int32
  mut peek_offset : Int32
  mut depth : Int32
  max_depth : Int32
}

///|
fn Cursor::new(source : String) -> Cursor {
  {
    chars: source.to_array(),
    index: 0,
    peek_offset: 0,
    depth: 0,
    max_depth: 64,
  }
}

///|
fn Cursor::raw(self : Cursor, offset : Int32) -> Char? {
  if offset >= 0 && offset < self.chars.length() {
    Some(self.chars[offset])
  } else {
    None
  }
}

///|
fn Cursor::at(self : Cursor, offset : Int32) -> Char? {
  if self.raw(offset) == Some('\r') && self.raw(offset + 1) == Some('\n') {
    Some('\n')
  } else {
    self.raw(offset)
  }
}

///|
fn Cursor::current(self : Cursor) -> Char? {
  self.at(self.index)
}

///|
fn Cursor::current_peek(self : Cursor) -> Char? {
  self.at(self.index + self.peek_offset)
}

///|
fn Cursor::next(self : Cursor) -> Char? {
  self.peek_offset = 0
  if self.raw(self.index) == Some('\r') &&
    self.raw(self.index + 1) == Some('\n') {
    self.index += 1
  }
  self.index += 1
  self.raw(self.index)
}

///|
fn Cursor::peek(self : Cursor) -> Char? {
  let i = self.index + self.peek_offset
  if self.raw(i) == Some('\r') && self.raw(i + 1) == Some('\n') {
    self.peek_offset += 1
  }
  self.peek_offset += 1
  self.raw(self.index + self.peek_offset)
}

///|
fn Cursor::reset_peek(self : Cursor, offset~ : Int32 = 0) -> Unit {
  self.peek_offset = offset
}

///|
fn Cursor::skip_to_peek(self : Cursor) -> Unit {
  self.index += self.peek_offset
  self.peek_offset = 0
}

///|
fn Cursor::slice(self : Cursor, start : Int32, end : Int32) -> String {
  let buf = StringBuilder()
  for i in start.. String {
  let start = self.index + self.peek_offset
  while self.current_peek() == Some(' ') {
    ignore(self.peek())
  }
  self.slice(start, self.index + self.peek_offset)
}

///|
fn Cursor::skip_blank_inline(self : Cursor) -> String {
  let blank = self.peek_blank_inline()
  self.skip_to_peek()
  blank
}

///|
fn Cursor::peek_blank_block(self : Cursor) -> String {
  let buf = StringBuilder()
  while true {
    let line_start = self.peek_offset
    ignore(self.peek_blank_inline())
    if self.current_peek() == Some('\n') {
      buf.write_char('\n')
      ignore(self.peek())
      continue
    }
    if self.current_peek() != None {
      self.reset_peek(offset=line_start)
    }
    break
  }
  buf.to_string()
}

///|
fn Cursor::skip_blank_block(self : Cursor) -> String {
  let blank = self.peek_blank_block()
  self.skip_to_peek()
  blank
}

///|
fn Cursor::peek_blank(self : Cursor) -> Unit {
  while self.current_peek() == Some(' ') || self.current_peek() == Some('\n') {
    ignore(self.peek())
  }
}

///|
fn Cursor::skip_blank(self : Cursor) -> Unit {
  self.peek_blank()
  self.skip_to_peek()
}

///|
fn Cursor::expect(self : Cursor, ch : Char) -> Unit raise ParseFailure {
  if self.current() != Some(ch) {
    raise ParseFailure("E0003", [Some(ch.to_string())])
  }
  ignore(self.next())
}

///|
fn Cursor::expect_line_end(self : Cursor) -> Unit raise ParseFailure {
  if self.current() == None {
    return
  }
  if self.current() == Some('\n') {
    ignore(self.next())
    return
  }
  raise ParseFailure("E0003", [Some("\u{2424}")])
}

///|
fn is_id_start(ch : Char?) -> Bool {
  match ch {
    Some(c) => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
    None => false
  }
}

///|
fn is_digit(ch : Char?) -> Bool {
  match ch {
    Some(c) => c >= '0' && c <= '9'
    None => false
  }
}

///|
fn is_id_char(ch : Char?) -> Bool {
  is_id_start(ch) || is_digit(ch) || ch == Some('_') || ch == Some('-')
}

///|
fn is_hex(ch : Char?) -> Bool {
  match ch {
    Some(c) => is_digit(ch) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
    None => false
  }
}

///|
fn Cursor::is_number_start(self : Cursor) -> Bool {
  let ch = if self.current() == Some('-') {
    self.peek()
  } else {
    self.current()
  }
  self.reset_peek()
  is_digit(ch)
}

///|
fn Cursor::is_value_start(self : Cursor) -> Bool {
  self.current_peek() != None && self.current_peek() != Some('\n')
}

///|
fn Cursor::is_value_continuation(self : Cursor) -> Bool {
  let column1 = self.peek_offset
  ignore(self.peek_blank_inline())
  if self.current_peek() == Some('{') {
    self.reset_peek(offset=column1)
    return true
  }
  if self.peek_offset == column1 {
    return false
  }
  match self.current_peek() {
    None | Some('}') | Some('.') | Some('[') | Some('*') => false
    _ => {
      self.reset_peek(offset=column1)
      true
    }
  }
}

///|
fn Cursor::is_next_line_comment(self : Cursor, level : Int32) -> Bool {
  if self.current() != Some('\n') {
    return false
  }
  ignore(self.peek())
  for _ in 0..<(level + 1) {
    if self.current_peek() != Some('#') {
      self.reset_peek()
      return false
    }
    ignore(self.peek())
  }
  let result = self.current_peek() == Some(' ') ||
    self.current_peek() == Some('\n') ||
    self.current_peek() == None
  self.reset_peek()
  result
}

///|
fn Cursor::is_variant_start(self : Cursor) -> Bool {
  let offset = self.peek_offset
  if self.current_peek() == Some('*') {
    ignore(self.peek())
  }
  let result = self.current_peek() == Some('[') && self.peek() != Some('[')
  self.reset_peek(offset~)
  result
}

///|
fn Cursor::recover(self : Cursor, junk_start : Int32) -> Unit {
  let mut last_newline = -1
  for i in 0..