///|
fn ws(c : UInt16) -> Bool {
c == 32 || c == 9 || c == 13
}
///|
fn trim_ascii(text : String) -> String {
let mut a = 0
let mut b = text.length()
while a < b && ws(text[a]) {
a = a + 1
}
while b > a && ws(text[b - 1]) {
b = b - 1
}
text[a:b].to_owned()
}
///|
fn words(text : String) -> Array[String] {
let result : Array[String] = []
let mut a = 0
while a < text.length() {
if ws(text[a]) {
a = a + 1
continue
}
let mut b = a
while b < text.length() && !ws(text[b]) {
b = b + 1
}
result.push(text[a:b].to_owned())
a = b
}
result
}
///|
/// Parse directly into UInt64, with pre-operation overflow checks.
fn unsigned(text : String, line : Int32) -> UInt64 raise ParseError {
let hex = text.has_prefix("0x")
let start = if hex { 2 } else { 0 }
if text.length() == start || text.length() - start > 20 {
raise Syntax(line, "expected an unsigned decimal or hexadecimal integer")
}
let base : UInt64 = if hex { 16UL } else { 10UL }
let mut value = 0UL
for i = start; i < text.length(); i = i + 1 {
let c = text[i].to_int()
let digit = if c >= 48 && c <= 57 {
c - 48
} else if hex && c >= 65 && c <= 70 {
c - 55
} else if hex && c >= 97 && c <= 102 {
c - 87
} else {
raise Syntax(line, "invalid integer digit")
}
let d = digit.to_uint64()
if value > (18446744073709551615UL - d) / base {
raise Resource(line, "integer exceeds UInt64")
}
value = value * base + d
}
value
}
///|
fn add_checked(a : UInt64, b : UInt64, line : Int32) -> UInt64 raise ParseError {
if a > 18446744073709551615UL - b {
raise Resource(line, "counter sum exceeds UInt64")
}
a + b
}
///|
fn position(
token : String,
previous : UInt64,
line : Int32,
) -> UInt64 raise ParseError {
if token == "*" {
return previous
}
if token.has_prefix("+") {
return add_checked(previous, unsigned(token[1:].to_owned(), line), line)
}
if token.has_prefix("-") {
let delta = unsigned(token[1:].to_owned(), line)
if delta > previous {
raise Syntax(line, "relative position is negative")
}
return previous - delta
}
unsigned(token, line)
}