Add comments and fix documentation.

This commit is contained in:
Stephen Chung 2020-05-09 10:00:59 +08:00
parent e50fcc385f
commit d1de84fdd2
6 changed files with 103 additions and 59 deletions

View File

@ -2127,7 +2127,8 @@ engine.eval_expression_with_scope::<i64>(&scope, "question::inc(question::answer
### Creating a module from an `AST`
It is easy to convert a pre-compiled `AST` into a module, just use `Module::eval_ast_as_new`:
It is easy to convert a pre-compiled `AST` into a module, just use `Module::eval_ast_as_new`.
Don't forget the `export` statement, otherwise there will be nothing inside the module!
```rust
use rhai::{Engine, Module};
@ -2144,26 +2145,33 @@ let ast = engine.compile(r#"
x + y.len()
}
// Imported modules become sub-modules
// Imported modules can become sub-modules
import "another module" as extra;
// Variables defined at global level become module variables
// Variables defined at global level can become module variables
const x = 123;
let foo = 41;
let hello;
// Final variable values become constant module variable values
// Variable values become constant module variable values
foo = calc(foo);
hello = "hello, " + foo + " worlds!";
// Finally, export the variables and modules
export
x as abc, // aliased variable name
foo,
hello,
extra as foobar; // export sub-module
"#)?;
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
// 'module' now can be loaded into a custom 'Scope' for future use. It contains:
// - sub-module: 'extra'
// - sub-module: 'foobar' (renamed from 'extra')
// - functions: 'calc', 'add_len'
// - variables: 'x', 'foo', 'hello'
// - variables: 'abc' (renamed from 'x'), 'foo', 'hello'
```
### Module resolvers

View File

@ -160,21 +160,15 @@ impl<'a> State<'a> {
}
/// Does a certain script-defined function exist in the `State`?
pub fn has_function(&self, name: &str, params: usize) -> bool {
self.fn_lib.contains_key(&calc_fn_hash(
empty(),
name,
repeat(EMPTY_TYPE_ID()).take(params),
))
// Qualifiers (none) + function name + placeholders (one for each parameter).
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
self.fn_lib.contains_key(&hash)
}
/// Get a script-defined function definition from the `State`.
pub fn get_function(&self, name: &str, params: usize) -> Option<&FnDef> {
self.fn_lib
.get(&calc_fn_hash(
empty(),
name,
repeat(EMPTY_TYPE_ID()).take(params),
))
.map(|f| f.as_ref())
// Qualifiers (none) + function name + placeholders (one for each parameter).
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
self.fn_lib.get(&hash).map(|f| f.as_ref())
}
}
@ -207,20 +201,21 @@ impl FunctionsLib {
pub fn from_vec(vec: Vec<FnDef>) -> Self {
FunctionsLib(
vec.into_iter()
.map(|f| {
.map(|fn_def| {
// Qualifiers (none) + function name + placeholders (one for each parameter).
let hash = calc_fn_hash(
empty(),
&f.name,
repeat(EMPTY_TYPE_ID()).take(f.params.len()),
&fn_def.name,
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
);
#[cfg(feature = "sync")]
{
(hash, Arc::new(f))
(hash, Arc::new(fn_def))
}
#[cfg(not(feature = "sync"))]
{
(hash, Rc::new(f))
(hash, Rc::new(fn_def))
}
})
.collect(),
@ -240,6 +235,7 @@ impl FunctionsLib {
}
/// Get a function definition from the `FunctionsLib`.
pub fn get_function_by_signature(&self, name: &str, params: usize) -> Option<&FnDef> {
// Qualifiers (none) + function name + placeholders (one for each parameter).
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
self.get_function(hash)
}
@ -582,6 +578,7 @@ impl Engine {
}
// Search built-in's and external functions
// Qualifiers (none) + function name + argument `TypeId`'s.
let fn_spec = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
if let Some(func) = self
@ -731,6 +728,7 @@ impl Engine {
// Has a system function an override?
fn has_override(&self, state: &State, name: &str) -> bool {
// Qualifiers (none) + function name + argument `TypeId`'s.
let hash = calc_fn_hash(empty(), name, once(TypeId::of::<String>()));
// First check registered functions
@ -1386,10 +1384,18 @@ impl Engine {
self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
} else {
// Then search in Rust functions
let hash1 = modules.key();
let hash2 = calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id()));
match module.get_qualified_fn(fn_name, hash1 ^ hash2, *pos) {
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
let hash1 = modules.key();
// 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s
let hash2 = calc_fn_hash(empty(), "", args.iter().map(|a| a.type_id()));
// 3) The final hash is the XOR of the two hashes.
let hash = hash1 ^ hash2;
match module.get_qualified_fn(fn_name, hash, *pos) {
Ok(func) => func(&mut args, *pos),
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
Err(err) => Err(err),

View File

@ -618,41 +618,51 @@ impl Module {
/// Scan through all the sub-modules in the `Module` build an index of all
/// variables and external Rust functions via hashing.
pub(crate) fn collect_all_sub_modules(&mut self) {
pub(crate) fn index_all_sub_modules(&mut self) {
// Collect a particular module.
fn collect<'a>(
fn index_module<'a>(
module: &'a mut Module,
names: &mut Vec<&'a str>,
qualifiers: &mut Vec<&'a str>,
variables: &mut Vec<(u64, Dynamic)>,
functions: &mut Vec<(u64, NativeFunction)>,
fn_lib: &mut Vec<(u64, ScriptedFunction)>,
) {
for (n, m) in module.modules.iter_mut() {
// Collect all the sub-modules first.
names.push(n);
collect(m, names, variables, functions, fn_lib);
names.pop();
for (name, m) in module.modules.iter_mut() {
// Index all the sub-modules first.
qualifiers.push(name);
index_module(m, qualifiers, variables, functions, fn_lib);
qualifiers.pop();
}
// Collect all variables
// Index all variables
for (var_name, value) in module.variables.iter() {
let hash = calc_fn_hash(names.iter().map(|v| *v), var_name, empty());
// Qualifiers + variable name
let hash = calc_fn_hash(qualifiers.iter().map(|v| *v), var_name, empty());
variables.push((hash, value.clone()));
}
// Collect all Rust functions
// Index all Rust functions
for (fn_name, params, func) in module.functions.values() {
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
let hash1 = calc_fn_hash(
names.iter().map(|v| *v),
qualifiers.iter().map(|v| *v),
fn_name,
repeat(EMPTY_TYPE_ID()).take(params.len()),
);
// 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s
let hash2 = calc_fn_hash(empty(), "", params.iter().cloned());
functions.push((hash1 ^ hash2, func.clone()));
// 3) The final hash is the XOR of the two hashes.
let hash = hash1 ^ hash2;
functions.push((hash, func.clone()));
}
// Collect all script-defined functions
// Index all script-defined functions
for fn_def in module.fn_lib.values() {
// Qualifiers + function name + placeholders (one for each parameter)
let hash = calc_fn_hash(
names.iter().map(|v| *v),
qualifiers.iter().map(|v| *v),
&fn_def.name,
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
);
@ -664,7 +674,7 @@ impl Module {
let mut functions = Vec::new();
let mut fn_lib = Vec::new();
collect(
index_module(
self,
&mut vec!["root"],
&mut variables,

View File

@ -716,9 +716,16 @@ fn parse_call_expr<'a>(
#[cfg(not(feature = "no_module"))]
{
if let Some(modules) = modules.as_mut() {
// Calculate hash
let hash = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
modules.set_key(hash);
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
let hash1 = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
// 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s
// 3) The final hash is the XOR of the two hashes.
// Cache the first hash
modules.set_key(hash1);
modules.set_index(stack.find_module(&modules.get(0).0));
}
}
@ -745,13 +752,20 @@ fn parse_call_expr<'a>(
#[cfg(not(feature = "no_module"))]
{
if let Some(modules) = modules.as_mut() {
// Calculate hash
let hash = calc_fn_hash(
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
let hash1 = calc_fn_hash(
modules.iter().map(|(m, _)| m.as_str()),
&id,
repeat(EMPTY_TYPE_ID()).take(args.len()),
);
modules.set_key(hash);
// 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s
// 3) The final hash is the XOR of the two hashes.
// Cache the first hash
modules.set_key(hash1);
modules.set_index(stack.find_module(&modules.get(0).0));
}
}
@ -1176,9 +1190,10 @@ fn parse_primary<'a>(
}
match &mut root_expr {
// Calculate hash key for module-qualified variables
// Cache the hash key for module-qualified variables
#[cfg(not(feature = "no_module"))]
Expr::Variable(id, Some(modules), _, _) => {
// Qualifiers + variable name
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
modules.set_key(hash);
modules.set_index(stack.find_module(&modules.get(0).0));
@ -2317,15 +2332,16 @@ fn parse_global_level<'a>(
{
if let (Token::Fn, _) = input.peek().unwrap() {
let mut stack = Stack::new();
let f = parse_fn(input, &mut stack, true)?;
functions.insert(
calc_fn_hash(
empty(),
&f.name,
repeat(EMPTY_TYPE_ID()).take(f.params.len()),
),
f,
let func = parse_fn(input, &mut stack, true)?;
// Qualifiers (none) + function name + argument `TypeId`'s
let hash = calc_fn_hash(
empty(),
&func.name,
repeat(EMPTY_TYPE_ID()).take(func.params.len()),
);
functions.insert(hash, func);
continue;
}
}

View File

@ -178,7 +178,7 @@ impl<'a> Scope<'a> {
/// Modules are used for accessing member variables, functions and plugins under a namespace.
#[cfg(not(feature = "no_module"))]
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, mut value: Module) {
value.collect_all_sub_modules();
value.index_all_sub_modules();
self.push_dynamic_value(
name,

View File

@ -115,7 +115,11 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
foo = calc(foo);
hello = "hello, " + foo + " worlds!";
export x as abc, foo, hello, extra as foobar;
export
x as abc,
foo,
hello,
extra as foobar;
"#,
)?;