Implement module-qualified functions.
This commit is contained in:
parent
38e717a838
commit
aae9e43109
@ -395,7 +395,7 @@ fn search_scope<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
module.get_qualified_variable_mut(name, modules.as_ref(), pos)?,
|
module.get_qualified_var_mut(name, modules.as_ref(), pos)?,
|
||||||
// Module variables are constant
|
// Module variables are constant
|
||||||
ScopeEntryType::Constant,
|
ScopeEntryType::Constant,
|
||||||
))
|
))
|
||||||
@ -469,13 +469,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Universal method for calling functions either registered with the `Engine` or written in Rhai
|
/// Universal method for calling functions either registered with the `Engine` or written in Rhai
|
||||||
// TODO - handle moduled function call
|
|
||||||
pub(crate) fn call_fn_raw(
|
pub(crate) fn call_fn_raw(
|
||||||
&self,
|
&self,
|
||||||
scope: Option<&mut Scope>,
|
scope: Option<&mut Scope>,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
modules: &ModuleRef,
|
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -643,7 +641,6 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
fn_lib: &FunctionsLib,
|
fn_lib: &FunctionsLib,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
modules: &ModuleRef,
|
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -651,28 +648,19 @@ impl Engine {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
match fn_name {
|
match fn_name {
|
||||||
// type_of
|
// type_of
|
||||||
KEYWORD_TYPE_OF
|
KEYWORD_TYPE_OF if args.len() == 1 && !self.has_override(fn_lib, KEYWORD_TYPE_OF) => {
|
||||||
if modules.is_none()
|
|
||||||
&& args.len() == 1
|
|
||||||
&& !self.has_override(fn_lib, KEYWORD_TYPE_OF) =>
|
|
||||||
{
|
|
||||||
Ok(self.map_type_name(args[0].type_name()).to_string().into())
|
Ok(self.map_type_name(args[0].type_name()).to_string().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// eval - reaching this point it must be a method-style call
|
// eval - reaching this point it must be a method-style call
|
||||||
KEYWORD_EVAL
|
KEYWORD_EVAL if args.len() == 1 && !self.has_override(fn_lib, KEYWORD_EVAL) => {
|
||||||
if modules.is_none()
|
|
||||||
&& args.len() == 1
|
|
||||||
&& !self.has_override(fn_lib, KEYWORD_EVAL) =>
|
|
||||||
{
|
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
"'eval' should not be called in method style. Try eval(...);".into(),
|
"'eval' should not be called in method style. Try eval(...);".into(),
|
||||||
pos,
|
pos,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal method call
|
// Normal method call
|
||||||
_ => self.call_fn_raw(None, fn_lib, fn_name, modules, args, def_val, pos, level),
|
_ => self.call_fn_raw(None, fn_lib, fn_name, args, def_val, pos, level),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,7 +755,7 @@ impl Engine {
|
|||||||
let def_val = def_val.as_deref();
|
let def_val = def_val.as_deref();
|
||||||
// A function call is assumed to have side effects, so the value is changed
|
// A function call is assumed to have side effects, so the value is changed
|
||||||
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut
|
// TODO - Remove assumption of side effects by checking whether the first parameter is &mut
|
||||||
self.exec_fn_call(fn_lib, fn_name, &None, &mut args, def_val, *pos, 0).map(|v| (v, true))
|
self.exec_fn_call(fn_lib, fn_name, &mut args, def_val, *pos, 0).map(|v| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.module::fn_name(...) - syntax error
|
// xxx.module::fn_name(...) - syntax error
|
||||||
Expr::FnCall(_,_,_,_,_) => unreachable!(),
|
Expr::FnCall(_,_,_,_,_) => unreachable!(),
|
||||||
@ -788,13 +776,13 @@ impl Engine {
|
|||||||
Expr::Property(id, pos) if new_val.is_some() => {
|
Expr::Property(id, pos) if new_val.is_some() => {
|
||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
let mut args = [obj, new_val.as_mut().unwrap()];
|
let mut args = [obj, new_val.as_mut().unwrap()];
|
||||||
self.exec_fn_call(fn_lib, &fn_name, &None, &mut args, None, *pos, 0).map(|v| (v, true))
|
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(id, pos) => {
|
Expr::Property(id, pos) => {
|
||||||
let fn_name = make_getter(id);
|
let fn_name = make_getter(id);
|
||||||
let mut args = [obj];
|
let mut args = [obj];
|
||||||
self.exec_fn_call(fn_lib, &fn_name, &None, &mut args, None, *pos, 0).map(|v| (v, false))
|
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).map(|v| (v, false))
|
||||||
}
|
}
|
||||||
// {xxx:map}.idx_lhs[idx_expr]
|
// {xxx:map}.idx_lhs[idx_expr]
|
||||||
Expr::Index(dot_lhs, dot_rhs, pos) |
|
Expr::Index(dot_lhs, dot_rhs, pos) |
|
||||||
@ -824,7 +812,7 @@ impl Engine {
|
|||||||
|
|
||||||
let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() {
|
let indexed_val = &mut (if let Expr::Property(id, pos) = dot_lhs.as_ref() {
|
||||||
let fn_name = make_getter(id);
|
let fn_name = make_getter(id);
|
||||||
self.exec_fn_call(fn_lib, &fn_name, &None, &mut args[..1], None, *pos, 0)?
|
self.exec_fn_call(fn_lib, &fn_name, &mut args[..1], None, *pos, 0)?
|
||||||
} else {
|
} else {
|
||||||
// Syntax error
|
// Syntax error
|
||||||
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
||||||
@ -842,7 +830,7 @@ impl Engine {
|
|||||||
let fn_name = make_setter(id);
|
let fn_name = make_setter(id);
|
||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
args[1] = indexed_val;
|
args[1] = indexed_val;
|
||||||
self.exec_fn_call(fn_lib, &fn_name, &None, &mut args, None, *pos, 0).or_else(|err| match *err {
|
self.exec_fn_call(fn_lib, &fn_name, &mut args, None, *pos, 0).or_else(|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()),
|
EvalAltResult::ErrorDotExpr(_,_) => Ok(Default::default()),
|
||||||
err => Err(Box::new(err))
|
err => Err(Box::new(err))
|
||||||
@ -1073,7 +1061,7 @@ impl Engine {
|
|||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.call_fn_raw(None, fn_lib, "==", &None, args, def_value, pos, level)?
|
.call_fn_raw(None, fn_lib, "==", args, def_value, pos, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1213,12 +1201,26 @@ impl Engine {
|
|||||||
|
|
||||||
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
// eval - only in function call style
|
if let Some(modules) = modules {
|
||||||
if fn_name.as_ref() == KEYWORD_EVAL
|
// Module-qualified function call
|
||||||
&& modules.is_none()
|
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
|
let (id, root_pos) = modules.get(0); // First module
|
||||||
|
|
||||||
|
let module = scope.find_module(id).ok_or_else(|| {
|
||||||
|
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
|
||||||
|
})?;
|
||||||
|
match module.get_qualified_fn(fn_name, hash, modules.as_ref(), *pos) {
|
||||||
|
Ok(func) => func(&mut args, *pos)
|
||||||
|
.map_err(|err| EvalAltResult::set_position(err, *pos)),
|
||||||
|
Err(_) if def_val.is_some() => Ok(def_val.as_deref().unwrap().clone()),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
} else if fn_name.as_ref() == KEYWORD_EVAL
|
||||||
&& args.len() == 1
|
&& args.len() == 1
|
||||||
&& !self.has_override(fn_lib, KEYWORD_EVAL)
|
&& !self.has_override(fn_lib, KEYWORD_EVAL)
|
||||||
{
|
{
|
||||||
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
|
|
||||||
// Evaluate the text string as a script
|
// Evaluate the text string as a script
|
||||||
@ -1231,12 +1233,12 @@ impl Engine {
|
|||||||
state.always_search = true;
|
state.always_search = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
result
|
||||||
|
} else {
|
||||||
|
// Normal function call - except for eval (handled above)
|
||||||
|
let def_value = def_val.as_deref();
|
||||||
|
self.exec_fn_call(fn_lib, fn_name, &mut args, def_value, *pos, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call - except for eval (handled above)
|
|
||||||
let def_value = def_val.as_deref();
|
|
||||||
self.exec_fn_call(fn_lib, fn_name, modules, &mut args, def_value, *pos, level)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::In(lhs, rhs, _) => {
|
Expr::In(lhs, rhs, _) => {
|
||||||
@ -1472,8 +1474,9 @@ impl Engine {
|
|||||||
.try_cast::<String>()
|
.try_cast::<String>()
|
||||||
{
|
{
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
module.set_variable("kitty", "foo".to_string());
|
module.set_var("kitty", "foo".to_string());
|
||||||
module.set_variable("path", path);
|
module.set_var("path", path);
|
||||||
|
module.set_fn_1_mut("calc", |x: &mut String| Ok(x.len() as crate::parser::INT));
|
||||||
|
|
||||||
// TODO - avoid copying module name in inner block?
|
// TODO - avoid copying module name in inner block?
|
||||||
let mod_name = name.as_ref().clone();
|
let mod_name = name.as_ref().clone();
|
||||||
|
263
src/module.rs
263
src/module.rs
@ -1,12 +1,14 @@
|
|||||||
//! Module defining external-loaded modules for Rhai.
|
//! Module defining external-loaded modules for Rhai.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::engine::{FnAny, FunctionsLib};
|
use crate::calc_fn_hash;
|
||||||
|
use crate::engine::{FnAny, FnCallArgs, FunctionsLib};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
use crate::token::Token;
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{collections::HashMap, fmt, string::String};
|
use crate::stdlib::{any::TypeId, collections::HashMap, fmt, iter::empty, mem, string::String};
|
||||||
|
|
||||||
/// An imported module, which may contain variables, sub-modules,
|
/// An imported module, which may contain variables, sub-modules,
|
||||||
/// external Rust functions, and script-defined functions.
|
/// external Rust functions, and script-defined functions.
|
||||||
@ -51,34 +53,34 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Does a variable exist in the module?
|
/// Does a variable exist in the module?
|
||||||
pub fn contains_variable(&self, name: &str) -> bool {
|
pub fn contains_var(&self, name: &str) -> bool {
|
||||||
self.variables.contains_key(name)
|
self.variables.contains_key(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of a module variable.
|
/// Get the value of a module variable.
|
||||||
pub fn get_variable_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
self.get_variable(name).and_then(|v| v.try_cast::<T>())
|
self.get_var(name).and_then(|v| v.try_cast::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a module variable.
|
/// Get a module variable.
|
||||||
pub fn get_variable(&self, name: &str) -> Option<Dynamic> {
|
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
|
||||||
self.variables.get(name).cloned()
|
self.variables.get(name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a module variable.
|
/// Get a mutable reference to a module variable.
|
||||||
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
pub fn get_var_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||||
self.variables.get_mut(name)
|
self.variables.get_mut(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a variable into the module.
|
/// Set a variable into the module.
|
||||||
///
|
///
|
||||||
/// If there is an existing variable of the same name, it is replaced.
|
/// If there is an existing variable of the same name, it is replaced.
|
||||||
pub fn set_variable<K: Into<String>, T: Into<Dynamic>>(&mut self, name: K, value: T) {
|
pub fn set_var<K: Into<String>, T: Into<Dynamic>>(&mut self, name: K, value: T) {
|
||||||
self.variables.insert(name.into(), value.into());
|
self.variables.insert(name.into(), value.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a modules-qualified variable.
|
/// Get a mutable reference to a modules-qualified variable.
|
||||||
pub(crate) fn get_qualified_variable_mut(
|
pub(crate) fn get_qualified_var_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
modules: &StaticVec<(String, Position)>,
|
modules: &StaticVec<(String, Position)>,
|
||||||
@ -86,7 +88,7 @@ impl Module {
|
|||||||
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_qualified_module_mut(modules)?
|
.get_qualified_module_mut(modules)?
|
||||||
.get_variable_mut(name)
|
.get_var_mut(name)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?)
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,4 +133,245 @@ impl Module {
|
|||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does the particular Rust function exist in the module?
|
||||||
|
///
|
||||||
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
|
self.functions.contains_key(&hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function into the module, returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
|
pub fn set_fn(&mut self, fn_name: &str, params: &[TypeId], func: Box<FnAny>) -> u64 {
|
||||||
|
let hash = calc_fn_hash(fn_name, params.iter().cloned());
|
||||||
|
self.functions.insert(hash, func);
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking no parameters into the module, returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_0<T: Into<Dynamic>>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> Result<T, Box<EvalAltResult>> + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn() -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(fn_name, empty());
|
||||||
|
let f = move |_: &mut FnCallArgs, _: Position| func().map(|v| v.into());
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(A) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(fn_name, [TypeId::of::<A>()].iter().cloned());
|
||||||
|
|
||||||
|
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||||
|
func(mem::take(args[0]).cast::<A>()).map(|v| v.into())
|
||||||
|
};
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(fn_name, [TypeId::of::<A>()].iter().cloned());
|
||||||
|
|
||||||
|
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||||
|
func(args[0].downcast_mut::<A>().unwrap()).map(|v| v.into())
|
||||||
|
};
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||||
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
|
|
||||||
|
func(a, b).map(|v| v.into())
|
||||||
|
};
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||||
|
/// returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||||
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
|
let a = args[0].downcast_mut::<A>().unwrap();
|
||||||
|
|
||||||
|
func(a, b).map(|v| v.into())
|
||||||
|
};
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_3<
|
||||||
|
A: Variant + Clone,
|
||||||
|
B: Variant + Clone,
|
||||||
|
C: Variant + Clone,
|
||||||
|
T: Into<Dynamic>,
|
||||||
|
>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||||
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||||
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
|
let c = mem::take(args[2]).cast::<C>();
|
||||||
|
|
||||||
|
func(a, b, c).map(|v| v.into())
|
||||||
|
};
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||||
|
/// returning a hash key.
|
||||||
|
///
|
||||||
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
|
pub fn set_fn_3_mut<
|
||||||
|
A: Variant + Clone,
|
||||||
|
B: Variant + Clone,
|
||||||
|
C: Variant + Clone,
|
||||||
|
T: Into<Dynamic>,
|
||||||
|
>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||||
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = move |args: &mut FnCallArgs, _: Position| {
|
||||||
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
|
let c = mem::take(args[2]).cast::<C>();
|
||||||
|
let a = args[0].downcast_mut::<A>().unwrap();
|
||||||
|
|
||||||
|
func(a, b, c).map(|v| v.into())
|
||||||
|
};
|
||||||
|
self.functions.insert(hash, Box::new(f));
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Rust function.
|
||||||
|
///
|
||||||
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
|
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||||
|
self.functions.get(&hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a modules-qualified function.
|
||||||
|
///
|
||||||
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
|
pub(crate) fn get_qualified_fn(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
hash: u64,
|
||||||
|
modules: &StaticVec<(String, Position)>,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<&Box<FnAny>, Box<EvalAltResult>> {
|
||||||
|
Ok(self
|
||||||
|
.get_qualified_module_mut(modules)?
|
||||||
|
.get_fn(hash)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
let mut fn_name: String = Default::default();
|
||||||
|
|
||||||
|
modules.iter().for_each(|(n, _)| {
|
||||||
|
fn_name.push_str(n);
|
||||||
|
fn_name.push_str(Token::DoubleColon.syntax().as_ref());
|
||||||
|
});
|
||||||
|
|
||||||
|
fn_name.push_str(name);
|
||||||
|
|
||||||
|
Box::new(EvalAltResult::ErrorFunctionNotFound(fn_name, pos))
|
||||||
|
})?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -589,13 +589,8 @@ impl Expr {
|
|||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Variable(_, None, _, _) => match token {
|
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
Token::DoubleColon => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
Self::Variable(_, _, _, _) => match token {
|
Self::Variable(_, _, _, _) => match token {
|
||||||
|
Token::LeftBracket | Token::LeftParen => true,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Token::DoubleColon => true,
|
Token::DoubleColon => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -172,7 +172,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// Add (push) a new module to the Scope.
|
/// Add (push) a new module to the Scope.
|
||||||
///
|
///
|
||||||
/// 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.
|
||||||
pub(crate) fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
|
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
|
||||||
self.push_dynamic_value(
|
self.push_dynamic_value(
|
||||||
name,
|
name,
|
||||||
EntryType::Module,
|
EntryType::Module,
|
||||||
|
@ -1,33 +1,58 @@
|
|||||||
#![cfg(not(feature = "no_module"))]
|
#![cfg(not(feature = "no_module"))]
|
||||||
use rhai::{EvalAltResult, Module, Scope, INT};
|
use rhai::{Engine, EvalAltResult, Module, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_module() {
|
fn test_module() {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
module.set_variable("kitty", 42 as INT);
|
module.set_var("answer", 42 as INT);
|
||||||
|
|
||||||
assert!(module.contains_variable("kitty"));
|
assert!(module.contains_var("answer"));
|
||||||
assert_eq!(module.get_variable_value::<INT>("kitty").unwrap(), 42);
|
assert_eq!(module.get_var_value::<INT>("answer").unwrap(), 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sub_module() {
|
fn test_sub_module() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
let mut sub_module = Module::new();
|
let mut sub_module = Module::new();
|
||||||
|
|
||||||
let mut sub_module2 = Module::new();
|
let mut sub_module2 = Module::new();
|
||||||
sub_module2.set_variable("kitty", 42 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
|
let hash = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
||||||
|
|
||||||
sub_module.set_sub_module("world", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("hello", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
|
|
||||||
assert!(module.contains_sub_module("hello"));
|
assert!(module.contains_sub_module("life"));
|
||||||
let m = module.get_sub_module("hello").unwrap();
|
let m = module.get_sub_module("life").unwrap();
|
||||||
|
|
||||||
assert!(m.contains_sub_module("world"));
|
assert!(m.contains_sub_module("universe"));
|
||||||
let m2 = m.get_sub_module("world").unwrap();
|
let m2 = m.get_sub_module("universe").unwrap();
|
||||||
|
|
||||||
assert!(m2.contains_variable("kitty"));
|
assert!(m2.contains_var("answer"));
|
||||||
assert_eq!(m2.get_variable_value::<INT>("kitty").unwrap(), 42);
|
assert!(m2.contains_fn(hash));
|
||||||
|
|
||||||
|
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
scope.push_module("question", module);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"question::life::universe::answer + 1"
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"question::life::universe::inc(question::life::universe::answer)"
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user