Reduce overhead of Engine
by not creating hash maps until used.
This commit is contained in:
parent
12a379dd57
commit
d1cffac420
37
src/api.rs
37
src/api.rs
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::any::{Any, AnyExt, Dynamic};
|
use crate::any::{Any, AnyExt, Dynamic};
|
||||||
use crate::call::FuncArgs;
|
use crate::call::FuncArgs;
|
||||||
use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec};
|
use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec, FunctionsLib};
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::fn_register::RegisterFn;
|
use crate::fn_register::RegisterFn;
|
||||||
use crate::parser::{lex, parse, parse_global_expr, FnDef, Position, AST};
|
use crate::parser::{lex, parse, parse_global_expr, FnDef, Position, AST};
|
||||||
@ -15,6 +15,7 @@ use crate::optimize::optimize_into_ast;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
|
collections::HashMap,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -68,7 +69,10 @@ impl<'e> Engine<'e> {
|
|||||||
args,
|
args,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.functions.insert(spec, f);
|
if self.functions.is_none() {
|
||||||
|
self.functions = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
self.functions.as_mut().unwrap().insert(spec, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a custom type for use with the `Engine`.
|
/// Register a custom type for use with the `Engine`.
|
||||||
@ -157,15 +161,28 @@ impl<'e> Engine<'e> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn register_type_with_name<T: Any + Clone>(&mut self, name: &str) {
|
pub fn register_type_with_name<T: Any + Clone>(&mut self, name: &str) {
|
||||||
|
if self.type_names.is_none() {
|
||||||
|
self.type_names = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
// Add the pretty-print type name into the map
|
// Add the pretty-print type name into the map
|
||||||
self.type_names
|
self.type_names
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
.insert(type_name::<T>().to_string(), name.to_string());
|
.insert(type_name::<T>().to_string(), name.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register an iterator adapter for a type with the `Engine`.
|
/// Register an iterator adapter for a type with the `Engine`.
|
||||||
/// This is an advanced feature.
|
/// This is an advanced feature.
|
||||||
pub fn register_iterator<T: Any, F: IteratorCallback>(&mut self, f: F) {
|
pub fn register_iterator<T: Any, F: IteratorCallback>(&mut self, f: F) {
|
||||||
self.type_iterators.insert(TypeId::of::<T>(), Box::new(f));
|
if self.type_iterators.is_none() {
|
||||||
|
self.type_iterators = Some(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.type_iterators
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert(TypeId::of::<T>(), Box::new(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a getter function for a member of a registered type with the `Engine`.
|
/// Register a getter function for a member of a registered type with the `Engine`.
|
||||||
@ -876,8 +893,12 @@ impl<'e> Engine<'e> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
functions: impl IntoIterator<Item = &'a Arc<FnDef>>,
|
functions: impl IntoIterator<Item = &'a Arc<FnDef>>,
|
||||||
) {
|
) {
|
||||||
|
if self.fn_lib.is_none() {
|
||||||
|
self.fn_lib = Some(FunctionsLib::new());
|
||||||
|
}
|
||||||
|
|
||||||
functions.into_iter().cloned().for_each(|f| {
|
functions.into_iter().cloned().for_each(|f| {
|
||||||
self.fn_lib.add_or_replace_function(f);
|
self.fn_lib.as_mut().unwrap().add_or_replace_function(f);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,7 +986,7 @@ impl<'e> Engine<'e> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub fn on_print(&mut self, callback: impl FnMut(&str) + Send + Sync + 'e) {
|
pub fn on_print(&mut self, callback: impl FnMut(&str) + Send + Sync + 'e) {
|
||||||
self.on_print = Box::new(callback);
|
self.on_print = Some(Box::new(callback));
|
||||||
}
|
}
|
||||||
/// Override default action of `print` (print to stdout using `println!`)
|
/// Override default action of `print` (print to stdout using `println!`)
|
||||||
///
|
///
|
||||||
@ -989,7 +1010,7 @@ impl<'e> Engine<'e> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub fn on_print(&mut self, callback: impl FnMut(&str) + 'e) {
|
pub fn on_print(&mut self, callback: impl FnMut(&str) + 'e) {
|
||||||
self.on_print = Box::new(callback);
|
self.on_print = Some(Box::new(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override default action of `debug` (print to stdout using `println!`)
|
/// Override default action of `debug` (print to stdout using `println!`)
|
||||||
@ -1014,7 +1035,7 @@ impl<'e> Engine<'e> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub fn on_debug(&mut self, callback: impl FnMut(&str) + Send + Sync + 'e) {
|
pub fn on_debug(&mut self, callback: impl FnMut(&str) + Send + Sync + 'e) {
|
||||||
self.on_debug = Box::new(callback);
|
self.on_debug = Some(Box::new(callback));
|
||||||
}
|
}
|
||||||
/// Override default action of `debug` (print to stdout using `println!`)
|
/// Override default action of `debug` (print to stdout using `println!`)
|
||||||
///
|
///
|
||||||
@ -1038,6 +1059,6 @@ impl<'e> Engine<'e> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub fn on_debug(&mut self, callback: impl FnMut(&str) + 'e) {
|
pub fn on_debug(&mut self, callback: impl FnMut(&str) + 'e) {
|
||||||
self.on_debug = Box::new(callback);
|
self.on_debug = Some(Box::new(callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
205
src/engine.rs
205
src/engine.rs
@ -150,10 +150,6 @@ impl FunctionsLib {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
FunctionsLib(Vec::new())
|
FunctionsLib(Vec::new())
|
||||||
}
|
}
|
||||||
/// Clear the `FunctionsLib`.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.0.clear();
|
|
||||||
}
|
|
||||||
/// Does a certain function exist in the `FunctionsLib`?
|
/// Does a certain function exist in the `FunctionsLib`?
|
||||||
pub fn has_function(&self, name: &str, params: usize) -> bool {
|
pub fn has_function(&self, name: &str, params: usize) -> bool {
|
||||||
self.0.binary_search_by(|f| f.compare(name, params)).is_ok()
|
self.0.binary_search_by(|f| f.compare(name, params)).is_ok()
|
||||||
@ -196,25 +192,25 @@ impl FunctionsLib {
|
|||||||
/// Currently, `Engine` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, `Engine` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
pub struct Engine<'e> {
|
pub struct Engine<'e> {
|
||||||
/// A hashmap containing all compiled functions known to the engine.
|
/// A hashmap containing all compiled functions known to the engine.
|
||||||
pub(crate) functions: HashMap<FnSpec<'e>, Box<FnAny>>,
|
pub(crate) functions: Option<HashMap<FnSpec<'e>, Box<FnAny>>>,
|
||||||
/// A hashmap containing all script-defined functions.
|
/// A hashmap containing all script-defined functions.
|
||||||
pub(crate) fn_lib: FunctionsLib,
|
pub(crate) fn_lib: Option<FunctionsLib>,
|
||||||
/// A hashmap containing all iterators known to the engine.
|
/// A hashmap containing all iterators known to the engine.
|
||||||
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
pub(crate) type_iterators: Option<HashMap<TypeId, Box<IteratorFn>>>,
|
||||||
/// A hashmap mapping type names to pretty-print names.
|
/// A hashmap mapping type names to pretty-print names.
|
||||||
pub(crate) type_names: HashMap<String, String>,
|
pub(crate) type_names: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
/// Closure for implementing the `print` command.
|
/// Closure for implementing the `print` command.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub(crate) on_print: Box<dyn FnMut(&str) + Send + Sync + 'e>,
|
pub(crate) on_print: Option<Box<dyn FnMut(&str) + Send + Sync + 'e>>,
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub(crate) on_print: Box<dyn FnMut(&str) + 'e>,
|
pub(crate) on_print: Option<Box<dyn FnMut(&str) + 'e>>,
|
||||||
|
|
||||||
/// Closure for implementing the `debug` command.
|
/// Closure for implementing the `debug` command.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub(crate) on_debug: Box<dyn FnMut(&str) + Send + Sync + 'e>,
|
pub(crate) on_debug: Option<Box<dyn FnMut(&str) + Send + Sync + 'e>>,
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
pub(crate) on_debug: Option<Box<dyn FnMut(&str) + 'e>>,
|
||||||
|
|
||||||
/// Optimize the AST after compilation.
|
/// Optimize the AST after compilation.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
@ -241,12 +237,12 @@ impl Default for Engine<'_> {
|
|||||||
|
|
||||||
// Create the new scripting Engine
|
// Create the new scripting Engine
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
functions: HashMap::new(),
|
functions: None,
|
||||||
fn_lib: FunctionsLib::new(),
|
fn_lib: None,
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: None,
|
||||||
type_names,
|
type_names: Some(type_names),
|
||||||
on_print: Box::new(default_print), // default print/debug implementations
|
on_print: Some(Box::new(default_print)), // default print/debug implementations
|
||||||
on_debug: Box::new(default_print),
|
on_debug: Some(Box::new(default_print)),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[cfg(not(feature = "optimize_full"))]
|
#[cfg(not(feature = "optimize_full"))]
|
||||||
@ -307,6 +303,35 @@ impl Engine<'_> {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Engine` with minimal configurations - i.e. without pretty-print type names etc.
|
||||||
|
pub fn new_raw() -> Self {
|
||||||
|
let mut engine = Engine {
|
||||||
|
functions: None,
|
||||||
|
fn_lib: None,
|
||||||
|
type_iterators: None,
|
||||||
|
type_names: None,
|
||||||
|
on_print: None,
|
||||||
|
on_debug: None,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[cfg(not(feature = "optimize_full"))]
|
||||||
|
optimization_level: OptimizationLevel::Simple,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
#[cfg(feature = "optimize_full")]
|
||||||
|
optimization_level: OptimizationLevel::Full,
|
||||||
|
|
||||||
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
engine.register_core_lib();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_stdlib"))]
|
||||||
|
engine.register_stdlib(); // Register the standard library when no_stdlib is not set
|
||||||
|
|
||||||
|
engine
|
||||||
|
}
|
||||||
|
|
||||||
/// Control whether and how the `Engine` will optimize an AST after compilation
|
/// Control whether and how the `Engine` will optimize an AST after compilation
|
||||||
///
|
///
|
||||||
/// Not available under the `no_optimize` feature.
|
/// Not available under the `no_optimize` feature.
|
||||||
@ -335,9 +360,13 @@ impl Engine<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Search built-in's and external functions
|
// Search built-in's and external functions
|
||||||
if let Some(func) = self.functions.get(&spec) {
|
if let Some(ref functions) = self.functions {
|
||||||
// Run external function
|
if let Some(func) = functions.get(&spec) {
|
||||||
Ok(Some(func(args, pos)?))
|
// Run external function
|
||||||
|
Ok(Some(func(args, pos)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -353,26 +382,28 @@ impl Engine<'_> {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, EvalAltResult> {
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if let Some(fn_def) = self.fn_lib.get_function(fn_name, args.len()) {
|
if let Some(ref fn_lib) = self.fn_lib {
|
||||||
let mut scope = Scope::new();
|
if let Some(fn_def) = fn_lib.get_function(fn_name, args.len()) {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
scope.extend(
|
scope.extend(
|
||||||
// Put arguments into scope as variables
|
// Put arguments into scope as variables
|
||||||
fn_def
|
fn_def
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.zip(args.iter().map(|x| (*x).into_dynamic()))
|
.zip(args.iter().map(|x| (*x).into_dynamic()))
|
||||||
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
return self
|
return self.eval_stmt(&mut scope, &fn_def.body, level + 1).or_else(
|
||||||
.eval_stmt(&mut scope, &fn_def.body, level + 1)
|
|err| match err {
|
||||||
.or_else(|err| match err {
|
// Convert return statement to return value
|
||||||
// Convert return statement to return value
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
err => Err(err.set_position(pos)),
|
||||||
err => Err(err.set_position(pos)),
|
},
|
||||||
});
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let spec = FnSpec {
|
let spec = FnSpec {
|
||||||
@ -388,20 +419,25 @@ impl Engine<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search built-in's and external functions
|
// Search built-in's and external functions
|
||||||
if let Some(func) = self.functions.get(&spec) {
|
if let Some(ref functions) = self.functions {
|
||||||
// Run external function
|
if let Some(func) = functions.get(&spec) {
|
||||||
let result = func(args, pos)?;
|
// Run external function
|
||||||
|
let result = func(args, pos)?;
|
||||||
|
|
||||||
// See if the function match print/debug (which requires special processing)
|
// See if the function match print/debug (which requires special processing)
|
||||||
return Ok(match fn_name {
|
return Ok(match fn_name {
|
||||||
KEYWORD_PRINT => {
|
KEYWORD_PRINT if self.on_print.is_some() => {
|
||||||
self.on_print.as_mut()(cast_to_string(result.as_ref(), pos)?).into_dynamic()
|
self.on_print.as_deref_mut().unwrap()(cast_to_string(result.as_ref(), pos)?)
|
||||||
}
|
.into_dynamic()
|
||||||
KEYWORD_DEBUG => {
|
}
|
||||||
self.on_debug.as_mut()(cast_to_string(result.as_ref(), pos)?).into_dynamic()
|
KEYWORD_DEBUG if self.on_debug.is_some() => {
|
||||||
}
|
self.on_debug.as_deref_mut().unwrap()(cast_to_string(result.as_ref(), pos)?)
|
||||||
_ => result,
|
.into_dynamic()
|
||||||
});
|
}
|
||||||
|
KEYWORD_PRINT | KEYWORD_DEBUG => ().into_dynamic(),
|
||||||
|
_ => result,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
||||||
@ -1192,12 +1228,13 @@ impl Engine<'_> {
|
|||||||
Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => {
|
Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => {
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
fn has_override(engine: &Engine, name: &str) -> bool {
|
fn has_override(engine: &Engine, name: &str) -> bool {
|
||||||
let spec = FnSpec {
|
(engine.functions.is_some() && {
|
||||||
name: name.into(),
|
engine.functions.as_ref().unwrap().contains_key(&FnSpec {
|
||||||
args: vec![TypeId::of::<String>()],
|
name: name.into(),
|
||||||
};
|
args: vec![TypeId::of::<String>()],
|
||||||
|
})
|
||||||
engine.functions.contains_key(&spec) || engine.fn_lib.has_function(name, 1)
|
}) || (engine.fn_lib.is_some()
|
||||||
|
&& engine.fn_lib.as_ref().unwrap().has_function(name, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
match fn_name.as_str() {
|
match fn_name.as_str() {
|
||||||
@ -1399,27 +1436,31 @@ impl Engine<'_> {
|
|||||||
let arr = self.eval_expr(scope, expr, level)?;
|
let arr = self.eval_expr(scope, expr, level)?;
|
||||||
let tid = Any::type_id(&*arr);
|
let tid = Any::type_id(&*arr);
|
||||||
|
|
||||||
if let Some(iter_fn) = self.type_iterators.get(&tid) {
|
if let Some(ref type_iterators) = self.type_iterators {
|
||||||
scope.push(name.clone(), ());
|
if let Some(iter_fn) = type_iterators.get(&tid) {
|
||||||
|
scope.push(name.clone(), ());
|
||||||
|
|
||||||
let entry = ScopeSource {
|
let entry = ScopeSource {
|
||||||
name,
|
name,
|
||||||
index: scope.len() - 1,
|
index: scope.len() - 1,
|
||||||
typ: ScopeEntryType::Normal,
|
typ: ScopeEntryType::Normal,
|
||||||
};
|
};
|
||||||
|
|
||||||
for a in iter_fn(&arr) {
|
for a in iter_fn(&arr) {
|
||||||
*scope.get_mut(entry) = a;
|
*scope.get_mut(entry) = a;
|
||||||
|
|
||||||
match self.eval_stmt(scope, body, level) {
|
match self.eval_stmt(scope, body, level) {
|
||||||
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (),
|
||||||
Err(EvalAltResult::ErrorLoopBreak(true, _)) => break,
|
Err(EvalAltResult::ErrorLoopBreak(true, _)) => break,
|
||||||
Err(x) => return Err(x),
|
Err(x) => return Err(x),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scope.rewind(scope.len() - 1);
|
scope.rewind(scope.len() - 1);
|
||||||
Ok(().into_dynamic())
|
Ok(().into_dynamic())
|
||||||
|
} else {
|
||||||
|
Err(EvalAltResult::ErrorFor(expr.position()))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorFor(expr.position()))
|
Err(EvalAltResult::ErrorFor(expr.position()))
|
||||||
}
|
}
|
||||||
@ -1481,15 +1522,21 @@ impl Engine<'_> {
|
|||||||
|
|
||||||
/// Map a type_name into a pretty-print name
|
/// Map a type_name into a pretty-print name
|
||||||
pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
pub(crate) fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
||||||
self.type_names
|
if self.type_names.is_none() {
|
||||||
.get(name)
|
name
|
||||||
.map(String::as_str)
|
} else {
|
||||||
.unwrap_or(name)
|
self.type_names
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get(name)
|
||||||
|
.map(String::as_str)
|
||||||
|
.unwrap_or(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up all script-defined functions within the `Engine`.
|
/// Clean up all script-defined functions within the `Engine`.
|
||||||
pub fn clear_functions(&mut self) {
|
pub fn clear_functions(&mut self) {
|
||||||
self.fn_lib.clear();
|
self.fn_lib = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,9 +451,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if state.engine.fn_lib.has_function(&id, args.len()) {
|
if let Some(ref fn_lib) = state.engine.fn_lib {
|
||||||
// A script-defined function overrides the built-in function - do not make the call
|
if fn_lib.has_function(&id, args.len()) {
|
||||||
return Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos);
|
// A script-defined function overrides the built-in function - do not make the call
|
||||||
|
return Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||||
|
Loading…
Reference in New Issue
Block a user