///|
/// The position of `offset`, which must be on the current line.
fn Parser::pos_at(self : Parser, offset : Int32) -> Position {
{ offset, line: self.line, column: offset - self.line_start, }
}
///|
/// Decodes the body of a string literal (without its quotes) that the fast
/// scanner has validated, so every escape is well formed. `base` is the
/// offset of the body, for errors.
fn Parser::decode_string(
self : Parser,
body : StringView,
base : Int32,
) -> String raise ParseError {
let len = body.length()
let mut i = 0
while i < len && body.unsafe_get(i) != '\\' {
i += 1
}
if i == len {
return body.to_owned()
}
let sb = StringBuilder(size_hint=len)
let mut run = 0
while i < len {
if body.unsafe_get(i) != '\\' {
i += 1
continue
}
if i > run {
sb.write_view(body[run:i])
}
match body.unsafe_get(i + 1) {
'u' => {
let c = hex4(body, i + 2)
if c is (0xD800..<=0xDBFF) {
guard i + 11 < len &&
body.unsafe_get(i + 6) == '\\' &&
body.unsafe_get(i + 7) == 'u' else {
raise UnpairedSurrogate(self.pos_at(base + i))
}
let c2 = hex4(body, i + 8)
guard c2 is (0xDC00..<=0xDFFF) else {
raise UnpairedSurrogate(self.pos_at(base + i))
}
sb.write_char(((c << 10) + c2 - 0x35fdc00).unsafe_to_char())
i += 12
} else if c is (0xDC00..<=0xDFFF) {
raise UnpairedSurrogate(self.pos_at(base + i))
} else {
sb.write_char(c.unsafe_to_char())
i += 6
}
}
e => {
sb.write_char(unescape(e))
i += 2
}
}
run = i
}
if run < len {
sb.write_view(body[run:len])
}
sb.to_string()
}
///|
/// The character a single-character escape `\e` stands for.
fn unescape(e : UInt16) -> Char {
match e {
'b' => '\b'
'f' => '\u{0C}'
'n' => '\n'
'r' => '\r'
't' => '\t'
'"' => '"'
'\\' => '\\'
'/' => '/'
_ => abort("unreachable: unvalidated escape")
}
}
///|
/// The value of the four hex digits at `start`, which must be valid.
fn hex4(s : StringView, start : Int32) -> Int32 {
let mut n = 0
for i in start..<(start + 4) {
let c = s.unsafe_get(i).to_int()
let d = if c <= '9' { c - '0' } else { (c | 0x20) - 'a' + 10 }
n = (n << 4) | d
}
n
}
///|
/// Converts a validated JSON number literal to the value `@json.parse` gives.
/// Short integers are converted here; anything else goes to `@json.parse`,
/// which keeps rounding and `repr` identical.
fn number_value(text : StringView) -> Json {
let len = text.length()
let negative = text.unsafe_get(0) == '-'
let start = if negative { 1 } else { 0 }
// At most 15 digits: below 2^53, so the double is exact.
if len - start <= 15 {
let mut acc = 0L
let mut simple = true
for i in start.. '9' {
simple = false
break
}
acc = acc * 10L + (c - '0').to_int64()
}
if simple {
let v = acc.to_double()
return Json::number(if negative { -v } else { v })
}
}
@json.parse(text) catch {
_ => abort("unreachable: invalid number literal")
}
}