diff --git a/src/operator/display.rs b/src/operator/display.rs index 7de8d29..ef2ef52 100644 --- a/src/operator/display.rs +++ b/src/operator/display.rs @@ -111,6 +111,12 @@ impl Display for Tuple { } impl Display for Assign { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "; ") + } +} + +impl Display for Chain { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { write!(f, " = ") } diff --git a/src/operator/mod.rs b/src/operator/mod.rs index af5d9eb..4252854 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -78,10 +78,12 @@ pub struct Not; #[derive(Debug)] pub struct Tuple; - #[derive(Debug)] pub struct Assign; +#[derive(Debug)] +pub struct Chain; + #[derive(Debug)] pub struct Const { value: Value, @@ -627,6 +629,24 @@ impl Operator for Assign { } } +impl Operator for Chain { + fn precedence(&self) -> i32 { + 0 + } + + fn max_argument_amount(&self) -> usize { + 2 + } + + fn eval(&self, arguments: &[Value], _context: &Context) -> Result { + if arguments.is_empty() { + return Err(EvalexprError::wrong_operator_argument_amount(0, 1)); + } + + Ok(arguments.get(1).cloned().unwrap_or(Value::Empty)) + } +} + impl Operator for Const { fn precedence(&self) -> i32 { 200 diff --git a/src/token/display.rs b/src/token/display.rs index 224c6ea..8d73954 100644 --- a/src/token/display.rs +++ b/src/token/display.rs @@ -28,11 +28,10 @@ impl fmt::Display for Token { LBrace => write!(f, "("), RBrace => write!(f, ")"), - // Aggregation + // Special Comma => write!(f, ","), - - // Assignment Assign => write!(f, "="), + Semicolon => write!(f, ";"), // Values => write!(f, ""), Variables and Functions Identifier(identifier) => identifier.fmt(f), diff --git a/src/token/mod.rs b/src/token/mod.rs index 7a39665..8d7a99e 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -28,11 +28,10 @@ pub enum Token { LBrace, RBrace, - // Aggregation + // Special Comma, - - // Assignment Assign, + Semicolon, // Values, Variables and Functions Identifier(String), @@ -75,6 +74,7 @@ fn char_to_partial_token(c: char) -> PartialToken { ')' => PartialToken::Token(Token::RBrace), ',' => PartialToken::Token(Token::Comma), + ';' => PartialToken::Token(Token::Semicolon), c => { if c.is_whitespace() { @@ -111,8 +111,8 @@ impl Token { Token::RBrace => false, Token::Comma => false, - Token::Assign => false, + Token::Semicolon => false, Token::Identifier(_) => true, Token::Float(_) => true, @@ -145,8 +145,8 @@ impl Token { Token::RBrace => true, Token::Comma => false, - Token::Assign => false, + Token::Semicolon => false, Token::Identifier(_) => true, Token::Float(_) => true, diff --git a/src/tree/mod.rs b/src/tree/mod.rs index d355afe..ee2300e 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -383,8 +383,8 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> EvalexprResult Some(Node::new(Tuple)), - Token::Assign => Some(Node::new(Assign)), + Token::Semicolon => Some(Node::new(Chain)), Token::Identifier(identifier) => { let mut result = Some(Node::new(VariableIdentifier::new(identifier.clone()))); diff --git a/tests/integration.rs b/tests/integration.rs index f0e2d94..7f21a31 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -522,6 +522,21 @@ fn test_assignment() { ); assert_eq!(eval_empty_with_context("empty", &context), Ok(EMPTY_VALUE)); assert_eq!(eval_boolean_with_context("boolean", &context), Ok(false)); + + assert_eq!( + eval_empty_with_context_mut("b = a = 5", &mut context), + Ok(EMPTY_VALUE) + ); + assert_eq!(eval_empty_with_context("b", &context), Ok(EMPTY_VALUE)); +} + +#[test] +fn test_expression_chaining() { + let mut context = HashMapContext::new(); + assert_eq!( + eval_int_with_context_mut("a = 5; a = a + 2; a", &mut context), + Ok(7) + ); } #[cfg(feature = "serde")]