From 2fc762b9d2676a8dd852c1a86d09253c88b6cddf Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Sat, 22 Feb 2025 04:52:06 +0100 Subject: [PATCH] expr: Evaluate parenthesis content before checking for closing parenthesis --- src/uu/expr/src/expr.rs | 4 +--- src/uu/expr/src/syntax_tree.rs | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/uu/expr/src/expr.rs b/src/uu/expr/src/expr.rs index dd2b36f74..4b9f6b9d6 100644 --- a/src/uu/expr/src/expr.rs +++ b/src/uu/expr/src/expr.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. use clap::{crate_version, Arg, ArgAction, Command}; -use syntax_tree::AstNode; +use syntax_tree::{is_truthy, AstNode}; use thiserror::Error; use uucore::{ display::Quotable, @@ -12,8 +12,6 @@ use uucore::{ format_usage, help_about, help_section, help_usage, }; -use crate::syntax_tree::is_truthy; - mod syntax_tree; mod options { diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 0288a6736..008cd5307 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -292,7 +292,7 @@ const PRECEDENCE: &[&[(&str, BinOp)]] = &[ &[(":", BinOp::String(StringOp::Match))], ]; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum NumOrStr { Num(BigInt), Str(String), @@ -343,6 +343,9 @@ impl NumOrStr { #[derive(Debug, PartialEq, Eq)] pub enum AstNode { + Evaluated { + value: NumOrStr, + }, Leaf { value: String, }, @@ -366,8 +369,15 @@ impl AstNode { Parser::new(input).parse() } + pub fn evaluated(self) -> ExprResult { + Ok(Self::Evaluated { + value: self.eval()?, + }) + } + pub fn eval(&self) -> ExprResult { match self { + Self::Evaluated { value } => Ok(value.clone()), Self::Leaf { value } => Ok(value.to_string().into()), Self::BinOp { op_type, @@ -536,7 +546,10 @@ impl<'a> Parser<'a> { value: self.next()?.into(), }, "(" => { - let s = self.parse_expression()?; + // Evaluate the node just after parsing to we detect arithmetic + // errors before checking for the closing parenthesis. + let s = self.parse_expression()?.evaluated()?; + match self.next() { Ok(")") => {} // Since we have parsed at least a '(', there will be a token @@ -680,7 +693,9 @@ mod test { AstNode::parse(&["(", "1", "+", "2", ")", "*", "3"]), Ok(op( BinOp::Numeric(NumericOp::Mul), - op(BinOp::Numeric(NumericOp::Add), "1", "2"), + op(BinOp::Numeric(NumericOp::Add), "1", "2") + .evaluated() + .unwrap(), "3" )) );