Strengthen staged expression oracles and preserve source8 evidence

This commit is contained in:
2026-09-12 20:48:41 -07:00
parent e8a18a7cf9
commit 2c515cf3bd
7 changed files with 540 additions and 0 deletions
@@ -21,6 +21,93 @@ fn number(value: &Expression) -> String {
exact.canonical_literal()
}
fn binary_children(value: Expression, expected: BinaryOp) -> (Expression, Expression) {
let Kind::Binary { op, left, right } = value.kind else {
panic!("expected Binary {expected:?}");
};
assert_eq!(op, expected);
(*left, *right)
}
#[test]
fn mixed_precedence_and_grouping_have_independent_expected_child_topology() {
let (one, product) = binary_children(expression("1 + 2 * 3"), BinaryOp::Add);
assert_eq!(number(&one), "1");
let (two, three) = binary_children(product, BinaryOp::Multiply);
assert_eq!(
(number(&two), number(&three)),
("2".to_owned(), "3".to_owned())
);
let (conjunction, last) =
binary_children(expression("not(false) and true or false"), BinaryOp::Or);
assert_eq!(last.kind, Kind::Boolean(false));
let (negated, middle) = binary_children(conjunction, BinaryOp::And);
assert_eq!(middle.kind, Kind::Boolean(true));
let Kind::Unary { op, operand } = negated.kind else {
panic!("expected Not");
};
assert_eq!(op, UnaryOp::Not);
assert_eq!(operand.kind, Kind::Boolean(false));
let (negative, two) = binary_children(expression("-1 * 2"), BinaryOp::Multiply);
assert_eq!(number(&two), "2");
let Kind::Unary { op, operand } = negative.kind else {
panic!("expected Negate");
};
assert_eq!(op, UnaryOp::Negate);
assert_eq!(number(&operand), "1");
let (quotient, three) = binary_children(expression("1 / 2 * 3"), BinaryOp::Multiply);
assert_eq!(number(&three), "3");
let (one, two) = binary_children(quotient, BinaryOp::Divide);
assert_eq!(
(number(&one), number(&two)),
("1".to_owned(), "2".to_owned())
);
let (one, grouped) = binary_children(expression("1 + (2 + 3)"), BinaryOp::Add);
assert_eq!(number(&one), "1");
assert!(grouped.grouped);
let (two, three) = binary_children(grouped, BinaryOp::Add);
assert_eq!(
(number(&two), number(&three)),
("2".to_owned(), "3".to_owned())
);
}
#[test]
fn flat_binary_depth_and_malformed_constructor_retain_bounded_completed_children() {
let flat = |count: usize| vec!["true"; count].join(" and ");
let deep = flat(128);
let mut tree = expression(&deep);
assert_eq!(tree.depth, 128);
// Independently verify the complete left-associated shape, not just depth.
for _ in 1..128 {
let (left, right) = binary_children(tree, BinaryOp::And);
assert_eq!(right.kind, Kind::Boolean(true));
tree = left;
}
assert_eq!(tree.kind, Kind::Boolean(true));
assert!(
parse(&flat(129), &mut budget())
.unwrap_err()
.message
.contains("logical depth")
);
// The first argument is already a complete depth128 tree when parsing the
// second fails. Refusal tears down the held child without building Sum.
let malformed = format!("sum({deep}, @)");
let failure = parse(&malformed, &mut budget()).unwrap_err();
assert_eq!(
failure.message,
"Expected a valid reference or parameter name."
);
assert_eq!(&malformed[failure.span.start..failure.span.end], ")");
assert_eq!(number(&expression("1")), "1");
}
#[test]
fn significant_ratio_and_quantity_trivia_preserve_original_spans() {
let source = "ratio // explanation café\n ( - // sign\n 3, // denominator\n 4 ) // measurement\n [@millisecond]";