rhai/src/eval/expr.rs

524 lines
20 KiB
Rust
Raw Normal View History

2022-01-07 04:43:47 +01:00
//! Module defining functions for evaluating an expression.
2022-04-16 10:36:53 +02:00
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
2022-01-07 05:19:01 +01:00
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,
2022-03-05 10:57:23 +01:00
namespace: &crate::ast::Namespace,
2022-01-29 04:09:43 +01:00
) -> Option<crate::Shared<Module>> {
2022-03-05 10:57:23 +01:00
assert!(!namespace.is_empty());
2022-03-03 06:02:57 +01:00
let root = namespace.root();
2022-01-07 04:43:47 +01:00
// Qualified - check if the root module is directly indexed
2022-04-16 10:36:53 +02:00
let index = if global.always_search_scope {
2022-01-07 04:43:47 +01:00
None
} else {
namespace.index()
};
if let Some(index) = index {
let offset = global.num_imports() - index.get();
Some(global.get_shared_import(offset).unwrap())
2022-01-07 04:43:47 +01:00
} else {
global
.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,
lib: &[&Module],
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &Expr,
2022-02-04 15:16:12 +01:00
level: usize,
2022-01-07 04:43:47 +01:00
) -> RhaiResultOf<(Target<'s>, Position)> {
match expr {
2022-03-05 10:57:23 +01:00
Expr::Variable(_, Some(_), _) => {
2022-04-16 17:32:14 +02:00
self.search_scope_only(scope, global, lib, this_ptr, expr, level)
2022-01-07 04:43:47 +01:00
}
2022-03-05 10:57:23 +01:00
Expr::Variable(v, None, _var_pos) => match v.as_ref() {
2022-01-07 04:43:47 +01:00
// Normal variable access
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-03-05 10:57:23 +01:00
(_, ns, ..) if ns.is_empty() => {
2022-04-16 17:32:14 +02:00
self.search_scope_only(scope, global, lib, this_ptr, expr, level)
2022-02-04 15:16:12 +01:00
}
2022-01-29 04:09:43 +01:00
#[cfg(feature = "no_module")]
2022-03-05 10:57:23 +01:00
(_, (), ..) => {
2022-04-16 10:36:53 +02:00
self.search_scope_only(scope, global, caches, lib, this_ptr, expr, level)
2022-02-04 16:08:09 +01:00
}
2022-01-07 04:43:47 +01:00
// Qualified variable access
#[cfg(not(feature = "no_module"))]
2022-03-05 10:57:23 +01:00
(_, namespace, hash_var, var_name) => {
2022-01-28 11:59:18 +01:00
// foo:bar::baz::VARIABLE
2022-04-16 10:36:53 +02:00
if let Some(module) = self.search_imports(global, namespace) {
2022-03-03 06:02:57 +01:00
return if let Some(mut target) = module.get_qualified_var(*hash_var) {
// Module variables are constant
target.set_access_mode(AccessMode::ReadOnly);
Ok((target.into(), *_var_pos))
} else {
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
Err(ERR::ErrorVariableNotFound(
format!("{}{}{}", namespace, sep, var_name),
namespace.position(),
)
.into())
2022-01-07 04:43:47 +01:00
};
}
2022-01-28 11:59:18 +01:00
// global::VARIABLE
2022-01-07 04:43:47 +01:00
#[cfg(not(feature = "no_function"))]
2022-03-03 06:02:57 +01:00
if namespace.len() == 1 && namespace.root() == 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
}
2022-03-03 06:02:57 +01:00
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
2022-01-07 04:43:47 +01:00
return Err(ERR::ErrorVariableNotFound(
2022-03-03 06:02:57 +01:00
format!("{}{}{}", namespace, sep, var_name),
namespace.position(),
2022-01-07 04:43:47 +01:00
)
.into());
}
2022-03-03 06:02:57 +01:00
Err(
ERR::ErrorModuleNotFound(namespace.to_string(), namespace.position())
.into(),
)
2022-01-07 04:43:47 +01:00
}
},
_ => 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,
lib: &[&Module],
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &Expr,
2022-02-04 15:16:12 +01:00
level: usize,
2022-01-07 04:43:47 +01:00
) -> 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`
2022-03-05 10:57:23 +01:00
Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => {
2022-01-07 04:43:47 +01:00
return if let Some(val) = this_ptr {
Ok(((*val).into(), *pos))
} else {
Err(ERR::ErrorUnboundThis(*pos).into())
}
}
2022-04-16 10:36:53 +02:00
_ if global.always_search_scope => (0, expr.start_position()),
2022-03-05 10:57:23 +01:00
Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos),
Expr::Variable(v, None, pos) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
2022-01-07 04:43:47 +01:00
_ => 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,
2022-04-16 17:32:14 +02:00
caches: None,
2022-01-07 04:43:47 +01:00
lib,
this_ptr,
2022-02-04 15:16:12 +01:00
level,
2022-01-07 04:43:47 +01:00
};
let var_name = expr.get_variable_name(true).expect("`Expr::Variable`");
match resolve_var(var_name, index, &context) {
2022-01-07 04:43:47 +01:00
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`");
match scope.get_index(var_name) {
Some((index, _)) => index,
None => {
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) {
Some(val) => Ok((val.into(), var_pos)),
None => {
Err(ERR::ErrorVariableNotFound(var_name.to_string(), var_pos).into())
}
}
}
}
2022-01-07 04:43:47 +01:00
};
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,
2022-04-16 10:36:53 +02:00
caches: &mut Caches,
2022-01-07 04:43:47 +01:00
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,
..
} = expr;
2022-01-29 04:09:43 +01:00
#[cfg(not(feature = "no_module"))]
2022-03-05 10:57:23 +01:00
if !namespace.is_empty() {
2022-01-07 04:43:47 +01:00
// Qualified function call
let hash = hashes.native;
2022-01-29 04:09:43 +01:00
return self.make_qualified_function_call(
2022-04-16 10:36:53 +02:00
scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level,
);
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-04-16 10:36:53 +02:00
scope, global, caches, lib, this_ptr, name, first_arg, args, *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,
2022-04-16 10:36:53 +02:00
caches: &mut Caches,
2022-01-07 04:43:47 +01:00
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.
2022-02-08 02:02:15 +01:00
if let Expr::FnCall(x, ..) = expr {
2022-01-25 05:24:30 +01:00
#[cfg(feature = "debugging")]
2022-02-03 04:56:08 +01:00
let reset_debugger =
2022-04-16 17:32:14 +02:00
self.run_debugger_with_reset(scope, global, 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 =
2022-04-16 10:36:53 +02:00
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
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
}
// 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.
2022-03-05 10:57:23 +01:00
if let Expr::Variable(x, index, var_pos) = expr {
2022-01-25 05:24:30 +01:00
#[cfg(feature = "debugging")]
2022-04-16 17:32:14 +02:00
self.run_debugger(scope, global, 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-03-05 10:57:23 +01:00
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
2022-01-07 04:43:47 +01:00
this_ptr
.as_deref()
.cloned()
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
} else {
2022-04-16 17:32:14 +02:00
self.search_namespace(scope, global, lib, this_ptr, expr, level)
2022-02-08 02:02:15 +01:00
.map(|(val, ..)| val.take_or_clone())
2022-01-07 04:43:47 +01:00
};
}
2022-01-25 05:24:30 +01:00
#[cfg(feature = "debugging")]
2022-02-03 04:56:08 +01:00
let reset_debugger =
2022-04-16 17:32:14 +02:00
self.run_debugger_with_reset(scope, global, 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
2022-02-08 02:02:15 +01:00
Expr::DynamicConstant(x, ..) => Ok(x.as_ref().clone()),
Expr::IntegerConstant(x, ..) => Ok((*x).into()),
2022-01-07 04:43:47 +01:00
#[cfg(not(feature = "no_float"))]
2022-02-08 02:02:15 +01:00
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()),
2022-02-08 02:46:14 +01:00
Expr::Unit(..) => Ok(Dynamic::UNIT),
2022-01-07 04:43:47 +01:00
// `... ${...} ...`
2022-02-10 10:55:32 +01:00
Expr::InterpolatedString(x, _) => {
2022-04-18 17:12:47 +02:00
let mut concat = self.const_empty_string().into();
let target = &mut concat;
2022-01-24 10:04:40 +01:00
let mut result = Ok(Dynamic::UNIT);
2022-01-07 04:43:47 +01:00
2022-04-18 17:12:47 +02:00
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
let root = ("", Position::NONE);
2022-01-07 04:43:47 +01:00
for expr in x.iter() {
2022-01-24 10:04:40 +01:00
let item =
2022-04-16 10:36:53 +02:00
match self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) {
2022-01-24 10:04:40 +01:00
Ok(r) => r,
err => {
result = err;
break;
}
};
2022-01-07 04:43:47 +01:00
2022-04-18 17:12:47 +02:00
op_info.pos = expr.start_position();
if let Err(err) = self
.eval_op_assignment(global, caches, lib, op_info, target, root, item, level)
{
result = Err(err);
2022-01-24 10:04:40 +01:00
break;
}
2022-01-07 04:43:47 +01:00
}
2022-04-18 17:12:47 +02:00
result.map(|_| concat.take_or_clone())
2022-01-07 04:43:47 +01:00
}
#[cfg(not(feature = "no_index"))]
2022-02-08 02:02:15 +01:00
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
2022-04-16 10:36:53 +02:00
.eval_expr(scope, global, caches, lib, this_ptr, item_expr, level)
2022-01-24 10:04:40 +01:00
{
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"))]
2022-02-08 02:02:15 +01:00
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-03-05 10:57:23 +01:00
for (key, value_expr) in x.0.iter() {
2022-01-24 10:04:40 +01:00
let value = match self
2022-04-16 10:36:53 +02:00
.eval_expr(scope, global, caches, lib, this_ptr, value_expr, level)
2022-01-24 10:04:40 +01:00
{
Ok(r) => r.flatten(),
err => {
result = err;
break;
}
};
2022-01-07 04:43:47 +01:00
#[cfg(not(feature = "unchecked"))]
2022-02-24 03:36:20 +01:00
let delta = Self::calc_data_sizes(&value, true);
2022-01-07 04:43:47 +01:00
2022-03-05 10:57:23 +01:00
*map.get_mut(key.as_str()).unwrap() = value;
2022-01-07 04:43:47 +01:00
#[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() {
2022-02-24 03:36:20 +01:00
sizes = (sizes.0 + delta.0, sizes.1 + delta.1, sizes.2 + delta.2);
2022-01-07 04:43:47 +01:00
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
}
2022-02-08 02:02:15 +01:00
Expr::And(x, ..) => {
2022-01-24 10:04:40 +01:00
let lhs = self
2022-04-16 10:36:53 +02:00
.eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level)
2022-01-24 10:04:40 +01:00
.and_then(|v| {
v.as_bool().map_err(|typ| {
self.make_type_mismatch_err::<bool>(typ, x.lhs.position())
})
});
if let Ok(true) = lhs {
2022-04-16 10:36:53 +02:00
self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level)
2022-01-24 10:04:40 +01:00
.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-02-08 02:02:15 +01:00
Expr::Or(x, ..) => {
2022-01-24 10:04:40 +01:00
let lhs = self
2022-04-16 10:36:53 +02:00
.eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level)
2022-01-24 10:04:40 +01:00
.and_then(|v| {
v.as_bool().map_err(|typ| {
self.make_type_mismatch_err::<bool>(typ, x.lhs.position())
})
});
if let Ok(false) = lhs {
2022-04-16 10:36:53 +02:00
self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level)
2022-01-24 10:04:40 +01:00
.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,
2022-04-16 17:32:14 +02:00
caches: Some(caches),
2022-01-07 04:43:47 +01:00
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) => {
2022-04-16 10:36:53 +02:00
self.eval_stmt_block(scope, global, caches, lib, this_ptr, x, true, level)
2022-01-07 04:43:47 +01:00
}
#[cfg(not(feature = "no_index"))]
2022-02-08 02:02:15 +01:00
Expr::Index(..) => {
2022-04-16 10:36:53 +02:00
self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None)
2022-01-07 04:43:47 +01:00
}
#[cfg(not(feature = "no_object"))]
2022-02-08 02:02:15 +01:00
Expr::Dot(..) => {
2022-04-16 10:36:53 +02:00
self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None)
2022-01-07 04:43:47 +01:00
}
_ => 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
}
}