Unify all functions into CallableFunction type, remove FunctionsLib.

This commit is contained in:
Stephen Chung 2020-06-05 15:14:42 +08:00
parent e21d25a0c5
commit 56eb659d5a
6 changed files with 316 additions and 253 deletions

View File

@ -1,7 +1,9 @@
//! Module that defines the extern API of `Engine`. //! Module that defines the extern API of `Engine`.
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::engine::{make_getter, make_setter, Engine, State, FUNC_INDEXER}; use crate::engine::{
get_script_function_by_signature, make_getter, make_setter, Engine, State, FUNC_INDEXER,
};
use crate::error::ParseError; use crate::error::ParseError;
use crate::fn_call::FuncArgs; use crate::fn_call::FuncArgs;
use crate::fn_native::{IteratorFn, SendSync}; use crate::fn_native::{IteratorFn, SendSync};
@ -1077,10 +1079,7 @@ impl Engine {
arg_values: &mut [Dynamic], arg_values: &mut [Dynamic],
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut args: StaticVec<_> = arg_values.iter_mut().collect(); let mut args: StaticVec<_> = arg_values.iter_mut().collect();
let lib = ast.lib(); let fn_def = get_script_function_by_signature(ast.lib(), name, args.len(), true)
let fn_def = lib
.get_function_by_signature(name, args.len(), true)
.ok_or_else(|| { .ok_or_else(|| {
Box::new(EvalAltResult::ErrorFunctionNotFound( Box::new(EvalAltResult::ErrorFunctionNotFound(
name.into(), name.into(),
@ -1091,7 +1090,7 @@ impl Engine {
let mut state = State::new(); let mut state = State::new();
let args = args.as_mut(); let args = args.as_mut();
self.call_script_fn(scope, &mut state, &lib, name, fn_def, args, 0) self.call_script_fn(scope, &mut state, ast.lib(), name, fn_def, args, 0)
} }
/// Optimize the `AST` with constants defined in an external Scope. /// Optimize the `AST` with constants defined in an external Scope.
@ -1114,8 +1113,9 @@ impl Engine {
) -> AST { ) -> AST {
let lib = ast let lib = ast
.lib() .lib()
.iter() .iter_fn()
.map(|(_, fn_def)| fn_def.as_ref().clone()) .filter(|(_, _, _, f)| f.is_script())
.map(|(_, _, _, f)| f.get_fn_def().clone())
.collect(); .collect();
let stmt = mem::take(ast.statements_mut()); let stmt = mem::take(ast.statements_mut());

View File

@ -3,16 +3,16 @@
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::error::ParseErrorType; use crate::error::ParseErrorType;
use crate::fn_native::{CallableFunction, Callback, FnCallArgs, Shared}; use crate::fn_native::{CallableFunction, Callback, FnCallArgs};
use crate::module::{resolvers, Module, ModuleResolver}; use crate::module::{resolvers, Module, ModuleResolver};
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage}; use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
use crate::parser::{Expr, FnAccess, FnDef, ImmutableString, ReturnType, Stmt, AST, INT}; use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, Stmt, AST, INT};
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime}; use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
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;
use crate::utils::{StaticVec, StraightHasherBuilder}; use crate::utils::StaticVec;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::parser::FLOAT; use crate::parser::FLOAT;
@ -24,7 +24,6 @@ use crate::stdlib::{
format, format,
iter::{empty, once}, iter::{empty, once},
mem, mem,
ops::{Deref, DerefMut},
string::{String, ToString}, string::{String, ToString},
vec::Vec, vec::Vec,
}; };
@ -189,88 +188,24 @@ impl State {
} }
} }
/// A type that holds a library (`HashMap`) of script-defined functions. /// Get a script-defined function definition from a module.
/// pub fn get_script_function_by_signature<'a>(
/// Since script-defined functions have `Dynamic` parameters, functions with the same name module: &'a Module,
/// and number of parameters are considered equivalent.
///
/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash`.
#[derive(Debug, Clone, Default)]
pub struct FunctionsLib(HashMap<u64, Shared<FnDef>, StraightHasherBuilder>);
impl FunctionsLib {
/// Create a new `FunctionsLib` from a collection of `FnDef`.
pub fn from_iter(vec: impl IntoIterator<Item = FnDef>) -> Self {
FunctionsLib(
vec.into_iter()
.map(|fn_def| {
// Qualifiers (none) + function name + number of arguments.
let hash = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty());
(hash, fn_def.into())
})
.collect(),
)
}
/// Does a certain function exist in the `FunctionsLib`?
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
pub fn has_function(&self, hash_fn_def: u64) -> bool {
self.contains_key(&hash_fn_def)
}
/// Get a function definition from the `FunctionsLib`.
///
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
pub fn get_function(&self, hash_fn_def: u64) -> Option<&FnDef> {
self.get(&hash_fn_def).map(|fn_def| fn_def.as_ref())
}
/// Get a function definition from the `FunctionsLib`.
pub fn get_function_by_signature(
&self,
name: &str, name: &str,
params: usize, params: usize,
public_only: bool, public_only: bool,
) -> Option<&FnDef> { ) -> Option<&'a ScriptFnDef> {
// Qualifiers (none) + function name + number of arguments. // Qualifiers (none) + function name + number of arguments.
let hash_fn_def = calc_fn_hash(empty(), name, params, empty()); let hash_fn_def = calc_fn_hash(empty(), name, params, empty());
let fn_def = self.get_function(hash_fn_def); let func = module.get_fn(hash_fn_def)?;
if !func.is_script() {
return None;
}
let fn_def = func.get_fn_def();
match fn_def.as_ref().map(|f| f.access) { match fn_def.access {
None => None, FnAccess::Private if public_only => None,
Some(FnAccess::Private) if public_only => None, FnAccess::Private | FnAccess::Public => Some(&fn_def),
Some(FnAccess::Private) | Some(FnAccess::Public) => fn_def,
}
}
/// Merge another `FunctionsLib` into this `FunctionsLib`.
pub fn merge(&self, other: &Self) -> Self {
if self.is_empty() {
other.clone()
} else if other.is_empty() {
self.clone()
} else {
let mut functions = self.clone();
functions.extend(other.iter().map(|(hash, fn_def)| (*hash, fn_def.clone())));
functions
}
}
}
impl From<Vec<(u64, Shared<FnDef>)>> for FunctionsLib {
fn from(values: Vec<(u64, Shared<FnDef>)>) -> Self {
FunctionsLib(values.into_iter().collect())
}
}
impl Deref for FunctionsLib {
type Target = HashMap<u64, Shared<FnDef>, StraightHasherBuilder>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for FunctionsLib {
fn deref_mut(&mut self) -> &mut HashMap<u64, Shared<FnDef>, StraightHasherBuilder> {
&mut self.0
} }
} }
@ -575,7 +510,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
fn_name: &str, fn_name: &str,
hashes: (u64, u64), hashes: (u64, u64),
args: &mut FnCallArgs, args: &mut FnCallArgs,
@ -634,12 +569,27 @@ impl Engine {
} }
} }
// Search for the function
// First search in script-defined functions (can override built-in) // First search in script-defined functions (can override built-in)
if !native_only { // Then search registered native functions (can override packages)
if let Some(fn_def) = lib.get(&hashes.1) { // Then search packages
// NOTE: We skip script functions for global_module and packages, and native functions for lib
let func = if !native_only {
lib.get_fn(hashes.1) //.or_else(|| lib.get_fn(hashes.0))
} else {
None
}
//.or_else(|| self.global_module.get_fn(hashes.1))
.or_else(|| self.global_module.get_fn(hashes.0))
//.or_else(|| self.packages.get_fn(hashes.1))
.or_else(|| self.packages.get_fn(hashes.0));
if let Some(func) = func {
if func.is_script() {
// Run scripted function
normalize_first_arg(is_ref, &mut this_copy, &mut old_this_ptr, args); normalize_first_arg(is_ref, &mut this_copy, &mut old_this_ptr, args);
// Run scripted function let fn_def = func.get_fn_def();
let result = let result =
self.call_script_fn(scope, state, lib, fn_name, fn_def, args, level)?; self.call_script_fn(scope, state, lib, fn_name, fn_def, args, level)?;
@ -647,15 +597,7 @@ impl Engine {
restore_first_arg(old_this_ptr, args); restore_first_arg(old_this_ptr, args);
return Ok((result, false)); return Ok((result, false));
} } else {
}
// Search built-in's and external functions
if let Some(func) = self
.global_module
.get_fn(hashes.0)
.or_else(|| self.packages.get_fn(hashes.0))
{
// Calling pure function in method-call? // Calling pure function in method-call?
normalize_first_arg( normalize_first_arg(
func.is_pure() && is_ref, func.is_pure() && is_ref,
@ -695,6 +637,7 @@ impl Engine {
_ => (result, func.is_method()), _ => (result, func.is_method()),
}); });
} }
}
// See if it is built in. // See if it is built in.
if args.len() == 2 { if args.len() == 2 {
@ -757,9 +700,9 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
fn_name: &str, fn_name: &str,
fn_def: &FnDef, fn_def: &ScriptFnDef,
args: &mut FnCallArgs, args: &mut FnCallArgs,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -809,13 +752,18 @@ impl Engine {
} }
// Has a system function an override? // Has a system function an override?
fn has_override(&self, lib: &FunctionsLib, hashes: (u64, u64)) -> bool { fn has_override(&self, lib: &Module, hashes: (u64, u64)) -> bool {
// First check registered functions // NOTE: We skip script functions for global_module and packages, and native functions for lib
self.global_module.contains_fn(hashes.0)
// First check script-defined functions
lib.contains_fn(hashes.1)
//|| lib.contains_fn(hashes.0)
// Then check registered functions
//|| self.global_module.contains_fn(hashes.1)
|| self.global_module.contains_fn(hashes.0)
// Then check packages // Then check packages
//|| self.packages.contains_fn(hashes.1)
|| self.packages.contains_fn(hashes.0) || self.packages.contains_fn(hashes.0)
// Then check script-defined functions
|| lib.contains_key(&hashes.1)
} }
/// Perform an actual function call, taking care of special functions /// Perform an actual function call, taking care of special functions
@ -829,7 +777,7 @@ impl Engine {
fn exec_fn_call( fn exec_fn_call(
&self, &self,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
fn_name: &str, fn_name: &str,
native_only: bool, native_only: bool,
hash_fn_def: u64, hash_fn_def: u64,
@ -878,7 +826,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
script: &Dynamic, script: &Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let script = script.as_str().map_err(|type_name| { let script = script.as_str().map_err(|type_name| {
@ -894,7 +842,7 @@ impl Engine {
)?; )?;
// If new functions are defined within the eval string, it is an error // If new functions are defined within the eval string, it is an error
if !ast.lib().is_empty() { if ast.lib().num_fn() != 0 {
return Err(Box::new(EvalAltResult::ErrorParsing( return Err(Box::new(EvalAltResult::ErrorParsing(
ParseErrorType::WrongFnDefinition.into_err(Position::none()), ParseErrorType::WrongFnDefinition.into_err(Position::none()),
))); )));
@ -917,7 +865,7 @@ impl Engine {
fn eval_dot_index_chain_helper( fn eval_dot_index_chain_helper(
&self, &self,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
target: &mut Target, target: &mut Target,
rhs: &Expr, rhs: &Expr,
idx_values: &mut StaticVec<Dynamic>, idx_values: &mut StaticVec<Dynamic>,
@ -1097,7 +1045,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
expr: &Expr, expr: &Expr,
level: usize, level: usize,
new_val: Option<Dynamic>, new_val: Option<Dynamic>,
@ -1166,7 +1114,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
expr: &Expr, expr: &Expr,
idx_values: &mut StaticVec<Dynamic>, idx_values: &mut StaticVec<Dynamic>,
size: usize, size: usize,
@ -1211,7 +1159,7 @@ impl Engine {
fn get_indexed_mut<'a>( fn get_indexed_mut<'a>(
&self, &self,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
val: &'a mut Dynamic, val: &'a mut Dynamic,
is_ref: bool, is_ref: bool,
mut idx: Dynamic, mut idx: Dynamic,
@ -1305,7 +1253,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
lhs: &Expr, lhs: &Expr,
rhs: &Expr, rhs: &Expr,
level: usize, level: usize,
@ -1368,7 +1316,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
expr: &Expr, expr: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
@ -1650,14 +1598,14 @@ impl Engine {
}; };
match func { match func {
Ok(x) if x.is_script() => { Ok(f) if f.is_script() => {
let args = args.as_mut(); let args = args.as_mut();
let fn_def = x.get_fn_def(); let fn_def = f.get_fn_def();
let mut scope = Scope::new(); let mut scope = Scope::new();
self.call_script_fn(&mut scope, state, lib, name, fn_def, args, level) self.call_script_fn(&mut scope, state, lib, name, fn_def, args, level)
.map_err(|err| EvalAltResult::new_position(err, *pos)) .map_err(|err| EvalAltResult::new_position(err, *pos))
} }
Ok(x) => x.get_native_fn()(args.as_mut()).map_err(|err| err.new_position(*pos)), Ok(f) => f.get_native_fn()(args.as_mut()).map_err(|err| err.new_position(*pos)),
Err(err) Err(err)
if def_val.is_some() if def_val.is_some()
&& matches!(*err, EvalAltResult::ErrorFunctionNotFound(_, _)) => && matches!(*err, EvalAltResult::ErrorFunctionNotFound(_, _)) =>
@ -1719,7 +1667,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
state: &mut State, state: &mut State,
lib: &FunctionsLib, lib: &Module,
stmt: &Stmt, stmt: &Stmt,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {

View File

@ -1,8 +1,8 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::parser::FnDef; use crate::parser::ScriptFnDef;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::stdlib::{boxed::Box, rc::Rc, sync::Arc}; use crate::stdlib::{boxed::Box, fmt, rc::Rc, sync::Arc};
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub trait SendSync: Send + Sync {} pub trait SendSync: Send + Sync {}
@ -73,7 +73,18 @@ pub enum CallableFunction {
/// An iterator function. /// An iterator function.
Iterator(IteratorFn), Iterator(IteratorFn),
/// A script-defined function. /// A script-defined function.
Script(Shared<FnDef>), Script(Shared<ScriptFnDef>),
}
impl fmt::Debug for CallableFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pure(_) => write!(f, "NativePureFunction"),
Self::Method(_) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"),
Self::Script(fn_def) => write!(f, "{:?}", fn_def),
}
}
} }
impl CallableFunction { impl CallableFunction {
@ -116,12 +127,23 @@ impl CallableFunction {
Self::Iterator(_) | Self::Script(_) => panic!(), Self::Iterator(_) | Self::Script(_) => panic!(),
} }
} }
/// Get a shared reference to a script-defined function definition.
///
/// # Panics
///
/// Panics if the `CallableFunction` is not `Script`.
pub fn get_shared_fn_def(&self) -> Shared<ScriptFnDef> {
match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => panic!(),
Self::Script(f) => f.clone(),
}
}
/// Get a reference to a script-defined function definition. /// Get a reference to a script-defined function definition.
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if the `CallableFunction` is not `Script`. /// Panics if the `CallableFunction` is not `Script`.
pub fn get_fn_def(&self) -> &FnDef { pub fn get_fn_def(&self) -> &ScriptFnDef {
match self { match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => panic!(), Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => panic!(),
Self::Script(f) => f, Self::Script(f) => f,
@ -147,3 +169,21 @@ impl CallableFunction {
Self::Method(func.into()) Self::Method(func.into())
} }
} }
impl From<IteratorFn> for CallableFunction {
fn from(func: IteratorFn) -> Self {
Self::Iterator(func)
}
}
impl From<ScriptFnDef> for CallableFunction {
fn from(func: ScriptFnDef) -> Self {
Self::Script(func.into())
}
}
impl From<Shared<ScriptFnDef>> for CallableFunction {
fn from(func: Shared<ScriptFnDef>) -> Self {
Self::Script(func)
}
}

View File

@ -2,12 +2,12 @@
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib, FUNC_INDEXER}; use crate::engine::{make_getter, make_setter, Engine, FUNC_INDEXER};
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync};
use crate::parser::{ use crate::parser::{
FnAccess, FnAccess,
FnAccess::{Private, Public}, FnAccess::{Private, Public},
AST, ScriptFnDef, 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};
@ -53,9 +53,6 @@ pub struct Module {
StraightHasherBuilder, StraightHasherBuilder,
>, >,
/// Script-defined functions.
lib: FunctionsLib,
/// Iterator functions, keyed by the type producing the iterator. /// Iterator functions, keyed by the type producing the iterator.
type_iterators: HashMap<TypeId, IteratorFn>, type_iterators: HashMap<TypeId, IteratorFn>,
@ -68,10 +65,9 @@ impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"<module {:?}, functions={}, lib={}>", "<module {:?}, functions={}>",
self.variables, self.variables,
self.functions.len(), self.functions.len(),
self.lib.len()
) )
} }
} }
@ -186,6 +182,23 @@ impl Module {
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.to_string(), pos))) .ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.to_string(), pos)))
} }
/// Set a script-defined function into the module.
///
/// If there is an existing function of the same name and number of arguments, it is replaced.
pub(crate) fn set_script_fn(&mut self, fn_def: ScriptFnDef) {
// None + function name + number of arguments.
let hash_fn_def = calc_fn_hash(empty(), &fn_def.name, fn_def.params.len(), empty());
self.functions.insert(
hash_fn_def,
(
fn_def.name.to_string(),
fn_def.access,
Default::default(),
fn_def.into(),
),
);
}
/// Does a sub-module exist in the module? /// Does a sub-module exist in the module?
/// ///
/// # Examples /// # Examples
@ -750,6 +763,41 @@ impl Module {
}) })
} }
/// Merge another module into this module.
pub fn merge(&mut self, other: &Self) {
self.variables
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
self.functions
.extend(other.functions.iter().map(|(&k, v)| (k, v.clone())));
self.type_iterators
.extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone())));
}
/// Get the number of variables in the module.
pub fn num_var(&self) -> usize {
self.variables.len()
}
/// Get the number of functions in the module.
pub fn num_fn(&self) -> usize {
self.variables.len()
}
/// Get the number of type iterators in the module.
pub fn num_iter(&self) -> usize {
self.variables.len()
}
/// Get an iterator to the variables in the module.
pub fn iter_var(&self) -> impl Iterator<Item = (&String, &Dynamic)> {
self.variables.iter()
}
/// Get an iterator to the functions in the module.
pub(crate) fn iter_fn(
&self,
) -> impl Iterator<Item = &(String, FnAccess, StaticVec<TypeId>, CallableFunction)> {
self.functions.values()
}
/// Create a new `Module` by evaluating an `AST`. /// Create a new `Module` by evaluating an `AST`.
/// ///
/// # Examples /// # Examples
@ -795,12 +843,12 @@ impl Module {
}, },
); );
module.lib = module.lib.merge(ast.lib()); module.merge(ast.lib());
Ok(module) Ok(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 index_all_sub_modules(&mut self) { pub(crate) fn index_all_sub_modules(&mut self) {
// Collect a particular module. // Collect a particular module.
@ -830,6 +878,18 @@ impl Module {
Private => continue, Private => continue,
Public => (), Public => (),
} }
if func.is_script() {
let fn_def = func.get_shared_fn_def();
// Qualifiers + function name + number of arguments.
let hash_fn_def = calc_fn_hash(
qualifiers.iter().map(|&v| v),
&fn_def.name,
fn_def.params.len(),
empty(),
);
functions.push((hash_fn_def, fn_def.into()));
} else {
// 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 + number of arguments. // i.e. qualifiers + function name + number of arguments.
@ -843,21 +903,6 @@ impl Module {
functions.push((hash_fn_native, func.clone())); functions.push((hash_fn_native, func.clone()));
} }
// Index all script-defined functions
for fn_def in module.lib.values() {
match fn_def.access {
// Private functions are not exported
Private => continue,
Public => (),
}
// Qualifiers + function name + number of arguments.
let hash_fn_def = calc_fn_hash(
qualifiers.iter().map(|&v| v),
&fn_def.name,
fn_def.params.len(),
empty(),
);
functions.push((hash_fn_def, CallableFunction::Script(fn_def.clone()).into()));
} }
} }

View File

@ -1,10 +1,8 @@
use crate::any::Dynamic; use crate::any::Dynamic;
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{ use crate::engine::{Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, use crate::module::Module;
KEYWORD_TYPE_OF, use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
};
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::utils::StaticVec; use crate::utils::StaticVec;
@ -54,14 +52,14 @@ struct State<'a> {
/// An `Engine` instance for eager function evaluation. /// An `Engine` instance for eager function evaluation.
engine: &'a Engine, engine: &'a Engine,
/// Library of script-defined functions. /// Library of script-defined functions.
lib: &'a FunctionsLib, lib: &'a Module,
/// Optimization level. /// Optimization level.
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
} }
impl<'a> State<'a> { impl<'a> State<'a> {
/// Create a new State. /// Create a new State.
pub fn new(engine: &'a Engine, lib: &'a FunctionsLib, level: OptimizationLevel) -> Self { pub fn new(engine: &'a Engine, lib: &'a Module, level: OptimizationLevel) -> Self {
Self { Self {
changed: false, changed: false,
constants: vec![], constants: vec![],
@ -547,14 +545,15 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
&& state.optimization_level == OptimizationLevel::Full // full optimizations && state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants && x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
=> { => {
let ((name, native_only, pos), _, _, args, def_value) = x.as_mut(); let ((name, _, pos), _, _, args, def_value) = x.as_mut();
// First search in script-defined functions (can override built-in) // First search in functions lib (can override built-in)
// Cater for both normal function call style and method call style (one additional arguments) // Cater for both normal function call style and method call style (one additional arguments)
if !*native_only && state.lib.values().find(|f| if state.lib.iter_fn().find(|(_, _, _, f)| {
&f.name == name if !f.is_script() { return false; }
&& (args.len()..=args.len() + 1).contains(&f.params.len()) let fn_def = f.get_fn_def();
).is_some() { &fn_def.name == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
}).is_some() {
// A script-defined function overrides the built-in function - do not make the call // A script-defined function overrides the built-in function - do not make the call
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
return Expr::FnCall(x); return Expr::FnCall(x);
@ -616,7 +615,7 @@ fn optimize(
statements: Vec<Stmt>, statements: Vec<Stmt>,
engine: &Engine, engine: &Engine,
scope: &Scope, scope: &Scope,
lib: &FunctionsLib, lib: &Module,
level: OptimizationLevel, level: OptimizationLevel,
) -> Vec<Stmt> { ) -> Vec<Stmt> {
// If optimization level is None then skip optimizing // If optimization level is None then skip optimizing
@ -703,7 +702,7 @@ pub fn optimize_into_ast(
engine: &Engine, engine: &Engine,
scope: &Scope, scope: &Scope,
statements: Vec<Stmt>, statements: Vec<Stmt>,
functions: Vec<FnDef>, functions: Vec<ScriptFnDef>,
level: OptimizationLevel, level: OptimizationLevel,
) -> AST { ) -> AST {
#[cfg(feature = "no_optimize")] #[cfg(feature = "no_optimize")]
@ -711,37 +710,65 @@ pub fn optimize_into_ast(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let lib = { let lib = {
if !level.is_none() { let mut module = Module::new();
let lib = FunctionsLib::from_iter(functions.iter().cloned());
FunctionsLib::from_iter(functions.into_iter().map(|mut fn_def| { if !level.is_none() {
// We only need the script library's signatures for optimization purposes
let mut lib2 = Module::new();
functions
.iter()
.map(|fn_def| {
ScriptFnDef {
name: fn_def.name.clone(),
access: fn_def.access,
body: Default::default(),
params: fn_def.params.clone(),
pos: fn_def.pos,
}
.into()
})
.for_each(|fn_def| lib2.set_script_fn(fn_def));
functions
.into_iter()
.map(|mut fn_def| {
let pos = fn_def.body.position(); let pos = fn_def.body.position();
// Optimize the function body // Optimize the function body
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib, level); let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib2, level);
// {} -> Noop // {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) { fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val // { return val; } -> val
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal(x)
if x.1.is_some() && (x.0).0 == ReturnType::Return =>
{
Stmt::Expr(Box::new(x.1.unwrap())) Stmt::Expr(Box::new(x.1.unwrap()))
} }
// { return; } -> () // { return; } -> ()
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => { Stmt::ReturnWithVal(x)
if x.1.is_none() && (x.0).0 == ReturnType::Return =>
{
Stmt::Expr(Box::new(Expr::Unit((x.0).1))) Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
} }
// All others // All others
stmt => stmt, stmt => stmt,
}; };
fn_def fn_def.into()
})) })
.for_each(|fn_def| module.set_script_fn(fn_def));
} else { } else {
FunctionsLib::from_iter(functions.into_iter()) functions
.into_iter()
.for_each(|fn_def| module.set_script_fn(fn_def));
} }
module
}; };
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let lib: FunctionsLib = Default::default(); let lib = Default::default();
AST::new( AST::new(
match level { match level {

View File

@ -2,9 +2,9 @@
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib}; use crate::engine::{make_getter, make_setter, Engine};
use crate::error::{LexError, ParseError, ParseErrorType}; use crate::error::{LexError, ParseError, ParseErrorType};
use crate::module::ModuleRef; use crate::module::{Module, ModuleRef};
use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token, TokenIterator}; use crate::token::{Position, Token, TokenIterator};
@ -55,12 +55,12 @@ pub struct AST(
/// Global statements. /// Global statements.
Vec<Stmt>, Vec<Stmt>,
/// Script-defined functions. /// Script-defined functions.
FunctionsLib, Module,
); );
impl AST { impl AST {
/// Create a new `AST`. /// Create a new `AST`.
pub fn new(statements: Vec<Stmt>, lib: FunctionsLib) -> Self { pub fn new(statements: Vec<Stmt>, lib: Module) -> Self {
Self(statements, lib) Self(statements, lib)
} }
@ -75,7 +75,7 @@ impl AST {
} }
/// Get the script-defined functions. /// Get the script-defined functions.
pub(crate) fn lib(&self) -> &FunctionsLib { pub(crate) fn lib(&self) -> &Module {
&self.1 &self.1
} }
@ -135,7 +135,10 @@ impl AST {
(true, true) => vec![], (true, true) => vec![],
}; };
Self::new(ast, functions.merge(&other.1)) let mut functions = functions.clone();
functions.merge(&other.1);
Self::new(ast, functions)
} }
/// Clear all function definitions in the `AST`. /// Clear all function definitions in the `AST`.
@ -170,7 +173,7 @@ pub enum FnAccess {
/// A scripted function definition. /// A scripted function definition.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FnDef { pub struct ScriptFnDef {
/// Function name. /// Function name.
pub name: String, pub name: String,
/// Function access mode. /// Function access mode.
@ -2393,7 +2396,7 @@ fn parse_fn<'a>(
level: usize, level: usize,
if_expr: bool, if_expr: bool,
stmt_expr: bool, stmt_expr: bool,
) -> Result<FnDef, ParseError> { ) -> Result<ScriptFnDef, ParseError> {
let pos = eat_token(input, Token::Fn); let pos = eat_token(input, Token::Fn);
if level > state.max_expr_depth { if level > state.max_expr_depth {
@ -2469,7 +2472,7 @@ fn parse_fn<'a>(
let params = params.into_iter().map(|(p, _)| p).collect(); let params = params.into_iter().map(|(p, _)| p).collect();
Ok(FnDef { Ok(ScriptFnDef {
name, name,
access, access,
params, params,
@ -2483,9 +2486,9 @@ fn parse_global_level<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
max_expr_depth: usize, max_expr_depth: usize,
max_function_expr_depth: usize, max_function_expr_depth: usize,
) -> Result<(Vec<Stmt>, Vec<FnDef>), ParseError> { ) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements = Vec::<Stmt>::new(); let mut statements = Vec::<Stmt>::new();
let mut functions = HashMap::<u64, FnDef, _>::with_hasher(StraightHasherBuilder); let mut functions = HashMap::<u64, ScriptFnDef, _>::with_hasher(StraightHasherBuilder);
let mut state = ParseState::new(max_expr_depth); let mut state = ParseState::new(max_expr_depth);
while !input.peek().unwrap().0.is_eof() { while !input.peek().unwrap().0.is_eof() {