Build-in certain common operators.
This commit is contained in:
parent
a743c47345
commit
b49e1e199a
10
RELEASES.md
10
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<ParseError>`.
|
||||
* The `RegisterDynamicFn` trait is merged into the `RegisterResutlFn` trait which now always returns
|
||||
`Result<Dynamic, Box<EvalAltResult>>`.
|
||||
* 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.
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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::<bool>(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::<bool>(script).unwrap());
|
||||
}
|
||||
|
168
src/engine.rs
168
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::<String>() {
|
||||
let hash_fn = calc_fn_hash(empty(), name, 1, once(TypeId::of::<String>()));
|
||||
|
||||
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<Option<Dynamic>, Box<EvalAltResult>> {
|
||||
use crate::packages::arithmetic::*;
|
||||
|
||||
if x.type_id() == TypeId::of::<INT>() {
|
||||
let x = x.downcast_ref::<INT>().unwrap().clone();
|
||||
let y = y.downcast_ref::<INT>().unwrap().clone();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
"+" => return add(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"-" => return sub(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"*" => return mul(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"/" => return div(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"%" => return modulo(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"~" => return pow_i_i(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
">>" => return shr(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"<<" => return shl(x, y).map(Into::<Dynamic>::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::<Dynamic>::into).map(Some),
|
||||
">>" => return shr_u(x, y).map(Into::<Dynamic>::into).map(Some),
|
||||
"<<" => return shl_u(x, y).map(Into::<Dynamic>::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::<bool>() {
|
||||
let x = x.downcast_ref::<bool>().unwrap().clone();
|
||||
let y = y.downcast_ref::<bool>().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::<String>() {
|
||||
let x = x.downcast_ref::<String>().unwrap();
|
||||
let y = y.downcast_ref::<String>().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::<char>() {
|
||||
let x = x.downcast_ref::<char>().unwrap().clone();
|
||||
let y = y.downcast_ref::<char>().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::<FLOAT>() {
|
||||
let x = x.downcast_ref::<FLOAT>().unwrap().clone();
|
||||
let y = y.downcast_ref::<FLOAT>().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::<Dynamic>::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)
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -20,7 +20,7 @@ use crate::stdlib::{
|
||||
};
|
||||
|
||||
// Checked add
|
||||
fn add<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> {
|
||||
pub(crate) fn add<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> {
|
||||
x.checked_add(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Addition overflow: {} + {}", x, y),
|
||||
@ -29,7 +29,7 @@ fn add<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> {
|
||||
})
|
||||
}
|
||||
// Checked subtract
|
||||
fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> {
|
||||
pub(crate) fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> {
|
||||
x.checked_sub(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Subtraction underflow: {} - {}", x, y),
|
||||
@ -38,7 +38,7 @@ fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> {
|
||||
})
|
||||
}
|
||||
// Checked multiply
|
||||
fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> {
|
||||
pub(crate) fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> {
|
||||
x.checked_mul(&y).ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Multiplication overflow: {} * {}", x, y),
|
||||
@ -47,7 +47,7 @@ fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> {
|
||||
})
|
||||
}
|
||||
// Checked divide
|
||||
fn div<T>(x: T, y: T) -> FuncReturn<T>
|
||||
pub(crate) fn div<T>(x: T, y: T) -> FuncReturn<T>
|
||||
where
|
||||
T: Display + CheckedDiv + PartialEq + Zero,
|
||||
{
|
||||
@ -67,7 +67,7 @@ where
|
||||
})
|
||||
}
|
||||
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
|
||||
fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> {
|
||||
pub(crate) fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> {
|
||||
x.checked_neg().ok_or_else(|| {
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Negation overflow: -{}", x),
|
||||
@ -76,7 +76,7 @@ fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> {
|
||||
})
|
||||
}
|
||||
// Checked absolute
|
||||
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> FuncReturn<T> {
|
||||
pub(crate) fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> FuncReturn<T> {
|
||||
// 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 >= <T as Zero>::zero() {
|
||||
@ -133,7 +133,7 @@ fn binary_xor<T: BitXor>(x: T, y: T) -> FuncReturn<<T as BitXor>::Output> {
|
||||
Ok(x ^ y)
|
||||
}
|
||||
// Checked left-shift
|
||||
fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> {
|
||||
pub(crate) fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> {
|
||||
// Cannot shift by a negative number of bits
|
||||
if y < 0 {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
@ -150,7 +150,7 @@ fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> {
|
||||
})
|
||||
}
|
||||
// Checked right-shift
|
||||
fn shr<T: Display + CheckedShr>(x: T, y: INT) -> FuncReturn<T> {
|
||||
pub(crate) fn shr<T: Display + CheckedShr>(x: T, y: INT) -> FuncReturn<T> {
|
||||
// Cannot shift by a negative number of bits
|
||||
if y < 0 {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
@ -167,15 +167,15 @@ fn shr<T: Display + CheckedShr>(x: T, y: INT) -> FuncReturn<T> {
|
||||
})
|
||||
}
|
||||
// Unchecked left-shift - may panic if shifting by a negative number of bits
|
||||
fn shl_u<T: Shl<T>>(x: T, y: T) -> FuncReturn<<T as Shl<T>>::Output> {
|
||||
pub(crate) fn shl_u<T: Shl<T>>(x: T, y: T) -> FuncReturn<<T as Shl<T>>::Output> {
|
||||
Ok(x.shl(y))
|
||||
}
|
||||
// Unchecked right-shift - may panic if shifting by a negative number of bits
|
||||
fn shr_u<T: Shr<T>>(x: T, y: T) -> FuncReturn<<T as Shr<T>>::Output> {
|
||||
pub(crate) fn shr_u<T: Shr<T>>(x: T, y: T) -> FuncReturn<<T as Shr<T>>::Output> {
|
||||
Ok(x.shr(y))
|
||||
}
|
||||
// Checked modulo
|
||||
fn modulo<T: Display + CheckedRem>(x: T, y: T) -> FuncReturn<T> {
|
||||
pub(crate) fn modulo<T: Display + CheckedRem>(x: T, y: T) -> FuncReturn<T> {
|
||||
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<T: Rem>(x: T, y: T) -> FuncReturn<<T as Rem>::Output> {
|
||||
Ok(x % y)
|
||||
}
|
||||
// Checked power
|
||||
fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> {
|
||||
pub(crate) fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
if y > (u32::MAX as INT) {
|
||||
@ -229,17 +229,17 @@ fn pow_i_i(x: INT, y: INT) -> FuncReturn<INT> {
|
||||
}
|
||||
}
|
||||
// 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<INT> {
|
||||
pub(crate) fn pow_i_i_u(x: INT, y: INT) -> FuncReturn<INT> {
|
||||
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<FLOAT> {
|
||||
pub(crate) fn pow_f_f(x: FLOAT, y: FLOAT) -> FuncReturn<FLOAT> {
|
||||
Ok(x.powf(y))
|
||||
}
|
||||
// Checked power
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn<FLOAT> {
|
||||
pub(crate) fn pow_f_i(x: FLOAT, y: INT) -> FuncReturn<FLOAT> {
|
||||
// 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<FLOAT> {
|
||||
// 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<FLOAT> {
|
||||
pub(crate) fn pow_f_i_u(x: FLOAT, y: INT) -> FuncReturn<FLOAT> {
|
||||
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
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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<Box<ModuleRef>>,
|
||||
u64,
|
||||
StaticVec<Expr>,
|
||||
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user