///|
priv struct ParserState {
  text : String
  cursor : Array[Int]
  line : Array[Int]
  column : Array[Int]
  diagnostics : Array[Diagnostic]
  max_depth : Int
}

///|
fn ParserState::new(text : String, max_depth : Int) -> ParserState {
  { text, cursor: [0], line: [1], column: [1], diagnostics: [], max_depth }
}

///|
fn ParserState::location(self : ParserState) -> SourceLocation {
  SourceLocation::new(self.line[0], self.column[0], self.cursor[0])
}

///|
fn ParserState::peek(self : ParserState) -> UInt16? {
  if self.cursor[0] >= self.text.length() {
    None
  } else {
    Some(self.text[self.cursor[0]])
  }
}

///|
fn ParserState::advance(self : ParserState) -> UInt16? {
  match self.peek() {
    None => None
    Some(ch) => {
      self.cursor[0] += 1
      if ch == '\n' {
        self.line[0] += 1
        self.column[0] = 1
      } else {
        self.column[0] += 1
      }
      Some(ch)
    }
  }
}

///|
fn ParserState::error(
  self : ParserState,
  path : String,
  message : String,
) -> Unit {
  self.diagnostics.push(Diagnostic::error(path, message, Some(self.location())))
}

///|
fn ParserState::skip_space(self : ParserState) -> Unit {
  while self.cursor[0] < self.text.length() {
    let ch = self.text[self.cursor[0]]
    if ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' {
      ignore(self.advance())
    } else {
      break
    }
  }
}

///|
fn append_code_unit(builder : StringBuilder, unit : UInt16) -> Bool {
  match unit.to_char() {
    Some(ch) => {
      builder.write_char(ch)
      true
    }
    None => false
  }
}