From 9a409b5b4903cdacbd46228fd1638fab2e2927f6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 17 Apr 2021 16:25:39 +0800 Subject: [PATCH 1/5] Simplify script. --- scripts/mat_mul.rhai | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/scripts/mat_mul.rhai b/scripts/mat_mul.rhai index 65743a37..11830ff2 100644 --- a/scripts/mat_mul.rhai +++ b/scripts/mat_mul.rhai @@ -12,7 +12,7 @@ fn new_mat(x, y) { fn mat_gen(n) { let m = new_mat(n, n); - let tmp = 1.0 / n / n; + const tmp = 1.0 / n / n; for i in range(0, n) { for j in range(0, n) { @@ -24,43 +24,36 @@ fn mat_gen(n) { } fn mat_mul(a, b) { - let m = a.len; - let n = a[0].len; - let p = b[0].len; - - let b2 = new_mat(n, p); + let b2 = new_mat(a[0].len, b[0].len); - for i in range(0, n) { - for j in range(0, p) { + for i in range(0, a[0].len) { + for j in range(0, b[0].len) { b2[j][i] = b[i][j]; } } - let c = new_mat(m, p); + let c = new_mat(a.len, b[0].len); for i in range(0, c.len) { - let ci = c[i]; - for j in range(0, ci.len) { - let b2j = b2[j]; - ci[j] = 0.0; + for j in range(0, c[i].len) { + c[i][j] = 0.0; for z in range(0, a[i].len) { let x = a[i][z]; - let y = b2j[z]; - ci[j] += x * y; + let y = b2[j][z]; + c[i][j] += x * y; } } - c[i] = ci; } c } -let now = timestamp(); +const now = timestamp(); -let a = mat_gen(SIZE); -let b = mat_gen(SIZE); -let c = mat_mul(a, b); +const a = mat_gen(SIZE); +const b = mat_gen(SIZE); +const c = mat_mul(a, b); /* for i in range(0, SIZE) { From 1be7e60be2e3ae002a52f068c820ad6bd8a2dd8b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 17 Apr 2021 17:25:35 +0800 Subject: [PATCH 2/5] Automatic global module. --- CHANGELOG.md | 12 ++++++++++++ src/engine.rs | 21 ++++++++++++++++++++- src/fn_call.rs | 4 ++++ tests/functions.rs | 17 ++++++++++++++++- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50328aab..3ede0882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,24 @@ Rhai Release Notes Version 0.20.1 ============== +This version enables functions to access constants declared at global level via the special `global` module. + Breaking changes ---------------- * `Dynamic::is_shared` and `Dynamic::is_locked` are removed under the `no_closure` feature. They used to always return `false`. * `Engine::call_fn` now evaluates the `AST` before calling the function. +Enhancements +------------ + +* The crate [`no-std-compat`](https://crates.io/crates/no_std_compat) is used to compile for `no-std`. This removes the need to use a special `crate::stdlib` namespace for `std` imports. + +New features +------------ + +* A module called `global` is automatically created to hold global-level constants, which can then be accessed from functions. + Version 0.20.0 ============== diff --git a/src/engine.rs b/src/engine.rs index 7541a56a..8196d18d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -52,7 +52,10 @@ pub type Precedence = NonZeroU8; // the module name will live beyond the AST of the eval script text. // The best we can do is a shared reference. #[derive(Clone, Default)] -pub struct Imports(StaticVec, StaticVec>); +pub struct Imports( + smallvec::SmallVec<[Identifier; 8]>, + smallvec::SmallVec<[Shared; 8]>, +); impl Imports { /// Get the length of this stack of imported [modules][Module]. @@ -70,6 +73,12 @@ impl Imports { pub fn get(&self, index: usize) -> Option> { self.1.get(index).cloned() } + /// Get the imported [modules][Module] at a particular index. + #[cfg(not(feature = "no_function"))] + #[inline(always)] + pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared> { + self.1.get_mut(index) + } /// Get the index of an imported [modules][Module] by name. #[inline(always)] pub fn find(&self, name: &str) -> Option { @@ -199,6 +208,8 @@ pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var"; #[cfg(not(feature = "no_function"))] pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn"; pub const KEYWORD_THIS: &str = "this"; +#[cfg(not(feature = "no_function"))] +pub const KEYWORD_GLOBAL: &str = "global"; #[cfg(not(feature = "no_object"))] pub const FN_GET: &str = "get$"; #[cfg(not(feature = "no_object"))] @@ -2504,6 +2515,14 @@ impl Engine { .flatten(); let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { + #[cfg(not(feature = "no_function"))] + if entry_type == AccessMode::ReadOnly { + let global = mods.get_mut(mods.find(KEYWORD_GLOBAL).unwrap()).unwrap(); + let global = Shared::get_mut(global).unwrap(); + global.set_var(name.clone(), value.clone()); + global.build_index(); + } + ( name.to_string().into(), if *export { Some(name.clone()) } else { None }, diff --git a/src/fn_call.rs b/src/fn_call.rs index 2a5c904c..3840c587 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -826,6 +826,10 @@ impl Engine { lib: &[&Module], level: usize, ) -> RhaiResult { + // Create the global module + #[cfg(not(feature = "no_function"))] + mods.push(crate::engine::KEYWORD_GLOBAL, Module::new()); + self.eval_stmt_block(scope, mods, state, lib, &mut None, statements, false, level) .or_else(|err| match *err { EvalAltResult::Return(out, _) => Ok(out), diff --git a/tests/functions.rs b/tests/functions.rs index 05805a9e..1387f3eb 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -62,7 +62,22 @@ fn test_functions_namespaces() -> Result<(), Box> { assert_eq!(engine.eval::("test()")?, 42); #[cfg(not(feature = "no_function"))] - assert_eq!(engine.eval::("fn test() { 123 } test()")?, 123); + { + assert_eq!(engine.eval::("fn test() { 123 } test()")?, 123); + + assert_eq!( + engine.eval::( + r" + const ANSWER = 42; + + fn foo() { global::ANSWER } + + foo() + " + )?, + 42 + ); + } Ok(()) } From 9a8da93145e24666e1676120d791135d4e4eb2e5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 17 Apr 2021 18:10:57 +0800 Subject: [PATCH 3/5] Delay creation of global module until actually needed. --- src/engine.rs | 10 +++++++++- src/fn_call.rs | 4 ---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 8196d18d..1eaca300 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2517,7 +2517,15 @@ impl Engine { let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { #[cfg(not(feature = "no_function"))] if entry_type == AccessMode::ReadOnly { - let global = mods.get_mut(mods.find(KEYWORD_GLOBAL).unwrap()).unwrap(); + let index = if let Some(index) = mods.find(KEYWORD_GLOBAL) { + index + } else { + // Create automatic global module + mods.push(crate::engine::KEYWORD_GLOBAL, Module::new()); + mods.len() - 1 + }; + + let global = mods.get_mut(index).unwrap(); let global = Shared::get_mut(global).unwrap(); global.set_var(name.clone(), value.clone()); global.build_index(); diff --git a/src/fn_call.rs b/src/fn_call.rs index 3840c587..2a5c904c 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -826,10 +826,6 @@ impl Engine { lib: &[&Module], level: usize, ) -> RhaiResult { - // Create the global module - #[cfg(not(feature = "no_function"))] - mods.push(crate::engine::KEYWORD_GLOBAL, Module::new()); - self.eval_stmt_block(scope, mods, state, lib, &mut None, statements, false, level) .or_else(|err| match *err { EvalAltResult::Return(out, _) => Ok(out), From eefdc0935242a6137eb05d1bb2b72ae2e1842c66 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 17 Apr 2021 18:40:16 +0800 Subject: [PATCH 4/5] Allow shadowing of global. --- scripts/function_decl3.rhai | 6 ++++-- src/engine.rs | 43 +++++++++++++++++++++++-------------- src/module/mod.rs | 42 +++++++++++++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/scripts/function_decl3.rhai b/scripts/function_decl3.rhai index 4901f5c9..872b9745 100644 --- a/scripts/function_decl3.rhai +++ b/scripts/function_decl3.rhai @@ -1,9 +1,11 @@ // This script defines a function with many parameters and calls it +const KEY = 38; + fn f(a, b, c, d, e, f) { - a - b * c - d * e - f + a - b * c - d * e - f + global::KEY } -print("f() call should be 4:"); +print("f() call should be 42:"); print(f(100, 5, 2, 9, 6, 32)); diff --git a/src/engine.rs b/src/engine.rs index 1eaca300..c5a24897 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -52,10 +52,7 @@ pub type Precedence = NonZeroU8; // the module name will live beyond the AST of the eval script text. // The best we can do is a shared reference. #[derive(Clone, Default)] -pub struct Imports( - smallvec::SmallVec<[Identifier; 8]>, - smallvec::SmallVec<[Shared; 8]>, -); +pub struct Imports(StaticVec, StaticVec>); impl Imports { /// Get the length of this stack of imported [modules][Module]. @@ -74,7 +71,7 @@ impl Imports { self.1.get(index).cloned() } /// Get the imported [modules][Module] at a particular index. - #[cfg(not(feature = "no_function"))] + #[allow(dead_code)] #[inline(always)] pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared> { self.1.get_mut(index) @@ -907,6 +904,7 @@ impl Engine { disable_doc_comments: false, }; + engine.global_namespace.set_internal(true); engine.register_global_module(StandardPackage::new().as_shared_module()); engine @@ -917,7 +915,7 @@ impl Engine { /// Use [`register_global_module`][Engine::register_global_module] to add packages of functions. #[inline(always)] pub fn new_raw() -> Self { - Self { + let mut engine = Self { global_namespace: Default::default(), global_modules: Default::default(), global_sub_modules: Default::default(), @@ -963,7 +961,11 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] disable_doc_comments: false, - } + }; + + engine.global_namespace.set_internal(true); + + engine } /// Search for a module within an imports stack. @@ -2516,19 +2518,28 @@ impl Engine { let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() { #[cfg(not(feature = "no_function"))] - if entry_type == AccessMode::ReadOnly { - let index = if let Some(index) = mods.find(KEYWORD_GLOBAL) { - index + if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) { + let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) { + let global = mods.get_mut(index).unwrap(); + + if !global.is_internal() { + None + } else { + Some(global) + } } else { // Create automatic global module - mods.push(crate::engine::KEYWORD_GLOBAL, Module::new()); - mods.len() - 1 + let mut global = Module::new(); + global.set_internal(true); + mods.push(crate::engine::KEYWORD_GLOBAL, global); + Some(mods.get_mut(mods.len() - 1).unwrap()) }; - let global = mods.get_mut(index).unwrap(); - let global = Shared::get_mut(global).unwrap(); - global.set_var(name.clone(), value.clone()); - global.build_index(); + if let Some(global) = global { + let global = Shared::get_mut(global).unwrap(); + global.set_var(name.clone(), value.clone()); + global.build_index(); + } } ( diff --git a/src/module/mod.rs b/src/module/mod.rs index 3542d6b3..ed18b507 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -128,6 +128,8 @@ fn calc_native_fn_hash<'a>( pub struct Module { /// ID identifying the module. id: Option, + /// Is this module internal? + internal: bool, /// Sub-modules. modules: BTreeMap>, /// [`Module`] variables. @@ -156,6 +158,7 @@ impl Default for Module { fn default() -> Self { Self { id: None, + internal: false, modules: Default::default(), variables: Default::default(), all_variables: Default::default(), @@ -301,8 +304,45 @@ impl Module { /// assert_eq!(module.id(), Some("hello")); /// ``` #[inline(always)] - pub fn set_id>(&mut self, id: Option) { + pub fn set_id>(&mut self, id: Option) -> &mut Self { self.id = id.map(|s| s.into()); + self + } + + /// Is the [`Module`] internal? + /// + /// # Example + /// + /// ``` + /// use rhai::Module; + /// + /// let mut module = Module::new(); + /// assert!(!module.is_internal()); + /// module.set_internal(true); + /// assert!(module.is_internal()); + /// ``` + #[allow(dead_code)] + #[inline(always)] + pub(crate) fn is_internal(&self) -> bool { + self.internal + } + + /// Set the interal status of the [`Module`]. + /// + /// # Example + /// + /// ``` + /// use rhai::Module; + /// + /// let mut module = Module::new(); + /// assert!(!module.is_internal()); + /// module.set_internal(true); + /// assert!(module.is_internal()); + /// ``` + #[inline(always)] + pub(crate) fn set_internal(&mut self, value: bool) -> &mut Self { + self.internal = value; + self } /// Is the [`Module`] empty? From 90198d5440316140f06c61d2253e113da4225394 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 17 Apr 2021 18:59:20 +0800 Subject: [PATCH 5/5] Fix test. --- src/module/mod.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index ed18b507..7a8881c6 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -310,17 +310,6 @@ impl Module { } /// Is the [`Module`] internal? - /// - /// # Example - /// - /// ``` - /// use rhai::Module; - /// - /// let mut module = Module::new(); - /// assert!(!module.is_internal()); - /// module.set_internal(true); - /// assert!(module.is_internal()); - /// ``` #[allow(dead_code)] #[inline(always)] pub(crate) fn is_internal(&self) -> bool { @@ -328,17 +317,6 @@ impl Module { } /// Set the interal status of the [`Module`]. - /// - /// # Example - /// - /// ``` - /// use rhai::Module; - /// - /// let mut module = Module::new(); - /// assert!(!module.is_internal()); - /// module.set_internal(true); - /// assert!(module.is_internal()); - /// ``` #[inline(always)] pub(crate) fn set_internal(&mut self, value: bool) -> &mut Self { self.internal = value;