diff --git a/CHANGELOG.md b/CHANGELOG.md index 75cea982..aec63d31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Bug fixes New features ------------ -* New options for `Engine` which allows disabling `if`-expressions, `switch`-expressions, statement expressions and/or anonymous functions. +* New options for `Engine` which allows disabling `if`-expressions, `switch`-expressions, statement expressions, anonymous functions and/or looping (i.e. `while`, `loop`, `do` and `for` statements). Enhancements ------------ diff --git a/src/api/options.rs b/src/api/options.rs index 24785236..0a8f725a 100644 --- a/src/api/options.rs +++ b/src/api/options.rs @@ -16,6 +16,8 @@ pub struct LanguageOptions { /// Is anonymous function allowed? #[cfg(not(feature = "no_function"))] pub allow_anonymous_fn: bool, + /// Is looping allowed? + pub allow_loop: bool, } impl LanguageOptions { @@ -26,7 +28,9 @@ impl LanguageOptions { allow_if_expr: true, allow_switch_expr: true, allow_stmt_expr: true, + #[cfg(not(feature = "no_function"))] allow_anonymous_fn: true, + allow_loop: true, } } } @@ -84,4 +88,14 @@ impl Engine { pub fn set_allow_anonymous_fn(&mut self, enable: bool) { self.options.allow_anonymous_fn = enable; } + /// Is looping allowed? + #[inline(always)] + pub fn allow_looping(&self) -> bool { + self.options.allow_loop + } + /// Set whether looping is allowed. + #[inline(always)] + pub fn set_allow_looping(&mut self, enable: bool) { + self.options.allow_loop = enable; + } } diff --git a/src/parser.rs b/src/parser.rs index ce819648..3388ded2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -239,6 +239,8 @@ struct ParseSettings { allow_switch_expr: bool, /// Is statement-expression allowed? allow_stmt_expr: bool, + /// Is looping allowed? + allow_loop: bool, /// Current expression nesting level. level: usize, } @@ -1174,6 +1176,7 @@ fn parse_primary( allow_switch_expr: settings.default_options.allow_switch_expr, allow_stmt_expr: settings.default_options.allow_stmt_expr, allow_anonymous_fn: settings.default_options.allow_anonymous_fn, + allow_loop: settings.default_options.allow_loop, is_global: false, is_function_scope: true, is_breakable: false, @@ -2802,6 +2805,7 @@ fn parse_stmt( allow_switch_expr: settings.default_options.allow_switch_expr, allow_stmt_expr: settings.default_options.allow_stmt_expr, allow_anonymous_fn: settings.default_options.allow_anonymous_fn, + allow_loop: settings.default_options.allow_loop, is_global: false, is_function_scope: true, is_breakable: false, @@ -2846,19 +2850,23 @@ fn parse_stmt( Token::If => parse_if(input, state, lib, settings.level_up()), Token::Switch => parse_switch(input, state, lib, settings.level_up()), - Token::While | Token::Loop => parse_while_loop(input, state, lib, settings.level_up()), - Token::Do => parse_do(input, state, lib, settings.level_up()), - Token::For => parse_for(input, state, lib, settings.level_up()), + Token::While | Token::Loop if settings.allow_loop => { + parse_while_loop(input, state, lib, settings.level_up()) + } + Token::Do if settings.allow_loop => parse_do(input, state, lib, settings.level_up()), + Token::For if settings.allow_loop => parse_for(input, state, lib, settings.level_up()), - Token::Continue if settings.is_breakable => { + Token::Continue if settings.allow_loop && settings.is_breakable => { let pos = eat_token(input, Token::Continue); Ok(Stmt::BreakLoop(AST_OPTION_NONE, pos)) } - Token::Break if settings.is_breakable => { + Token::Break if settings.allow_loop && settings.is_breakable => { let pos = eat_token(input, Token::Break); Ok(Stmt::BreakLoop(AST_OPTION_BREAK_OUT, pos)) } - Token::Continue | Token::Break => Err(PERR::LoopBreak.into_err(token_pos)), + Token::Continue | Token::Break if settings.allow_loop => { + Err(PERR::LoopBreak.into_err(token_pos)) + } Token::Return | Token::Throw => { let (return_type, token_pos) = input @@ -3242,6 +3250,7 @@ impl Engine { allow_stmt_expr: false, #[cfg(not(feature = "no_function"))] allow_anonymous_fn: false, + allow_loop: false, is_global: true, #[cfg(not(feature = "no_function"))] is_function_scope: false, @@ -3299,6 +3308,7 @@ impl Engine { allow_stmt_expr: self.options.allow_stmt_expr, #[cfg(not(feature = "no_function"))] allow_anonymous_fn: self.options.allow_anonymous_fn, + allow_loop: self.options.allow_loop, is_global: true, #[cfg(not(feature = "no_function"))] is_function_scope: false, diff --git a/tests/options.rs b/tests/options.rs index 26de92ab..09a79a3c 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -22,5 +22,11 @@ fn test_options() -> Result<(), Box> { assert!(engine.compile("let x = || 42;").is_err()); + engine.compile("while x > y { foo(z); }")?; + + engine.set_allow_looping(false); + + assert!(engine.compile("while x > y { foo(z); }").is_err()); + Ok(()) }