2022-01-07 04:43:47 +01:00
|
|
|
//! Module defining functions for evaluating an expression.
|
|
|
|
|
2022-01-07 05:19:01 +01:00
|
|
|
use super::{EvalContext, EvalState, GlobalRuntimeState, Target};
|
|
|
|
use crate::ast::{Expr, FnCallExpr, OpAssignment};
|
|
|
|
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
|
|
|
use crate::types::dynamic::AccessMode;
|
2022-01-29 04:09:43 +01:00
|
|
|
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR};
|
2022-01-07 04:43:47 +01:00
|
|
|
use std::num::NonZeroUsize;
|
|
|
|
#[cfg(feature = "no_std")]
|
|
|
|
use std::prelude::v1::*;
|
|
|
|
|
|
|
|
impl Engine {
|
|
|
|
/// Search for a module within an imports stack.
|
2022-01-29 04:09:43 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-01-07 04:43:47 +01:00
|
|
|
#[inline]
|
|
|
|
#[must_use]
|
|
|
|
pub(crate) fn search_imports(
|
|
|
|
&self,
|
|
|
|
global: &GlobalRuntimeState,
|
|
|
|
state: &mut EvalState,
|
2022-01-29 04:09:43 +01:00
|
|
|
namespace: &crate::module::Namespace,
|
|
|
|
) -> Option<crate::Shared<Module>> {
|
2022-01-07 04:43:47 +01:00
|
|
|
let root = &namespace[0].name;
|
|
|
|
|
|
|
|
// Qualified - check if the root module is directly indexed
|
|
|
|
let index = if state.always_search_scope {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
namespace.index()
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(index) = index {
|
2022-01-20 01:17:34 +01:00
|
|
|
let offset = global.num_imports() - index.get();
|
|
|
|
Some(global.get_shared_import(offset).unwrap())
|
2022-01-07 04:43:47 +01:00
|
|
|
} else {
|
|
|
|
global
|
2022-01-20 01:17:34 +01:00
|
|
|
.find_import(root)
|
|
|
|
.map(|n| global.get_shared_import(n).unwrap())
|
2022-01-07 04:43:47 +01:00
|
|
|
.or_else(|| self.global_sub_modules.get(root).cloned())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for a variable within the scope or within imports,
|
|
|
|
/// depending on whether the variable name is namespace-qualified.
|
|
|
|
pub(crate) fn search_namespace<'s>(
|
|
|
|
&self,
|
|
|
|
scope: &'s mut Scope,
|
|
|
|
global: &mut GlobalRuntimeState,
|
|
|
|
state: &mut EvalState,
|
|
|
|
lib: &[&Module],
|
|
|
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
|
|
|
expr: &Expr,
|
|
|
|
) -> RhaiResultOf<(Target<'s>, Position)> {
|
|
|
|
match expr {
|
|
|
|
Expr::Variable(Some(_), _, _) => {
|
|
|
|
self.search_scope_only(scope, global, state, lib, this_ptr, expr)
|
|
|
|
}
|
|
|
|
Expr::Variable(None, _var_pos, v) => match v.as_ref() {
|
|
|
|
// Normal variable access
|
2022-01-29 04:09:43 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-01-07 04:43:47 +01:00
|
|
|
(_, None, _) => self.search_scope_only(scope, global, state, lib, this_ptr, expr),
|
2022-01-29 04:09:43 +01:00
|
|
|
#[cfg(feature = "no_module")]
|
|
|
|
(_, (), _) => self.search_scope_only(scope, global, state, lib, this_ptr, expr),
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
// Qualified variable access
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
(_, Some((namespace, hash_var)), var_name) => {
|
2022-01-28 11:59:18 +01:00
|
|
|
// foo:bar::baz::VARIABLE
|
2022-01-07 04:43:47 +01:00
|
|
|
if let Some(module) = self.search_imports(global, state, namespace) {
|
|
|
|
return match module.get_qualified_var(*hash_var) {
|
|
|
|
Ok(target) => {
|
|
|
|
let mut target = target.clone();
|
|
|
|
// Module variables are constant
|
|
|
|
target.set_access_mode(AccessMode::ReadOnly);
|
|
|
|
Ok((target.into(), *_var_pos))
|
|
|
|
}
|
|
|
|
Err(err) => Err(match *err {
|
|
|
|
ERR::ErrorVariableNotFound(_, _) => ERR::ErrorVariableNotFound(
|
|
|
|
format!(
|
|
|
|
"{}{}{}",
|
|
|
|
namespace,
|
2022-01-07 05:19:01 +01:00
|
|
|
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
2022-01-07 04:43:47 +01:00
|
|
|
var_name
|
|
|
|
),
|
|
|
|
namespace[0].pos,
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
_ => err.fill_position(*_var_pos),
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-01-28 11:59:18 +01:00
|
|
|
// global::VARIABLE
|
2022-01-07 04:43:47 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2022-01-07 05:19:01 +01:00
|
|
|
if namespace.len() == 1 && namespace[0].name == crate::engine::KEYWORD_GLOBAL {
|
2022-01-28 15:07:49 +01:00
|
|
|
if let Some(ref constants) = global.constants {
|
2022-01-28 15:37:59 +01:00
|
|
|
if let Some(value) =
|
|
|
|
crate::func::locked_write(constants).get_mut(var_name)
|
|
|
|
{
|
2022-01-28 15:07:49 +01:00
|
|
|
let mut target: Target = value.clone().into();
|
|
|
|
// Module variables are constant
|
|
|
|
target.set_access_mode(AccessMode::ReadOnly);
|
|
|
|
return Ok((target.into(), *_var_pos));
|
|
|
|
}
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return Err(ERR::ErrorVariableNotFound(
|
|
|
|
format!(
|
|
|
|
"{}{}{}",
|
|
|
|
namespace,
|
2022-01-07 05:19:01 +01:00
|
|
|
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
2022-01-07 04:43:47 +01:00
|
|
|
var_name
|
|
|
|
),
|
|
|
|
namespace[0].pos,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(ERR::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos).into())
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for a variable within the scope
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if `expr` is not [`Expr::Variable`].
|
|
|
|
pub(crate) fn search_scope_only<'s>(
|
|
|
|
&self,
|
|
|
|
scope: &'s mut Scope,
|
|
|
|
global: &mut GlobalRuntimeState,
|
|
|
|
state: &mut EvalState,
|
|
|
|
lib: &[&Module],
|
|
|
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
|
|
|
expr: &Expr,
|
|
|
|
) -> RhaiResultOf<(Target<'s>, Position)> {
|
|
|
|
// Make sure that the pointer indirection is taken only when absolutely necessary.
|
|
|
|
|
|
|
|
let (index, var_pos) = match expr {
|
|
|
|
// Check if the variable is `this`
|
|
|
|
Expr::Variable(None, pos, v) if v.0.is_none() && v.2 == KEYWORD_THIS => {
|
|
|
|
return if let Some(val) = this_ptr {
|
|
|
|
Ok(((*val).into(), *pos))
|
|
|
|
} else {
|
|
|
|
Err(ERR::ErrorUnboundThis(*pos).into())
|
|
|
|
}
|
|
|
|
}
|
2022-02-04 05:04:33 +01:00
|
|
|
_ if state.always_search_scope => (0, expr.start_position()),
|
2022-01-07 04:43:47 +01:00
|
|
|
Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos),
|
|
|
|
Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
|
|
|
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check the variable resolver, if any
|
|
|
|
if let Some(ref resolve_var) = self.resolve_var {
|
|
|
|
let context = EvalContext {
|
|
|
|
engine: self,
|
|
|
|
scope,
|
|
|
|
global,
|
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
this_ptr,
|
|
|
|
level: 0,
|
|
|
|
};
|
|
|
|
match resolve_var(
|
|
|
|
expr.get_variable_name(true).expect("`Expr::Variable`"),
|
|
|
|
index,
|
|
|
|
&context,
|
|
|
|
) {
|
|
|
|
Ok(Some(mut result)) => {
|
|
|
|
result.set_access_mode(AccessMode::ReadOnly);
|
|
|
|
return Ok((result.into(), var_pos));
|
|
|
|
}
|
|
|
|
Ok(None) => (),
|
|
|
|
Err(err) => return Err(err.fill_position(var_pos)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let index = if index > 0 {
|
|
|
|
scope.len() - index
|
|
|
|
} else {
|
|
|
|
// Find the variable in the scope
|
|
|
|
let var_name = expr.get_variable_name(true).expect("`Expr::Variable`");
|
|
|
|
scope
|
|
|
|
.get_index(var_name)
|
|
|
|
.ok_or_else(|| ERR::ErrorVariableNotFound(var_name.to_string(), var_pos))?
|
|
|
|
.0
|
|
|
|
};
|
|
|
|
|
|
|
|
let val = scope.get_mut_by_index(index);
|
|
|
|
|
|
|
|
Ok((val.into(), var_pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Evaluate a function call expression.
|
|
|
|
pub(crate) fn eval_fn_call_expr(
|
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
global: &mut GlobalRuntimeState,
|
|
|
|
state: &mut EvalState,
|
|
|
|
lib: &[&Module],
|
|
|
|
this_ptr: &mut Option<&mut Dynamic>,
|
|
|
|
expr: &FnCallExpr,
|
|
|
|
pos: Position,
|
|
|
|
level: usize,
|
|
|
|
) -> RhaiResult {
|
|
|
|
let FnCallExpr {
|
|
|
|
name,
|
2022-01-29 04:09:43 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-01-07 04:43:47 +01:00
|
|
|
namespace,
|
|
|
|
capture_parent_scope: capture,
|
|
|
|
hashes,
|
|
|
|
args,
|
|
|
|
constants,
|
|
|
|
..
|
|
|
|
} = expr;
|
|
|
|
|
2022-01-29 04:09:43 +01:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-01-07 04:43:47 +01:00
|
|
|
if let Some(namespace) = namespace.as_ref() {
|
|
|
|
// Qualified function call
|
|
|
|
let hash = hashes.native;
|
|
|
|
|
2022-01-29 04:09:43 +01:00
|
|
|
return self.make_qualified_function_call(
|
2022-01-07 04:43:47 +01:00
|
|
|
scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos,
|
|
|
|
level,
|
2022-01-08 11:40:19 +01:00
|
|
|
);
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
2022-01-29 04:09:43 +01:00
|
|
|
|
|
|
|
// Normal function call
|
|
|
|
let (first_arg, args) = args.split_first().map_or_else(
|
|
|
|
|| (None, args.as_ref()),
|
|
|
|
|(first, rest)| (Some(first), rest),
|
|
|
|
);
|
|
|
|
|
|
|
|
self.make_function_call(
|
2022-02-02 15:42:33 +01:00
|
|
|
scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes,
|
|
|
|
*capture, pos, level,
|
2022-01-29 04:09:43 +01:00
|
|
|
)
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Evaluate an expression.
|
2022-01-24 10:04:40 +01:00
|
|
|
//
|
|
|
|
// # Implementation Notes
|
|
|
|
//
|
|
|
|
// Do not use the `?` operator within the main body as it makes this function return early,
|
|
|
|
// possibly by-passing important cleanup tasks at the end.
|
|
|
|
//
|
|
|
|
// Errors that are not recoverable, such as system errors or safety errors, can use `?`.
|
2022-01-07 04:43:47 +01:00
|
|
|
pub(crate) fn eval_expr(
|
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
global: &mut GlobalRuntimeState,
|
|
|
|
state: &mut EvalState,
|
|
|
|
lib: &[&Module],
|
|
|
|
this_ptr: &mut Option<&mut Dynamic>,
|
|
|
|
expr: &Expr,
|
|
|
|
level: usize,
|
|
|
|
) -> RhaiResult {
|
|
|
|
// Coded this way for better branch prediction.
|
|
|
|
// Popular branches are lifted out of the `match` statement into their own branches.
|
|
|
|
|
|
|
|
// Function calls should account for a relatively larger portion of expressions because
|
|
|
|
// binary operators are also function calls.
|
|
|
|
if let Expr::FnCall(x, pos) = expr {
|
2022-01-25 05:24:30 +01:00
|
|
|
#[cfg(feature = "debugging")]
|
2022-02-03 04:56:08 +01:00
|
|
|
let reset_debugger =
|
|
|
|
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?;
|
2022-01-25 05:24:30 +01:00
|
|
|
|
2022-01-07 04:43:47 +01:00
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
let result =
|
|
|
|
self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level);
|
|
|
|
|
|
|
|
#[cfg(feature = "debugging")]
|
2022-01-25 05:24:30 +01:00
|
|
|
global.debugger.reset_status(reset_debugger);
|
2022-01-24 10:04:40 +01:00
|
|
|
|
|
|
|
return result;
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Then variable access.
|
|
|
|
// We shouldn't do this for too many variants because, soon or later, the added comparisons
|
|
|
|
// will cost more than the mis-predicted `match` branch.
|
|
|
|
if let Expr::Variable(index, var_pos, x) = expr {
|
2022-01-25 05:24:30 +01:00
|
|
|
#[cfg(feature = "debugging")]
|
2022-02-03 04:56:08 +01:00
|
|
|
self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?;
|
2022-01-25 05:24:30 +01:00
|
|
|
|
2022-01-07 04:43:47 +01:00
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
|
|
|
2022-01-25 05:24:30 +01:00
|
|
|
return if index.is_none() && x.0.is_none() && x.2 == KEYWORD_THIS {
|
2022-01-07 04:43:47 +01:00
|
|
|
this_ptr
|
|
|
|
.as_deref()
|
|
|
|
.cloned()
|
|
|
|
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
|
|
|
|
} else {
|
|
|
|
self.search_namespace(scope, global, state, lib, this_ptr, expr)
|
|
|
|
.map(|(val, _)| val.take_or_clone())
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-01-25 05:24:30 +01:00
|
|
|
#[cfg(feature = "debugging")]
|
2022-02-03 04:56:08 +01:00
|
|
|
let reset_debugger =
|
|
|
|
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?;
|
2022-01-25 05:24:30 +01:00
|
|
|
|
2022-01-07 04:43:47 +01:00
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
let result = match expr {
|
2022-01-07 04:43:47 +01:00
|
|
|
// Constants
|
|
|
|
Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()),
|
|
|
|
Expr::IntegerConstant(x, _) => Ok((*x).into()),
|
|
|
|
#[cfg(not(feature = "no_float"))]
|
|
|
|
Expr::FloatConstant(x, _) => Ok((*x).into()),
|
|
|
|
Expr::StringConstant(x, _) => Ok(x.clone().into()),
|
|
|
|
Expr::CharConstant(x, _) => Ok((*x).into()),
|
|
|
|
Expr::BoolConstant(x, _) => Ok((*x).into()),
|
|
|
|
Expr::Unit(_) => Ok(Dynamic::UNIT),
|
|
|
|
|
|
|
|
// `... ${...} ...`
|
|
|
|
Expr::InterpolatedString(x, pos) => {
|
|
|
|
let mut pos = *pos;
|
2022-01-24 10:04:40 +01:00
|
|
|
let mut concat: Dynamic = self.const_empty_string().into();
|
|
|
|
let mut result = Ok(Dynamic::UNIT);
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
for expr in x.iter() {
|
2022-01-24 10:04:40 +01:00
|
|
|
let item =
|
|
|
|
match self.eval_expr(scope, global, state, lib, this_ptr, expr, level) {
|
|
|
|
Ok(r) => r,
|
|
|
|
err => {
|
|
|
|
result = err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
2022-01-07 04:43:47 +01:00
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
if let Err(err) = self.eval_op_assignment(
|
2022-01-07 04:43:47 +01:00
|
|
|
global,
|
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
Some(OpAssignment::new(OP_CONCAT)),
|
|
|
|
pos,
|
2022-01-24 10:04:40 +01:00
|
|
|
&mut (&mut concat).into(),
|
2022-01-07 04:43:47 +01:00
|
|
|
("", Position::NONE),
|
|
|
|
item,
|
2022-02-02 07:47:35 +01:00
|
|
|
level,
|
2022-01-24 10:04:40 +01:00
|
|
|
) {
|
2022-02-04 05:04:33 +01:00
|
|
|
result = Err(err.fill_position(expr.start_position()));
|
2022-01-24 10:04:40 +01:00
|
|
|
break;
|
|
|
|
}
|
2022-01-07 04:43:47 +01:00
|
|
|
|
2022-02-04 05:04:33 +01:00
|
|
|
pos = expr.start_position();
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
result.map(|_| concat)
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
Expr::Array(x, _) => {
|
2022-01-24 10:04:40 +01:00
|
|
|
let mut arr = crate::Array::with_capacity(x.len());
|
|
|
|
let mut result = Ok(Dynamic::UNIT);
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
let mut sizes = (0, 0, 0);
|
|
|
|
|
|
|
|
for item_expr in x.iter() {
|
2022-01-24 10:04:40 +01:00
|
|
|
let value = match self
|
|
|
|
.eval_expr(scope, global, state, lib, this_ptr, item_expr, level)
|
|
|
|
{
|
|
|
|
Ok(r) => r.flatten(),
|
|
|
|
err => {
|
|
|
|
result = err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
let val_sizes = Self::calc_data_sizes(&value, true);
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
arr.push(value);
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
if self.has_data_size_limit() {
|
|
|
|
sizes = (
|
|
|
|
sizes.0 + val_sizes.0,
|
|
|
|
sizes.1 + val_sizes.1,
|
|
|
|
sizes.2 + val_sizes.2,
|
|
|
|
);
|
|
|
|
self.raise_err_if_over_data_size_limit(sizes, item_expr.position())?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
result.map(|_| arr.into())
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
Expr::Map(x, _) => {
|
2022-01-24 10:04:40 +01:00
|
|
|
let mut map = x.1.clone();
|
|
|
|
let mut result = Ok(Dynamic::UNIT);
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
let mut sizes = (0, 0, 0);
|
|
|
|
|
2022-01-07 05:19:01 +01:00
|
|
|
for (crate::ast::Ident { name, .. }, value_expr) in x.0.iter() {
|
2022-01-07 04:43:47 +01:00
|
|
|
let key = name.as_str();
|
2022-01-24 10:04:40 +01:00
|
|
|
let value = match self
|
|
|
|
.eval_expr(scope, global, state, lib, this_ptr, value_expr, level)
|
|
|
|
{
|
|
|
|
Ok(r) => r.flatten(),
|
|
|
|
err => {
|
|
|
|
result = err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
let val_sizes = Self::calc_data_sizes(&value, true);
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
*map.get_mut(key).unwrap() = value;
|
2022-01-07 04:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
if self.has_data_size_limit() {
|
|
|
|
sizes = (
|
|
|
|
sizes.0 + val_sizes.0,
|
|
|
|
sizes.1 + val_sizes.1,
|
|
|
|
sizes.2 + val_sizes.2,
|
|
|
|
);
|
|
|
|
self.raise_err_if_over_data_size_limit(sizes, value_expr.position())?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-24 10:04:40 +01:00
|
|
|
result.map(|_| map.into())
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Expr::And(x, _) => {
|
2022-01-24 10:04:40 +01:00
|
|
|
let lhs = self
|
|
|
|
.eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)
|
|
|
|
.and_then(|v| {
|
|
|
|
v.as_bool().map_err(|typ| {
|
|
|
|
self.make_type_mismatch_err::<bool>(typ, x.lhs.position())
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Ok(true) = lhs {
|
|
|
|
self.eval_expr(scope, global, state, lib, this_ptr, &x.rhs, level)
|
|
|
|
.and_then(|v| {
|
|
|
|
v.as_bool()
|
|
|
|
.map_err(|typ| {
|
|
|
|
self.make_type_mismatch_err::<bool>(typ, x.rhs.position())
|
|
|
|
})
|
|
|
|
.map(Into::into)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
lhs.map(Into::into)
|
|
|
|
}
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Expr::Or(x, _) => {
|
2022-01-24 10:04:40 +01:00
|
|
|
let lhs = self
|
|
|
|
.eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)
|
|
|
|
.and_then(|v| {
|
|
|
|
v.as_bool().map_err(|typ| {
|
|
|
|
self.make_type_mismatch_err::<bool>(typ, x.lhs.position())
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Ok(false) = lhs {
|
|
|
|
self.eval_expr(scope, global, state, lib, this_ptr, &x.rhs, level)
|
|
|
|
.and_then(|v| {
|
|
|
|
v.as_bool()
|
|
|
|
.map_err(|typ| {
|
|
|
|
self.make_type_mismatch_err::<bool>(typ, x.rhs.position())
|
|
|
|
})
|
|
|
|
.map(Into::into)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
lhs.map(Into::into)
|
|
|
|
}
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
2022-01-11 04:33:54 +01:00
|
|
|
Expr::Custom(custom, pos) => {
|
2022-01-07 04:43:47 +01:00
|
|
|
let expressions: StaticVec<_> = custom.inputs.iter().map(Into::into).collect();
|
2022-01-11 04:33:54 +01:00
|
|
|
// The first token acts as the custom syntax's key
|
2022-01-07 04:43:47 +01:00
|
|
|
let key_token = custom.tokens.first().unwrap();
|
2022-01-11 04:33:54 +01:00
|
|
|
// The key should exist, unless the AST is compiled in a different Engine
|
2022-01-10 13:08:03 +01:00
|
|
|
let custom_def = self.custom_syntax.get(key_token).ok_or_else(|| {
|
2022-01-11 04:33:54 +01:00
|
|
|
Box::new(ERR::ErrorCustomSyntax(
|
2022-01-10 13:08:03 +01:00
|
|
|
format!("Invalid custom syntax prefix: {}", key_token),
|
2022-01-11 04:33:54 +01:00
|
|
|
custom.tokens.iter().map(|s| s.to_string()).collect(),
|
|
|
|
*pos,
|
2022-01-10 13:08:03 +01:00
|
|
|
))
|
|
|
|
})?;
|
2022-01-07 04:43:47 +01:00
|
|
|
let mut context = EvalContext {
|
|
|
|
engine: self,
|
|
|
|
scope,
|
|
|
|
global,
|
|
|
|
state,
|
|
|
|
lib,
|
|
|
|
this_ptr,
|
|
|
|
level,
|
|
|
|
};
|
|
|
|
|
|
|
|
let result = (custom_def.func)(&mut context, &expressions);
|
|
|
|
|
2022-02-04 05:04:33 +01:00
|
|
|
self.check_return_value(result, expr.start_position())
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
|
|
|
Expr::Stmt(x) => {
|
|
|
|
self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
Expr::Index(_, _, _) => {
|
|
|
|
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
Expr::Dot(_, _, _) => {
|
|
|
|
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
2022-01-24 10:04:40 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#[cfg(feature = "debugging")]
|
2022-01-25 05:24:30 +01:00
|
|
|
global.debugger.reset_status(reset_debugger);
|
2022-01-24 10:04:40 +01:00
|
|
|
|
|
|
|
return result;
|
2022-01-07 04:43:47 +01:00
|
|
|
}
|
|
|
|
}
|