///|
/// One-based line/column information for a character offset.
pub(all) struct SourceLocation {
  offset : Int
  line : Int
  column : Int
}

///|
pub fn source_location(input : String, offset : Int) -> SourceLocation {
  let mut position = 0
  let mut line = 1
  let mut column = 1
  for char in input {
    if position >= offset {
      break
    }
    if char == '\n' {
      line = line + 1
      column = 1
    } else {
      column = column + 1
    }
    position = position + 1
  }
  { offset, line, column }
}

///|
pub fn ParseError::location(
  self : ParseError,
  input : String,
) -> SourceLocation {
  let offset = match self {
    UnexpectedEnd(position) => position
    UnexpectedCharacter(position, _) => position
    Expected(position, _) => position
    InvalidEscape(position, _) => position
    InvalidNumber(position, _) => position
    DuplicateKey(position, _) => position
    TrailingContent(position) => position
  }
  source_location(input, offset)
}

///|
pub fn SourceLocation::to_string(self : SourceLocation) -> String {
  "line " + self.line.to_string() + ", column " + self.column.to_string()
}

///|
pub fn format_parse_error(input : String, error : ParseError) -> String {
  let location = error.location(input)
  location.to_string() + ": " + error.message()
}