Unify all functions into CallableFunction type, remove FunctionsLib.
This commit is contained in:
parent
e21d25a0c5
commit
56eb659d5a
16
src/api.rs
16
src/api.rs
@ -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());
|
||||||
|
262
src/engine.rs
262
src/engine.rs
@ -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.
|
name: &str,
|
||||||
///
|
params: usize,
|
||||||
/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_hash`.
|
public_only: bool,
|
||||||
#[derive(Debug, Clone, Default)]
|
) -> Option<&'a ScriptFnDef> {
|
||||||
pub struct FunctionsLib(HashMap<u64, Shared<FnDef>, StraightHasherBuilder>);
|
// Qualifiers (none) + function name + number of arguments.
|
||||||
|
let hash_fn_def = calc_fn_hash(empty(), name, params, empty());
|
||||||
|
let func = module.get_fn(hash_fn_def)?;
|
||||||
|
if !func.is_script() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let fn_def = func.get_fn_def();
|
||||||
|
|
||||||
impl FunctionsLib {
|
match fn_def.access {
|
||||||
/// Create a new `FunctionsLib` from a collection of `FnDef`.
|
FnAccess::Private if public_only => None,
|
||||||
pub fn from_iter(vec: impl IntoIterator<Item = FnDef>) -> Self {
|
FnAccess::Private | FnAccess::Public => Some(&fn_def),
|
||||||
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,
|
|
||||||
params: usize,
|
|
||||||
public_only: bool,
|
|
||||||
) -> Option<&FnDef> {
|
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
|
||||||
let hash_fn_def = calc_fn_hash(empty(), name, params, empty());
|
|
||||||
let fn_def = self.get_function(hash_fn_def);
|
|
||||||
|
|
||||||
match fn_def.as_ref().map(|f| f.access) {
|
|
||||||
None => None,
|
|
||||||
Some(FnAccess::Private) if public_only => None,
|
|
||||||
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,55 +597,48 @@ 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 {
|
||||||
|
// Calling pure function in method-call?
|
||||||
|
normalize_first_arg(
|
||||||
|
func.is_pure() && is_ref,
|
||||||
|
&mut this_copy,
|
||||||
|
&mut old_this_ptr,
|
||||||
|
args,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Run external function
|
||||||
|
let result = func.get_native_fn()(args)?;
|
||||||
|
|
||||||
|
// Restore the original reference
|
||||||
|
restore_first_arg(old_this_ptr, args);
|
||||||
|
|
||||||
|
// See if the function match print/debug (which requires special processing)
|
||||||
|
return Ok(match fn_name {
|
||||||
|
KEYWORD_PRINT => (
|
||||||
|
(self.print)(result.as_str().map_err(|type_name| {
|
||||||
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
type_name.into(),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?)
|
||||||
|
.into(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
KEYWORD_DEBUG => (
|
||||||
|
(self.debug)(result.as_str().map_err(|type_name| {
|
||||||
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
type_name.into(),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
})?)
|
||||||
|
.into(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
_ => (result, func.is_method()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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?
|
|
||||||
normalize_first_arg(
|
|
||||||
func.is_pure() && is_ref,
|
|
||||||
&mut this_copy,
|
|
||||||
&mut old_this_ptr,
|
|
||||||
args,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Run external function
|
|
||||||
let result = func.get_native_fn()(args)?;
|
|
||||||
|
|
||||||
// Restore the original reference
|
|
||||||
restore_first_arg(old_this_ptr, args);
|
|
||||||
|
|
||||||
// See if the function match print/debug (which requires special processing)
|
|
||||||
return Ok(match fn_name {
|
|
||||||
KEYWORD_PRINT => (
|
|
||||||
(self.print)(result.as_str().map_err(|type_name| {
|
|
||||||
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
||||||
type_name.into(),
|
|
||||||
Position::none(),
|
|
||||||
))
|
|
||||||
})?)
|
|
||||||
.into(),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
KEYWORD_DEBUG => (
|
|
||||||
(self.debug)(result.as_str().map_err(|type_name| {
|
|
||||||
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
||||||
type_name.into(),
|
|
||||||
Position::none(),
|
|
||||||
))
|
|
||||||
})?)
|
|
||||||
.into(),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
_ => (result, func.is_method()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if it is built in.
|
// See if it is built in.
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
||||||
@ -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)
|
|
||||||
// Then check packages
|
// First check script-defined functions
|
||||||
|| self.packages.contains_fn(hashes.0)
|
lib.contains_fn(hashes.1)
|
||||||
// Then check script-defined functions
|
//|| lib.contains_fn(hashes.0)
|
||||||
|| lib.contains_key(&hashes.1)
|
// Then check registered functions
|
||||||
|
//|| self.global_module.contains_fn(hashes.1)
|
||||||
|
|| self.global_module.contains_fn(hashes.0)
|
||||||
|
// Then check packages
|
||||||
|
//|| self.packages.contains_fn(hashes.1)
|
||||||
|
|| self.packages.contains_fn(hashes.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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>> {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
115
src/module.rs
115
src/module.rs
@ -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,34 +878,31 @@ impl Module {
|
|||||||
Private => continue,
|
Private => continue,
|
||||||
Public => (),
|
Public => (),
|
||||||
}
|
}
|
||||||
// Rust functions are indexed in two steps:
|
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
|
||||||
// i.e. qualifiers + function name + number of arguments.
|
|
||||||
let hash_fn_def =
|
|
||||||
calc_fn_hash(qualifiers.iter().map(|&v| v), name, params.len(), empty());
|
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
|
||||||
// zero number of arguments, and the actual list of argument `TypeId`'.s
|
|
||||||
let hash_fn_args = calc_fn_hash(empty(), "", 0, params.iter().cloned());
|
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
|
||||||
let hash_fn_native = hash_fn_def ^ hash_fn_args;
|
|
||||||
|
|
||||||
functions.push((hash_fn_native, func.clone()));
|
if func.is_script() {
|
||||||
}
|
let fn_def = func.get_shared_fn_def();
|
||||||
// Index all script-defined functions
|
// Qualifiers + function name + number of arguments.
|
||||||
for fn_def in module.lib.values() {
|
let hash_fn_def = calc_fn_hash(
|
||||||
match fn_def.access {
|
qualifiers.iter().map(|&v| v),
|
||||||
// Private functions are not exported
|
&fn_def.name,
|
||||||
Private => continue,
|
fn_def.params.len(),
|
||||||
Public => (),
|
empty(),
|
||||||
|
);
|
||||||
|
functions.push((hash_fn_def, fn_def.into()));
|
||||||
|
} else {
|
||||||
|
// Rust functions are indexed in two steps:
|
||||||
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
|
// i.e. qualifiers + function name + number of arguments.
|
||||||
|
let hash_fn_def =
|
||||||
|
calc_fn_hash(qualifiers.iter().map(|&v| v), name, params.len(), empty());
|
||||||
|
// 2) Calculate a second hash with no qualifiers, empty function name,
|
||||||
|
// zero number of arguments, and the actual list of argument `TypeId`'.s
|
||||||
|
let hash_fn_args = calc_fn_hash(empty(), "", 0, params.iter().cloned());
|
||||||
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
|
let hash_fn_native = hash_fn_def ^ hash_fn_args;
|
||||||
|
|
||||||
|
functions.push((hash_fn_native, func.clone()));
|
||||||
}
|
}
|
||||||
// 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()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
src/optimize.rs
103
src/optimize.rs
@ -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 = {
|
||||||
|
let mut module = Module::new();
|
||||||
|
|
||||||
if !level.is_none() {
|
if !level.is_none() {
|
||||||
let lib = FunctionsLib::from_iter(functions.iter().cloned());
|
// We only need the script library's signatures for optimization purposes
|
||||||
|
let mut lib2 = Module::new();
|
||||||
|
|
||||||
FunctionsLib::from_iter(functions.into_iter().map(|mut fn_def| {
|
functions
|
||||||
let pos = fn_def.body.position();
|
.iter()
|
||||||
|
.map(|fn_def| {
|
||||||
// Optimize the function body
|
ScriptFnDef {
|
||||||
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib, level);
|
name: fn_def.name.clone(),
|
||||||
|
access: fn_def.access,
|
||||||
// {} -> Noop
|
body: Default::default(),
|
||||||
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
params: fn_def.params.clone(),
|
||||||
// { return val; } -> val
|
pos: fn_def.pos,
|
||||||
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
|
|
||||||
Stmt::Expr(Box::new(x.1.unwrap()))
|
|
||||||
}
|
}
|
||||||
// { return; } -> ()
|
.into()
|
||||||
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => {
|
})
|
||||||
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
|
.for_each(|fn_def| lib2.set_script_fn(fn_def));
|
||||||
}
|
|
||||||
// All others
|
functions
|
||||||
stmt => stmt,
|
.into_iter()
|
||||||
};
|
.map(|mut fn_def| {
|
||||||
fn_def
|
let pos = fn_def.body.position();
|
||||||
}))
|
|
||||||
|
// Optimize the function body
|
||||||
|
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib2, level);
|
||||||
|
|
||||||
|
// {} -> Noop
|
||||||
|
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
|
// { return val; } -> val
|
||||||
|
Stmt::ReturnWithVal(x)
|
||||||
|
if x.1.is_some() && (x.0).0 == ReturnType::Return =>
|
||||||
|
{
|
||||||
|
Stmt::Expr(Box::new(x.1.unwrap()))
|
||||||
|
}
|
||||||
|
// { return; } -> ()
|
||||||
|
Stmt::ReturnWithVal(x)
|
||||||
|
if x.1.is_none() && (x.0).0 == ReturnType::Return =>
|
||||||
|
{
|
||||||
|
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
|
||||||
|
}
|
||||||
|
// All others
|
||||||
|
stmt => stmt,
|
||||||
|
};
|
||||||
|
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 {
|
||||||
|
@ -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() {
|
||||||
|
Loading…
Reference in New Issue
Block a user