///|
pub(all) enum Expr {
Constant(Value)
NumericLiteral(String, Int64)
SymbolRef(String)
Negation(Expr)
Conjunction(Expr, Expr)
Disjunction(Expr, Expr)
Comparison(String, Expr, Expr)
} derive(Eq, Debug)
///|
/// Checked signed decimal/hex input without host-dependent conversions.
pub fn parse_number(text : String) -> Int64 raise ConfigError {
if text.is_empty() {
raise Invalid("empty number")
}
let mut i = 0
let negative = text[0] == 45
if negative || text[0] == 43 {
i += 1
}
let mut base = 10L
if i + 1 < text.length() &&
text[i] == 48 &&
(text[i + 1] == 120 || text[i + 1] == 88) {
base = 16L
i += 2
}
if i == text.length() {
raise Invalid("number has no digits")
}
let limit = if negative {
-9223372036854775807L - 1L
} else {
-9223372036854775807L
}
let mut acc = 0L
while i < text.length() {
let c = text[i].to_int()
let d = if c >= 48 && c <= 57 {
c - 48
} else if c >= 65 && c <= 70 {
c - 55
} else if c >= 97 && c <= 102 {
c - 87
} else {
raise Invalid("invalid number: " + text)
}
let digit = d.to_int64()
if digit >= base || acc < limit / base {
raise Invalid("numeric overflow or digit")
}
acc *= base
if acc < limit + digit {
raise Invalid("numeric overflow")
}
acc -= digit
i += 1
}
if negative {
acc
} else {
-acc
}
}
///|
pub fn Expr::evaluate(
self : Expr,
values : Map[String, Value],
linux612~ : Bool = false,
) -> Value {
match self {
Constant(v) => v
NumericLiteral(_, n) => Number(n)
SymbolRef(name) => values.get(name).unwrap_or(Text(name))
Negation(a) => Logic(a.evaluate(values, linux612~).logic().negate())
Conjunction(a, b) =>
Logic(
a
.evaluate(values, linux612~)
.logic()
.meet(b.evaluate(values, linux612~).logic()),
)
Disjunction(a, b) =>
Logic(
a
.evaluate(values, linux612~)
.logic()
.join(b.evaluate(values, linux612~).logic()),
)
Comparison(op, a, b) => {
let x = a.evaluate(values, linux612~)
let y = b.evaluate(values, linux612~)
let order = if linux612 {
linux_compare(a, b, values)
} else {
match (x, y) {
(Number(n), Number(m)) => n.compare(m)
_ => x.text().compare(y.text())
}
}
let yes = match op {
"=" => order == 0
"!=" => order != 0
"<" => order < 0
">" => order > 0
"<=" => order <= 0
">=" => order >= 0
_ => false
}
Logic(if yes { Y } else { N })
}
}
}
///|
pub fn Expr::references(self : Expr) -> Array[String] {
let out : Array[String] = []
fn visit(expr : Expr) -> Unit {
match expr {
SymbolRef(s) => if !out.contains(s) { out.push(s) }
Negation(a) => visit(a)
Conjunction(a, b) | Disjunction(a, b) | Comparison(_, a, b) => {
visit(a)
visit(b)
}
Constant(_) | NumericLiteral(_, _) => ()
}
}
visit(self)
out
}
///|
#deprecated
pub extend Expr with @debug.Debug::{to_repr}
///|
#deprecated
pub extend Expr with Eq::{equal, not_equal}