mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
expr: fix eager evaluation of parenthesized dead branches (#11395)
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
co-authored by
Sylvestre Ledru
parent
90c7896d23
commit
76b2f7877f
@@ -624,9 +624,6 @@ pub struct AstNode {
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub enum AstNodeInner {
|
||||
Evaluated {
|
||||
value: NumOrStr,
|
||||
},
|
||||
Leaf {
|
||||
value: MaybeNonUtf8String,
|
||||
},
|
||||
@@ -650,15 +647,6 @@ impl AstNode {
|
||||
Parser::new(input).parse()
|
||||
}
|
||||
|
||||
pub fn evaluated(self) -> ExprResult<Self> {
|
||||
Ok(Self {
|
||||
id: get_next_id(),
|
||||
inner: AstNodeInner::Evaluated {
|
||||
value: self.eval()?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eval(&self) -> ExprResult<NumOrStr> {
|
||||
// This function implements a recursive tree-walking algorithm, but uses an explicit
|
||||
// stack approach instead of native recursion to avoid potential stack overflow
|
||||
@@ -669,9 +657,6 @@ impl AstNode {
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
match &node.inner {
|
||||
AstNodeInner::Evaluated { value, .. } => {
|
||||
result_stack.insert(node.id, Ok(value.clone()));
|
||||
}
|
||||
AstNodeInner::Leaf { value, .. } => {
|
||||
result_stack.insert(node.id, Ok(value.to_owned().into()));
|
||||
}
|
||||
@@ -896,9 +881,7 @@ impl<'a, S: AsRef<MaybeNonUtf8Str>> Parser<'a, S> {
|
||||
value: self.next()?.into(),
|
||||
},
|
||||
b"(" => {
|
||||
// Evaluate the node just after parsing to we detect arithmetic
|
||||
// errors before checking for the closing parenthesis.
|
||||
let s = self.parse_expression()?.evaluated()?;
|
||||
let s = self.parse_expression()?;
|
||||
|
||||
match self.next() {
|
||||
Ok(b")") => {}
|
||||
@@ -1070,9 +1053,7 @@ mod test {
|
||||
AstNode::parse(&["(", "1", "+", "2", ")", "*", "3"]),
|
||||
Ok(op(
|
||||
BinOp::Numeric(NumericOp::Mul),
|
||||
op(BinOp::Numeric(NumericOp::Add), "1", "2")
|
||||
.evaluated()
|
||||
.unwrap(),
|
||||
op(BinOp::Numeric(NumericOp::Add), "1", "2"),
|
||||
"3"
|
||||
))
|
||||
);
|
||||
|
||||
@@ -217,6 +217,29 @@ fn test_and() {
|
||||
new_ucmd!().args(&["", "&", ""]).fails().stdout_only("0\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parenthesized_short_circuit_dead_branches() {
|
||||
new_ucmd!()
|
||||
.args(&["1", "|", "(", "1", "/", "0", ")"])
|
||||
.succeeds()
|
||||
.stdout_only("1\n");
|
||||
|
||||
new_ucmd!()
|
||||
.args(&["0", "&", "(", "1", "/", "0", ")"])
|
||||
.fails_with_code(1)
|
||||
.stdout_only("0\n");
|
||||
|
||||
new_ucmd!()
|
||||
.args(&["1", "|", "(", "0", "&", "(", "1", "/", "0", ")", ")"])
|
||||
.succeeds()
|
||||
.stdout_only("1\n");
|
||||
|
||||
new_ucmd!()
|
||||
.args(&["0", "&", "(", "1", "|", "(", "1", "/", "0", ")", ")"])
|
||||
.fails_with_code(1)
|
||||
.stdout_only("0\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_length_fail() {
|
||||
new_ucmd!().args(&["length", "αbcdef", "1"]).fails();
|
||||
@@ -580,11 +603,11 @@ fn test_num_str_comparison() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eager_evaluation() {
|
||||
fn test_missing_closing_parenthesis_reports_syntax_error() {
|
||||
new_ucmd!()
|
||||
.args(&["(", "1", "/", "0"])
|
||||
.fails()
|
||||
.stderr_contains("division by zero");
|
||||
.stderr_contains("expecting ')' after '0'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user