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_.
|
||||
* 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
|
||||
===============
|
||||
|
||||
|
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`].
|
||||
|
||||
use crate::builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
||||
use crate::engine::{
|
||||
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,
|
||||
MAX_DYNAMIC_PARAMETERS,
|
||||
};
|
||||
use crate::fn_native::FnCallArgs;
|
||||
use crate::fn_native::{FnAny, FnCallArgs};
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::stdlib::{
|
||||
@ -29,19 +30,9 @@ use crate::{
|
||||
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
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.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
@ -199,6 +190,7 @@ impl Engine {
|
||||
mut hash: NonZeroU64,
|
||||
args: &mut FnCallArgs,
|
||||
allow_dynamic: bool,
|
||||
is_op_assignment: bool,
|
||||
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
||||
fn find_function(
|
||||
engine: &Engine,
|
||||
@ -254,7 +246,36 @@ impl Engine {
|
||||
Some(f) => return Some(f),
|
||||
|
||||
// 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
|
||||
None => {
|
||||
@ -304,7 +325,16 @@ impl Engine {
|
||||
let source = state.source.clone();
|
||||
|
||||
// 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 {
|
||||
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?
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if let Some(prop) = extract_prop_from_getter(fn_name) {
|
||||
@ -714,7 +719,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
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()
|
||||
.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
|
||||
|
||||
mod ast;
|
||||
mod builtin;
|
||||
mod dynamic;
|
||||
mod engine;
|
||||
mod engine_api;
|
||||
|
@ -1,9 +1,9 @@
|
||||
//! Module implementing the [`AST`] optimizer.
|
||||
|
||||
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
||||
use crate::builtin::get_builtin_binary_op_fn;
|
||||
use crate::dynamic::AccessMode;
|
||||
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::stdlib::{
|
||||
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
|
||||
&& !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();
|
||||
|
||||
// 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 let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||
.ok().flatten()
|
||||
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||
.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))
|
||||
{
|
||||
state.set_dirty();
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::builtin::get_builtin_binary_op_fn;
|
||||
use crate::def_package;
|
||||
use crate::fn_call::run_builtin_binary_op;
|
||||
use crate::plugin::*;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
@ -128,20 +128,21 @@ mod logic_functions {
|
||||
name = ">=",
|
||||
name = "<",
|
||||
name = "<=",
|
||||
return_raw
|
||||
return_raw,
|
||||
pure
|
||||
)]
|
||||
pub fn cmp(
|
||||
ctx: NativeCallContext,
|
||||
x: Dynamic,
|
||||
y: Dynamic,
|
||||
x: &mut Dynamic,
|
||||
mut y: Dynamic,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let type_x = x.type_id();
|
||||
let type_y = y.type_id();
|
||||
|
||||
if type_x != type_y && is_numeric(type_x) && is_numeric(type_y) {
|
||||
// Disallow comparisons between different number types
|
||||
} else if let Some(x) = run_builtin_binary_op(ctx.fn_name(), &x, &y)? {
|
||||
return Ok(x);
|
||||
} else if let Some(f) = get_builtin_binary_op_fn(ctx.fn_name(), x, &y) {
|
||||
return f(ctx, &mut [x, &mut y]);
|
||||
}
|
||||
|
||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||
|
Loading…
Reference in New Issue
Block a user