///|
fn python_number_string(
ctx : QueryContext,
input : String,
integer : Bool,
) -> QueryValue {
let raw = normalize_python_digits(input.trim().to_owned())
let plain = raw.replace_all(old="_", new="").to_lower()
let word = if plain.has_prefix("+") || plain.has_prefix("-") {
plain[1:].to_owned()
} else {
plain
}
if !integer &&
(
word == "infinity" ||
word == "inf" ||
word.has_prefix("nan") ||
word.has_prefix("snan")
) {
let tail = if word.has_prefix("snan") {
word[4:].to_owned()
} else if word.has_prefix("nan") {
word[3:].to_owned()
} else {
""
}
if tail.iter().all(fn(c) { c.is_ascii_digit() }) {
query_error(
ctx, "unsupported query.unsupported.decimal_representation: nonfinite strings",
)
return QNull
}
}
if !integer && plain.contains("e") {
let parts = plain.split("e").to_array()
if parts.length() != 2 {
return QNull
}
let base = python_number_string(ctx, parts[0].to_owned(), false)
let exp = python_number_string(ctx, parts[1].to_owned(), true)
match (value_number(base), exp) {
(Some(number), QInt(_)) =>
match query_integer(exp) {
Some(e) =>
if e > 1000000 || e < -1000000 {
query_error(
ctx, "unsupported query.unsupported.decimal_representation: exponent resource bound",
)
QNull
} else if base is QNegativeZero(_) {
QNegativeZero(number.scale() - e)
} else {
QNumber(
@decimal.Decimal::new(number.mantissa(), number.scale() - e),
)
}
None => {
query_error(
ctx, "unsupported query.unsupported.decimal_representation: exponent resource bound",
)
QNull
}
}
_ => QNull
}
} else {
let out = StringBuilder()
let chars = raw.iter().to_array()
for i in 0..= start && n < start + 10 {
digit = n - start
break
}
}
if digit >= 0 {
out.write_string(digit.to_string())
} else {
return QNull
}
}
}
match @decimal.Decimal::parse(out.to_string()) {
Some(n) =>
if integer {
QInt(n.mantissa())
} else if n.is_zero() && out.to_string().has_prefix("-") {
QNegativeZero(n.scale())
} else {
QNumber(n)
}
None => QNull
}
}
}
///|
fn normalize_python_digits(input : String) -> String {
let out = StringBuilder()
for c in input.iter() {
let mut mapped = c
for start in unicode_decimal_starts {
if c.to_int() >= start && c.to_int() < start + 10 {
mapped = (48 + c.to_int() - start).unsafe_to_char()
break
}
}
out.write_char(mapped)
}
out.to_string()
}