Implement private modifier for functions.
This commit is contained in:
parent
d1de84fdd2
commit
0d20137d6c
22
README.md
22
README.md
@ -268,6 +268,7 @@ let ast = engine.compile_file("hello_world.rhai".into())?;
|
|||||||
### Calling Rhai functions from Rust
|
### Calling Rhai functions from Rust
|
||||||
|
|
||||||
Rhai also allows working _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust via `call_fn`.
|
Rhai also allows working _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust via `call_fn`.
|
||||||
|
Functions declared with `private` are hidden and cannot be called from Rust (see also [modules]).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Define functions in a script.
|
// Define functions in a script.
|
||||||
@ -287,6 +288,11 @@ let ast = engine.compile(true,
|
|||||||
fn hello() {
|
fn hello() {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this one is private and cannot be called by 'call_fn'
|
||||||
|
private hidden() {
|
||||||
|
throw "you shouldn't see me!";
|
||||||
|
}
|
||||||
")?;
|
")?;
|
||||||
|
|
||||||
// A custom scope can also contain any variables/constants available to the functions
|
// A custom scope can also contain any variables/constants available to the functions
|
||||||
@ -300,11 +306,15 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", ( String::from("abc"
|
|||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
// put arguments in a tuple
|
// put arguments in a tuple
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", (123_i64,) )?
|
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", (123_i64,) )?;
|
||||||
// ^^^^^^^^^^ tuple of one
|
// ^^^^^^^^^^ tuple of one
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?
|
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?;
|
||||||
// ^^ unit = tuple of zero
|
// ^^ unit = tuple of zero
|
||||||
|
|
||||||
|
// The following call will return a function-not-found error because
|
||||||
|
// 'hidden' is declared with 'private'.
|
||||||
|
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Creating Rust anonymous functions from Rhai script
|
### Creating Rust anonymous functions from Rhai script
|
||||||
@ -2052,13 +2062,17 @@ Modules can be disabled via the [`no_module`] feature.
|
|||||||
A module is a single script (or pre-compiled `AST`) containing global variables and functions.
|
A module is a single script (or pre-compiled `AST`) containing global variables and functions.
|
||||||
The `export` statement, which can only be at global level, exposes selected variables as members of a module.
|
The `export` statement, which can only be at global level, exposes selected variables as members of a module.
|
||||||
Variables not exported are private and invisible to the outside.
|
Variables not exported are private and invisible to the outside.
|
||||||
|
All functions are automatically exported, unless it is prefixed with `private`.
|
||||||
|
Functions declared `private` are invisible to the outside.
|
||||||
|
|
||||||
All functions are automatically exported. Everything exported from a module is **constant** (**read-only**).
|
Everything exported from a module is **constant** (**read-only**).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// This is a module script.
|
// This is a module script.
|
||||||
|
|
||||||
fn inc(x) { x + 1 } // function
|
fn inc(x) { x + 1 } // public function
|
||||||
|
|
||||||
|
private fn foo() {} // private function - invisible to outside
|
||||||
|
|
||||||
let private = 123; // variable not exported - invisible to outside
|
let private = 123; // variable not exported - invisible to outside
|
||||||
let x = 42; // this will be exported below
|
let x = 42; // this will be exported below
|
||||||
|
@ -999,7 +999,7 @@ impl Engine {
|
|||||||
let pos = Position::none();
|
let pos = Position::none();
|
||||||
|
|
||||||
let fn_def = fn_lib
|
let fn_def = fn_lib
|
||||||
.get_function_by_signature(name, args.len())
|
.get_function_by_signature(name, args.len(), true)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
||||||
|
|
||||||
let state = State::new(fn_lib);
|
let state = State::new(fn_lib);
|
||||||
|
@ -7,7 +7,7 @@ use crate::optimize::OptimizationLevel;
|
|||||||
use crate::packages::{
|
use crate::packages::{
|
||||||
CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage,
|
CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage,
|
||||||
};
|
};
|
||||||
use crate::parser::{Expr, FnDef, ReturnType, Stmt, AST};
|
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, Stmt, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
@ -234,10 +234,22 @@ impl FunctionsLib {
|
|||||||
self.get(&hash).map(|fn_def| fn_def.as_ref())
|
self.get(&hash).map(|fn_def| fn_def.as_ref())
|
||||||
}
|
}
|
||||||
/// 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,
|
||||||
|
public_only: bool,
|
||||||
|
) -> Option<&FnDef> {
|
||||||
// Qualifiers (none) + function name + placeholders (one for each parameter).
|
// 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)
|
let fn_def = self.get_function(hash);
|
||||||
|
|
||||||
|
match fn_def.as_ref().map(|f| f.access) {
|
||||||
|
None => None,
|
||||||
|
Some(FnAccess::Private) if public_only => None,
|
||||||
|
Some(FnAccess::Private) => fn_def,
|
||||||
|
Some(FnAccess::Public) => fn_def,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Merge another `FunctionsLib` into this `FunctionsLib`.
|
/// Merge another `FunctionsLib` into this `FunctionsLib`.
|
||||||
pub fn merge(&self, other: &Self) -> Self {
|
pub fn merge(&self, other: &Self) -> Self {
|
||||||
|
106
src/module.rs
106
src/module.rs
@ -4,7 +4,7 @@
|
|||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib, NativeFunction, ScriptedFunction};
|
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib, NativeFunction, ScriptedFunction};
|
||||||
use crate::parser::{FnDef, AST};
|
use crate::parser::{FnAccess, FnDef, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::{Position, Token};
|
use crate::token::{Position, Token};
|
||||||
@ -57,7 +57,7 @@ pub struct Module {
|
|||||||
all_variables: HashMap<u64, Dynamic>,
|
all_variables: HashMap<u64, Dynamic>,
|
||||||
|
|
||||||
/// External Rust functions.
|
/// External Rust functions.
|
||||||
functions: HashMap<u64, (String, Vec<TypeId>, NativeFunction)>,
|
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, NativeFunction)>,
|
||||||
|
|
||||||
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
||||||
all_functions: HashMap<u64, NativeFunction>,
|
all_functions: HashMap<u64, NativeFunction>,
|
||||||
@ -260,15 +260,21 @@ impl Module {
|
|||||||
/// Set a Rust function into the module, returning a hash key.
|
/// 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.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
pub fn set_fn(&mut self, fn_name: String, params: Vec<TypeId>, func: Box<FnAny>) -> u64 {
|
pub fn set_fn(
|
||||||
|
&mut self,
|
||||||
|
fn_name: String,
|
||||||
|
access: FnAccess,
|
||||||
|
params: Vec<TypeId>,
|
||||||
|
func: Box<FnAny>,
|
||||||
|
) -> u64 {
|
||||||
let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned());
|
let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned());
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
self.functions
|
self.functions
|
||||||
.insert(hash, (fn_name, params, Rc::new(func)));
|
.insert(hash, (fn_name, access, params, Rc::new(func)));
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
self.functions
|
self.functions
|
||||||
.insert(hash, (fn_name, params, Arc::new(func)));
|
.insert(hash, (fn_name, access, params, Arc::new(func)));
|
||||||
|
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -283,7 +289,7 @@ impl Module {
|
|||||||
/// use rhai::Module;
|
/// use rhai::Module;
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64), false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
|
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
|
||||||
@ -291,6 +297,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: &mut FnCallArgs, pos| {
|
let f = move |_: &mut FnCallArgs, pos| {
|
||||||
func()
|
func()
|
||||||
@ -298,7 +305,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![];
|
let arg_types = vec![];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||||
@ -311,7 +323,7 @@ impl Module {
|
|||||||
/// use rhai::Module;
|
/// use rhai::Module;
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1), false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
@ -319,6 +331,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
func(mem::take(args[0]).cast::<A>())
|
func(mem::take(args[0]).cast::<A>())
|
||||||
@ -326,7 +339,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>()];
|
let arg_types = vec![TypeId::of::<A>()];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||||
@ -339,7 +357,7 @@ impl Module {
|
|||||||
/// use rhai::Module;
|
/// use rhai::Module;
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) }, false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
@ -347,6 +365,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
func(args[0].downcast_mut::<A>().unwrap())
|
func(args[0].downcast_mut::<A>().unwrap())
|
||||||
@ -354,7 +373,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>()];
|
let arg_types = vec![TypeId::of::<A>()];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||||
@ -369,7 +393,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
|
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
|
||||||
/// Ok(x + y.len() as i64)
|
/// Ok(x + y.len() as i64)
|
||||||
/// });
|
/// }, false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
@ -377,6 +401,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let a = mem::take(args[0]).cast::<A>();
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
@ -387,7 +412,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||||
@ -401,7 +431,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
|
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
|
||||||
/// *x += y.len() as i64; Ok(*x)
|
/// *x += y.len() as i64; Ok(*x)
|
||||||
/// });
|
/// }, false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_2_mut<
|
pub fn set_fn_2_mut<
|
||||||
@ -414,6 +444,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
@ -424,7 +455,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||||
@ -439,7 +475,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
|
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
|
||||||
/// Ok(x + y.len() as i64 + z)
|
/// Ok(x + y.len() as i64 + z)
|
||||||
/// });
|
/// }, false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_3<
|
pub fn set_fn_3<
|
||||||
@ -453,6 +489,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let a = mem::take(args[0]).cast::<A>();
|
let a = mem::take(args[0]).cast::<A>();
|
||||||
@ -464,7 +501,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||||
@ -480,7 +522,7 @@ impl Module {
|
|||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
|
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
|
||||||
/// *x += y.len() as i64 + z; Ok(*x)
|
/// *x += y.len() as i64 + z; Ok(*x)
|
||||||
/// });
|
/// }, false);
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_3_mut<
|
pub fn set_fn_3_mut<
|
||||||
@ -494,6 +536,7 @@ impl Module {
|
|||||||
fn_name: K,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
|
is_private: bool,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |args: &mut FnCallArgs, pos| {
|
let f = move |args: &mut FnCallArgs, pos| {
|
||||||
let b = mem::take(args[1]).cast::<B>();
|
let b = mem::take(args[1]).cast::<B>();
|
||||||
@ -505,7 +548,12 @@ impl Module {
|
|||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
self.set_fn(fn_name.into(), arg_types, Box::new(f))
|
let access = if is_private {
|
||||||
|
FnAccess::Private
|
||||||
|
} else {
|
||||||
|
FnAccess::Public
|
||||||
|
};
|
||||||
|
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a Rust function.
|
/// Get a Rust function.
|
||||||
@ -523,7 +571,7 @@ impl Module {
|
|||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||||
self.functions.get(&hash).map(|(_, _, v)| v.as_ref())
|
self.functions.get(&hash).map(|(_, _, _, v)| v.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a modules-qualified function.
|
/// Get a modules-qualified function.
|
||||||
@ -588,11 +636,7 @@ impl Module {
|
|||||||
|
|
||||||
scope.into_iter().for_each(
|
scope.into_iter().for_each(
|
||||||
|ScopeEntry {
|
|ScopeEntry {
|
||||||
name,
|
typ, value, alias, ..
|
||||||
typ,
|
|
||||||
value,
|
|
||||||
alias,
|
|
||||||
..
|
|
||||||
}| {
|
}| {
|
||||||
match typ {
|
match typ {
|
||||||
// Variables with an alias left in the scope become module variables
|
// Variables with an alias left in the scope become module variables
|
||||||
@ -641,7 +685,12 @@ impl Module {
|
|||||||
variables.push((hash, value.clone()));
|
variables.push((hash, value.clone()));
|
||||||
}
|
}
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
for (fn_name, params, func) in module.functions.values() {
|
for (fn_name, access, params, func) in module.functions.values() {
|
||||||
|
match access {
|
||||||
|
// Private functions are not exported
|
||||||
|
FnAccess::Private => continue,
|
||||||
|
FnAccess::Public => (),
|
||||||
|
}
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
||||||
@ -660,6 +709,11 @@ impl Module {
|
|||||||
}
|
}
|
||||||
// Index 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() {
|
||||||
|
match fn_def.access {
|
||||||
|
// Private functions are not exported
|
||||||
|
FnAccess::Private => continue,
|
||||||
|
FnAccess::Public => (),
|
||||||
|
}
|
||||||
// Qualifiers + function name + placeholders (one for each parameter)
|
// Qualifiers + function name + placeholders (one for each parameter)
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
qualifiers.iter().map(|v| *v),
|
qualifiers.iter().map(|v| *v),
|
||||||
|
@ -178,11 +178,22 @@ impl Add<Self> for &AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A script-function definition.
|
/// A type representing the access mode of a scripted function.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FnAccess {
|
||||||
|
/// Private function.
|
||||||
|
Private,
|
||||||
|
/// Public function.
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A scripted function definition.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FnDef {
|
pub struct FnDef {
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Function access mode.
|
||||||
|
pub access: FnAccess,
|
||||||
/// Names of function parameters.
|
/// Names of function parameters.
|
||||||
pub params: Vec<String>,
|
pub params: Vec<String>,
|
||||||
/// Function body.
|
/// Function body.
|
||||||
@ -2208,6 +2219,7 @@ fn parse_stmt<'a>(
|
|||||||
fn parse_fn<'a>(
|
fn parse_fn<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
access: FnAccess,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<FnDef, Box<ParseError>> {
|
) -> Result<FnDef, Box<ParseError>> {
|
||||||
let pos = eat_token(input, Token::Fn);
|
let pos = eat_token(input, Token::Fn);
|
||||||
@ -2283,6 +2295,7 @@ fn parse_fn<'a>(
|
|||||||
|
|
||||||
Ok(FnDef {
|
Ok(FnDef {
|
||||||
name,
|
name,
|
||||||
|
access,
|
||||||
params,
|
params,
|
||||||
body,
|
body,
|
||||||
pos,
|
pos,
|
||||||
@ -2330,19 +2343,37 @@ fn parse_global_level<'a>(
|
|||||||
// Collect all the function definitions
|
// Collect all the function definitions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
if let (Token::Fn, _) = input.peek().unwrap() {
|
let mut access = FnAccess::Public;
|
||||||
let mut stack = Stack::new();
|
let mut must_be_fn = false;
|
||||||
let func = parse_fn(input, &mut stack, true)?;
|
|
||||||
|
|
||||||
// Qualifiers (none) + function name + argument `TypeId`'s
|
if match_token(input, Token::Private)? {
|
||||||
let hash = calc_fn_hash(
|
access = FnAccess::Private;
|
||||||
empty(),
|
must_be_fn = true;
|
||||||
&func.name,
|
}
|
||||||
repeat(EMPTY_TYPE_ID()).take(func.params.len()),
|
|
||||||
);
|
|
||||||
|
|
||||||
functions.insert(hash, func);
|
match input.peek().unwrap() {
|
||||||
continue;
|
(Token::Fn, _) => {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
let func = parse_fn(input, &mut stack, access, 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;
|
||||||
|
}
|
||||||
|
(_, pos) if must_be_fn => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Fn.into(),
|
||||||
|
format!("following '{}'", Token::Private.syntax()),
|
||||||
|
)
|
||||||
|
.into_err(*pos))
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Actual statement
|
// Actual statement
|
||||||
|
@ -196,6 +196,7 @@ pub enum Token {
|
|||||||
XOrAssign,
|
XOrAssign,
|
||||||
ModuloAssign,
|
ModuloAssign,
|
||||||
PowerOfAssign,
|
PowerOfAssign,
|
||||||
|
Private,
|
||||||
Import,
|
Import,
|
||||||
Export,
|
Export,
|
||||||
As,
|
As,
|
||||||
@ -279,6 +280,7 @@ impl Token {
|
|||||||
ModuloAssign => "%=",
|
ModuloAssign => "%=",
|
||||||
PowerOf => "~",
|
PowerOf => "~",
|
||||||
PowerOfAssign => "~=",
|
PowerOfAssign => "~=",
|
||||||
|
Private => "private",
|
||||||
Import => "import",
|
Import => "import",
|
||||||
Export => "export",
|
Export => "export",
|
||||||
As => "as",
|
As => "as",
|
||||||
@ -750,6 +752,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
"throw" => Token::Throw,
|
"throw" => Token::Throw,
|
||||||
"for" => Token::For,
|
"for" => Token::For,
|
||||||
"in" => Token::In,
|
"in" => Token::In,
|
||||||
|
"private" => Token::Private,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
"import" => Token::Import,
|
"import" => Token::Import,
|
||||||
|
@ -18,7 +18,12 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut sub_module2 = Module::new();
|
let mut sub_module2 = Module::new();
|
||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
let hash = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1), false);
|
||||||
|
let hash_hidden = sub_module2.set_fn_0(
|
||||||
|
"hidden",
|
||||||
|
|| Err("shouldn't see me!".into()) as Result<(), Box<EvalAltResult>>,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
sub_module.set_sub_module("universe", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
@ -30,11 +35,12 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let m2 = m.get_sub_module("universe").unwrap();
|
let m2 = m.get_sub_module("universe").unwrap();
|
||||||
|
|
||||||
assert!(m2.contains_var("answer"));
|
assert!(m2.contains_var("answer"));
|
||||||
assert!(m2.contains_fn(hash));
|
assert!(m2.contains_fn(hash_inc));
|
||||||
|
assert!(m2.contains_fn(hash_hidden));
|
||||||
|
|
||||||
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
scope.push_module("question", module);
|
scope.push_module("question", module);
|
||||||
@ -53,6 +59,11 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.eval_expression_with_scope::<()>(&mut scope, "question::life::universe::hidden()")
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "hidden"
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -102,6 +113,9 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn add_len(x, y) {
|
fn add_len(x, y) {
|
||||||
x + y.len()
|
x + y.len()
|
||||||
}
|
}
|
||||||
|
private fn hidden() {
|
||||||
|
throw "you shouldn't see me!";
|
||||||
|
}
|
||||||
|
|
||||||
// Imported modules become sub-modules
|
// Imported modules become sub-modules
|
||||||
import "another module" as extra;
|
import "another module" as extra;
|
||||||
@ -152,6 +166,12 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?,
|
)?,
|
||||||
59
|
59
|
||||||
);
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.eval_expression_with_scope::<()>(&mut scope, "testing::hidden()")
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "hidden"
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user