From d80184ba14f9a4b1c000d5989a5d4dfa7449d806 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Aug 2022 23:10:58 +0800 Subject: [PATCH] Allow if-expressions and switch-expressions in Engine::eval_expression. --- CHANGELOG.md | 5 +++++ src/parser.rs | 40 +++++++++++++++++++++++++++++++++++----- tests/expressions.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d936b051..115f189d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ New features * For very special needs, the ability to register fallible type iterators is added. +### Expressions + +* `if`-expressions are allowed in `Engine::eval_expression` and `Engine::compile_expression` provided that both statement blocks each contain at most a single expression. +* `switch`-expressions are allowed in `Engine::eval_expression` and `Engine::compile_expression` provided that match actions are expressions only. + Enhancements ------------ diff --git a/src/parser.rs b/src/parser.rs index 48a8a97b..8498b90d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -291,6 +291,8 @@ struct ParseSettings { in_closure: bool, /// Is the construct being parsed located inside a breakable loop? is_breakable: bool, + /// Allow statements in blocks? + allow_statements: bool, /// Language options in effect (overrides Engine options). options: LangOptions, /// Current expression nesting level. @@ -1193,12 +1195,21 @@ impl Engine { } }; - let stmt = self.parse_stmt(input, state, lib, settings.level_up())?; - let need_comma = !stmt.is_self_terminated(); + let (action_expr, need_comma) = if settings.allow_statements { + let stmt = self.parse_stmt(input, state, lib, settings.level_up())?; + let need_comma = !stmt.is_self_terminated(); + + let stmt_block: StmtBlock = stmt.into(); + (Expr::Stmt(stmt_block.into()), need_comma) + } else { + ( + self.parse_expr(input, state, lib, settings.level_up())?, + true, + ) + }; let has_condition = !matches!(condition, Expr::BoolConstant(true, ..)); - let stmt_block: StmtBlock = stmt.into(); - expressions.push((condition, Expr::Stmt(stmt_block.into())).into()); + expressions.push((condition, action_expr).into()); let index = expressions.len() - 1; if case_expr_list.is_empty() { @@ -3076,6 +3087,22 @@ impl Engine { let mut statements = StaticVec::new_const(); + if !settings.allow_statements { + let stmt = self.parse_expr_stmt(input, state, lib, settings.level_up())?; + statements.push(stmt); + + // Must end with } + return match input.next().expect(NEVER_ENDS) { + (Token::RightBrace, pos) => Ok((statements, settings.pos, pos).into()), + (Token::LexError(err), pos) => Err(err.into_err(pos)), + (.., pos) => Err(PERR::MissingToken( + Token::LeftBrace.into(), + "to start a statement block".into(), + ) + .into_err(pos)), + }; + } + let prev_entry_stack_len = state.block_stack_len; state.block_stack_len = state.stack.len(); @@ -3290,6 +3317,7 @@ impl Engine { #[cfg(not(feature = "no_closure"))] in_closure: false, is_breakable: false, + allow_statements: true, level: 0, options, pos, @@ -3761,7 +3789,7 @@ impl Engine { let mut functions = BTreeMap::new(); let mut options = self.options; - options.remove(LangOptions::IF_EXPR | LangOptions::SWITCH_EXPR | LangOptions::STMT_EXPR); + options.remove(LangOptions::STMT_EXPR); #[cfg(not(feature = "no_function"))] options.remove(LangOptions::ANON_FN); @@ -3773,6 +3801,7 @@ impl Engine { #[cfg(not(feature = "no_closure"))] in_closure: false, is_breakable: false, + allow_statements: false, level: 0, options, pos: Position::NONE, @@ -3828,6 +3857,7 @@ impl Engine { #[cfg(not(feature = "no_closure"))] in_closure: false, is_breakable: false, + allow_statements: true, options: self.options, level: 0, pos: Position::NONE, diff --git a/tests/expressions.rs b/tests/expressions.rs index 6c0f16b1..4d0b4888 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -12,8 +12,47 @@ fn test_expressions() -> Result<(), Box> { engine.eval_expression_with_scope::(&mut scope, "2 + (x + 10) * 2")?, 42 ); + assert_eq!( + engine.eval_expression_with_scope::(&mut scope, "if x > 0 { 42 } else { 123 }")?, + 42 + ); assert!(engine - .eval_expression_with_scope::(&mut scope, "if x > 0 { 42 } else { 123 }") + .eval_expression_with_scope::(&mut scope, "if x > 0 { let y = 42; y } else { 123 }") + .is_err()); + assert!(engine + .eval_expression_with_scope::(&mut scope, "if x > 0 { 42 } else { let y = 123; y }") + .is_err()); + assert!(engine + .eval_expression_with_scope::(&mut scope, "if x > 0 { 42 } else {}") + .is_err()); + + assert_eq!( + engine.eval_expression_with_scope::( + &mut scope, + " + switch x { + 0 => 1, + 1..10 => 123, + 10 => 42, + } + " + )?, + 42 + ); + assert!(engine + .eval_expression_with_scope::( + &mut scope, + " + switch x { + 0 => 1, + 1..10 => { + let y = 123; + y + } + 10 => 42, + } + " + ) .is_err()); assert!(engine.eval_expression::<()>("40 + 2;").is_err());