Pack Imports.

This commit is contained in:
Stephen Chung 2020-11-01 23:42:00 +08:00
parent 717e8e7eee
commit b07a2aa79c
3 changed files with 73 additions and 22 deletions

View File

@ -25,6 +25,14 @@ import "crypto_init"; // run the script file 'crypto_init.rhai' withou
import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock' import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock'
const SECRET_NUMBER = 42;
let mod_file = "crypto_" + SECRET_NUMBER;
import mod_file as my_mod; // load the script file "crypto_42.rhai" and import it as a module named 'my_mod'
// notice that module path names can be dynamically constructed!
// any expression that evaluates to a string is acceptable after the 'import' keyword
lock::encrypt(secret); // use functions defined under the module via '::' lock::encrypt(secret); // use functions defined under the module via '::'
lock::hash::sha256(key); // sub-modules are also supported lock::hash::sha256(key); // sub-modules are also supported

View File

@ -69,10 +69,58 @@ pub type Map = HashMap<ImmutableString, Dynamic>;
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
// //
// Note - We cannot use &str or Cow<str> here because `eval` may load a module // # Implementation Notes
// and the module name will live beyond the AST of the eval script text. //
// The best we can do is a shared reference. // We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
pub type Imports = Vec<(ImmutableString, Module)>; // the AST of the eval script text. The best we can do is a shared reference.
//
// `Imports` is implemented as two `Vec`'s of exactly the same length. That's because a `Module` is large,
// so packing the import names together improves cache locality.
#[derive(Debug, Clone, Default)]
pub struct Imports(StaticVec<ImmutableString>, StaticVec<Module>);
impl Imports {
/// Get the length of this stack of imported modules.
pub fn len(&self) -> usize {
self.0.len()
}
/// Get the imported module at a particular index.
pub fn get(&self, index: usize) -> Option<&Module> {
self.1.get(index)
}
/// Get a mutable reference to the imported module at a particular index.
pub fn get_mut(&mut self, index: usize) -> Option<&mut Module> {
self.1.get_mut(index)
}
/// Get the index of an imported module by name.
pub fn find(&self, name: &str) -> Option<usize> {
self.0
.iter()
.enumerate()
.rev()
.find(|(_, key)| key.as_str() == name)
.map(|(index, _)| index)
}
/// Push an imported module onto the stack.
pub fn push(&mut self, name: impl Into<ImmutableString>, module: Module) {
self.0.push(name.into());
self.1.push(module);
}
/// Truncate the stack of imported modules to a particular length.
pub fn truncate(&mut self, size: usize) {
self.0.truncate(size);
self.1.truncate(size);
}
/// Get an iterator to this stack of imported modules.
#[allow(dead_code)]
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.0.iter().map(|name| name.as_str()).zip(self.1.iter())
}
/// Get a consuming iterator to this stack of imported modules.
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Module)> {
self.0.into_iter().zip(self.1.into_iter())
}
}
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -602,12 +650,10 @@ pub fn search_imports<'s>(
Ok(if index > 0 { Ok(if index > 0 {
let offset = mods.len() - index; let offset = mods.len() - index;
&mods.get(offset).unwrap().1 mods.get(offset).expect("invalid index in Imports")
} else { } else {
mods.iter() mods.find(root)
.rev() .map(|n| mods.get(n).expect("invalid index in Imports"))
.find(|(n, _)| n == root)
.map(|(_, m)| m)
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))? .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
}) })
} }
@ -630,13 +676,14 @@ pub fn search_imports_mut<'s>(
Ok(if index > 0 { Ok(if index > 0 {
let offset = mods.len() - index; let offset = mods.len() - index;
&mut mods.get_mut(offset).unwrap().1 mods.get_mut(offset).expect("invalid index in Imports")
} else { } else {
mods.iter_mut() if let Some(n) = mods.find(root) {
.rev() mods.get_mut(n)
.find(|(n, _)| n == root) } else {
.map(|(_, m)| m) None
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))? }
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
}) })
} }
@ -2092,7 +2139,7 @@ impl Engine {
if let Some(name_def) = alias { if let Some(name_def) = alias {
module.index_all_sub_modules(); module.index_all_sub_modules();
mods.push((name_def.name.clone(), module)); mods.push(name_def.name.clone(), module);
} }
state.modules += 1; state.modules += 1;

View File

@ -13,11 +13,7 @@ use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
use crate::{ast::ScriptFnDef, fn_native::Shared}; use crate::{ast::ScriptFnDef, fn_native::Shared};
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::{ use crate::{ast::AST, engine::Engine, scope::Scope};
ast::AST,
engine::{Engine, Imports},
scope::Scope,
};
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET}; use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
@ -1340,7 +1336,7 @@ impl Module {
ast: &AST, ast: &AST,
engine: &Engine, engine: &Engine,
) -> Result<Self, Box<EvalAltResult>> { ) -> Result<Self, Box<EvalAltResult>> {
let mut mods = Imports::new(); let mut mods = Default::default();
// Run the script // Run the script
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?; engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;