Cache built-in functions to improve resolution speed.
This commit is contained in:
parent
67d277aa21
commit
b76e8da5ee
@ -29,6 +29,7 @@ Enhancements
|
|||||||
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
||||||
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.13
|
Version 0.19.13
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
1194
src/builtin.rs
Normal file
1194
src/builtin.rs
Normal file
File diff suppressed because it is too large
Load Diff
532
src/fn_call.rs
532
src/fn_call.rs
@ -1,11 +1,12 @@
|
|||||||
//! Implement function-calling mechanism for [`Engine`].
|
//! Implement function-calling mechanism for [`Engine`].
|
||||||
|
|
||||||
|
use crate::builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL,
|
Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL,
|
||||||
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
MAX_DYNAMIC_PARAMETERS,
|
MAX_DYNAMIC_PARAMETERS,
|
||||||
};
|
};
|
||||||
use crate::fn_native::FnCallArgs;
|
use crate::fn_native::{FnAny, FnCallArgs};
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
@ -29,19 +30,9 @@ use crate::{
|
|||||||
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT,
|
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
use crate::FLOAT;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
use rust_decimal::Decimal;
|
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
use num_traits::float::Float;
|
|
||||||
|
|
||||||
/// Extract the property name from a getter function name.
|
/// Extract the property name from a getter function name.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -199,6 +190,7 @@ impl Engine {
|
|||||||
mut hash: NonZeroU64,
|
mut hash: NonZeroU64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
|
is_op_assignment: bool,
|
||||||
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
||||||
fn find_function(
|
fn find_function(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
@ -254,7 +246,36 @@ impl Engine {
|
|||||||
Some(f) => return Some(f),
|
Some(f) => return Some(f),
|
||||||
|
|
||||||
// Stop when all permutations are exhausted
|
// Stop when all permutations are exhausted
|
||||||
None if bitmask >= max_bitmask => return None,
|
None if bitmask >= max_bitmask => {
|
||||||
|
return if num_args != 2 || args[0].is_variant() || args[1].is_variant()
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else if !is_op_assignment {
|
||||||
|
if let Some(f) =
|
||||||
|
get_builtin_binary_op_fn(fn_name, &args[0], &args[1])
|
||||||
|
{
|
||||||
|
Some((
|
||||||
|
CallableFunction::from_method(Box::new(f) as Box<FnAny>),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (first, second) = args.split_first().unwrap();
|
||||||
|
|
||||||
|
if let Some(f) =
|
||||||
|
get_builtin_op_assignment_fn(fn_name, *first, second[0])
|
||||||
|
{
|
||||||
|
Some((
|
||||||
|
CallableFunction::from_method(Box::new(f) as Box<FnAny>),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try all permutations with `Dynamic` wildcards
|
// Try all permutations with `Dynamic` wildcards
|
||||||
None => {
|
None => {
|
||||||
@ -304,7 +325,16 @@ impl Engine {
|
|||||||
let source = state.source.clone();
|
let source = state.source.clone();
|
||||||
|
|
||||||
// Check if function access already in the cache
|
// Check if function access already in the cache
|
||||||
let func = self.resolve_function(mods, state, lib, fn_name, hash_fn, args, true);
|
let func = self.resolve_function(
|
||||||
|
mods,
|
||||||
|
state,
|
||||||
|
lib,
|
||||||
|
fn_name,
|
||||||
|
hash_fn,
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
is_op_assignment,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some((func, src)) = func {
|
if let Some((func, src)) = func {
|
||||||
assert!(func.is_native());
|
assert!(func.is_native());
|
||||||
@ -354,31 +384,6 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if it is built in.
|
|
||||||
if args.len() == 2 && !args[0].is_variant() && !args[1].is_variant() {
|
|
||||||
// Op-assignment?
|
|
||||||
if is_op_assignment {
|
|
||||||
if !is_ref {
|
|
||||||
unreachable!("op-assignments must have ref argument");
|
|
||||||
}
|
|
||||||
let (first, second) = args.split_first_mut().unwrap();
|
|
||||||
|
|
||||||
match run_builtin_op_assignment(fn_name, first, second[0])
|
|
||||||
.map_err(|err| err.fill_position(pos))?
|
|
||||||
{
|
|
||||||
Some(_) => return Ok((Dynamic::UNIT, false)),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match run_builtin_binary_op(fn_name, args[0], args[1])
|
|
||||||
.map_err(|err| err.fill_position(pos))?
|
|
||||||
{
|
|
||||||
Some(v) => return Ok((v, false)),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter function not found?
|
// Getter function not found?
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
||||||
@ -714,7 +719,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some((func, source)) = hash_script.and_then(|hash| {
|
if let Some((func, source)) = hash_script.and_then(|hash| {
|
||||||
self.resolve_function(mods, state, lib, fn_name, hash, args, false)
|
self.resolve_function(mods, state, lib, fn_name, hash, args, false, false)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|(f, s)| (f.clone(), s.clone()))
|
.map(|(f, s)| (f.clone(), s.clone()))
|
||||||
}) {
|
}) {
|
||||||
@ -1401,452 +1406,3 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build in common binary operator implementations to avoid the cost of calling a registered function.
|
|
||||||
pub fn run_builtin_binary_op(
|
|
||||||
op: &str,
|
|
||||||
x: &Dynamic,
|
|
||||||
y: &Dynamic,
|
|
||||||
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
|
||||||
let type1 = x.type_id();
|
|
||||||
let type2 = y.type_id();
|
|
||||||
|
|
||||||
if x.is_variant() || y.is_variant() {
|
|
||||||
// One of the operands is a custom type, so it is never built-in
|
|
||||||
return Ok(match op {
|
|
||||||
"!=" if type1 != type2 => Some(Dynamic::TRUE),
|
|
||||||
"==" | ">" | ">=" | "<" | "<=" if type1 != type2 => Some(Dynamic::FALSE),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let types_pair = (type1, type2);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
if let Some((x, y)) = if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
|
||||||
// FLOAT op FLOAT
|
|
||||||
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<FLOAT>()))
|
|
||||||
} else if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
|
||||||
// FLOAT op INT
|
|
||||||
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<INT>() as FLOAT))
|
|
||||||
} else if types_pair == (TypeId::of::<INT>(), TypeId::of::<FLOAT>()) {
|
|
||||||
// INT op FLOAT
|
|
||||||
Some((x.clone().cast::<INT>() as FLOAT, y.clone().cast::<FLOAT>()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
} {
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some((x + y).into())),
|
|
||||||
"-" => return Ok(Some((x - y).into())),
|
|
||||||
"*" => return Ok(Some((x * y).into())),
|
|
||||||
"/" => return Ok(Some((x / y).into())),
|
|
||||||
"%" => return Ok(Some((x % y).into())),
|
|
||||||
"**" => return Ok(Some(x.powf(y).into())),
|
|
||||||
"==" => return Ok(Some((x == y).into())),
|
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
|
||||||
">" => return Ok(Some((x > y).into())),
|
|
||||||
">=" => return Ok(Some((x >= y).into())),
|
|
||||||
"<" => return Ok(Some((x < y).into())),
|
|
||||||
"<=" => return Ok(Some((x <= y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
if let Some((x, y)) = if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
|
||||||
// Decimal op Decimal
|
|
||||||
Some((
|
|
||||||
*x.read_lock::<Decimal>().unwrap(),
|
|
||||||
*y.read_lock::<Decimal>().unwrap(),
|
|
||||||
))
|
|
||||||
} else if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
|
||||||
// Decimal op INT
|
|
||||||
Some((
|
|
||||||
*x.read_lock::<Decimal>().unwrap(),
|
|
||||||
y.clone().cast::<INT>().into(),
|
|
||||||
))
|
|
||||||
} else if types_pair == (TypeId::of::<INT>(), TypeId::of::<Decimal>()) {
|
|
||||||
// INT op Decimal
|
|
||||||
Some((
|
|
||||||
x.clone().cast::<INT>().into(),
|
|
||||||
*y.read_lock::<Decimal>().unwrap(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
} {
|
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return add(x, y).map(Some),
|
|
||||||
"-" => return subtract(x, y).map(Some),
|
|
||||||
"*" => return multiply(x, y).map(Some),
|
|
||||||
"/" => return divide(x, y).map(Some),
|
|
||||||
"%" => return modulo(x, y).map(Some),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some((x + y).into())),
|
|
||||||
"-" => return Ok(Some((x - y).into())),
|
|
||||||
"*" => return Ok(Some((x * y).into())),
|
|
||||||
"/" => return Ok(Some((x / y).into())),
|
|
||||||
"%" => return Ok(Some((x % y).into())),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"==" => return Ok(Some((x == y).into())),
|
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
|
||||||
">" => return Ok(Some((x > y).into())),
|
|
||||||
">=" => return Ok(Some((x >= y).into())),
|
|
||||||
"<" => return Ok(Some((x < y).into())),
|
|
||||||
"<=" => return Ok(Some((x <= y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// char op string
|
|
||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
|
||||||
let x = x.clone().cast::<char>();
|
|
||||||
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
|
||||||
"==" | "!=" | ">" | ">=" | "<" | "<=" => {
|
|
||||||
let s1 = [x, '\0'];
|
|
||||||
let mut y = y.chars();
|
|
||||||
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"==" => return Ok(Some((s1 == s2).into())),
|
|
||||||
"!=" => return Ok(Some((s1 != s2).into())),
|
|
||||||
">" => return Ok(Some((s1 > s2).into())),
|
|
||||||
">=" => return Ok(Some((s1 >= s2).into())),
|
|
||||||
"<" => return Ok(Some((s1 < s2).into())),
|
|
||||||
"<=" => return Ok(Some((s1 <= s2).into())),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// string op char
|
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
|
||||||
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
|
||||||
let y = y.clone().cast::<char>();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some((x + y).into())),
|
|
||||||
"-" => return Ok(Some((x - y).into())),
|
|
||||||
"==" | "!=" | ">" | ">=" | "<" | "<=" => {
|
|
||||||
let mut x = x.chars();
|
|
||||||
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
|
||||||
let s2 = [y, '\0'];
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"==" => return Ok(Some((s1 == s2).into())),
|
|
||||||
"!=" => return Ok(Some((s1 != s2).into())),
|
|
||||||
">" => return Ok(Some((s1 > s2).into())),
|
|
||||||
">=" => return Ok(Some((s1 >= s2).into())),
|
|
||||||
"<" => return Ok(Some((s1 < s2).into())),
|
|
||||||
"<=" => return Ok(Some((s1 <= s2).into())),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default comparison operators for different types
|
|
||||||
if type2 != type1 {
|
|
||||||
return Ok(match op {
|
|
||||||
"!=" => Some(Dynamic::TRUE),
|
|
||||||
"==" | ">" | ">=" | "<" | "<=" => Some(Dynamic::FALSE),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beyond here, type1 == type2
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<INT>() {
|
|
||||||
let x = x.clone().cast::<INT>();
|
|
||||||
let y = y.clone().cast::<INT>();
|
|
||||||
|
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return add(x, y).map(Some),
|
|
||||||
"-" => return subtract(x, y).map(Some),
|
|
||||||
"*" => return multiply(x, y).map(Some),
|
|
||||||
"/" => return divide(x, y).map(Some),
|
|
||||||
"%" => return modulo(x, y).map(Some),
|
|
||||||
"**" => return power(x, y).map(Some),
|
|
||||||
">>" => return shift_right(x, y).map(Some),
|
|
||||||
"<<" => return shift_left(x, y).map(Some),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some((x + y).into())),
|
|
||||||
"-" => return Ok(Some((x - y).into())),
|
|
||||||
"*" => return Ok(Some((x * y).into())),
|
|
||||||
"/" => return Ok(Some((x / y).into())),
|
|
||||||
"%" => return Ok(Some((x % y).into())),
|
|
||||||
"**" => return Ok(Some(x.pow(y as u32).into())),
|
|
||||||
">>" => return Ok(Some((x >> y).into())),
|
|
||||||
"<<" => return Ok(Some((x << y).into())),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"==" => return Ok(Some((x == y).into())),
|
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
|
||||||
">" => return Ok(Some((x > y).into())),
|
|
||||||
">=" => return Ok(Some((x >= y).into())),
|
|
||||||
"<" => return Ok(Some((x < y).into())),
|
|
||||||
"<=" => return Ok(Some((x <= y).into())),
|
|
||||||
"&" => return Ok(Some((x & y).into())),
|
|
||||||
"|" => return Ok(Some((x | y).into())),
|
|
||||||
"^" => return Ok(Some((x ^ y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<bool>() {
|
|
||||||
let x = x.clone().cast::<bool>();
|
|
||||||
let y = y.clone().cast::<bool>();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"&" => return Ok(Some((x && y).into())),
|
|
||||||
"|" => return Ok(Some((x || y).into())),
|
|
||||||
"^" => return Ok(Some((x ^ y).into())),
|
|
||||||
"==" => return Ok(Some((x == y).into())),
|
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
|
||||||
let x = &*x.read_lock::<ImmutableString>().unwrap();
|
|
||||||
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some((x + y).into())),
|
|
||||||
"-" => return Ok(Some((x - y).into())),
|
|
||||||
"==" => return Ok(Some((x == y).into())),
|
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
|
||||||
">" => return Ok(Some((x > y).into())),
|
|
||||||
">=" => return Ok(Some((x >= y).into())),
|
|
||||||
"<" => return Ok(Some((x < y).into())),
|
|
||||||
"<=" => return Ok(Some((x <= y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<char>() {
|
|
||||||
let x = x.clone().cast::<char>();
|
|
||||||
let y = y.clone().cast::<char>();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+" => return Ok(Some(format!("{}{}", x, y).into())),
|
|
||||||
"==" => return Ok(Some((x == y).into())),
|
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
|
||||||
">" => return Ok(Some((x > y).into())),
|
|
||||||
">=" => return Ok(Some((x >= y).into())),
|
|
||||||
"<" => return Ok(Some((x < y).into())),
|
|
||||||
"<=" => return Ok(Some((x <= y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<()>() {
|
|
||||||
match op {
|
|
||||||
"==" => return Ok(Some(true.into())),
|
|
||||||
"!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build in common operator assignment implementations to avoid the cost of calling a registered function.
|
|
||||||
pub fn run_builtin_op_assignment(
|
|
||||||
op: &str,
|
|
||||||
x: &mut Dynamic,
|
|
||||||
y: &Dynamic,
|
|
||||||
) -> Result<Option<()>, Box<EvalAltResult>> {
|
|
||||||
let type1 = x.type_id();
|
|
||||||
let type2 = y.type_id();
|
|
||||||
|
|
||||||
let types_pair = (type1, type2);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
if let Some((mut x, y)) = if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
|
|
||||||
// FLOAT op= FLOAT
|
|
||||||
let y = y.clone().cast::<FLOAT>();
|
|
||||||
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
|
||||||
} else if types_pair == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
|
|
||||||
// FLOAT op= INT
|
|
||||||
let y = y.clone().cast::<INT>() as FLOAT;
|
|
||||||
Some((x.write_lock::<FLOAT>().unwrap(), y))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
} {
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x += y)),
|
|
||||||
"-=" => return Ok(Some(*x -= y)),
|
|
||||||
"*=" => return Ok(Some(*x *= y)),
|
|
||||||
"/=" => return Ok(Some(*x /= y)),
|
|
||||||
"%=" => return Ok(Some(*x %= y)),
|
|
||||||
"**=" => return Ok(Some(*x = x.powf(y))),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
if let Some((mut x, y)) = if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<Decimal>()) {
|
|
||||||
// Decimal op= Decimal
|
|
||||||
let y = *y.read_lock::<Decimal>().unwrap();
|
|
||||||
Some((x.write_lock::<Decimal>().unwrap(), y))
|
|
||||||
} else if types_pair == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
|
|
||||||
// Decimal op= INT
|
|
||||||
let y = y.clone().cast::<INT>().into();
|
|
||||||
Some((x.write_lock::<Decimal>().unwrap(), y))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
} {
|
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x = add(*x, y)?.as_decimal().unwrap())),
|
|
||||||
"-=" => return Ok(Some(*x = subtract(*x, y)?.as_decimal().unwrap())),
|
|
||||||
"*=" => return Ok(Some(*x = multiply(*x, y)?.as_decimal().unwrap())),
|
|
||||||
"/=" => return Ok(Some(*x = divide(*x, y)?.as_decimal().unwrap())),
|
|
||||||
"%=" => return Ok(Some(*x = modulo(*x, y)?.as_decimal().unwrap())),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x += y)),
|
|
||||||
"-=" => return Ok(Some(*x -= y)),
|
|
||||||
"*=" => return Ok(Some(*x *= y)),
|
|
||||||
"/=" => return Ok(Some(*x /= y)),
|
|
||||||
"%=" => return Ok(Some(*x %= y)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// string op= char
|
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
|
||||||
let y = y.clone().cast::<char>();
|
|
||||||
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x += y)),
|
|
||||||
"-=" => return Ok(Some(*x -= y)),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// char op= string
|
|
||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
|
||||||
let y = y.read_lock::<ImmutableString>().unwrap();
|
|
||||||
let mut ch = x.read_lock::<char>().unwrap().to_string();
|
|
||||||
let mut x = x.write_lock::<Dynamic>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+=" => {
|
|
||||||
ch.push_str(y.as_str());
|
|
||||||
return Ok(Some(*x = ch.into()));
|
|
||||||
}
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No built-in op-assignments for different types.
|
|
||||||
if type2 != type1 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beyond here, type1 == type2
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<INT>() {
|
|
||||||
let y = y.clone().cast::<INT>();
|
|
||||||
let mut x = x.write_lock::<INT>().unwrap();
|
|
||||||
|
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x = add(*x, y)?.as_int().unwrap())),
|
|
||||||
"-=" => return Ok(Some(*x = subtract(*x, y)?.as_int().unwrap())),
|
|
||||||
"*=" => return Ok(Some(*x = multiply(*x, y)?.as_int().unwrap())),
|
|
||||||
"/=" => return Ok(Some(*x = divide(*x, y)?.as_int().unwrap())),
|
|
||||||
"%=" => return Ok(Some(*x = modulo(*x, y)?.as_int().unwrap())),
|
|
||||||
"**=" => return Ok(Some(*x = power(*x, y)?.as_int().unwrap())),
|
|
||||||
">>=" => return Ok(Some(*x = shift_right(*x, y)?.as_int().unwrap())),
|
|
||||||
"<<=" => return Ok(Some(*x = shift_left(*x, y)?.as_int().unwrap())),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x += y)),
|
|
||||||
"-=" => return Ok(Some(*x -= y)),
|
|
||||||
"*=" => return Ok(Some(*x *= y)),
|
|
||||||
"/=" => return Ok(Some(*x /= y)),
|
|
||||||
"%=" => return Ok(Some(*x %= y)),
|
|
||||||
"**=" => return Ok(Some(*x = x.pow(y as u32))),
|
|
||||||
">>=" => return Ok(Some(*x = *x >> y)),
|
|
||||||
"<<=" => return Ok(Some(*x = *x << y)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"&=" => return Ok(Some(*x &= y)),
|
|
||||||
"|=" => return Ok(Some(*x |= y)),
|
|
||||||
"^=" => return Ok(Some(*x ^= y)),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<bool>() {
|
|
||||||
let y = y.clone().cast::<bool>();
|
|
||||||
let mut x = x.write_lock::<bool>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"&=" => return Ok(Some(*x = *x && y)),
|
|
||||||
"|=" => return Ok(Some(*x = *x || y)),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<char>() {
|
|
||||||
let y = y.clone().cast::<char>();
|
|
||||||
let mut x = x.write_lock::<Dynamic>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x = format!("{}{}", *x, y).into())),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
|
||||||
let y = &*y.read_lock::<ImmutableString>().unwrap();
|
|
||||||
let mut x = x.write_lock::<ImmutableString>().unwrap();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
"+=" => return Ok(Some(*x += y)),
|
|
||||||
"-=" => return Ok(Some(*x -= y)),
|
|
||||||
_ => return Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
@ -64,6 +64,7 @@ extern crate alloc;
|
|||||||
// Internal modules
|
// Internal modules
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
mod builtin;
|
||||||
mod dynamic;
|
mod dynamic;
|
||||||
mod engine;
|
mod engine;
|
||||||
mod engine_api;
|
mod engine_api;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! Module implementing the [`AST`] optimizer.
|
//! Module implementing the [`AST`] optimizer.
|
||||||
|
|
||||||
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
||||||
|
use crate::builtin::get_builtin_binary_op_fn;
|
||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
|
||||||
use crate::parser::map_dynamic_to_expr;
|
use crate::parser::map_dynamic_to_expr;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -669,13 +669,17 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
// Search for overloaded operators (can override built-in).
|
||||||
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), state.lib, x.name.as_ref(), arg_types.as_ref()) {
|
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), state.lib, x.name.as_ref(), arg_types.as_ref()) {
|
||||||
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.ok().flatten()
|
.and_then(|f| {
|
||||||
|
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
||||||
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
|
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
||||||
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use crate::builtin::get_builtin_binary_op_fn;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
@ -128,20 +128,21 @@ mod logic_functions {
|
|||||||
name = ">=",
|
name = ">=",
|
||||||
name = "<",
|
name = "<",
|
||||||
name = "<=",
|
name = "<=",
|
||||||
return_raw
|
return_raw,
|
||||||
|
pure
|
||||||
)]
|
)]
|
||||||
pub fn cmp(
|
pub fn cmp(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
x: Dynamic,
|
x: &mut Dynamic,
|
||||||
y: Dynamic,
|
mut y: Dynamic,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let type_x = x.type_id();
|
let type_x = x.type_id();
|
||||||
let type_y = y.type_id();
|
let type_y = y.type_id();
|
||||||
|
|
||||||
if type_x != type_y && is_numeric(type_x) && is_numeric(type_y) {
|
if type_x != type_y && is_numeric(type_x) && is_numeric(type_y) {
|
||||||
// Disallow comparisons between different number types
|
// Disallow comparisons between different number types
|
||||||
} else if let Some(x) = run_builtin_binary_op(ctx.fn_name(), &x, &y)? {
|
} else if let Some(f) = get_builtin_binary_op_fn(ctx.fn_name(), x, &y) {
|
||||||
return Ok(x);
|
return f(ctx, &mut [x, &mut y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||||
|
Loading…
Reference in New Issue
Block a user