From 0ece75aba364226d531c2f4f6409c598f01b2f30 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 22 Aug 2020 22:44:24 +0800 Subject: [PATCH] Allow module access in closures. --- Cargo.toml | 2 +- RELEASES.md | 3 ++- src/parser.rs | 15 +++++++++++++++ tests/call_fn.rs | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index df785e3e..25305837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai" -version = "0.19.0" +version = "0.18.3" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index 6ed51c4a..db26c661 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,13 +1,14 @@ Rhai Release Notes ================== -Version 0.19.0 +Version 0.18.3 ============== Bug fixes --------- * `Engine::compile_expression`, `Engine::eval_expression` etc. no longer parse anonymous functions and closures. +* Imported modules now work inside closures. Version 0.18.2 diff --git a/src/parser.rs b/src/parser.rs index 39ffaada..01be7715 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1645,11 +1645,22 @@ fn parse_primary( } Expr::Variable(Box::new(((s, settings.pos), None, 0, None))) } + // Module qualification + #[cfg(not(feature = "no_module"))] + Token::Identifier(s) if *next_token == Token::DoubleColon => { + // Once the identifier consumed we must enable next variables capturing + #[cfg(not(feature = "no_closure"))] + { + state.allow_capture = true; + } + Expr::Variable(Box::new(((s, settings.pos), None, 0, None))) + } // Normal variable access Token::Identifier(s) => { let index = state.access_var(&s, settings.pos); Expr::Variable(Box::new(((s, settings.pos), None, 0, index))) } + // Function call is allowed to have reserved keyword Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { if is_keyword_function(&s) { @@ -1658,6 +1669,7 @@ fn parse_primary( return Err(PERR::Reserved(s).into_err(settings.pos)); } } + // Access to `this` as a variable is OK Token::Reserved(s) if s == KEYWORD_THIS && *next_token != Token::LeftParen => { if !settings.is_function_scope { @@ -1669,9 +1681,11 @@ fn parse_primary( Expr::Variable(Box::new(((s, settings.pos), None, 0, None))) } } + Token::Reserved(s) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(settings.pos)); } + Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?, #[cfg(not(feature = "no_index"))] Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?, @@ -1680,6 +1694,7 @@ fn parse_primary( Token::True => Expr::True(settings.pos), Token::False => Expr::False(settings.pos), Token::LexError(err) => return Err(err.into_err(settings.pos)), + _ => { return Err( PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos) diff --git a/tests/call_fn.rs b/tests/call_fn.rs index a7a5d514..5d22abd1 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -120,6 +120,20 @@ fn test_fn_ptr_raw() -> Result<(), Box> { 42 ); + assert_eq!( + engine.eval::( + r#" + fn foo(x, y) { this += x + y; } + + let x = 40; + let v = 1; + x.bar(Fn("foo").curry(v), 1); + x + "# + )?, + 42 + ); + assert!(matches!( *engine.eval::( r#"