// Portions adapted from google/go-jsonnet v0.21.0 (Apache-2.0).
// https://github.com/google/go-jsonnet/tree/67968688d9952f50662e979fab1bef889d0d3e57
// Source: internal/parser/lexer.go
// Copyright 2016 Google Inc. All rights reserved.
// Modifications: Adapted token classification, numeric scanning and errors to MoonBit source spans.
// License and retained attribution: LICENSE and NOTICE at the module root.
// Rune quoting also adapts Go 1.24.0 src/strconv/quote.go to MoonBit Char.
// Copyright 2009 The Go Authors. All rights reserved.
// BSD 3-Clause terms: licenses/GO-BSD-3-Clause.txt; see NOTICE.
///|
// Go's QuoteRuneToASCII notation, including replacement rune for EOF.
fn quote_lex_char(c : Char?) -> String {
let c = c.unwrap_or('\ufffd')
let escaped = match c {
'\'' => "\\'"
'\\' => "\\\\"
'\u0007' => "\\a"
'\b' => "\\b"
'\u000c' => "\\f"
'\n' => "\\n"
'\r' => "\\r"
'\t' => "\\t"
'\u000b' => "\\v"
_ => {
let cp = c.to_int()
if cp >= 0x20 && cp < 0x7f {
c.to_string()
} else {
let (prefix, width) = if cp < 0x20 || cp == 0x7f {
("\\x", 2)
} else if cp < 0x10000 {
("\\u", 4)
} else {
("\\U", 8)
}
let hex = cp.to_string(radix=16)
prefix + "0".repeat(width - hex.length()) + hex
}
}
}
"'" + escaped + "'"
}
///|
fn number_end(
chars : Array[Char],
start : Int,
loc : Location,
) -> Int raise Error {
let mut i = start + 1
if chars[start] != '0' {
while chars.get(i) is Some(c) && digit(c) {
i += 1
}
}
let require_digit = fn(index : Int, context : String) raise Error {
if !(chars.get(index) is Some(c) && digit(c)) {
// Everything consumed inside a numeric token is ASCII.
raise problem(
Syntax,
{ ..loc, column: loc.column + index - start, },
"Couldn't lex number, junk after " +
context +
": " +
quote_lex_char(chars.get(index)),
)
}
}
if chars.get(i) == Some('.') {
i += 1
require_digit(i, "decimal point")
while chars.get(i) is Some(c) && digit(c) {
i += 1
}
}
if chars.get(i) == Some('e') || chars.get(i) == Some('E') {
i += 1
if chars.get(i) == Some('+') || chars.get(i) == Some('-') {
i += 1
require_digit(i, "exponent sign")
} else {
require_digit(i, "'E'")
}
while chars.get(i) is Some(c) && digit(c) {
i += 1
}
}
i
}