Merge branch 'master' into plugins
This commit is contained in:
@@ -630,6 +630,14 @@ impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
|
||||
)))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
||||
fn from(value: &[T]) -> Self {
|
||||
Self(Union::Array(Box::new(
|
||||
value.iter().cloned().map(Dynamic::from).collect(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic {
|
||||
fn from(value: HashMap<String, T>) -> Self {
|
||||
|
@@ -1051,8 +1051,7 @@ impl Engine {
|
||||
let mut state = State::new();
|
||||
let args = args.as_mut();
|
||||
|
||||
let result =
|
||||
self.call_script_fn(Some(scope), &mut state, &lib, name, fn_def, args, pos, 0)?;
|
||||
let result = self.call_script_fn(scope, &mut state, &lib, name, fn_def, args, pos, 0)?;
|
||||
|
||||
let return_type = self.map_type_name(result.type_name());
|
||||
|
||||
|
300
src/engine.rs
300
src/engine.rs
@@ -432,11 +432,11 @@ fn default_print(s: &str) {
|
||||
}
|
||||
|
||||
/// Search for a variable within the scope
|
||||
fn search_scope<'a>(
|
||||
scope: &'a mut Scope,
|
||||
fn search_scope<'s, 'a>(
|
||||
scope: &'s mut Scope,
|
||||
state: &mut State,
|
||||
expr: &'a Expr,
|
||||
) -> Result<(&'a mut Dynamic, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||
) -> Result<(&'s mut Dynamic, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||
let ((name, pos), modules, hash_var, index) = match expr {
|
||||
Expr::Variable(x) => x.as_ref(),
|
||||
_ => unreachable!(),
|
||||
@@ -592,7 +592,7 @@ impl Engine {
|
||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||
pub(crate) fn call_fn_raw(
|
||||
&self,
|
||||
scope: Option<&mut Scope>,
|
||||
scope: &mut Scope,
|
||||
state: &mut State,
|
||||
lib: &FunctionsLib,
|
||||
fn_name: &str,
|
||||
@@ -615,11 +615,55 @@ impl Engine {
|
||||
return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
|
||||
}
|
||||
}
|
||||
|
||||
let mut this_copy: Dynamic = Default::default();
|
||||
let mut old_this_ptr: Option<&mut Dynamic> = None;
|
||||
|
||||
/// This function replaces the first argument of a method call with a clone copy.
|
||||
/// This is to prevent a pure function unintentionally consuming the first argument.
|
||||
fn normalize_first_arg<'a>(
|
||||
normalize: bool,
|
||||
this_copy: &mut Dynamic,
|
||||
old_this_ptr: &mut Option<&'a mut Dynamic>,
|
||||
args: &mut FnCallArgs<'a>,
|
||||
) {
|
||||
// Only do it for method calls with arguments.
|
||||
if !normalize || args.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clone the original value.
|
||||
*this_copy = args[0].clone();
|
||||
|
||||
// Replace the first reference with a reference to the clone, force-casting the lifetime.
|
||||
// Keep the original reference. Must remember to restore it later with `restore_first_arg_of_method_call`.
|
||||
let this_pointer = mem::replace(
|
||||
args.get_mut(0).unwrap(),
|
||||
unsafe_mut_cast_to_lifetime(this_copy),
|
||||
);
|
||||
|
||||
*old_this_ptr = Some(this_pointer);
|
||||
}
|
||||
|
||||
/// This function restores the first argument that was replaced by `normalize_first_arg_of_method_call`.
|
||||
fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCallArgs<'a>) {
|
||||
if let Some(this_pointer) = old_this_ptr {
|
||||
mem::replace(args.get_mut(0).unwrap(), this_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
// First search in script-defined functions (can override built-in)
|
||||
if !native_only {
|
||||
if let Some(fn_def) = lib.get(&hashes.1) {
|
||||
normalize_first_arg(is_ref, &mut this_copy, &mut old_this_ptr, args);
|
||||
|
||||
// Run scripted function
|
||||
let result =
|
||||
self.call_script_fn(scope, state, lib, fn_name, fn_def, args, pos, level)?;
|
||||
|
||||
// Restore the original reference
|
||||
restore_first_arg(old_this_ptr, args);
|
||||
|
||||
return Ok((result, false));
|
||||
}
|
||||
}
|
||||
@@ -631,29 +675,18 @@ impl Engine {
|
||||
.or_else(|| self.packages.get_fn(hashes.0))
|
||||
{
|
||||
// Calling pure function in method-call?
|
||||
let mut this_copy: Option<Dynamic>;
|
||||
let mut this_pointer: Option<&mut Dynamic> = None;
|
||||
|
||||
if func.is_pure() && is_ref && !args.is_empty() {
|
||||
// Clone the original value. It'll be consumed because the function
|
||||
// is pure and doesn't know that the first value is a reference (i.e. `is_ref`)
|
||||
this_copy = Some(args[0].clone());
|
||||
|
||||
// Replace the first reference with a reference to the clone, force-casting the lifetime.
|
||||
// Keep the original reference. Must remember to restore it before existing this function.
|
||||
this_pointer = Some(mem::replace(
|
||||
args.get_mut(0).unwrap(),
|
||||
unsafe_mut_cast_to_lifetime(this_copy.as_mut().unwrap()),
|
||||
));
|
||||
}
|
||||
normalize_first_arg(
|
||||
func.is_pure() && is_ref,
|
||||
&mut this_copy,
|
||||
&mut old_this_ptr,
|
||||
args,
|
||||
);
|
||||
|
||||
// Run external function
|
||||
let result = func.get_native_fn()(args);
|
||||
|
||||
// Restore the original reference
|
||||
if let Some(this_pointer) = this_pointer {
|
||||
mem::replace(args.get_mut(0).unwrap(), this_pointer);
|
||||
}
|
||||
restore_first_arg(old_this_ptr, args);
|
||||
|
||||
let result = result.map_err(|err| err.new_position(pos))?;
|
||||
|
||||
@@ -741,7 +774,7 @@ impl Engine {
|
||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||
pub(crate) fn call_script_fn(
|
||||
&self,
|
||||
scope: Option<&mut Scope>,
|
||||
scope: &mut Scope,
|
||||
state: &mut State,
|
||||
lib: &FunctionsLib,
|
||||
fn_name: &str,
|
||||
@@ -753,88 +786,42 @@ impl Engine {
|
||||
let orig_scope_level = state.scope_level;
|
||||
state.scope_level += 1;
|
||||
|
||||
match scope {
|
||||
// Extern scope passed in which is not empty
|
||||
Some(scope) if !scope.is_empty() => {
|
||||
let scope_len = scope.len();
|
||||
let scope_len = scope.len();
|
||||
|
||||
// Put arguments into scope as variables
|
||||
// Actually consume the arguments instead of cloning them
|
||||
scope.extend(
|
||||
fn_def
|
||||
.params
|
||||
.iter()
|
||||
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
||||
.map(|(name, value)| {
|
||||
let var_name = unsafe_cast_var_name_to_lifetime(name.as_str(), state);
|
||||
(var_name, ScopeEntryType::Normal, value)
|
||||
}),
|
||||
);
|
||||
// Put arguments into scope as variables
|
||||
// Actually consume the arguments instead of cloning them
|
||||
scope.extend(
|
||||
fn_def
|
||||
.params
|
||||
.iter()
|
||||
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
||||
.map(|(name, value)| {
|
||||
let var_name = unsafe_cast_var_name_to_lifetime(name.as_str(), state);
|
||||
(var_name, ScopeEntryType::Normal, value)
|
||||
}),
|
||||
);
|
||||
|
||||
// Evaluate the function at one higher level of call depth
|
||||
let result = self
|
||||
.eval_stmt(scope, state, lib, &fn_def.body, level + 1)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
||||
Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
format!("{} > {}", fn_name, name),
|
||||
err,
|
||||
pos,
|
||||
)))
|
||||
}
|
||||
_ => Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
fn_name.to_string(),
|
||||
err,
|
||||
pos,
|
||||
))),
|
||||
});
|
||||
// Evaluate the function at one higher level of call depth
|
||||
let result = self
|
||||
.eval_stmt(scope, state, lib, &fn_def.body, level + 1)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => Err(Box::new(
|
||||
EvalAltResult::ErrorInFunctionCall(format!("{} > {}", fn_name, name), err, pos),
|
||||
)),
|
||||
_ => Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
fn_name.to_string(),
|
||||
err,
|
||||
pos,
|
||||
))),
|
||||
});
|
||||
|
||||
// Remove all local variables
|
||||
scope.rewind(scope_len);
|
||||
state.scope_level = orig_scope_level;
|
||||
// Remove all local variables
|
||||
scope.rewind(scope_len);
|
||||
state.scope_level = orig_scope_level;
|
||||
|
||||
return result;
|
||||
}
|
||||
// No new scope - create internal scope
|
||||
_ => {
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Put arguments into scope as variables
|
||||
// Actually consume the arguments instead of cloning them
|
||||
scope.extend(
|
||||
fn_def
|
||||
.params
|
||||
.iter()
|
||||
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
||||
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
||||
);
|
||||
|
||||
// Evaluate the function at one higher level of call depth
|
||||
let result = self
|
||||
.eval_stmt(&mut scope, state, lib, &fn_def.body, level + 1)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
||||
Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
format!("{} > {}", fn_name, name),
|
||||
err,
|
||||
pos,
|
||||
)))
|
||||
}
|
||||
_ => Err(Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
fn_name.to_string(),
|
||||
err,
|
||||
pos,
|
||||
))),
|
||||
});
|
||||
|
||||
state.scope_level = orig_scope_level;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// Has a system function an override?
|
||||
@@ -892,9 +879,12 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Normal function call
|
||||
_ => self.call_fn_raw(
|
||||
None, state, lib, fn_name, hashes, args, is_ref, def_val, pos, level,
|
||||
),
|
||||
_ => {
|
||||
let mut scope = Scope::new();
|
||||
self.call_fn_raw(
|
||||
&mut scope, state, lib, fn_name, hashes, args, is_ref, def_val, pos, level,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,14 +1209,16 @@ impl Engine {
|
||||
Expr::FnCall(_) => unreachable!(),
|
||||
Expr::Property(_) => idx_values.push(()), // Store a placeholder - no need to copy the property name
|
||||
Expr::Index(x) | Expr::Dot(x) => {
|
||||
let (lhs, rhs, _) = x.as_ref();
|
||||
|
||||
// Evaluate in left-to-right order
|
||||
let lhs_val = match x.0 {
|
||||
let lhs_val = match lhs {
|
||||
Expr::Property(_) => Default::default(), // Store a placeholder in case of a property
|
||||
_ => self.eval_expr(scope, state, lib, &x.0, level)?,
|
||||
_ => self.eval_expr(scope, state, lib, lhs, level)?,
|
||||
};
|
||||
|
||||
// Push in reverse order
|
||||
self.eval_indexed_chain(scope, state, lib, &x.1, idx_values, size, level)?;
|
||||
self.eval_indexed_chain(scope, state, lib, rhs, idx_values, size, level)?;
|
||||
|
||||
idx_values.push(lhs_val);
|
||||
}
|
||||
@@ -1347,6 +1339,7 @@ impl Engine {
|
||||
Dynamic(Union::Array(mut rhs_value)) => {
|
||||
let op = "==";
|
||||
let def_value = false.into();
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Call the `==` operator to compare each value
|
||||
for value in rhs_value.iter_mut() {
|
||||
@@ -1361,7 +1354,7 @@ impl Engine {
|
||||
);
|
||||
|
||||
let (r, _) = self.call_fn_raw(
|
||||
None, state, lib, op, hashes, args, false, def_value, pos, level,
|
||||
&mut scope, state, lib, op, hashes, args, false, def_value, pos, level,
|
||||
)?;
|
||||
if r.as_bool().unwrap_or(false) {
|
||||
return Ok(true.into());
|
||||
@@ -1399,6 +1392,8 @@ impl Engine {
|
||||
self.inc_operations(state, expr.position())?;
|
||||
|
||||
match expr {
|
||||
Expr::Expr(x) => self.eval_expr(scope, state, lib, x.as_ref(), level),
|
||||
|
||||
Expr::IntegerConstant(x) => Ok(x.0.into()),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||
@@ -1550,14 +1545,8 @@ impl Engine {
|
||||
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
|
||||
.iter()
|
||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
||||
.collect::<Result<StaticVec<_>, _>>()?;
|
||||
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
|
||||
if name == KEYWORD_EVAL && args.len() == 1 && args.get(0).is::<ImmutableString>() {
|
||||
// Handle eval
|
||||
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||
let hash_fn =
|
||||
calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
||||
|
||||
@@ -1567,7 +1556,8 @@ impl Engine {
|
||||
let pos = args_expr.get(0).position();
|
||||
|
||||
// Evaluate the text string as a script
|
||||
let result = self.eval_script_expr(scope, state, lib, args.pop(), pos);
|
||||
let script = self.eval_expr(scope, state, lib, args_expr.get(0), level)?;
|
||||
let result = self.eval_script_expr(scope, state, lib, &script, pos);
|
||||
|
||||
if scope.len() != prev_len {
|
||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||
@@ -1580,9 +1570,52 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Normal function call - except for eval (handled above)
|
||||
let mut arg_values: StaticVec<Dynamic>;
|
||||
let mut args: StaticVec<_>;
|
||||
let mut is_ref = false;
|
||||
|
||||
if args_expr.is_empty() {
|
||||
// No arguments
|
||||
args = Default::default();
|
||||
} else {
|
||||
// See if the first argument is a variable, if so, convert to method-call style
|
||||
// in order to leverage potential &mut first argument and avoid cloning the value
|
||||
match args_expr.get(0) {
|
||||
// func(x, ...) -> x.func(...)
|
||||
lhs @ Expr::Variable(_) => {
|
||||
arg_values = args_expr
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let (target, _, typ, pos) = search_scope(scope, state, lhs)?;
|
||||
self.inc_operations(state, pos)?;
|
||||
|
||||
match typ {
|
||||
ScopeEntryType::Module => unreachable!(),
|
||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||
}
|
||||
|
||||
args = once(target).chain(arg_values.iter_mut()).collect();
|
||||
|
||||
is_ref = true;
|
||||
}
|
||||
// func(..., ...)
|
||||
_ => {
|
||||
arg_values = args_expr
|
||||
.iter()
|
||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
args = arg_values.iter_mut().collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let args = args.as_mut();
|
||||
self.exec_fn_call(
|
||||
state, lib, name, *native, *hash, args, false, def_val, *pos, level,
|
||||
state, lib, name, *native, *hash, args, is_ref, def_val, *pos, level,
|
||||
)
|
||||
.map(|(v, _)| v)
|
||||
}
|
||||
@@ -1639,9 +1672,8 @@ impl Engine {
|
||||
Ok(x) if x.is_script() => {
|
||||
let args = args.as_mut();
|
||||
let fn_def = x.get_fn_def();
|
||||
let result =
|
||||
self.call_script_fn(None, state, lib, name, fn_def, args, *pos, level)?;
|
||||
Ok(result)
|
||||
let mut scope = Scope::new();
|
||||
self.call_script_fn(&mut scope, state, lib, name, fn_def, args, *pos, level)
|
||||
}
|
||||
Ok(x) => x.get_native_fn()(args.as_mut()).map_err(|err| err.new_position(*pos)),
|
||||
Err(err)
|
||||
@@ -1701,9 +1733,9 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Evaluate a statement
|
||||
pub(crate) fn eval_stmt<'s>(
|
||||
pub(crate) fn eval_stmt(
|
||||
&self,
|
||||
scope: &mut Scope<'s>,
|
||||
scope: &mut Scope,
|
||||
state: &mut State,
|
||||
lib: &FunctionsLib,
|
||||
stmt: &Stmt,
|
||||
@@ -2012,8 +2044,8 @@ fn run_builtin_binary_op(
|
||||
}
|
||||
|
||||
if args_type == TypeId::of::<INT>() {
|
||||
let x = x.downcast_ref::<INT>().unwrap().clone();
|
||||
let y = y.downcast_ref::<INT>().unwrap().clone();
|
||||
let x = *x.downcast_ref::<INT>().unwrap();
|
||||
let y = *y.downcast_ref::<INT>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
@@ -2054,8 +2086,8 @@ fn run_builtin_binary_op(
|
||||
_ => (),
|
||||
}
|
||||
} else if args_type == TypeId::of::<bool>() {
|
||||
let x = x.downcast_ref::<bool>().unwrap().clone();
|
||||
let y = y.downcast_ref::<bool>().unwrap().clone();
|
||||
let x = *x.downcast_ref::<bool>().unwrap();
|
||||
let y = *y.downcast_ref::<bool>().unwrap();
|
||||
|
||||
match op {
|
||||
"&" => return Ok(Some((x && y).into())),
|
||||
@@ -2079,8 +2111,8 @@ fn run_builtin_binary_op(
|
||||
_ => (),
|
||||
}
|
||||
} else if args_type == TypeId::of::<char>() {
|
||||
let x = x.downcast_ref::<char>().unwrap().clone();
|
||||
let y = y.downcast_ref::<char>().unwrap().clone();
|
||||
let x = *x.downcast_ref::<char>().unwrap();
|
||||
let y = *y.downcast_ref::<char>().unwrap();
|
||||
|
||||
match op {
|
||||
"==" => return Ok(Some((x == y).into())),
|
||||
@@ -2102,8 +2134,8 @@ fn run_builtin_binary_op(
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
if args_type == TypeId::of::<FLOAT>() {
|
||||
let x = x.downcast_ref::<FLOAT>().unwrap().clone();
|
||||
let y = y.downcast_ref::<FLOAT>().unwrap().clone();
|
||||
let x = *x.downcast_ref::<FLOAT>().unwrap();
|
||||
let y = *y.downcast_ref::<FLOAT>().unwrap();
|
||||
|
||||
match op {
|
||||
"+" => return Ok(Some((x + y).into())),
|
||||
@@ -2142,7 +2174,7 @@ fn run_builtin_op_assignment(
|
||||
|
||||
if args_type == TypeId::of::<INT>() {
|
||||
let x = x.downcast_mut::<INT>().unwrap();
|
||||
let y = y.downcast_ref::<INT>().unwrap().clone();
|
||||
let y = *y.downcast_ref::<INT>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
@@ -2178,7 +2210,7 @@ fn run_builtin_op_assignment(
|
||||
}
|
||||
} else if args_type == TypeId::of::<bool>() {
|
||||
let x = x.downcast_mut::<bool>().unwrap();
|
||||
let y = y.downcast_ref::<bool>().unwrap().clone();
|
||||
let y = *y.downcast_ref::<bool>().unwrap();
|
||||
|
||||
match op {
|
||||
"&=" => return Ok(Some(*x = *x && y)),
|
||||
@@ -2199,7 +2231,7 @@ fn run_builtin_op_assignment(
|
||||
{
|
||||
if args_type == TypeId::of::<FLOAT>() {
|
||||
let x = x.downcast_mut::<FLOAT>().unwrap();
|
||||
let y = y.downcast_ref::<FLOAT>().unwrap().clone();
|
||||
let y = *y.downcast_ref::<FLOAT>().unwrap();
|
||||
|
||||
match op {
|
||||
"+=" => return Ok(Some(*x += y)),
|
||||
|
111
src/module.rs
111
src/module.rs
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::any::{Dynamic, Variant};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{Engine, FunctionsLib};
|
||||
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib, FUNC_INDEXER};
|
||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn};
|
||||
use crate::parser::{
|
||||
FnAccess,
|
||||
@@ -378,9 +378,9 @@ impl Module {
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||
/// Set a Rust getter function taking one mutable parameter, returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
/// If there is a similar existing Rust getter function, it is replaced.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -388,7 +388,30 @@ impl Module {
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
|
||||
/// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn set_getter_fn<A: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
self.set_fn_1_mut(make_getter(&name.into()), func)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_2("calc", |x: i64, y: ImmutableString| {
|
||||
/// Ok(x + y.len() as i64)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
@@ -417,13 +440,15 @@ impl Module {
|
||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
|
||||
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: ImmutableString| {
|
||||
/// *x += y.len() as i64; Ok(*x)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
@@ -449,6 +474,59 @@ impl Module {
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust setter function taking two parameters (the first one mutable) into the module,
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing setter Rust function, it is replaced.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_setter_fn("value", |x: &mut i64, y: ImmutableString| {
|
||||
/// *x = y.len() as i64;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn set_setter_fn<A: Variant + Clone, B: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<()> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<()> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
self.set_fn_2_mut(make_setter(&name.into()), func)
|
||||
}
|
||||
|
||||
/// Set a Rust indexer function taking two parameters (the first one mutable) into the module,
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing setter Rust function, it is replaced.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_indexer_fn(|x: &mut i64, y: ImmutableString| {
|
||||
/// Ok(*x + y.len() as i64)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn set_indexer_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||
) -> u64 {
|
||||
self.set_fn_2_mut(FUNC_INDEXER, func)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
@@ -456,10 +534,10 @@ impl Module {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
|
||||
/// let hash = module.set_fn_3("calc", |x: i64, y: ImmutableString, z: i64| {
|
||||
/// Ok(x + y.len() as i64 + z)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
@@ -499,10 +577,10 @@ impl Module {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
|
||||
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: ImmutableString, z: i64| {
|
||||
/// *x += y.len() as i64 + z; Ok(*x)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
@@ -541,10 +619,10 @@ impl Module {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64, _w: ()| {
|
||||
/// let hash = module.set_fn_4("calc", |x: i64, y: ImmutableString, z: i64, _w: ()| {
|
||||
/// Ok(x + y.len() as i64 + z)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
@@ -583,7 +661,7 @@ impl Module {
|
||||
)
|
||||
}
|
||||
|
||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||
/// Set a Rust function taking four parameters (the first one mutable) into the module,
|
||||
/// returning a hash key.
|
||||
///
|
||||
/// If there is a similar existing Rust function, it is replaced.
|
||||
@@ -591,10 +669,10 @@ impl Module {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
/// use rhai::{Module, ImmutableString};
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64, _w: ()| {
|
||||
/// let hash = module.set_fn_4_mut("calc", |x: &mut i64, y: ImmutableString, z: i64, _w: ()| {
|
||||
/// *x += y.len() as i64 + z; Ok(*x)
|
||||
/// });
|
||||
/// assert!(module.get_fn(hash).is_some());
|
||||
@@ -1044,6 +1122,9 @@ mod stat {
|
||||
|
||||
/// Module resolution service that serves modules added into it.
|
||||
///
|
||||
/// `StaticModuleResolver` is a smart pointer to a `HashMap<String, Module>`.
|
||||
/// It can simply be treated as `&HashMap<String, Module>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
@@ -122,7 +122,7 @@ fn call_fn_with_constant_arguments(
|
||||
state
|
||||
.engine
|
||||
.call_fn_raw(
|
||||
None,
|
||||
&mut Scope::new(),
|
||||
&mut Default::default(),
|
||||
state.lib,
|
||||
fn_name,
|
||||
@@ -138,7 +138,7 @@ fn call_fn_with_constant_arguments(
|
||||
}
|
||||
|
||||
/// Optimize a statement.
|
||||
fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt {
|
||||
fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt {
|
||||
match stmt {
|
||||
// if expr { Noop }
|
||||
Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) => {
|
||||
@@ -359,11 +359,13 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
||||
}
|
||||
|
||||
/// Optimize an expression.
|
||||
fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
||||
fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
||||
// These keywords are handled specially
|
||||
const DONT_EVAL_KEYWORDS: [&str; 3] = [KEYWORD_PRINT, KEYWORD_DEBUG, KEYWORD_EVAL];
|
||||
|
||||
match expr {
|
||||
// expr - do not promote because there is a reason it is wrapped in an `Expr::Expr`
|
||||
Expr::Expr(x) => Expr::Expr(Box::new(optimize_expr(*x, state))),
|
||||
// ( stmt )
|
||||
Expr::Stmt(x) => match optimize_stmt(x.0, state, true) {
|
||||
// ( Noop ) -> ()
|
||||
|
@@ -105,6 +105,10 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
||||
},
|
||||
);
|
||||
lib.set_fn_1_mut("len", |list: &mut Array| Ok(list.len() as INT));
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
lib.set_getter_fn("len", |list: &mut Array| Ok(list.len() as INT));
|
||||
|
||||
lib.set_fn_1_mut("clear", |list: &mut Array| {
|
||||
list.clear();
|
||||
Ok(())
|
||||
|
@@ -43,6 +43,18 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
||||
lib.set_fn_1("is_finite", |x: FLOAT| Ok(x.is_finite()));
|
||||
lib.set_fn_1("is_infinite", |x: FLOAT| Ok(x.is_infinite()));
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
lib.set_getter_fn("floor", |x: &mut FLOAT| Ok(x.floor()));
|
||||
lib.set_getter_fn("ceiling", |x: &mut FLOAT| Ok(x.ceil()));
|
||||
lib.set_getter_fn("round", |x: &mut FLOAT| Ok(x.ceil()));
|
||||
lib.set_getter_fn("int", |x: &mut FLOAT| Ok(x.trunc()));
|
||||
lib.set_getter_fn("fraction", |x: &mut FLOAT| Ok(x.fract()));
|
||||
lib.set_getter_fn("is_nan", |x: &mut FLOAT| Ok(x.is_nan()));
|
||||
lib.set_getter_fn("is_finite", |x: &mut FLOAT| Ok(x.is_finite()));
|
||||
lib.set_getter_fn("is_infinite", |x: &mut FLOAT| Ok(x.is_infinite()));
|
||||
}
|
||||
|
||||
// Register conversion functions
|
||||
lib.set_fn_1("to_float", |x: INT| Ok(x as FLOAT));
|
||||
lib.set_fn_1("to_float", |x: f32| Ok(x as FLOAT));
|
||||
|
@@ -101,22 +101,26 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
{
|
||||
lib.set_fn_2("+", |x: ImmutableString, y: Array| Ok(format!("{}{:?}", x, y)));
|
||||
lib.set_fn_2_mut("+", |x: &mut ImmutableString, y: Array| Ok(format!("{}{:?}", x, y)));
|
||||
lib.set_fn_2_mut("+", |x: &mut Array, y: ImmutableString| Ok(format!("{:?}{}", x, y)));
|
||||
}
|
||||
|
||||
lib.set_fn_1("len", |s: ImmutableString| Ok(s.chars().count() as INT));
|
||||
lib.set_fn_2(
|
||||
lib.set_fn_1_mut("len", |s: &mut ImmutableString| Ok(s.chars().count() as INT));
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
lib.set_getter_fn("len", |s: &mut ImmutableString| Ok(s.chars().count() as INT));
|
||||
|
||||
lib.set_fn_2_mut(
|
||||
"contains",
|
||||
|s: ImmutableString, ch: char| Ok(s.contains(ch)),
|
||||
|s: &mut ImmutableString, ch: char| Ok(s.contains(ch)),
|
||||
);
|
||||
lib.set_fn_2(
|
||||
lib.set_fn_2_mut(
|
||||
"contains",
|
||||
|s: ImmutableString, find: ImmutableString| Ok(s.contains(find.as_str())),
|
||||
|s: &mut ImmutableString, find: ImmutableString| Ok(s.contains(find.as_str())),
|
||||
);
|
||||
lib.set_fn_3(
|
||||
lib.set_fn_3_mut(
|
||||
"index_of",
|
||||
|s: ImmutableString, ch: char, start: INT| {
|
||||
|s: &mut ImmutableString, ch: char, start: INT| {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
@@ -131,17 +135,17 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
.unwrap_or(-1 as INT))
|
||||
},
|
||||
);
|
||||
lib.set_fn_2(
|
||||
lib.set_fn_2_mut(
|
||||
"index_of",
|
||||
|s: ImmutableString, ch: char| {
|
||||
|s: &mut ImmutableString, ch: char| {
|
||||
Ok(s.find(ch)
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT))
|
||||
},
|
||||
);
|
||||
lib.set_fn_3(
|
||||
lib.set_fn_3_mut(
|
||||
"index_of",
|
||||
|s: ImmutableString, find: ImmutableString, start: INT| {
|
||||
|s: &mut ImmutableString, find: ImmutableString, start: INT| {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
@@ -156,9 +160,9 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
.unwrap_or(-1 as INT))
|
||||
},
|
||||
);
|
||||
lib.set_fn_2(
|
||||
lib.set_fn_2_mut(
|
||||
"index_of",
|
||||
|s: ImmutableString, find: ImmutableString| {
|
||||
|s: &mut ImmutableString, find: ImmutableString| {
|
||||
Ok(s.find(find.as_str())
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT))
|
||||
|
@@ -10,6 +10,9 @@ use crate::token::Position;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use crate::stdlib::time::Instant;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
|
||||
// Register date/time functions
|
||||
@@ -70,27 +73,29 @@ def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, {
|
||||
lib.set_fn_2("==", eq::<Instant>);
|
||||
lib.set_fn_2("!=", ne::<Instant>);
|
||||
|
||||
lib.set_fn_1(
|
||||
"elapsed",
|
||||
|timestamp: Instant| {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(timestamp.elapsed().as_secs_f64());
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn elapsed (timestamp: &mut Instant) -> Result<FLOAT, Box<EvalAltResult>> {
|
||||
Ok(timestamp.elapsed().as_secs_f64())
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
{
|
||||
let seconds = timestamp.elapsed().as_secs();
|
||||
#[cfg(feature = "no_float")]
|
||||
fn elapsed (timestamp: &mut Instant) -> Result<INT, Box<EvalAltResult>> {
|
||||
let seconds = timestamp.elapsed().as_secs();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
if seconds > (MAX_INT as u64) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer overflow for timestamp.elapsed(): {}", seconds),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
return Ok(seconds as INT);
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
if seconds > (MAX_INT as u64) {
|
||||
return Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
format!("Integer overflow for timestamp.elapsed: {}", seconds),
|
||||
Position::none(),
|
||||
)));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(seconds as INT)
|
||||
}
|
||||
|
||||
lib.set_fn_1_mut("elapsed", elapsed);
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
lib.set_getter_fn("elapsed", elapsed);
|
||||
});
|
||||
|
@@ -23,6 +23,7 @@ use crate::stdlib::{
|
||||
collections::HashMap,
|
||||
format,
|
||||
iter::{empty, Peekable},
|
||||
mem,
|
||||
num::NonZeroUsize,
|
||||
ops::{Add, Deref, DerefMut},
|
||||
string::{String, ToString},
|
||||
@@ -391,6 +392,8 @@ pub enum Expr {
|
||||
Property(Box<((String, String, String), Position)>),
|
||||
/// { stmt }
|
||||
Stmt(Box<(Stmt, Position)>),
|
||||
/// Wrapped expression - should not be optimized away.
|
||||
Expr(Box<Expr>),
|
||||
/// 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`.
|
||||
@@ -441,6 +444,8 @@ impl Expr {
|
||||
/// Panics when the expression is not constant.
|
||||
pub fn get_constant_value(&self) -> Dynamic {
|
||||
match self {
|
||||
Self::Expr(x) => x.get_constant_value(),
|
||||
|
||||
Self::IntegerConstant(x) => x.0.into(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.0.into(),
|
||||
@@ -475,6 +480,8 @@ impl Expr {
|
||||
/// Panics when the expression is not constant.
|
||||
pub fn get_constant_str(&self) -> String {
|
||||
match self {
|
||||
Self::Expr(x) => x.get_constant_str(),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.0.to_string(),
|
||||
|
||||
@@ -494,6 +501,8 @@ impl Expr {
|
||||
/// Get the `Position` of the expression.
|
||||
pub fn position(&self) -> Position {
|
||||
match self {
|
||||
Self::Expr(x) => x.position(),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.1,
|
||||
|
||||
@@ -519,6 +528,11 @@ impl Expr {
|
||||
/// Override the `Position` of the expression.
|
||||
pub(crate) fn set_position(mut self, new_pos: Position) -> Self {
|
||||
match &mut self {
|
||||
Self::Expr(ref mut x) => {
|
||||
let expr = mem::take(x);
|
||||
*x = Box::new(expr.set_position(new_pos));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(x) => x.1 = new_pos,
|
||||
|
||||
@@ -550,6 +564,8 @@ impl Expr {
|
||||
/// A pure expression has no side effects.
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_pure(),
|
||||
|
||||
Self::Array(x) => x.0.iter().all(Self::is_pure),
|
||||
|
||||
Self::Index(x) | Self::And(x) | Self::Or(x) | Self::In(x) => {
|
||||
@@ -568,6 +584,8 @@ impl Expr {
|
||||
/// Is the expression a constant?
|
||||
pub fn is_constant(&self) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_constant(),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_) => true,
|
||||
|
||||
@@ -598,6 +616,8 @@ impl Expr {
|
||||
/// Is a particular token allowed as a postfix operator to this expression?
|
||||
pub fn is_valid_postfix(&self, token: &Token) -> bool {
|
||||
match self {
|
||||
Self::Expr(x) => x.is_valid_postfix(token),
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_) => false,
|
||||
|
||||
@@ -996,7 +1016,7 @@ fn parse_index_chain<'a>(
|
||||
(Token::LeftBracket, _) => {
|
||||
let idx_pos = eat_token(input, Token::LeftBracket);
|
||||
// Recursively parse the indexing chain, right-binding each
|
||||
let idx = parse_index_chain(
|
||||
let idx_expr = parse_index_chain(
|
||||
input,
|
||||
state,
|
||||
idx_expr,
|
||||
@@ -1005,10 +1025,20 @@ fn parse_index_chain<'a>(
|
||||
allow_stmt_expr,
|
||||
)?;
|
||||
// Indexing binds to right
|
||||
Ok(Expr::Index(Box::new((lhs, idx, pos))))
|
||||
Ok(Expr::Index(Box::new((lhs, idx_expr, pos))))
|
||||
}
|
||||
// Otherwise terminate the indexing chain
|
||||
_ => Ok(Expr::Index(Box::new((lhs, idx_expr, pos)))),
|
||||
_ => {
|
||||
match idx_expr {
|
||||
// Terminate with an `Expr::Expr` wrapper to prevent the last index expression
|
||||
// inside brackets to be mis-parsed as another level of indexing, or a
|
||||
// dot expression/function call to be mis-parsed as following the indexing chain.
|
||||
Expr::Index(_) | Expr::Dot(_) | Expr::FnCall(_) => Ok(Expr::Index(
|
||||
Box::new((lhs, Expr::Expr(Box::new(idx_expr)), pos)),
|
||||
)),
|
||||
_ => Ok(Expr::Index(Box::new((lhs, idx_expr, pos)))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
|
||||
@@ -1732,37 +1762,34 @@ fn parse_binary_op<'a>(
|
||||
let cmp_def = Some(false.into());
|
||||
let op = op_token.syntax();
|
||||
let hash = calc_fn_hash(empty(), &op, 2, empty());
|
||||
let op = (op, true, pos);
|
||||
|
||||
let mut args = StaticVec::new();
|
||||
args.push(root);
|
||||
args.push(rhs);
|
||||
|
||||
root = match op_token {
|
||||
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::Plus
|
||||
| Token::Minus
|
||||
| Token::Multiply
|
||||
| Token::Divide
|
||||
| Token::LeftShift
|
||||
| Token::RightShift
|
||||
| Token::Modulo
|
||||
| Token::PowerOf
|
||||
| Token::Ampersand
|
||||
| Token::Pipe
|
||||
| Token::XOr => Expr::FnCall(Box::new((op, 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))),
|
||||
// '!=' defaults to true when passed invalid operands
|
||||
Token::NotEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, Some(true.into())))),
|
||||
|
||||
// Comparison operators default to false when passed invalid operands
|
||||
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::GreaterThanEqualsTo => {
|
||||
Expr::FnCall(Box::new(((op, true, pos), None, hash, args, cmp_def)))
|
||||
}
|
||||
Token::EqualsTo
|
||||
| Token::LessThan
|
||||
| Token::LessThanEqualsTo
|
||||
| Token::GreaterThan
|
||||
| Token::GreaterThanEqualsTo => Expr::FnCall(Box::new((op, None, hash, args, cmp_def))),
|
||||
|
||||
Token::Or => {
|
||||
let rhs = args.pop();
|
||||
@@ -1774,10 +1801,6 @@ 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, 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();
|
||||
let current_lhs = args.pop();
|
||||
|
Reference in New Issue
Block a user