diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e43efc..2d172724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 =============== diff --git a/src/builtin.rs b/src/builtin.rs new file mode 100644 index 00000000..b2a17037 --- /dev/null +++ b/src/builtin.rs @@ -0,0 +1,1194 @@ +//! Built-in implementations for common operators. + +use crate::fn_native::{FnCallArgs, NativeCallContext}; +use crate::stdlib::{any::TypeId, boxed::Box, format, string::ToString}; +use crate::{Dynamic, EvalAltResult, ImmutableString, INT}; + +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; + +#[cfg(feature = "decimal")] +use rust_decimal::Decimal; + +#[cfg(feature = "no_std")] +#[cfg(not(feature = "no_float"))] +use num_traits::float::Float; + +/// Build in common binary operator implementations to avoid the cost of calling a registered function. +pub fn get_builtin_binary_op_fn( + op: &str, + x: &Dynamic, + y: &Dynamic, +) -> Option Result>> { + 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 match op { + "!=" if type1 != type2 => Some(|_, _| Ok(Dynamic::TRUE)), + "==" | ">" | ">=" | "<" | "<=" if type1 != type2 => Some(|_, _| Ok(Dynamic::FALSE)), + _ => None, + }; + } + + let types_pair = (type1, type2); + + #[cfg(not(feature = "no_float"))] + if types_pair == (TypeId::of::(), TypeId::of::()) + || types_pair == (TypeId::of::(), TypeId::of::()) + || types_pair == (TypeId::of::(), TypeId::of::()) + { + #[inline(always)] + fn get_xy(args: &FnCallArgs) -> (FLOAT, FLOAT) { + let type1 = args[0].type_id(); + let type2 = args[1].type_id(); + + let types_pair = (type1, type2); + + if types_pair == (TypeId::of::(), TypeId::of::()) { + // FLOAT op FLOAT + ( + args[0].clone().cast::(), + args[1].clone().cast::(), + ) + } else if types_pair == (TypeId::of::(), TypeId::of::()) { + // FLOAT op INT + ( + args[0].clone().cast::(), + args[1].clone().cast::() as FLOAT, + ) + } else if types_pair == (TypeId::of::(), TypeId::of::()) { + // INT op FLOAT + ( + args[0].clone().cast::() as FLOAT, + args[1].clone().cast::(), + ) + } else { + unreachable!() + } + } + + match op { + "+" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x + y).into()) + }) + } + "-" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x - y).into()) + }) + } + "*" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x * y).into()) + }) + } + "/" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x / y).into()) + }) + } + "%" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x % y).into()) + }) + } + "**" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok(x.powf(y).into()) + }) + } + "==" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x == y).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x != y).into()) + }) + } + ">" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x > y).into()) + }) + } + ">=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x >= y).into()) + }) + } + "<" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x < y).into()) + }) + } + "<=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x <= y).into()) + }) + } + _ => return None, + } + } + + #[cfg(feature = "decimal")] + if types_pair == (TypeId::of::(), TypeId::of::()) + || types_pair == (TypeId::of::(), TypeId::of::()) + || types_pair == (TypeId::of::(), TypeId::of::()) + { + #[inline(always)] + fn get_xy(args: &FnCallArgs) -> (Decimal, Decimal) { + let type1 = args[0].type_id(); + let type2 = args[1].type_id(); + + let types_pair = (type1, type2); + + if types_pair == (TypeId::of::(), TypeId::of::()) { + // Decimal op Decimal + ( + args[0].clone().cast::(), + args[1].clone().cast::(), + ) + } else if types_pair == (TypeId::of::(), TypeId::of::()) { + // Decimal op INT + ( + args[0].clone().cast::(), + Decimal::from(args[1].clone().cast::()), + ) + } else if types_pair == (TypeId::of::(), TypeId::of::()) { + // INT op Decimal + ( + Decimal::from(args[0].clone().cast::()), + args[1].clone().cast::(), + ) + } else { + unreachable!() + } + } + + if cfg!(not(feature = "unchecked")) { + use crate::packages::arithmetic::decimal_functions::*; + + match op { + "+" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + add(x, y) + }) + } + "-" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + subtract(x, y) + }) + } + "*" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + multiply(x, y) + }) + } + "/" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + divide(x, y) + }) + } + "%" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + modulo(x, y) + }) + } + _ => (), + } + } else { + match op { + "+" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x + y).into()) + }) + } + "-" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x - y).into()) + }) + } + "*" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x * y).into()) + }) + } + "/" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x / y).into()) + }) + } + "%" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x % y).into()) + }) + } + _ => (), + } + } + + match op { + "==" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x == y).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x != y).into()) + }) + } + ">" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x > y).into()) + }) + } + ">=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x >= y).into()) + }) + } + "<" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x < y).into()) + }) + } + "<=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x <= y).into()) + }) + } + _ => return None, + } + } + + // char op string + if types_pair == (TypeId::of::(), TypeId::of::()) { + match op { + "+" => { + return Some(|_, args| { + let x = args[0].clone().cast::(); + let y = &*args[1].read_lock::().unwrap(); + Ok(format!("{}{}", x, y).into()) + }) + } + "==" | "!=" | ">" | ">=" | "<" | "<=" => { + #[inline(always)] + fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) { + let x = args[0].clone().cast::(); + let y = &*args[1].read_lock::().unwrap(); + let s1 = [x, '\0']; + let mut y = y.chars(); + let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')]; + (s1, s2) + } + + match op { + "==" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 == s2).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 != s2).into()) + }) + } + ">" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 > s2).into()) + }) + } + ">=" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 >= s2).into()) + }) + } + "<" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 < s2).into()) + }) + } + "<=" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 <= s2).into()) + }) + } + _ => unreachable!(), + } + } + _ => return None, + } + } + // string op char + if types_pair == (TypeId::of::(), TypeId::of::()) { + match op { + "+" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = args[1].clone().cast::(); + Ok((x + y).into()) + }) + } + "-" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = args[1].clone().cast::(); + Ok((x - y).into()) + }) + } + "==" | "!=" | ">" | ">=" | "<" | "<=" => { + #[inline(always)] + fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) { + let x = &*args[0].read_lock::().unwrap(); + let y = args[1].clone().cast::(); + let mut x = x.chars(); + let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')]; + let s2 = [y, '\0']; + (s1, s2) + } + + match op { + "==" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 == s2).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 != s2).into()) + }) + } + ">" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 > s2).into()) + }) + } + ">=" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 >= s2).into()) + }) + } + "<" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 < s2).into()) + }) + } + "<=" => { + return Some(|_, args| { + let (s1, s2) = get_s1s2(args); + Ok((s1 <= s2).into()) + }) + } + _ => unreachable!(), + } + } + _ => return None, + } + } + + // Default comparison operators for different types + if type2 != type1 { + return match op { + "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + _ => None, + }; + } + + // Beyond here, type1 == type2 + + if type1 == TypeId::of::() { + #[inline(always)] + fn get_xy(args: &FnCallArgs) -> (INT, INT) { + let x = args[0].clone().cast::(); + let y = args[1].clone().cast::(); + (x, y) + } + + if cfg!(not(feature = "unchecked")) { + use crate::packages::arithmetic::arith_basic::INT::functions::*; + + match op { + "+" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + add(x, y) + }) + } + "-" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + subtract(x, y) + }) + } + "*" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + multiply(x, y) + }) + } + "/" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + divide(x, y) + }) + } + "%" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + modulo(x, y) + }) + } + "**" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + power(x, y) + }) + } + ">>" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + shift_right(x, y) + }) + } + "<<" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + shift_left(x, y) + }) + } + _ => (), + } + } else { + match op { + "+" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x + y).into()) + }) + } + "-" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x - y).into()) + }) + } + "*" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x * y).into()) + }) + } + "/" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x / y).into()) + }) + } + "%" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x % y).into()) + }) + } + "**" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok(x.pow(y as u32).into()) + }) + } + ">>" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x >> y).into()) + }) + } + "<<" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x << y).into()) + }) + } + _ => (), + } + } + + match op { + "==" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x == y).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x != y).into()) + }) + } + ">" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x > y).into()) + }) + } + ">=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x >= y).into()) + }) + } + "<" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x < y).into()) + }) + } + "<=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x <= y).into()) + }) + } + "&" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x & y).into()) + }) + } + "|" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x | y).into()) + }) + } + "^" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x ^ y).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::() { + #[inline(always)] + fn get_xy(args: &FnCallArgs) -> (bool, bool) { + let x = args[0].clone().cast::(); + let y = args[1].clone().cast::(); + (x, y) + } + + match op { + "&" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x && y).into()) + }) + } + "|" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x || y).into()) + }) + } + "^" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x ^ y).into()) + }) + } + "==" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x == y).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x != y).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::() { + match op { + "+" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x + y).into()) + }) + } + "-" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x - y).into()) + }) + } + "==" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x == y).into()) + }) + } + "!=" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x != y).into()) + }) + } + ">" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x > y).into()) + }) + } + ">=" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x >= y).into()) + }) + } + "<" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x < y).into()) + }) + } + "<=" => { + return Some(|_, args| { + let x = &*args[0].read_lock::().unwrap(); + let y = &*args[1].read_lock::().unwrap(); + Ok((x <= y).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::() { + #[inline(always)] + fn get_xy(args: &FnCallArgs) -> (char, char) { + let x = args[0].clone().cast::(); + let y = args[1].clone().cast::(); + (x, y) + } + + match op { + "+" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok(format!("{}{}", x, y).into()) + }) + } + "==" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x == y).into()) + }) + } + "!=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x != y).into()) + }) + } + ">" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x > y).into()) + }) + } + ">=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x >= y).into()) + }) + } + "<" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x < y).into()) + }) + } + "<=" => { + return Some(|_, args| { + let (x, y) = get_xy(args); + Ok((x <= y).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::<()>() { + match op { + "==" => return Some(|_, _| Ok(Dynamic::TRUE)), + "!=" | ">" | ">=" | "<" | "<=" => return Some(|_, _| Ok(Dynamic::FALSE)), + _ => return None, + } + } + + None +} + +/// Build in common operator assignment implementations to avoid the cost of calling a registered function. +pub fn get_builtin_op_assignment_fn( + op: &str, + x: &Dynamic, + y: &Dynamic, +) -> Option Result>> { + let type1 = x.type_id(); + let type2 = y.type_id(); + + let types_pair = (type1, type2); + + #[cfg(not(feature = "no_float"))] + if types_pair == (TypeId::of::(), TypeId::of::()) + || types_pair == (TypeId::of::(), TypeId::of::()) + { + fn get_y(args: &FnCallArgs) -> FLOAT { + let type2 = args[1].type_id(); + + if type2 == TypeId::of::() { + // FLOAT op= FLOAT + args[1].clone().cast::() + } else if type2 == TypeId::of::() { + // FLOAT op= INT + args[1].clone().cast::() as FLOAT + } else { + unreachable!(); + } + } + + match op { + "+=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() += y).into()) + }) + } + "-=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() -= y).into()) + }) + } + "*=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() *= y).into()) + }) + } + "/=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() /= y).into()) + }) + } + "%=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() %= y).into()) + }) + } + "**=" => { + return Some(|_, args| { + let y = get_y(args); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = x.powf(y)).into()) + }) + } + _ => return None, + } + } + + #[cfg(feature = "decimal")] + if types_pair == (TypeId::of::(), TypeId::of::()) + || types_pair == (TypeId::of::(), TypeId::of::()) + { + fn get_y(args: &FnCallArgs) -> Decimal { + let type2 = args[1].type_id(); + + if type2 == TypeId::of::() { + // Decimal op= Decimal + args[1].clone().cast::() + } else if type2 == TypeId::of::() { + // Decimal op= INT + Decimal::from(args[1].clone().cast::()) + } else { + unreachable!(); + } + } + + if cfg!(not(feature = "unchecked")) { + use crate::packages::arithmetic::decimal_functions::*; + + match op { + "+=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = add(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "-=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = subtract(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "*=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = multiply(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "/=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = divide(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "%=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = modulo(*x, y)?.as_int().unwrap().into()).into()) + }) + } + _ => (), + } + } else { + match op { + "+=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() += y).into()) + }) + } + "-=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() -= y).into()) + }) + } + "*=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() *= y).into()) + }) + } + "/=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() /= y).into()) + }) + } + "%=" => { + return Some(|_, args| { + let y = get_y(args); + Ok((*args[0].write_lock::().unwrap() %= y).into()) + }) + } + _ => return None, + } + } + } + + // string op= char + if types_pair == (TypeId::of::(), TypeId::of::()) { + match op { + "+=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() += y).into()) + }) + } + "-=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() -= y).into()) + }) + } + _ => return None, + } + } + // char op= string + if types_pair == (TypeId::of::(), TypeId::of::()) { + match op { + "+=" => { + return Some(|_, args| { + let mut ch = args[0].read_lock::().unwrap().to_string(); + ch.push_str(args[1].read_lock::().unwrap().as_str()); + + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = ch.into()).into()) + }) + } + _ => return None, + } + } + + // No built-in op-assignments for different types. + if type2 != type1 { + return None; + } + + // Beyond here, type1 == type2 + if type1 == TypeId::of::() { + if cfg!(not(feature = "unchecked")) { + use crate::packages::arithmetic::arith_basic::INT::functions::*; + + match op { + "+=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = add(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "-=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = subtract(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "*=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = multiply(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "/=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = divide(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "%=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = modulo(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "**=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = power(*x, y)?.as_int().unwrap().into()).into()) + }) + } + ">>=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = shift_right(*x, y)?.as_int().unwrap().into()).into()) + }) + } + "<<=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = shift_left(*x, y)?.as_int().unwrap().into()).into()) + }) + } + _ => (), + } + } else { + match op { + "+=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() += y).into()) + }) + } + "-=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() -= y).into()) + }) + } + "*=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() *= y).into()) + }) + } + "/=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() /= y).into()) + }) + } + "%=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() %= y).into()) + }) + } + "**=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = x.pow(y as u32)).into()) + }) + } + ">>=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() >>= y).into()) + }) + } + "<<=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() <<= y).into()) + }) + } + _ => (), + } + } + + match op { + "&=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() &= y).into()) + }) + } + "|=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() |= y).into()) + }) + } + "^=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + Ok((*args[0].write_lock::().unwrap() ^= y).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::() { + match op { + "&=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = *x && y).into()) + }) + } + "|=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = *x || y).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::() { + match op { + "+=" => { + return Some(|_, args| { + let y = args[1].clone().cast::(); + let mut x = args[0].write_lock::().unwrap(); + Ok((*x = format!("{}{}", *x, y).into()).into()) + }) + } + _ => return None, + } + } + + if type1 == TypeId::of::() { + match op { + "+=" => { + return Some(|_, args| { + let (first, second) = args.split_first_mut().unwrap(); + let mut x = first.write_lock::().unwrap(); + let y = &*second[0].read_lock::().unwrap(); + Ok((*x += y).into()) + }) + } + "-=" => { + return Some(|_, args| { + let (first, second) = args.split_first_mut().unwrap(); + let mut x = first.write_lock::().unwrap(); + let y = &*second[0].read_lock::().unwrap(); + Ok((*x -= y).into()) + }) + } + _ => return None, + } + } + + None +} diff --git a/src/fn_call.rs b/src/fn_call.rs index c28efca8..29c6eda1 100644 --- a/src/fn_call.rs +++ b/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)> { 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), + 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), + 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, Box> { - 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::(), TypeId::of::()) { - // FLOAT op FLOAT - Some((x.clone().cast::(), y.clone().cast::())) - } else if types_pair == (TypeId::of::(), TypeId::of::()) { - // FLOAT op INT - Some((x.clone().cast::(), y.clone().cast::() as FLOAT)) - } else if types_pair == (TypeId::of::(), TypeId::of::()) { - // INT op FLOAT - Some((x.clone().cast::() as FLOAT, y.clone().cast::())) - } 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::(), TypeId::of::()) { - // Decimal op Decimal - Some(( - *x.read_lock::().unwrap(), - *y.read_lock::().unwrap(), - )) - } else if types_pair == (TypeId::of::(), TypeId::of::()) { - // Decimal op INT - Some(( - *x.read_lock::().unwrap(), - y.clone().cast::().into(), - )) - } else if types_pair == (TypeId::of::(), TypeId::of::()) { - // INT op Decimal - Some(( - x.clone().cast::().into(), - *y.read_lock::().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::(), TypeId::of::()) { - let x = x.clone().cast::(); - let y = &*y.read_lock::().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::(), TypeId::of::()) { - let x = &*x.read_lock::().unwrap(); - let y = y.clone().cast::(); - - 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::() { - let x = x.clone().cast::(); - let y = y.clone().cast::(); - - 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::() { - let x = x.clone().cast::(); - let y = y.clone().cast::(); - - 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::() { - let x = &*x.read_lock::().unwrap(); - let y = &*y.read_lock::().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::() { - let x = x.clone().cast::(); - let y = y.clone().cast::(); - - 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, Box> { - 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::(), TypeId::of::()) { - // FLOAT op= FLOAT - let y = y.clone().cast::(); - Some((x.write_lock::().unwrap(), y)) - } else if types_pair == (TypeId::of::(), TypeId::of::()) { - // FLOAT op= INT - let y = y.clone().cast::() as FLOAT; - Some((x.write_lock::().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::(), TypeId::of::()) { - // Decimal op= Decimal - let y = *y.read_lock::().unwrap(); - Some((x.write_lock::().unwrap(), y)) - } else if types_pair == (TypeId::of::(), TypeId::of::()) { - // Decimal op= INT - let y = y.clone().cast::().into(); - Some((x.write_lock::().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::(), TypeId::of::()) { - let y = y.clone().cast::(); - let mut x = x.write_lock::().unwrap(); - - match op { - "+=" => return Ok(Some(*x += y)), - "-=" => return Ok(Some(*x -= y)), - _ => return Ok(None), - } - } - // char op= string - if types_pair == (TypeId::of::(), TypeId::of::()) { - let y = y.read_lock::().unwrap(); - let mut ch = x.read_lock::().unwrap().to_string(); - let mut x = x.write_lock::().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::() { - let y = y.clone().cast::(); - let mut x = x.write_lock::().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::() { - let y = y.clone().cast::(); - let mut x = x.write_lock::().unwrap(); - - match op { - "&=" => return Ok(Some(*x = *x && y)), - "|=" => return Ok(Some(*x = *x || y)), - _ => return Ok(None), - } - } - - if type1 == TypeId::of::() { - let y = y.clone().cast::(); - let mut x = x.write_lock::().unwrap(); - - match op { - "+=" => return Ok(Some(*x = format!("{}{}", *x, y).into())), - _ => return Ok(None), - } - } - - if type1 == TypeId::of::() { - let y = &*y.read_lock::().unwrap(); - let mut x = x.write_lock::().unwrap(); - - match op { - "+=" => return Ok(Some(*x += y)), - "-=" => return Ok(Some(*x -= y)), - _ => return Ok(None), - } - } - - Ok(None) -} diff --git a/src/lib.rs b/src/lib.rs index db6e4687..e809e254 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,7 @@ extern crate alloc; // Internal modules mod ast; +mod builtin; mod dynamic; mod engine; mod engine_api; diff --git a/src/optimize.rs b/src/optimize.rs index 81543baa..32a98b7f 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -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(); diff --git a/src/packages/logic.rs b/src/packages/logic.rs index fdd6e0a3..5466b0a2 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -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> { 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(