diff --git a/RELEASES.md b/RELEASES.md index e8fe628d..87df6bab 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,10 +9,18 @@ Regression * Do not optimize script with `eval_expression` - it is assumed to be one-off and short. +Breaking changes +---------------- + +* `Engine::compile_XXX` functions now return `ParseError` instead of `Box`. +* The `RegisterDynamicFn` trait is merged into the `RegisterResutlFn` trait which now always returns + `Result>`. +* Default maximum limit on levels of nested function calls is fine-tuned and set to a different value. + New features ------------ -* Set limits on maximum level of nesting expressions and statements to avoid panics during parsing. +* Set limit on maximum level of nesting expressions and statements to avoid panics during parsing. * New `EvalPackage` to disable `eval`. * More benchmarks. diff --git a/benches/engine.rs b/benches/engine.rs index 254288de..f1ac588b 100644 --- a/benches/engine.rs +++ b/benches/engine.rs @@ -29,7 +29,7 @@ fn bench_engine_new_raw_core(bench: &mut Bencher) { #[bench] fn bench_engine_register_fn(bench: &mut Bencher) { - fn hello(a: INT, b: Array, c: Map) -> bool { + fn hello(_a: INT, _b: Array, _c: Map) -> bool { true } diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index c647c1b5..8f46fb18 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -86,7 +86,7 @@ fn bench_eval_call_expression(bench: &mut Bencher) { modifierTest + 1000 / 2 > (80 * 100 % 2) "#; - let mut engine = Engine::new(); + let engine = Engine::new(); bench.iter(|| engine.eval_expression::(script).unwrap()); } @@ -101,7 +101,7 @@ fn bench_eval_call(bench: &mut Bencher) { modifierTest + 1000 / 2 > (80 * 100 % 2) "#; - let mut engine = Engine::new(); + let engine = Engine::new(); bench.iter(|| engine.eval::(script).unwrap()); } diff --git a/src/engine.rs b/src/engine.rs index cbb7dc02..18878a65 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -7,13 +7,16 @@ use crate::fn_native::{FnCallArgs, Shared}; use crate::module::Module; use crate::optimize::OptimizationLevel; use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage}; -use crate::parser::{Expr, FnAccess, FnDef, ReturnType, Stmt, AST}; +use crate::parser::{Expr, FnAccess, FnDef, ReturnType, Stmt, AST, INT}; use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime}; use crate::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::Position; use crate::utils::StaticVec; +#[cfg(not(feature = "no_float"))] +use crate::parser::FLOAT; + #[cfg(not(feature = "no_module"))] use crate::module::{resolvers, ModuleRef, ModuleResolver}; @@ -706,6 +709,14 @@ impl Engine { }); } + // If it is a 2-operand operator, see if it is built in + if args.len() == 2 && args[0].type_id() == args[1].type_id() { + match run_builtin_op(fn_name, args[0], args[1])? { + Some(v) => return Ok((v, false)), + None => (), + } + } + // Return default value (if any) if let Some(val) = def_val { return Ok((val.clone(), false)); @@ -873,6 +884,7 @@ impl Engine { &self, state: &mut State, fn_name: &str, + native_only: bool, hash_fn_def: u64, args: &mut FnCallArgs, is_ref: bool, @@ -887,7 +899,7 @@ impl Engine { args.len(), args.iter().map(|a| a.type_id()), ); - let hashes = (hash_fn, hash_fn_def); + let hashes = (hash_fn, if native_only { 0 } else { hash_fn_def }); match fn_name { // type_of @@ -1003,7 +1015,7 @@ impl Engine { match rhs { // xxx.fn_name(arg_expr_list) Expr::FnCall(x) if x.1.is_none() => { - let ((name, pos), _, hash_fn_def, _, def_val) = x.as_ref(); + let ((name, native, pos), _, hash, _, def_val) = x.as_ref(); let def_val = def_val.as_ref(); let mut arg_values: StaticVec<_> = once(obj) @@ -1016,7 +1028,7 @@ impl Engine { .collect(); let args = arg_values.as_mut(); - self.exec_fn_call(state, name, *hash_fn_def, args, is_ref, def_val, *pos, 0) + self.exec_fn_call(state, name, *native, *hash, args, is_ref, def_val, *pos, 0) } // xxx.module::fn_name(...) - syntax error Expr::FnCall(_) => unreachable!(), @@ -1045,14 +1057,14 @@ impl Engine { Expr::Property(x) if new_val.is_some() => { let ((_, _, setter), pos) = x.as_ref(); let mut args = [obj, new_val.as_mut().unwrap()]; - self.exec_fn_call(state, setter, 0, &mut args, is_ref, None, *pos, 0) + self.exec_fn_call(state, setter, true, 0, &mut args, is_ref, None, *pos, 0) .map(|(v, _)| (v, true)) } // xxx.id Expr::Property(x) => { let ((_, getter, _), pos) = x.as_ref(); let mut args = [obj]; - self.exec_fn_call(state, getter, 0, &mut args, is_ref, None, *pos, 0) + self.exec_fn_call(state, getter, true, 0, &mut args, is_ref, None, *pos, 0) .map(|(v, _)| (v, false)) } #[cfg(not(feature = "no_object"))] @@ -1083,7 +1095,8 @@ impl Engine { let (mut val, updated) = if let Expr::Property(p) = &x.0 { let ((_, getter, _), _) = p.as_ref(); - self.exec_fn_call(state, getter, 0, &mut args[..1], is_ref, None, x.2, 0)? + let args = &mut args[..1]; + self.exec_fn_call(state, getter, true, 0, args, is_ref, None, x.2, 0)? } else { // Syntax error return Err(Box::new(EvalAltResult::ErrorDotExpr( @@ -1104,7 +1117,7 @@ impl Engine { let ((_, _, setter), _) = p.as_ref(); // Re-use args because the first &mut parameter will not be consumed args[1] = val; - self.exec_fn_call(state, setter, 0, args, is_ref, None, x.2, 0) + self.exec_fn_call(state, setter, true, 0, args, is_ref, None, x.2, 0) .or_else(|err| match *err { // If there is no setter, no need to feed it back because the property is read-only EvalAltResult::ErrorDotExpr(_, _) => Ok(Default::default()), @@ -1306,7 +1319,7 @@ impl Engine { _ => { let type_name = self.map_type_name(val.type_name()); let args = &mut [val, &mut idx]; - self.exec_fn_call(state, FUNC_INDEXER, 0, args, is_ref, None, op_pos, 0) + self.exec_fn_call(state, FUNC_INDEXER, true, 0, args, is_ref, None, op_pos, 0) .map(|(v, _)| v.into()) .map_err(|_| { Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), op_pos)) @@ -1334,7 +1347,6 @@ impl Engine { Dynamic(Union::Array(mut rhs_value)) => { let op = "=="; let def_value = false.into(); - let hash_fn_def = calc_fn_hash(empty(), op, 2, empty()); // Call the `==` operator to compare each value for value in rhs_value.iter_mut() { @@ -1345,7 +1357,7 @@ impl Engine { // Qualifiers (none) + function name + argument `TypeId`'s. let hash_fn = calc_fn_hash(empty(), op, args.len(), args.iter().map(|a| a.type_id())); - let hashes = (hash_fn, hash_fn_def); + let hashes = (hash_fn, 0); let (r, _) = self .call_fn_raw(None, state, op, hashes, args, true, def_value, pos, level)?; @@ -1488,7 +1500,7 @@ impl Engine { // Normal function call Expr::FnCall(x) if x.1.is_none() => { - let ((name, pos), _, hash_fn_def, args_expr, def_val) = x.as_ref(); + let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref(); let def_val = def_val.as_ref(); let mut arg_values = args_expr @@ -1501,7 +1513,7 @@ impl Engine { if name == KEYWORD_EVAL && args.len() == 1 && args.get(0).is::() { let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::())); - if !self.has_override(state, (hash_fn, *hash_fn_def)) { + if !self.has_override(state, (hash_fn, *hash)) { // eval - only in function call style let prev_len = scope.len(); let pos = args_expr.get(0).position(); @@ -1521,14 +1533,16 @@ impl Engine { // Normal function call - except for eval (handled above) let args = args.as_mut(); - self.exec_fn_call(state, name, *hash_fn_def, args, false, def_val, *pos, level) - .map(|(v, _)| v) + self.exec_fn_call( + state, name, *native, *hash, args, false, def_val, *pos, level, + ) + .map(|(v, _)| v) } // Module-qualified function call #[cfg(not(feature = "no_module"))] Expr::FnCall(x) if x.1.is_some() => { - let ((name, pos), modules, hash_fn_def, args_expr, def_val) = x.as_ref(); + let ((name, _, pos), modules, hash_fn_def, args_expr, def_val) = x.as_ref(); let modules = modules.as_ref().unwrap(); let mut arg_values = args_expr @@ -1934,3 +1948,125 @@ impl Engine { .unwrap_or(name) } } + +/// Build in certain common operator implementations to avoid the cost of searching through the functions space. +fn run_builtin_op( + op: &str, + x: &Dynamic, + y: &Dynamic, +) -> Result, Box> { + use crate::packages::arithmetic::*; + + if x.type_id() == TypeId::of::() { + let x = x.downcast_ref::().unwrap().clone(); + let y = y.downcast_ref::().unwrap().clone(); + + #[cfg(not(feature = "unchecked"))] + match op { + "+" => return add(x, y).map(Into::::into).map(Some), + "-" => return sub(x, y).map(Into::::into).map(Some), + "*" => return mul(x, y).map(Into::::into).map(Some), + "/" => return div(x, y).map(Into::::into).map(Some), + "%" => return modulo(x, y).map(Into::::into).map(Some), + "~" => return pow_i_i(x, y).map(Into::::into).map(Some), + ">>" => return shr(x, y).map(Into::::into).map(Some), + "<<" => return shl(x, y).map(Into::::into).map(Some), + _ => (), + } + + #[cfg(feature = "unchecked")] + 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 pow_i_i_u(x, y).map(Into::::into).map(Some), + ">>" => return shr_u(x, y).map(Into::::into).map(Some), + "<<" => return shl_u(x, y).map(Into::::into).map(Some), + _ => (), + } + + 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())), + _ => (), + } + } else if x.type_id() == TypeId::of::() { + let x = x.downcast_ref::().unwrap().clone(); + let y = y.downcast_ref::().unwrap().clone(); + + 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())), + _ => (), + } + } else if x.type_id() == TypeId::of::() { + let x = x.downcast_ref::().unwrap(); + let y = y.downcast_ref::().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())), + _ => (), + } + } else if x.type_id() == TypeId::of::() { + let x = x.downcast_ref::().unwrap().clone(); + let y = y.downcast_ref::().unwrap().clone(); + + 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())), + _ => (), + } + } else if x.type_id() == TypeId::of::<()>() { + match op { + "==" => return Ok(Some(true.into())), + "!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())), + _ => (), + } + } + + #[cfg(not(feature = "no_float"))] + { + if x.type_id() == TypeId::of::() { + let x = x.downcast_ref::().unwrap().clone(); + let y = y.downcast_ref::().unwrap().clone(); + + 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 pow_f_f(x, y).map(Into::::into).map(Some), + "==" => 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())), + _ => (), + } + } + } + + Ok(None) +} diff --git a/src/optimize.rs b/src/optimize.rs index 1bba1196..5e9aa911 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,11 +1,10 @@ use crate::any::Dynamic; use crate::calc_fn_hash; use crate::engine::{ - Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF, + Engine, FunctionsLib, State as EngineState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, + KEYWORD_TYPE_OF, }; use crate::fn_native::FnCallArgs; -use crate::module::Module; -use crate::packages::PackagesCollection; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::result::EvalAltResult; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; @@ -112,8 +111,7 @@ impl<'a> State<'a> { /// Call a registered function fn call_fn( - packages: &PackagesCollection, - global_module: &Module, + state: &State, fn_name: &str, args: &mut FnCallArgs, pos: Position, @@ -126,12 +124,21 @@ fn call_fn( args.iter().map(|a| a.type_id()), ); - global_module - .get_fn(hash_fn) - .or_else(|| packages.get_fn(hash_fn)) - .map(|func| func.get_native_fn()(args)) - .transpose() - .map_err(|err| err.new_position(pos)) + state + .engine + .call_fn_raw( + None, + &mut EngineState::new(&Default::default()), + fn_name, + (hash_fn, 0), + args, + true, + None, + pos, + 0, + ) + .map(|(v, _)| Some(v)) + .or_else(|_| Ok(None)) } /// Optimize a statement. @@ -546,10 +553,10 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { && state.optimization_level == OptimizationLevel::Full // full optimizations && x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants => { - let ((name, pos), _, _, args, def_value) = x.as_mut(); + let ((name, native_only, pos), _, _, args, def_value) = x.as_mut(); // First search in script-defined functions (can override built-in) - if state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() { + if !*native_only && state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() { // A script-defined function overrides the built-in function - do not make the call x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect(); return Expr::FnCall(x); @@ -566,7 +573,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { "" }; - call_fn(&state.engine.packages, &state.engine.global_module, name, call_args.as_mut(), *pos).ok() + call_fn(&state, name, call_args.as_mut(), *pos).ok() .and_then(|result| result.or_else(|| { if !arg_for_type_of.is_empty() { diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index e4a26844..8f7a0b5b 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -20,7 +20,7 @@ use crate::stdlib::{ }; // Checked add -fn add(x: T, y: T) -> FuncReturn { +pub(crate) fn add(x: T, y: T) -> FuncReturn { x.checked_add(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( format!("Addition overflow: {} + {}", x, y), @@ -29,7 +29,7 @@ fn add(x: T, y: T) -> FuncReturn { }) } // Checked subtract -fn sub(x: T, y: T) -> FuncReturn { +pub(crate) fn sub(x: T, y: T) -> FuncReturn { x.checked_sub(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( format!("Subtraction underflow: {} - {}", x, y), @@ -38,7 +38,7 @@ fn sub(x: T, y: T) -> FuncReturn { }) } // Checked multiply -fn mul(x: T, y: T) -> FuncReturn { +pub(crate) fn mul(x: T, y: T) -> FuncReturn { x.checked_mul(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( format!("Multiplication overflow: {} * {}", x, y), @@ -47,7 +47,7 @@ fn mul(x: T, y: T) -> FuncReturn { }) } // Checked divide -fn div(x: T, y: T) -> FuncReturn +pub(crate) fn div(x: T, y: T) -> FuncReturn where T: Display + CheckedDiv + PartialEq + Zero, { @@ -67,7 +67,7 @@ where }) } // Checked negative - e.g. -(i32::MIN) will overflow i32::MAX -fn neg(x: T) -> FuncReturn { +pub(crate) fn neg(x: T) -> FuncReturn { x.checked_neg().ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( format!("Negation overflow: -{}", x), @@ -76,7 +76,7 @@ fn neg(x: T) -> FuncReturn { }) } // Checked absolute -fn abs(x: T) -> FuncReturn { +pub(crate) fn abs(x: T) -> FuncReturn { // FIX - We don't use Signed::abs() here because, contrary to documentation, it panics // when the number is ::MIN instead of returning ::MIN itself. if x >= ::zero() { @@ -133,7 +133,7 @@ fn binary_xor(x: T, y: T) -> FuncReturn<::Output> { Ok(x ^ y) } // Checked left-shift -fn shl(x: T, y: INT) -> FuncReturn { +pub(crate) fn shl(x: T, y: INT) -> FuncReturn { // Cannot shift by a negative number of bits if y < 0 { return Err(Box::new(EvalAltResult::ErrorArithmetic( @@ -150,7 +150,7 @@ fn shl(x: T, y: INT) -> FuncReturn { }) } // Checked right-shift -fn shr(x: T, y: INT) -> FuncReturn { +pub(crate) fn shr(x: T, y: INT) -> FuncReturn { // Cannot shift by a negative number of bits if y < 0 { return Err(Box::new(EvalAltResult::ErrorArithmetic( @@ -167,15 +167,15 @@ fn shr(x: T, y: INT) -> FuncReturn { }) } // Unchecked left-shift - may panic if shifting by a negative number of bits -fn shl_u>(x: T, y: T) -> FuncReturn<>::Output> { +pub(crate) fn shl_u>(x: T, y: T) -> FuncReturn<>::Output> { Ok(x.shl(y)) } // Unchecked right-shift - may panic if shifting by a negative number of bits -fn shr_u>(x: T, y: T) -> FuncReturn<>::Output> { +pub(crate) fn shr_u>(x: T, y: T) -> FuncReturn<>::Output> { Ok(x.shr(y)) } // Checked modulo -fn modulo(x: T, y: T) -> FuncReturn { +pub(crate) fn modulo(x: T, y: T) -> FuncReturn { x.checked_rem(&y).ok_or_else(|| { Box::new(EvalAltResult::ErrorArithmetic( format!("Modulo division by zero or overflow: {} % {}", x, y), @@ -188,7 +188,7 @@ fn modulo_u(x: T, y: T) -> FuncReturn<::Output> { Ok(x % y) } // Checked power -fn pow_i_i(x: INT, y: INT) -> FuncReturn { +pub(crate) fn pow_i_i(x: INT, y: INT) -> FuncReturn { #[cfg(not(feature = "only_i32"))] { if y > (u32::MAX as INT) { @@ -229,17 +229,17 @@ fn pow_i_i(x: INT, y: INT) -> FuncReturn { } } // Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX) -fn pow_i_i_u(x: INT, y: INT) -> FuncReturn { +pub(crate) fn pow_i_i_u(x: INT, y: INT) -> FuncReturn { Ok(x.pow(y as u32)) } // Floating-point power - always well-defined #[cfg(not(feature = "no_float"))] -fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn { +pub(crate) fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn { Ok(x.powf(y)) } // Checked power #[cfg(not(feature = "no_float"))] -fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { +pub(crate) fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { // Raise to power that is larger than an i32 if y > (i32::MAX as INT) { return Err(Box::new(EvalAltResult::ErrorArithmetic( @@ -253,7 +253,7 @@ fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn { // Unchecked power - may be incorrect if the power index is too high (> i32::MAX) #[cfg(feature = "unchecked")] #[cfg(not(feature = "no_float"))] -fn pow_f_i_u(x: FLOAT, y: INT) -> FuncReturn { +pub(crate) fn pow_f_i_u(x: FLOAT, y: INT) -> FuncReturn { Ok(x.powi(y as i32)) } @@ -272,97 +272,118 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { // Checked basic arithmetic #[cfg(not(feature = "unchecked"))] { - reg_op!(lib, "+", add, INT); - reg_op!(lib, "-", sub, INT); - reg_op!(lib, "*", mul, INT); - reg_op!(lib, "/", div, INT); + // reg_op!(lib, "+", add, INT); + // reg_op!(lib, "-", sub, INT); + // reg_op!(lib, "*", mul, INT); + // reg_op!(lib, "/", div, INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "+", add, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "/", div, i8, u8, i16, u16, i32, u32, u64, i128, u128); } } // Unchecked basic arithmetic #[cfg(feature = "unchecked")] { - reg_op!(lib, "+", add_u, INT); - reg_op!(lib, "-", sub_u, INT); - reg_op!(lib, "*", mul_u, INT); - reg_op!(lib, "/", div_u, INT); + // reg_op!(lib, "+", add_u, INT); + // reg_op!(lib, "-", sub_u, INT); + // reg_op!(lib, "*", mul_u, INT); + // reg_op!(lib, "/", div_u, INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, u32, u64, i128, u128); } } // Basic arithmetic for floating-point - no need to check #[cfg(not(feature = "no_float"))] { - reg_op!(lib, "+", add_u, f32, f64); - reg_op!(lib, "-", sub_u, f32, f64); - reg_op!(lib, "*", mul_u, f32, f64); - reg_op!(lib, "/", div_u, f32, f64); + // reg_op!(lib, "+", add_u, f32, f64); + // reg_op!(lib, "-", sub_u, f32, f64); + // reg_op!(lib, "*", mul_u, f32, f64); + // reg_op!(lib, "/", div_u, f32, f64); + reg_op!(lib, "+", add_u, f32); + reg_op!(lib, "-", sub_u, f32); + reg_op!(lib, "*", mul_u, f32); + reg_op!(lib, "/", div_u, f32); } // Bit operations - reg_op!(lib, "|", binary_or, INT); - reg_op!(lib, "&", binary_and, INT); - reg_op!(lib, "^", binary_xor, INT); + // reg_op!(lib, "|", binary_or, INT); + // reg_op!(lib, "&", binary_and, INT); + // reg_op!(lib, "^", binary_xor, INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, u32, u64, i128, u128); } // Checked bit shifts #[cfg(not(feature = "unchecked"))] { - reg_op!(lib, "<<", shl, INT); - reg_op!(lib, ">>", shr, INT); - reg_op!(lib, "%", modulo, INT); + // reg_op!(lib, "<<", shl, INT); + // reg_op!(lib, ">>", shr, INT); + // reg_op!(lib, "%", modulo, INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, u32, u64, i128, u128); } } // Unchecked bit shifts #[cfg(feature = "unchecked")] { - reg_op!(lib, "<<", shl_u, INT, INT); - reg_op!(lib, ">>", shr_u, INT, INT); - reg_op!(lib, "%", modulo_u, INT); + // reg_op!(lib, "<<", shl_u, INT, INT); + // reg_op!(lib, ">>", shr_u, INT, INT); + // reg_op!(lib, "%", modulo_u, INT); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, u32, u64, i128, u128); } } // Checked power #[cfg(not(feature = "unchecked"))] { - lib.set_fn_2("~", pow_i_i); + // lib.set_fn_2("~", pow_i_i); #[cfg(not(feature = "no_float"))] lib.set_fn_2("~", pow_f_i); @@ -371,7 +392,7 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { // Unchecked power #[cfg(feature = "unchecked")] { - lib.set_fn_2("~", pow_i_i_u); + // lib.set_fn_2("~", pow_i_i_u); #[cfg(not(feature = "no_float"))] lib.set_fn_2("~", pow_f_i_u); @@ -380,8 +401,9 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { // Floating-point modulo and power #[cfg(not(feature = "no_float"))] { - reg_op!(lib, "%", modulo_u, f32, f64); - lib.set_fn_2("~", pow_f_f); + // reg_op!(lib, "%", modulo_u, f32, f64); + reg_op!(lib, "%", modulo_u, f32); + // lib.set_fn_2("~", pow_f_f); } // Checked unary diff --git a/src/packages/logic.rs b/src/packages/logic.rs index ef771688..03a12e9d 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -42,40 +42,52 @@ macro_rules! reg_op { } def_package!(crate:LogicPackage:"Logical operators.", lib, { - reg_op!(lib, "<", lt, INT, char); - reg_op!(lib, "<=", lte, INT, char); - reg_op!(lib, ">", gt, INT, char); - reg_op!(lib, ">=", gte, INT, char); - reg_op!(lib, "==", eq, INT, char, bool, ()); - reg_op!(lib, "!=", ne, INT, char, bool, ()); + // reg_op!(lib, "<", lt, INT, char); + // reg_op!(lib, "<=", lte, INT, char); + // reg_op!(lib, ">", gt, INT, char); + // reg_op!(lib, ">=", gte, INT, char); + // reg_op!(lib, "==", eq, INT, char, bool, ()); + // reg_op!(lib, "!=", ne, INT, char, bool, ()); // Special versions for strings - at least avoid copying the first string - lib.set_fn_2_mut("<", |x: &mut String, y: String| Ok(*x < y)); - lib.set_fn_2_mut("<=", |x: &mut String, y: String| Ok(*x <= y)); - lib.set_fn_2_mut(">", |x: &mut String, y: String| Ok(*x > y)); - lib.set_fn_2_mut(">=", |x: &mut String, y: String| Ok(*x >= y)); - lib.set_fn_2_mut("==", |x: &mut String, y: String| Ok(*x == y)); - lib.set_fn_2_mut("!=", |x: &mut String, y: String| Ok(*x != y)); + // lib.set_fn_2_mut("<", |x: &mut String, y: String| Ok(*x < y)); + // lib.set_fn_2_mut("<=", |x: &mut String, y: String| Ok(*x <= y)); + // lib.set_fn_2_mut(">", |x: &mut String, y: String| Ok(*x > y)); + // lib.set_fn_2_mut(">=", |x: &mut String, y: String| Ok(*x >= y)); + // lib.set_fn_2_mut("==", |x: &mut String, y: String| Ok(*x == y)); + // lib.set_fn_2_mut("!=", |x: &mut String, y: String| Ok(*x != y)); #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { - reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); - reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + // reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128); + reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, u32, u64, i128, u128); + reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, u32, u64, i128, u128); } #[cfg(not(feature = "no_float"))] { - reg_op!(lib, "<", lt, f32, f64); - reg_op!(lib, "<=", lte, f32, f64); - reg_op!(lib, ">", gt, f32, f64); - reg_op!(lib, ">=", gte, f32, f64); - reg_op!(lib, "==", eq, f32, f64); - reg_op!(lib, "!=", ne, f32, f64); + // reg_op!(lib, "<", lt, f32, f64); + // reg_op!(lib, "<=", lte, f32, f64); + // reg_op!(lib, ">", gt, f32, f64); + // reg_op!(lib, ">=", gte, f32, f64); + // reg_op!(lib, "==", eq, f32, f64); + // reg_op!(lib, "!=", ne, f32, f64); + reg_op!(lib, "<", lt, f32); + reg_op!(lib, "<=", lte, f32); + reg_op!(lib, ">", gt, f32); + reg_op!(lib, ">=", gte, f32); + reg_op!(lib, "==", eq, f32); + reg_op!(lib, "!=", ne, f32); } // `&&` and `||` are treated specially as they short-circuit. @@ -83,7 +95,7 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, { //reg_op!(lib, "||", or, bool); //reg_op!(lib, "&&", and, bool); - lib.set_fn_2("|", or); - lib.set_fn_2("&", and); + // lib.set_fn_2("|", or); + // lib.set_fn_2("&", and); lib.set_fn_1("!", not); }); diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 075a1c68..ddd6a30d 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -6,7 +6,7 @@ use crate::utils::StaticVec; use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec}; -mod arithmetic; +pub(crate) mod arithmetic; mod array_basic; mod eval; mod iter_basic; diff --git a/src/parser.rs b/src/parser.rs index 3c3581eb..928a81ff 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -389,12 +389,12 @@ pub enum Expr { Property(Box<((String, String, String), Position)>), /// { stmt } Stmt(Box<(Stmt, Position)>), - /// func(expr, ... ) - ((function name, position), optional modules, hash, arguments, optional default value) + /// func(expr, ... ) - ((function name, native_only, position), optional modules, hash, arguments, optional default value) /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls /// and the function names are predictable, so no need to allocate a new `String`. FnCall( Box<( - (Cow<'static, str>, Position), + (Cow<'static, str>, bool, Position), Option>, u64, StaticVec, @@ -503,7 +503,7 @@ impl Expr { Self::Property(x) => x.1, Self::Stmt(x) => x.1, Self::Variable(x) => (x.0).1, - Self::FnCall(x) => (x.0).1, + Self::FnCall(x) => (x.0).2, Self::And(x) | Self::Or(x) | Self::In(x) => x.2, @@ -527,7 +527,7 @@ impl Expr { Self::Variable(x) => (x.0).1 = new_pos, Self::Property(x) => x.1 = new_pos, Self::Stmt(x) => x.1 = new_pos, - Self::FnCall(x) => (x.0).1 = new_pos, + Self::FnCall(x) => (x.0).2 = new_pos, Self::And(x) => x.2 = new_pos, Self::Or(x) => x.2 = new_pos, Self::In(x) => x.2 = new_pos, @@ -761,7 +761,7 @@ fn parse_call_expr<'a>( let hash_fn_def = calc_fn_hash(empty(), &id, empty()); return Ok(Expr::FnCall(Box::new(( - (id.into(), begin), + (id.into(), false, begin), modules, hash_fn_def, args, @@ -802,7 +802,7 @@ fn parse_call_expr<'a>( let hash_fn_def = calc_fn_hash(empty(), &id, args_iter); return Ok(Expr::FnCall(Box::new(( - (id.into(), begin), + (id.into(), false, begin), modules, hash_fn_def, args, @@ -1347,7 +1347,7 @@ fn parse_unary<'a>( args.push(expr); Ok(Expr::FnCall(Box::new(( - (op.into(), pos), + (op.into(), true, pos), None, hash, args, @@ -1371,7 +1371,7 @@ fn parse_unary<'a>( let hash = calc_fn_hash(empty(), op, 2, empty()); Ok(Expr::FnCall(Box::new(( - (op.into(), pos), + (op.into(), true, pos), None, hash, args, @@ -1473,7 +1473,7 @@ fn parse_op_assignment_stmt<'a>( args.push(rhs); let hash = calc_fn_hash(empty(), &op, args.len(), empty()); - let rhs_expr = Expr::FnCall(Box::new(((op, pos), None, hash, args, None))); + let rhs_expr = Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))); make_assignment_stmt(state, lhs, rhs_expr, pos) } @@ -1767,26 +1767,30 @@ fn parse_binary_op<'a>( args.push(rhs); root = match op_token { - Token::Plus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::Minus => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::Multiply => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::Divide => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), + Token::Plus => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::Minus => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::Multiply => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::Divide => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), - Token::LeftShift => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::RightShift => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::Modulo => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::PowerOf => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), + Token::LeftShift => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::RightShift => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::Modulo => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::PowerOf => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), // Comparison operators default to false when passed invalid operands - Token::EqualsTo => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))), - Token::NotEqualsTo => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))), - Token::LessThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))), - Token::LessThanEqualsTo => { - Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))) + Token::EqualsTo => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))), + Token::NotEqualsTo => { + Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))) + } + Token::LessThan => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))), + Token::LessThanEqualsTo => { + Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))) + } + Token::GreaterThan => { + Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))) } - Token::GreaterThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))), Token::GreaterThanEqualsTo => { - Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))) + Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def))) } Token::Or => { @@ -1799,9 +1803,9 @@ fn parse_binary_op<'a>( let current_lhs = args.pop(); Expr::And(Box::new((current_lhs, rhs, pos))) } - Token::Ampersand => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::Pipe => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), - Token::XOr => Expr::FnCall(Box::new(((op, pos), None, hash, args, None))), + Token::Ampersand => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::Pipe => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), + Token::XOr => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))), Token::In => { let rhs = args.pop(); @@ -1817,7 +1821,7 @@ fn parse_binary_op<'a>( match &mut rhs { // current_lhs.rhs(...) - method call Expr::FnCall(x) => { - let ((id, _), _, hash, args, _) = x.as_mut(); + let ((id, _, _), _, hash, args, _) = x.as_mut(); // Recalculate function call hash because there is an additional argument *hash = calc_fn_hash(empty(), id, args.len() + 1, empty()); }