Allow if-expressions and switch-expressions in Engine::eval_expression.

This commit is contained in:
Stephen Chung 2022-08-26 23:10:58 +08:00
parent 204284f4f7
commit d80184ba14
3 changed files with 80 additions and 6 deletions

View File

@ -23,6 +23,11 @@ New features
* For very special needs, the ability to register fallible type iterators is added. * 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 Enhancements
------------ ------------

View File

@ -291,6 +291,8 @@ struct ParseSettings {
in_closure: bool, in_closure: bool,
/// Is the construct being parsed located inside a breakable loop? /// Is the construct being parsed located inside a breakable loop?
is_breakable: bool, is_breakable: bool,
/// Allow statements in blocks?
allow_statements: bool,
/// Language options in effect (overrides Engine options). /// Language options in effect (overrides Engine options).
options: LangOptions, options: LangOptions,
/// Current expression nesting level. /// Current expression nesting level.
@ -1193,12 +1195,21 @@ impl Engine {
} }
}; };
let stmt = self.parse_stmt(input, state, lib, settings.level_up())?; let (action_expr, need_comma) = if settings.allow_statements {
let need_comma = !stmt.is_self_terminated(); 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 has_condition = !matches!(condition, Expr::BoolConstant(true, ..));
let stmt_block: StmtBlock = stmt.into(); expressions.push((condition, action_expr).into());
expressions.push((condition, Expr::Stmt(stmt_block.into())).into());
let index = expressions.len() - 1; let index = expressions.len() - 1;
if case_expr_list.is_empty() { if case_expr_list.is_empty() {
@ -3076,6 +3087,22 @@ impl Engine {
let mut statements = StaticVec::new_const(); 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; let prev_entry_stack_len = state.block_stack_len;
state.block_stack_len = state.stack.len(); state.block_stack_len = state.stack.len();
@ -3290,6 +3317,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
in_closure: false, in_closure: false,
is_breakable: false, is_breakable: false,
allow_statements: true,
level: 0, level: 0,
options, options,
pos, pos,
@ -3761,7 +3789,7 @@ impl Engine {
let mut functions = BTreeMap::new(); let mut functions = BTreeMap::new();
let mut options = self.options; 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"))] #[cfg(not(feature = "no_function"))]
options.remove(LangOptions::ANON_FN); options.remove(LangOptions::ANON_FN);
@ -3773,6 +3801,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
in_closure: false, in_closure: false,
is_breakable: false, is_breakable: false,
allow_statements: false,
level: 0, level: 0,
options, options,
pos: Position::NONE, pos: Position::NONE,
@ -3828,6 +3857,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
in_closure: false, in_closure: false,
is_breakable: false, is_breakable: false,
allow_statements: true,
options: self.options, options: self.options,
level: 0, level: 0,
pos: Position::NONE, pos: Position::NONE,

View File

@ -12,8 +12,47 @@ fn test_expressions() -> Result<(), Box<EvalAltResult>> {
engine.eval_expression_with_scope::<INT>(&mut scope, "2 + (x + 10) * 2")?, engine.eval_expression_with_scope::<INT>(&mut scope, "2 + (x + 10) * 2")?,
42 42
); );
assert_eq!(
engine.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { 123 }")?,
42
);
assert!(engine assert!(engine
.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { 123 }") .eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { let y = 42; y } else { 123 }")
.is_err());
assert!(engine
.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { let y = 123; y }")
.is_err());
assert!(engine
.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else {}")
.is_err());
assert_eq!(
engine.eval_expression_with_scope::<INT>(
&mut scope,
"
switch x {
0 => 1,
1..10 => 123,
10 => 42,
}
"
)?,
42
);
assert!(engine
.eval_expression_with_scope::<INT>(
&mut scope,
"
switch x {
0 => 1,
1..10 => {
let y = 123;
y
}
10 => 42,
}
"
)
.is_err()); .is_err());
assert!(engine.eval_expression::<()>("40 + 2;").is_err()); assert!(engine.eval_expression::<()>("40 + 2;").is_err());