///|
fn is_digit(c : UInt16) -> Bool {
c is ('0'..<='9')
}
///|
fn is_hex(c : UInt16) -> Bool {
c is ('0'..<='9' | 'a'..<='f' | 'A'..<='F')
}
///|
fn is_ident_start(c : UInt16) -> Bool {
c is ('a'..<='z' | 'A'..<='Z' | '_') || c >= 0x80
}
///|
fn is_ident_char(c : UInt16) -> Bool {
is_ident_start(c) || is_digit(c) || c == '$'
}
///|
fn is_two_char_op(a : UInt16, b : UInt16) -> Bool {
match (a, b) {
('|', '|') | ('<', '>' | '=' | '<') | ('>', '=' | '>') | ('!' | '=', '=') =>
true
_ => false
}
}
///|
fn scan_while(s : String, from : Int32, pred : (UInt16) -> Bool) -> Int32 {
let mut j = from
while j < s.length() && pred(s[j]) {
j += 1
}
j
}
///|
fn has_at(s : String, i : Int32, pred : (UInt16) -> Bool) -> Bool {
i < s.length() && pred(s[i])
}
///|
/// The end of the quoted run opening at `from`, where a doubled `quote` is an
/// escaped quote. `None` when the run is unterminated.
fn scan_quoted(s : String, from : Int32, quote : UInt16) -> Int32? {
let n = s.length()
let mut best = None
let mut j = from + 1
while j < n {
guard s[j] == quote else {
j += 1
continue
}
best = Some(j + 1)
if j + 1 < n && s[j + 1] == quote {
j += 2
} else {
break
}
}
best
}
///|
/// The end of the numeric literal at `from`: hex (`0x1F`), decimal with an
/// optional fraction (`1.5`, `1.`, `.5`) and an optional exponent. An exponent
/// marker without digits is not part of the number, so `1e` lexes as `1`
/// then `e`.
fn scan_number(s : String, from : Int32) -> Int32 {
if s[from] == '0' && has_at(s, from + 1, c => c == 'x') {
let k = scan_while(s, from + 2, is_hex)
if k > from + 2 {
return k
}
}
let mut j = if s[from] == '.' {
scan_while(s, from + 1, is_digit)
} else {
let int_end = scan_while(s, from, is_digit)
if has_at(s, int_end, c => c == '.') {
scan_while(s, int_end + 1, is_digit)
} else {
int_end
}
}
if has_at(s, j, c => c is ('e' | 'E')) {
let sign_end = if has_at(s, j + 1, c => c is ('+' | '-')) {
j + 2
} else {
j + 1
}
let digits_end = scan_while(s, sign_end, is_digit)
if digits_end > sign_end {
j = digits_end
}
}
j
}
///|
fn scan_word(s : String, from : Int32) -> Int32 {
let mut j = scan_while(s, from + 1, is_ident_char)
while has_at(s, j, c => c == '.') && has_at(s, j + 1, is_ident_start) {
j = scan_while(s, j + 2, is_ident_char)
}
j
}