Add comments and fix documentation.
This commit is contained in:
parent
e50fcc385f
commit
d1de84fdd2
20
README.md
20
README.md
@ -2127,7 +2127,8 @@ engine.eval_expression_with_scope::<i64>(&scope, "question::inc(question::answer
|
|||||||
|
|
||||||
### Creating a module from an `AST`
|
### 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
|
```rust
|
||||||
use rhai::{Engine, Module};
|
use rhai::{Engine, Module};
|
||||||
@ -2144,26 +2145,33 @@ let ast = engine.compile(r#"
|
|||||||
x + y.len()
|
x + y.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imported modules become sub-modules
|
// Imported modules can become sub-modules
|
||||||
import "another module" as extra;
|
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;
|
const x = 123;
|
||||||
let foo = 41;
|
let foo = 41;
|
||||||
let hello;
|
let hello;
|
||||||
|
|
||||||
// Final variable values become constant module variable values
|
// Variable values become constant module variable values
|
||||||
foo = calc(foo);
|
foo = calc(foo);
|
||||||
hello = "hello, " + foo + " worlds!";
|
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
|
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first
|
||||||
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
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:
|
// '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'
|
// - functions: 'calc', 'add_len'
|
||||||
// - variables: 'x', 'foo', 'hello'
|
// - variables: 'abc' (renamed from 'x'), 'foo', 'hello'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Module resolvers
|
### Module resolvers
|
||||||
|
@ -160,21 +160,15 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
/// Does a certain script-defined function exist in the `State`?
|
/// Does a certain script-defined function exist in the `State`?
|
||||||
pub fn has_function(&self, name: &str, params: usize) -> bool {
|
pub fn has_function(&self, name: &str, params: usize) -> bool {
|
||||||
self.fn_lib.contains_key(&calc_fn_hash(
|
// Qualifiers (none) + function name + placeholders (one for each parameter).
|
||||||
empty(),
|
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
|
||||||
name,
|
self.fn_lib.contains_key(&hash)
|
||||||
repeat(EMPTY_TYPE_ID()).take(params),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
/// Get a script-defined function definition from the `State`.
|
/// Get a script-defined function definition from the `State`.
|
||||||
pub fn get_function(&self, name: &str, params: usize) -> Option<&FnDef> {
|
pub fn get_function(&self, name: &str, params: usize) -> Option<&FnDef> {
|
||||||
self.fn_lib
|
// Qualifiers (none) + function name + placeholders (one for each parameter).
|
||||||
.get(&calc_fn_hash(
|
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
|
||||||
empty(),
|
self.fn_lib.get(&hash).map(|f| f.as_ref())
|
||||||
name,
|
|
||||||
repeat(EMPTY_TYPE_ID()).take(params),
|
|
||||||
))
|
|
||||||
.map(|f| f.as_ref())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,20 +201,21 @@ impl FunctionsLib {
|
|||||||
pub fn from_vec(vec: Vec<FnDef>) -> Self {
|
pub fn from_vec(vec: Vec<FnDef>) -> Self {
|
||||||
FunctionsLib(
|
FunctionsLib(
|
||||||
vec.into_iter()
|
vec.into_iter()
|
||||||
.map(|f| {
|
.map(|fn_def| {
|
||||||
|
// Qualifiers (none) + function name + placeholders (one for each parameter).
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
empty(),
|
empty(),
|
||||||
&f.name,
|
&fn_def.name,
|
||||||
repeat(EMPTY_TYPE_ID()).take(f.params.len()),
|
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
{
|
{
|
||||||
(hash, Arc::new(f))
|
(hash, Arc::new(fn_def))
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
{
|
||||||
(hash, Rc::new(f))
|
(hash, Rc::new(fn_def))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -240,6 +235,7 @@ impl FunctionsLib {
|
|||||||
}
|
}
|
||||||
/// Get a function definition from the `FunctionsLib`.
|
/// Get a function definition from the `FunctionsLib`.
|
||||||
pub fn get_function_by_signature(&self, name: &str, params: usize) -> Option<&FnDef> {
|
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));
|
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
|
||||||
self.get_function(hash)
|
self.get_function(hash)
|
||||||
}
|
}
|
||||||
@ -582,6 +578,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search built-in's and external functions
|
// 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()));
|
let fn_spec = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
if let Some(func) = self
|
if let Some(func) = self
|
||||||
@ -731,6 +728,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
fn has_override(&self, state: &State, name: &str) -> bool {
|
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>()));
|
let hash = calc_fn_hash(empty(), name, once(TypeId::of::<String>()));
|
||||||
|
|
||||||
// First check registered functions
|
// First check registered functions
|
||||||
@ -1386,10 +1384,18 @@ impl Engine {
|
|||||||
self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
|
self.call_script_fn(None, state, fn_def, &mut args, *pos, level)
|
||||||
} else {
|
} else {
|
||||||
// Then search in Rust functions
|
// 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),
|
Ok(func) => func(&mut args, *pos),
|
||||||
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
|
@ -618,41 +618,51 @@ impl Module {
|
|||||||
|
|
||||||
/// Scan through all the sub-modules in the `Module` build an index of all
|
/// Scan through all the sub-modules in the `Module` build an index of all
|
||||||
/// variables and external Rust functions via hashing.
|
/// 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.
|
// Collect a particular module.
|
||||||
fn collect<'a>(
|
fn index_module<'a>(
|
||||||
module: &'a mut Module,
|
module: &'a mut Module,
|
||||||
names: &mut Vec<&'a str>,
|
qualifiers: &mut Vec<&'a str>,
|
||||||
variables: &mut Vec<(u64, Dynamic)>,
|
variables: &mut Vec<(u64, Dynamic)>,
|
||||||
functions: &mut Vec<(u64, NativeFunction)>,
|
functions: &mut Vec<(u64, NativeFunction)>,
|
||||||
fn_lib: &mut Vec<(u64, ScriptedFunction)>,
|
fn_lib: &mut Vec<(u64, ScriptedFunction)>,
|
||||||
) {
|
) {
|
||||||
for (n, m) in module.modules.iter_mut() {
|
for (name, m) in module.modules.iter_mut() {
|
||||||
// Collect all the sub-modules first.
|
// Index all the sub-modules first.
|
||||||
names.push(n);
|
qualifiers.push(name);
|
||||||
collect(m, names, variables, functions, fn_lib);
|
index_module(m, qualifiers, variables, functions, fn_lib);
|
||||||
names.pop();
|
qualifiers.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect all variables
|
// Index all variables
|
||||||
for (var_name, value) in module.variables.iter() {
|
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()));
|
variables.push((hash, value.clone()));
|
||||||
}
|
}
|
||||||
// Collect all Rust functions
|
// Index all Rust functions
|
||||||
for (fn_name, params, func) in module.functions.values() {
|
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(
|
let hash1 = calc_fn_hash(
|
||||||
names.iter().map(|v| *v),
|
qualifiers.iter().map(|v| *v),
|
||||||
fn_name,
|
fn_name,
|
||||||
repeat(EMPTY_TYPE_ID()).take(params.len()),
|
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());
|
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() {
|
for fn_def in module.fn_lib.values() {
|
||||||
|
// Qualifiers + function name + placeholders (one for each parameter)
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
names.iter().map(|v| *v),
|
qualifiers.iter().map(|v| *v),
|
||||||
&fn_def.name,
|
&fn_def.name,
|
||||||
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
|
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
|
||||||
);
|
);
|
||||||
@ -664,7 +674,7 @@ impl Module {
|
|||||||
let mut functions = Vec::new();
|
let mut functions = Vec::new();
|
||||||
let mut fn_lib = Vec::new();
|
let mut fn_lib = Vec::new();
|
||||||
|
|
||||||
collect(
|
index_module(
|
||||||
self,
|
self,
|
||||||
&mut vec!["root"],
|
&mut vec!["root"],
|
||||||
&mut variables,
|
&mut variables,
|
||||||
|
@ -716,9 +716,16 @@ fn parse_call_expr<'a>(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
// Calculate hash
|
// Rust functions are indexed in two steps:
|
||||||
let hash = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
modules.set_key(hash);
|
// 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));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -745,13 +752,20 @@ fn parse_call_expr<'a>(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
if let Some(modules) = modules.as_mut() {
|
if let Some(modules) = modules.as_mut() {
|
||||||
// Calculate hash
|
// Rust functions are indexed in two steps:
|
||||||
let hash = calc_fn_hash(
|
// 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()),
|
modules.iter().map(|(m, _)| m.as_str()),
|
||||||
&id,
|
&id,
|
||||||
repeat(EMPTY_TYPE_ID()).take(args.len()),
|
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));
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1176,9 +1190,10 @@ fn parse_primary<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match &mut root_expr {
|
match &mut root_expr {
|
||||||
// Calculate hash key for module-qualified variables
|
// Cache the hash key for module-qualified variables
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::Variable(id, Some(modules), _, _) => {
|
Expr::Variable(id, Some(modules), _, _) => {
|
||||||
|
// Qualifiers + variable name
|
||||||
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
||||||
modules.set_key(hash);
|
modules.set_key(hash);
|
||||||
modules.set_index(stack.find_module(&modules.get(0).0));
|
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() {
|
if let (Token::Fn, _) = input.peek().unwrap() {
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
let f = parse_fn(input, &mut stack, true)?;
|
let func = parse_fn(input, &mut stack, true)?;
|
||||||
functions.insert(
|
|
||||||
calc_fn_hash(
|
// Qualifiers (none) + function name + argument `TypeId`'s
|
||||||
|
let hash = calc_fn_hash(
|
||||||
empty(),
|
empty(),
|
||||||
&f.name,
|
&func.name,
|
||||||
repeat(EMPTY_TYPE_ID()).take(f.params.len()),
|
repeat(EMPTY_TYPE_ID()).take(func.params.len()),
|
||||||
),
|
|
||||||
f,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
functions.insert(hash, func);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// Modules are used for accessing member variables, functions and plugins under a namespace.
|
/// Modules are used for accessing member variables, functions and plugins under a namespace.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, mut value: 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(
|
self.push_dynamic_value(
|
||||||
name,
|
name,
|
||||||
|
@ -115,7 +115,11 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
|||||||
foo = calc(foo);
|
foo = calc(foo);
|
||||||
hello = "hello, " + foo + " worlds!";
|
hello = "hello, " + foo + " worlds!";
|
||||||
|
|
||||||
export x as abc, foo, hello, extra as foobar;
|
export
|
||||||
|
x as abc,
|
||||||
|
foo,
|
||||||
|
hello,
|
||||||
|
extra as foobar;
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user