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.
|
* 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
|
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`.
|
* New `EvalPackage` to disable `eval`.
|
||||||
* More benchmarks.
|
* More benchmarks.
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ fn bench_engine_new_raw_core(bench: &mut Bencher) {
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_engine_register_fn(bench: &mut Bencher) {
|
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
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ fn bench_eval_call_expression(bench: &mut Bencher) {
|
|||||||
modifierTest + 1000 / 2 > (80 * 100 % 2)
|
modifierTest + 1000 / 2 > (80 * 100 % 2)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
bench.iter(|| engine.eval_expression::<bool>(script).unwrap());
|
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)
|
modifierTest + 1000 / 2 > (80 * 100 % 2)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
bench.iter(|| engine.eval::<bool>(script).unwrap());
|
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::module::Module;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
|
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::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::module::{resolvers, ModuleRef, ModuleResolver};
|
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)
|
// Return default value (if any)
|
||||||
if let Some(val) = def_val {
|
if let Some(val) = def_val {
|
||||||
return Ok((val.clone(), false));
|
return Ok((val.clone(), false));
|
||||||
@ -873,6 +884,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
native_only: bool,
|
||||||
hash_fn_def: u64,
|
hash_fn_def: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
@ -887,7 +899,7 @@ impl Engine {
|
|||||||
args.len(),
|
args.len(),
|
||||||
args.iter().map(|a| a.type_id()),
|
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 {
|
match fn_name {
|
||||||
// type_of
|
// type_of
|
||||||
@ -1003,7 +1015,7 @@ impl Engine {
|
|||||||
match rhs {
|
match rhs {
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
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 def_val = def_val.as_ref();
|
||||||
|
|
||||||
let mut arg_values: StaticVec<_> = once(obj)
|
let mut arg_values: StaticVec<_> = once(obj)
|
||||||
@ -1016,7 +1028,7 @@ impl Engine {
|
|||||||
.collect();
|
.collect();
|
||||||
let args = arg_values.as_mut();
|
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
|
// xxx.module::fn_name(...) - syntax error
|
||||||
Expr::FnCall(_) => unreachable!(),
|
Expr::FnCall(_) => unreachable!(),
|
||||||
@ -1045,14 +1057,14 @@ impl Engine {
|
|||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let ((_, _, setter), pos) = x.as_ref();
|
let ((_, _, setter), pos) = x.as_ref();
|
||||||
let mut args = [obj, new_val.as_mut().unwrap()];
|
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))
|
.map(|(v, _)| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(x) => {
|
Expr::Property(x) => {
|
||||||
let ((_, getter, _), pos) = x.as_ref();
|
let ((_, getter, _), pos) = x.as_ref();
|
||||||
let mut args = [obj];
|
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))
|
.map(|(v, _)| (v, false))
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -1083,7 +1095,8 @@ impl Engine {
|
|||||||
|
|
||||||
let (mut val, updated) = if let Expr::Property(p) = &x.0 {
|
let (mut val, updated) = if let Expr::Property(p) = &x.0 {
|
||||||
let ((_, getter, _), _) = p.as_ref();
|
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 {
|
} else {
|
||||||
// Syntax error
|
// Syntax error
|
||||||
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
return Err(Box::new(EvalAltResult::ErrorDotExpr(
|
||||||
@ -1104,7 +1117,7 @@ impl Engine {
|
|||||||
let ((_, _, setter), _) = p.as_ref();
|
let ((_, _, setter), _) = p.as_ref();
|
||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
args[1] = val;
|
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 {
|
.or_else(|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
EvalAltResult::ErrorDotExpr(_, _) => Ok(Default::default()),
|
EvalAltResult::ErrorDotExpr(_, _) => Ok(Default::default()),
|
||||||
@ -1306,7 +1319,7 @@ impl Engine {
|
|||||||
_ => {
|
_ => {
|
||||||
let type_name = self.map_type_name(val.type_name());
|
let type_name = self.map_type_name(val.type_name());
|
||||||
let args = &mut [val, &mut idx];
|
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(|(v, _)| v.into())
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), op_pos))
|
Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), op_pos))
|
||||||
@ -1334,7 +1347,6 @@ impl Engine {
|
|||||||
Dynamic(Union::Array(mut rhs_value)) => {
|
Dynamic(Union::Array(mut rhs_value)) => {
|
||||||
let op = "==";
|
let op = "==";
|
||||||
let def_value = false.into();
|
let def_value = false.into();
|
||||||
let hash_fn_def = calc_fn_hash(empty(), op, 2, empty());
|
|
||||||
|
|
||||||
// Call the `==` operator to compare each value
|
// Call the `==` operator to compare each value
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
@ -1345,7 +1357,7 @@ impl Engine {
|
|||||||
// Qualifiers (none) + function name + argument `TypeId`'s.
|
// Qualifiers (none) + function name + argument `TypeId`'s.
|
||||||
let hash_fn =
|
let hash_fn =
|
||||||
calc_fn_hash(empty(), op, args.len(), args.iter().map(|a| a.type_id()));
|
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
|
let (r, _) = self
|
||||||
.call_fn_raw(None, state, op, hashes, args, true, def_value, pos, level)?;
|
.call_fn_raw(None, state, op, hashes, args, true, def_value, pos, level)?;
|
||||||
@ -1488,7 +1500,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
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 def_val = def_val.as_ref();
|
||||||
|
|
||||||
let mut arg_values = args_expr
|
let mut arg_values = args_expr
|
||||||
@ -1501,7 +1513,7 @@ impl Engine {
|
|||||||
if name == KEYWORD_EVAL && args.len() == 1 && args.get(0).is::<String>() {
|
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>()));
|
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
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let pos = args_expr.get(0).position();
|
let pos = args_expr.get(0).position();
|
||||||
@ -1521,14 +1533,16 @@ impl Engine {
|
|||||||
|
|
||||||
// Normal function call - except for eval (handled above)
|
// Normal function call - except for eval (handled above)
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
self.exec_fn_call(state, name, *hash_fn_def, args, false, def_val, *pos, level)
|
self.exec_fn_call(
|
||||||
.map(|(v, _)| v)
|
state, name, *native, *hash, args, false, def_val, *pos, level,
|
||||||
|
)
|
||||||
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Module-qualified function call
|
// Module-qualified function call
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::FnCall(x) if x.1.is_some() => {
|
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 modules = modules.as_ref().unwrap();
|
||||||
|
|
||||||
let mut arg_values = args_expr
|
let mut arg_values = args_expr
|
||||||
@ -1934,3 +1948,125 @@ impl Engine {
|
|||||||
.unwrap_or(name)
|
.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::any::Dynamic;
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::engine::{
|
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::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::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
@ -112,8 +111,7 @@ impl<'a> State<'a> {
|
|||||||
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
fn call_fn(
|
fn call_fn(
|
||||||
packages: &PackagesCollection,
|
state: &State,
|
||||||
global_module: &Module,
|
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -126,12 +124,21 @@ fn call_fn(
|
|||||||
args.iter().map(|a| a.type_id()),
|
args.iter().map(|a| a.type_id()),
|
||||||
);
|
);
|
||||||
|
|
||||||
global_module
|
state
|
||||||
.get_fn(hash_fn)
|
.engine
|
||||||
.or_else(|| packages.get_fn(hash_fn))
|
.call_fn_raw(
|
||||||
.map(|func| func.get_native_fn()(args))
|
None,
|
||||||
.transpose()
|
&mut EngineState::new(&Default::default()),
|
||||||
.map_err(|err| err.new_position(pos))
|
fn_name,
|
||||||
|
(hash_fn, 0),
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
None,
|
||||||
|
pos,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.map(|(v, _)| Some(v))
|
||||||
|
.or_else(|_| Ok(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize a statement.
|
/// 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
|
&& state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||||
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& 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)
|
// 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
|
// 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();
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
return Expr::FnCall(x);
|
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|
|
.and_then(|result|
|
||||||
result.or_else(|| {
|
result.or_else(|| {
|
||||||
if !arg_for_type_of.is_empty() {
|
if !arg_for_type_of.is_empty() {
|
||||||
|
@ -20,7 +20,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Checked add
|
// 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(|| {
|
x.checked_add(&y).ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorArithmetic(
|
Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Addition overflow: {} + {}", x, y),
|
format!("Addition overflow: {} + {}", x, y),
|
||||||
@ -29,7 +29,7 @@ fn add<T: Display + CheckedAdd>(x: T, y: T) -> FuncReturn<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked subtract
|
// 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(|| {
|
x.checked_sub(&y).ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorArithmetic(
|
Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Subtraction underflow: {} - {}", x, y),
|
format!("Subtraction underflow: {} - {}", x, y),
|
||||||
@ -38,7 +38,7 @@ fn sub<T: Display + CheckedSub>(x: T, y: T) -> FuncReturn<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked multiply
|
// 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(|| {
|
x.checked_mul(&y).ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorArithmetic(
|
Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Multiplication overflow: {} * {}", x, y),
|
format!("Multiplication overflow: {} * {}", x, y),
|
||||||
@ -47,7 +47,7 @@ fn mul<T: Display + CheckedMul>(x: T, y: T) -> FuncReturn<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked divide
|
// Checked divide
|
||||||
fn div<T>(x: T, y: T) -> FuncReturn<T>
|
pub(crate) fn div<T>(x: T, y: T) -> FuncReturn<T>
|
||||||
where
|
where
|
||||||
T: Display + CheckedDiv + PartialEq + Zero,
|
T: Display + CheckedDiv + PartialEq + Zero,
|
||||||
{
|
{
|
||||||
@ -67,7 +67,7 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
|
// 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(|| {
|
x.checked_neg().ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorArithmetic(
|
Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Negation overflow: -{}", x),
|
format!("Negation overflow: -{}", x),
|
||||||
@ -76,7 +76,7 @@ fn neg<T: Display + CheckedNeg>(x: T) -> FuncReturn<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked absolute
|
// 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
|
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
|
||||||
// when the number is ::MIN instead of returning ::MIN itself.
|
// when the number is ::MIN instead of returning ::MIN itself.
|
||||||
if x >= <T as Zero>::zero() {
|
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)
|
Ok(x ^ y)
|
||||||
}
|
}
|
||||||
// Checked left-shift
|
// 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
|
// Cannot shift by a negative number of bits
|
||||||
if y < 0 {
|
if y < 0 {
|
||||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
@ -150,7 +150,7 @@ fn shl<T: Display + CheckedShl>(x: T, y: INT) -> FuncReturn<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked right-shift
|
// 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
|
// Cannot shift by a negative number of bits
|
||||||
if y < 0 {
|
if y < 0 {
|
||||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
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
|
// 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))
|
Ok(x.shl(y))
|
||||||
}
|
}
|
||||||
// Unchecked right-shift - may panic if shifting by a negative number of bits
|
// 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))
|
Ok(x.shr(y))
|
||||||
}
|
}
|
||||||
// Checked modulo
|
// 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(|| {
|
x.checked_rem(&y).ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorArithmetic(
|
Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
format!("Modulo division by zero or overflow: {} % {}", x, y),
|
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)
|
Ok(x % y)
|
||||||
}
|
}
|
||||||
// Checked power
|
// 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"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
{
|
{
|
||||||
if y > (u32::MAX as INT) {
|
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)
|
// 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))
|
Ok(x.pow(y as u32))
|
||||||
}
|
}
|
||||||
// Floating-point power - always well-defined
|
// Floating-point power - always well-defined
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[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))
|
Ok(x.powf(y))
|
||||||
}
|
}
|
||||||
// Checked power
|
// Checked power
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[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
|
// Raise to power that is larger than an i32
|
||||||
if y > (i32::MAX as INT) {
|
if y > (i32::MAX as INT) {
|
||||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
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)
|
// Unchecked power - may be incorrect if the power index is too high (> i32::MAX)
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[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))
|
Ok(x.powi(y as i32))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,97 +272,118 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
|||||||
// Checked basic arithmetic
|
// Checked basic arithmetic
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "+", add, INT);
|
// reg_op!(lib, "+", add, INT);
|
||||||
reg_op!(lib, "-", sub, INT);
|
// reg_op!(lib, "-", sub, INT);
|
||||||
reg_op!(lib, "*", mul, INT);
|
// reg_op!(lib, "*", mul, INT);
|
||||||
reg_op!(lib, "/", div, INT);
|
// reg_op!(lib, "/", div, INT);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "+", add, 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, "-", 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, "*", 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, "/", 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
|
// Unchecked basic arithmetic
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "+", add_u, INT);
|
// reg_op!(lib, "+", add_u, INT);
|
||||||
reg_op!(lib, "-", sub_u, INT);
|
// reg_op!(lib, "-", sub_u, INT);
|
||||||
reg_op!(lib, "*", mul_u, INT);
|
// reg_op!(lib, "*", mul_u, INT);
|
||||||
reg_op!(lib, "/", div_u, INT);
|
// reg_op!(lib, "/", div_u, INT);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "+", add_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, "-", 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, "*", 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, "/", 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
|
// Basic arithmetic for floating-point - no need to check
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "+", add_u, f32, f64);
|
// reg_op!(lib, "+", add_u, f32, f64);
|
||||||
reg_op!(lib, "-", sub_u, f32, f64);
|
// reg_op!(lib, "-", sub_u, f32, f64);
|
||||||
reg_op!(lib, "*", mul_u, f32, f64);
|
// reg_op!(lib, "*", mul_u, f32, f64);
|
||||||
reg_op!(lib, "/", div_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
|
// Bit operations
|
||||||
reg_op!(lib, "|", binary_or, INT);
|
// reg_op!(lib, "|", binary_or, INT);
|
||||||
reg_op!(lib, "&", binary_and, INT);
|
// reg_op!(lib, "&", binary_and, INT);
|
||||||
reg_op!(lib, "^", binary_xor, INT);
|
// reg_op!(lib, "^", binary_xor, INT);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "|", binary_or, 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_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_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
|
// Checked bit shifts
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "<<", shl, INT);
|
// reg_op!(lib, "<<", shl, INT);
|
||||||
reg_op!(lib, ">>", shr, INT);
|
// reg_op!(lib, ">>", shr, INT);
|
||||||
reg_op!(lib, "%", modulo, INT);
|
// reg_op!(lib, "%", modulo, INT);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "<<", shl, 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, ">>", 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, "%", 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
|
// Unchecked bit shifts
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "<<", shl_u, INT, INT);
|
// reg_op!(lib, "<<", shl_u, INT, INT);
|
||||||
reg_op!(lib, ">>", shr_u, INT, INT);
|
// reg_op!(lib, ">>", shr_u, INT, INT);
|
||||||
reg_op!(lib, "%", modulo_u, INT);
|
// reg_op!(lib, "%", modulo_u, INT);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "<<", shl_u, i64, 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, ">>", 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, "%", 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
|
// Checked power
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
{
|
{
|
||||||
lib.set_fn_2("~", pow_i_i);
|
// lib.set_fn_2("~", pow_i_i);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
lib.set_fn_2("~", pow_f_i);
|
lib.set_fn_2("~", pow_f_i);
|
||||||
@ -371,7 +392,7 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
|||||||
// Unchecked power
|
// Unchecked power
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
{
|
{
|
||||||
lib.set_fn_2("~", pow_i_i_u);
|
// lib.set_fn_2("~", pow_i_i_u);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
lib.set_fn_2("~", pow_f_i_u);
|
lib.set_fn_2("~", pow_f_i_u);
|
||||||
@ -380,8 +401,9 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
|||||||
// Floating-point modulo and power
|
// Floating-point modulo and power
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "%", modulo_u, f32, f64);
|
// reg_op!(lib, "%", modulo_u, f32, f64);
|
||||||
lib.set_fn_2("~", pow_f_f);
|
reg_op!(lib, "%", modulo_u, f32);
|
||||||
|
// lib.set_fn_2("~", pow_f_f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checked unary
|
// Checked unary
|
||||||
|
@ -42,40 +42,52 @@ macro_rules! reg_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||||
reg_op!(lib, "<", lt, INT, char);
|
// reg_op!(lib, "<", lt, INT, char);
|
||||||
reg_op!(lib, "<=", lte, INT, char);
|
// reg_op!(lib, "<=", lte, INT, char);
|
||||||
reg_op!(lib, ">", gt, INT, char);
|
// reg_op!(lib, ">", gt, INT, char);
|
||||||
reg_op!(lib, ">=", gte, INT, char);
|
// reg_op!(lib, ">=", gte, INT, char);
|
||||||
reg_op!(lib, "==", eq, INT, char, bool, ());
|
// reg_op!(lib, "==", eq, INT, char, bool, ());
|
||||||
reg_op!(lib, "!=", ne, INT, char, bool, ());
|
// reg_op!(lib, "!=", ne, INT, char, bool, ());
|
||||||
|
|
||||||
// Special versions for strings - at least avoid copying the first string
|
// 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_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "<", lt, 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, "<=", 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, ">", 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, ">=", 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, "==", 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, "!=", 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"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
reg_op!(lib, "<", lt, f32, f64);
|
// reg_op!(lib, "<", lt, f32, f64);
|
||||||
reg_op!(lib, "<=", lte, f32, f64);
|
// reg_op!(lib, "<=", lte, f32, f64);
|
||||||
reg_op!(lib, ">", gt, f32, f64);
|
// reg_op!(lib, ">", gt, f32, f64);
|
||||||
reg_op!(lib, ">=", gte, f32, f64);
|
// reg_op!(lib, ">=", gte, f32, f64);
|
||||||
reg_op!(lib, "==", eq, f32, f64);
|
// reg_op!(lib, "==", eq, f32, f64);
|
||||||
reg_op!(lib, "!=", ne, 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.
|
// `&&` 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, "||", or, bool);
|
||||||
//reg_op!(lib, "&&", and, bool);
|
//reg_op!(lib, "&&", and, bool);
|
||||||
|
|
||||||
lib.set_fn_2("|", or);
|
// lib.set_fn_2("|", or);
|
||||||
lib.set_fn_2("&", and);
|
// lib.set_fn_2("&", and);
|
||||||
lib.set_fn_1("!", not);
|
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};
|
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 array_basic;
|
||||||
mod eval;
|
mod eval;
|
||||||
mod iter_basic;
|
mod iter_basic;
|
||||||
|
@ -389,12 +389,12 @@ pub enum Expr {
|
|||||||
Property(Box<((String, String, String), Position)>),
|
Property(Box<((String, String, String), Position)>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
Stmt(Box<(Stmt, Position)>),
|
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
|
/// 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`.
|
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||||
FnCall(
|
FnCall(
|
||||||
Box<(
|
Box<(
|
||||||
(Cow<'static, str>, Position),
|
(Cow<'static, str>, bool, Position),
|
||||||
Option<Box<ModuleRef>>,
|
Option<Box<ModuleRef>>,
|
||||||
u64,
|
u64,
|
||||||
StaticVec<Expr>,
|
StaticVec<Expr>,
|
||||||
@ -503,7 +503,7 @@ impl Expr {
|
|||||||
Self::Property(x) => x.1,
|
Self::Property(x) => x.1,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
Self::Variable(x) => (x.0).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,
|
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::Variable(x) => (x.0).1 = new_pos,
|
||||||
Self::Property(x) => x.1 = new_pos,
|
Self::Property(x) => x.1 = new_pos,
|
||||||
Self::Stmt(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::And(x) => x.2 = new_pos,
|
||||||
Self::Or(x) => x.2 = new_pos,
|
Self::Or(x) => x.2 = new_pos,
|
||||||
Self::In(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());
|
let hash_fn_def = calc_fn_hash(empty(), &id, empty());
|
||||||
|
|
||||||
return Ok(Expr::FnCall(Box::new((
|
return Ok(Expr::FnCall(Box::new((
|
||||||
(id.into(), begin),
|
(id.into(), false, begin),
|
||||||
modules,
|
modules,
|
||||||
hash_fn_def,
|
hash_fn_def,
|
||||||
args,
|
args,
|
||||||
@ -802,7 +802,7 @@ fn parse_call_expr<'a>(
|
|||||||
let hash_fn_def = calc_fn_hash(empty(), &id, args_iter);
|
let hash_fn_def = calc_fn_hash(empty(), &id, args_iter);
|
||||||
|
|
||||||
return Ok(Expr::FnCall(Box::new((
|
return Ok(Expr::FnCall(Box::new((
|
||||||
(id.into(), begin),
|
(id.into(), false, begin),
|
||||||
modules,
|
modules,
|
||||||
hash_fn_def,
|
hash_fn_def,
|
||||||
args,
|
args,
|
||||||
@ -1347,7 +1347,7 @@ fn parse_unary<'a>(
|
|||||||
args.push(expr);
|
args.push(expr);
|
||||||
|
|
||||||
Ok(Expr::FnCall(Box::new((
|
Ok(Expr::FnCall(Box::new((
|
||||||
(op.into(), pos),
|
(op.into(), true, pos),
|
||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
@ -1371,7 +1371,7 @@ fn parse_unary<'a>(
|
|||||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||||
|
|
||||||
Ok(Expr::FnCall(Box::new((
|
Ok(Expr::FnCall(Box::new((
|
||||||
(op.into(), pos),
|
(op.into(), true, pos),
|
||||||
None,
|
None,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
@ -1473,7 +1473,7 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
args.push(rhs);
|
args.push(rhs);
|
||||||
|
|
||||||
let hash = calc_fn_hash(empty(), &op, args.len(), empty());
|
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)
|
make_assignment_stmt(state, lhs, rhs_expr, pos)
|
||||||
}
|
}
|
||||||
@ -1767,26 +1767,30 @@ fn parse_binary_op<'a>(
|
|||||||
args.push(rhs);
|
args.push(rhs);
|
||||||
|
|
||||||
root = match op_token {
|
root = match op_token {
|
||||||
Token::Plus => 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, 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, 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, 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::LeftShift => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
|
||||||
Token::RightShift => Expr::FnCall(Box::new(((op, 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, 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, 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
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::EqualsTo => 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, pos), None, hash, args, cmp_def))),
|
Token::NotEqualsTo => {
|
||||||
Token::LessThan => Expr::FnCall(Box::new(((op, pos), None, hash, args, cmp_def))),
|
Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def)))
|
||||||
Token::LessThanEqualsTo => {
|
}
|
||||||
Expr::FnCall(Box::new(((op, 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 => {
|
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 => {
|
Token::Or => {
|
||||||
@ -1799,9 +1803,9 @@ fn parse_binary_op<'a>(
|
|||||||
let current_lhs = args.pop();
|
let current_lhs = args.pop();
|
||||||
Expr::And(Box::new((current_lhs, rhs, pos)))
|
Expr::And(Box::new((current_lhs, rhs, pos)))
|
||||||
}
|
}
|
||||||
Token::Ampersand => 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, 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, pos), None, hash, args, None))),
|
Token::XOr => Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None))),
|
||||||
|
|
||||||
Token::In => {
|
Token::In => {
|
||||||
let rhs = args.pop();
|
let rhs = args.pop();
|
||||||
@ -1817,7 +1821,7 @@ fn parse_binary_op<'a>(
|
|||||||
match &mut rhs {
|
match &mut rhs {
|
||||||
// current_lhs.rhs(...) - method call
|
// current_lhs.rhs(...) - method call
|
||||||
Expr::FnCall(x) => {
|
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
|
// Recalculate function call hash because there is an additional argument
|
||||||
*hash = calc_fn_hash(empty(), id, args.len() + 1, empty());
|
*hash = calc_fn_hash(empty(), id, args.len() + 1, empty());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user