refactor(source): name private analysis expression kind explicitly

This commit is contained in:
2026-09-12 21:31:40 -07:00
parent 2c515cf3bd
commit 9ff6e7cf7a
3 changed files with 64 additions and 54 deletions
@@ -33,7 +33,7 @@ pub struct Argument {
}
#[derive(Debug, PartialEq, Eq)]
pub struct Expression {
pub kind: Kind,
pub kind: ExpressionKind,
pub span: Span,
pub depth: usize,
pub grouped: bool,
@@ -47,7 +47,7 @@ pub struct LiteralSyntax {
pub tokens: Vec<Span>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Kind {
pub enum ExpressionKind {
Boolean(bool),
Number(Exact),
Quantity {
@@ -64,7 +64,7 @@ pub(super) fn parse(source: &str, tokens: &[Token], budget: &mut Budget) -> Resu
let value = p.text() == "true";
let span = p.span();
p.cursor += 1;
values.push(p.node(Kind::Boolean(value), span)?);
values.push(p.node(ExpressionKind::Boolean(value), span)?);
}
"-" => {
p.open()?;
@@ -95,14 +95,18 @@ pub(super) fn parse(source: &str, tokens: &[Token], budget: &mut Budget) -> Resu
)));
} else {
let end = reference.span.end;
values.push(p.node(Kind::Property(reference), Span { start, end })?);
values.push(
p.node(ExpressionKind::Property(reference), Span { start, end })?,
);
}
}
"$" => {
p.cursor += 1;
let reference = p.reference()?;
let end = reference.span.end;
values.push(p.node(Kind::Parameter(reference), Span { start, end })?);
values.push(
p.node(ExpressionKind::Parameter(reference), Span { start, end })?,
);
}
"ratio" => {
let (value, literal) = p.ratio()?;
@@ -184,7 +188,7 @@ pub(super) fn parse(source: &str, tokens: &[Token], budget: &mut Budget) -> Resu
end: right.span.end,
};
values.push(p.node(
Kind::Binary {
ExpressionKind::Binary {
op,
left: Box::new(left),
right: Box::new(right),
@@ -197,7 +201,7 @@ pub(super) fn parse(source: &str, tokens: &[Token], budget: &mut Budget) -> Resu
let end = operand.span.end;
p.close();
values.push(p.node(
Kind::Unary {
ExpressionKind::Unary {
op,
operand: Box::new(operand),
},
@@ -292,7 +296,7 @@ pub(super) fn parse(source: &str, tokens: &[Token], budget: &mut Budget) -> Resu
let end = p.take(")")?.end;
p.close();
values.push(p.node(
Kind::Power {
ExpressionKind::Power {
base: Box::new(base),
exponent,
},
@@ -304,7 +308,7 @@ pub(super) fn parse(source: &str, tokens: &[Token], budget: &mut Budget) -> Resu
let end = p.take(")")?.end;
p.close();
values.push(p.node(
Kind::AsKind {
ExpressionKind::AsKind {
kind,
value: Box::new(value),
},
@@ -441,29 +445,32 @@ impl Parser<'_, '_> {
let unit = self.reference()?;
let end = self.take("]")?.end;
self.close();
self.node(Kind::Quantity { magnitude, unit }, Span { start, end })
self.node(
ExpressionKind::Quantity { magnitude, unit },
Span { start, end },
)
} else {
self.node(Kind::Number(magnitude), Span { start, end })
self.node(ExpressionKind::Number(magnitude), Span { start, end })
}?;
expression.literal = Some(literal);
Ok(expression)
}
fn node(&mut self, kind: Kind, span: Span) -> Result<Expression, Error> {
fn node(&mut self, kind: ExpressionKind, span: Span) -> Result<Expression, Error> {
let child_depth = match &kind {
Kind::Unary { operand, .. } => operand.depth,
Kind::Binary { left, right, .. } => left.depth.max(right.depth),
Kind::If {
ExpressionKind::Unary { operand, .. } => operand.depth,
ExpressionKind::Binary { left, right, .. } => left.depth.max(right.depth),
ExpressionKind::If {
condition,
when_true,
when_false,
} => condition.depth.max(when_true.depth).max(when_false.depth),
Kind::List(values) => values.iter().map(|v| v.depth).max().unwrap_or(0),
Kind::Call { arguments, .. } => {
ExpressionKind::List(values) => values.iter().map(|v| v.depth).max().unwrap_or(0),
ExpressionKind::Call { arguments, .. } => {
arguments.iter().map(|a| a.value.depth).max().unwrap_or(0)
}
Kind::Sum { values, initial } => values.depth.max(initial.depth),
Kind::Power { base, .. } => base.depth,
Kind::AsKind { value, .. } => value.depth,
ExpressionKind::Sum { values, initial } => values.depth.max(initial.depth),
ExpressionKind::Power { base, .. } => base.depth,
ExpressionKind::AsKind { value, .. } => value.depth,
_ => 0,
};
if child_depth >= crate::analysis::MAX_EXPRESSION_DEPTH {
@@ -490,21 +497,21 @@ impl Parser<'_, '_> {
.ok_or_else(|| error(span, "Missing required constructor argument."))
};
let kind = match sequence.kind {
SequenceKind::If => Kind::If {
SequenceKind::If => ExpressionKind::If {
condition: next()?,
when_true: next()?,
when_false: next()?,
},
SequenceKind::Sum => Kind::Sum {
SequenceKind::Sum => ExpressionKind::Sum {
values: next()?,
initial: next()?,
},
SequenceKind::Unary(op) => Kind::Unary {
SequenceKind::Unary(op) => ExpressionKind::Unary {
op,
operand: next()?,
},
SequenceKind::List => Kind::List(values.map(|(_, value)| value).collect()),
SequenceKind::Call(definition) => Kind::Call {
SequenceKind::List => ExpressionKind::List(values.map(|(_, value)| value).collect()),
SequenceKind::Call(definition) => ExpressionKind::Call {
definition,
arguments: values
.map(|(parameter, value)| Argument {
@@ -529,7 +536,7 @@ fn comparison(op: BinaryOp) -> bool {
)
}
fn ungrouped_comparison(value: &Expression) -> bool {
!value.grouped && matches!(&value.kind, Kind::Binary { op, .. } if comparison(*op))
!value.grouped && matches!(&value.kind, ExpressionKind::Binary { op, .. } if comparison(*op))
}
fn operator(text: &str) -> Option<(BinaryOp, u8)> {
Some(match text {
@@ -15,14 +15,14 @@ fn expression(source: &str) -> Expression {
}
}
fn number(value: &Expression) -> String {
let Kind::Number(exact) = &value.kind else {
let ExpressionKind::Number(exact) = &value.kind else {
panic!("expected Number");
};
exact.canonical_literal()
}
fn binary_children(value: Expression, expected: BinaryOp) -> (Expression, Expression) {
let Kind::Binary { op, left, right } = value.kind else {
let ExpressionKind::Binary { op, left, right } = value.kind else {
panic!("expected Binary {expected:?}");
};
assert_eq!(op, expected);
@@ -41,18 +41,18 @@ fn mixed_precedence_and_grouping_have_independent_expected_child_topology() {
let (conjunction, last) =
binary_children(expression("not(false) and true or false"), BinaryOp::Or);
assert_eq!(last.kind, Kind::Boolean(false));
assert_eq!(last.kind, ExpressionKind::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 {
assert_eq!(middle.kind, ExpressionKind::Boolean(true));
let ExpressionKind::Unary { op, operand } = negated.kind else {
panic!("expected Not");
};
assert_eq!(op, UnaryOp::Not);
assert_eq!(operand.kind, Kind::Boolean(false));
assert_eq!(operand.kind, ExpressionKind::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 {
let ExpressionKind::Unary { op, operand } = negative.kind else {
panic!("expected Negate");
};
assert_eq!(op, UnaryOp::Negate);
@@ -85,10 +85,10 @@ fn flat_binary_depth_and_malformed_constructor_retain_bounded_completed_children
// 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));
assert_eq!(right.kind, ExpressionKind::Boolean(true));
tree = left;
}
assert_eq!(tree.kind, Kind::Boolean(true));
assert_eq!(tree.kind, ExpressionKind::Boolean(true));
assert!(
parse(&flat(129), &mut budget())
.unwrap_err()
@@ -129,7 +129,7 @@ fn significant_ratio_and_quantity_trivia_preserve_original_spans() {
.collect::<Vec<_>>(),
["ratio", "(", "-", "3", ",", "4", ")"]
);
let Kind::Quantity { magnitude, unit } = parsed.kind else {
let ExpressionKind::Quantity { magnitude, unit } = parsed.kind else {
panic!("expected Quantity");
};
assert_eq!(magnitude.canonical_literal(), "ratio(-3, 4)");
@@ -192,14 +192,14 @@ fn unary_literals_precedence_and_comparison_children_keep_distinct_trees() {
let negative = expression("-(1)");
assert!(matches!(
negative.kind,
Kind::Unary {
ExpressionKind::Unary {
op: UnaryOp::Negate,
..
}
));
let left = expression("(1 - 2) - 3");
let right = expression("1 - (2 - 3)");
let Kind::Binary {
let ExpressionKind::Binary {
left, right: last, ..
} = left.kind
else {
@@ -207,13 +207,13 @@ fn unary_literals_precedence_and_comparison_children_keep_distinct_trees() {
};
assert!(matches!(
left.kind,
Kind::Binary {
ExpressionKind::Binary {
op: BinaryOp::Subtract,
..
}
));
assert_eq!(number(&last), "3");
let Kind::Binary {
let ExpressionKind::Binary {
left: first, right, ..
} = right.kind
else {
@@ -222,7 +222,7 @@ fn unary_literals_precedence_and_comparison_children_keep_distinct_trees() {
assert_eq!(number(&first), "1");
assert!(matches!(
right.kind,
Kind::Binary {
ExpressionKind::Binary {
op: BinaryOp::Subtract,
..
}
@@ -249,7 +249,7 @@ fn unary_literals_precedence_and_comparison_children_keep_distinct_trees() {
#[test]
fn every_expression_shape_and_exact_constructor_arity_is_represented() {
let tree = expression("if(true, sum([@p, $x], 0), as_kind(@duration, power(abs(-(2)), -2)))");
let Kind::If {
let ExpressionKind::If {
condition,
when_true,
when_false,
@@ -257,24 +257,27 @@ fn every_expression_shape_and_exact_constructor_arity_is_represented() {
else {
panic!();
};
assert_eq!(condition.kind, Kind::Boolean(true));
let Kind::Sum { values, initial } = when_true.kind else {
assert_eq!(condition.kind, ExpressionKind::Boolean(true));
let ExpressionKind::Sum { values, initial } = when_true.kind else {
panic!();
};
assert_eq!(number(&initial), "0");
let Kind::List(values) = values.kind else {
let ExpressionKind::List(values) = values.kind else {
panic!();
};
assert!(matches!(&values[0].kind, Kind::Property(r) if r.name == "p"));
assert!(matches!(&values[1].kind, Kind::Parameter(r) if r.name == "x"));
let Kind::AsKind { kind, value } = when_false.kind else {
assert!(matches!(&values[0].kind, ExpressionKind::Property(r) if r.name == "p"));
assert!(matches!(&values[1].kind, ExpressionKind::Parameter(r) if r.name == "x"));
let ExpressionKind::AsKind { kind, value } = when_false.kind else {
panic!();
};
assert_eq!(kind.name, "duration");
assert!(matches!(value.kind, Kind::Power { exponent: -2, .. }));
assert!(matches!(expression("[]").kind, Kind::List(v) if v.is_empty()));
assert!(matches!(
value.kind,
ExpressionKind::Power { exponent: -2, .. }
));
assert!(matches!(expression("[]").kind, ExpressionKind::List(v) if v.is_empty()));
assert!(
matches!(expression("@zero()").kind, Kind::Call { arguments, .. } if arguments.is_empty())
matches!(expression("@zero()").kind, ExpressionKind::Call { arguments, .. } if arguments.is_empty())
);
assert!(matches!(
parse(" unbound // pending", &mut budget()).unwrap(),
@@ -301,17 +304,17 @@ fn every_expression_shape_and_exact_constructor_arity_is_represented() {
fn repeated_call_argument_occurrences_have_distinct_original_spans() {
let source = "@combine(a: @p, b: 2) + @combine(a: @q, b: 4)";
let tree = expression(source);
let Kind::Binary { left, right, .. } = tree.kind else {
let ExpressionKind::Binary { left, right, .. } = tree.kind else {
panic!();
};
let Kind::Call {
let ExpressionKind::Call {
definition: first,
arguments: a,
} = left.kind
else {
panic!();
};
let Kind::Call {
let ExpressionKind::Call {
definition: second,
arguments: b,
} = right.kind