///|
/// A 1-based source position, counted in UTF-16 code units.
pub struct Position {
line : Int
column : Int
}
///|
/// A failure to parse or print JavaScript source.
///
/// Each variant carries the data needed to describe the failure, and `Show`
/// renders the user-facing message.
pub suberror ParseError {
/// A token of `expected` was required, but `got` appeared.
UnexpectedToken(Position, String, String)
/// The lexer met a character that cannot start a token.
UnexpectedCharacter(Position, Char)
/// The input ends inside an unterminated comment, string, template or regexp.
Unterminated(Position, String)
/// A malformed escape sequence.
InvalidEscape(Position, String)
/// Any other syntax error, described by `message`.
Syntax(Position, String)
}
///|
/// Computes the 1-based line and column of a UTF-16 code-unit `offset`.
fn position_at(source : String, offset : Int) -> Position {
let length = source.length()
let limit = if offset < length { offset } else { length }
for index = 0, line = 1, column = 1 {
guard index < limit else { break { line, column, } }
lexmatch source.view(start_offset=index, end_offset=limit) with longest {
(re"^(?:\r\n|[\n\r\u2028\u2029])", after=rest) =>
continue rest.start_offset(), line + 1, 1
(re"^[^\n\r\u2028\u2029]+" as text, after=rest) =>
continue rest.start_offset(), line, column + text.length()
// Regex consumes Unicode scalars; a lone UTF-16 unit still occupies a column.
_ => continue index + 1, line, column + 1
}
}
}
///|
fn Position::text(position : Position) -> String {
" at \{position.line}:\{position.column}"
}
///|
pub impl Show for ParseError with fn output(self, logger) {
match self {
UnexpectedToken(position, expected, got) =>
logger.write("expected \{expected}, got \{got}\{position.text()}")
UnexpectedCharacter(position, character) =>
logger.write(
"unexpected character '\{character.escape(quote=false)}'\{position.text()}",
)
Unterminated(position, construct) =>
logger.write("unterminated \{construct}\{position.text()}")
InvalidEscape(position, message) =>
logger.write("\{message}\{position.text()}")
Syntax(position, message) => logger.write("\{message}\{position.text()}")
}
}
///|
#deprecated
pub extend ParseError with Show::{to_string, output}