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