Reverse tail call optimization.
This commit is contained in:
parent
bc6bf6c6ba
commit
ed89817d0c
252
src/engine.rs
252
src/engine.rs
@ -2308,23 +2308,49 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a constant expression.
|
/// Evaluate a function call expression.
|
||||||
fn eval_constant_expr(expr: &Expr) -> RhaiResult {
|
fn eval_fn_call_expr(
|
||||||
Ok(match expr {
|
&self,
|
||||||
Expr::DynamicConstant(x, _) => x.as_ref().clone(),
|
scope: &mut Scope,
|
||||||
Expr::IntegerConstant(x, _) => (*x).into(),
|
global: &mut GlobalRuntimeState,
|
||||||
#[cfg(not(feature = "no_float"))]
|
state: &mut EvalState,
|
||||||
Expr::FloatConstant(x, _) => (*x).into(),
|
lib: &[&Module],
|
||||||
Expr::StringConstant(x, _) => x.clone().into(),
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
Expr::CharConstant(x, _) => (*x).into(),
|
expr: &FnCallExpr,
|
||||||
Expr::BoolConstant(x, _) => (*x).into(),
|
pos: Position,
|
||||||
Expr::Unit(_) => Dynamic::UNIT,
|
level: usize,
|
||||||
_ => unreachable!("constant expression expected but gets {:?}", expr),
|
) -> RhaiResult {
|
||||||
})
|
let FnCallExpr {
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
capture_parent_scope: capture,
|
||||||
|
hashes,
|
||||||
|
args,
|
||||||
|
constants,
|
||||||
|
..
|
||||||
|
} = expr;
|
||||||
|
|
||||||
|
let result = if let Some(namespace) = namespace.as_ref() {
|
||||||
|
// Qualified function call
|
||||||
|
let hash = hashes.native;
|
||||||
|
|
||||||
|
self.make_qualified_function_call(
|
||||||
|
scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Normal function call
|
||||||
|
self.make_function_call(
|
||||||
|
scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.check_return_value(result, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a literal expression.
|
/// Evaluate an expression.
|
||||||
fn eval_literal_expr(
|
pub(crate) fn eval_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
@ -2334,7 +2360,31 @@ impl Engine {
|
|||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.inc_operations(&mut global.num_operations, expr.position())?;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
|
// 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),
|
||||||
|
|
||||||
|
// `this`
|
||||||
|
Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr
|
||||||
|
.as_deref()
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()),
|
||||||
|
|
||||||
|
// Normal variable
|
||||||
|
Expr::Variable(_, _, _) => self
|
||||||
|
.search_namespace(scope, global, state, lib, this_ptr, expr)
|
||||||
|
.map(|(val, _)| val.take_or_clone()),
|
||||||
|
|
||||||
// `... ${...} ...`
|
// `... ${...} ...`
|
||||||
Expr::InterpolatedString(x, pos) => {
|
Expr::InterpolatedString(x, pos) => {
|
||||||
let mut pos = *pos;
|
let mut pos = *pos;
|
||||||
@ -2405,22 +2455,6 @@ impl Engine {
|
|||||||
Ok(map)
|
Ok(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!("literal expression expected but gets {:?}", expr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a simple expression.
|
|
||||||
fn eval_simple_expr(
|
|
||||||
&self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
global: &mut GlobalRuntimeState,
|
|
||||||
state: &mut EvalState,
|
|
||||||
lib: &[&Module],
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
expr: &Expr,
|
|
||||||
level: usize,
|
|
||||||
) -> RhaiResult {
|
|
||||||
match expr {
|
|
||||||
Expr::And(x, _) => {
|
Expr::And(x, _) => {
|
||||||
Ok((self
|
Ok((self
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)?
|
.eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)?
|
||||||
@ -2466,158 +2500,25 @@ impl Engine {
|
|||||||
self.check_return_value(result, expr.position())
|
self.check_return_value(result, expr.position())
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!("simple expression expected but gets {:?}", expr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a variable expression.
|
|
||||||
fn eval_variable_expr(
|
|
||||||
&self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
global: &mut GlobalRuntimeState,
|
|
||||||
state: &mut EvalState,
|
|
||||||
lib: &[&Module],
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
expr: &Expr,
|
|
||||||
_level: usize,
|
|
||||||
) -> RhaiResult {
|
|
||||||
let result = match expr {
|
|
||||||
Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr
|
|
||||||
.as_deref()
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()),
|
|
||||||
|
|
||||||
Expr::Variable(_, _, _) => self
|
|
||||||
.search_namespace(scope, global, state, lib, this_ptr, expr)
|
|
||||||
.map(|(val, _)| val.take_or_clone()),
|
|
||||||
|
|
||||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.check_return_value(result, expr.position())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a function call expression.
|
|
||||||
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 result = if expr.is_qualified() {
|
|
||||||
// Qualified function call
|
|
||||||
let FnCallExpr {
|
|
||||||
name,
|
|
||||||
namespace,
|
|
||||||
hashes,
|
|
||||||
args,
|
|
||||||
constants,
|
|
||||||
..
|
|
||||||
} = expr;
|
|
||||||
let namespace = namespace.as_ref().expect("qualified function call");
|
|
||||||
let hash = hashes.native;
|
|
||||||
|
|
||||||
self.make_qualified_function_call(
|
|
||||||
scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos,
|
|
||||||
level,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Normal function call
|
|
||||||
let FnCallExpr {
|
|
||||||
name,
|
|
||||||
capture_parent_scope: capture,
|
|
||||||
hashes,
|
|
||||||
args,
|
|
||||||
constants,
|
|
||||||
..
|
|
||||||
} = expr;
|
|
||||||
|
|
||||||
self.make_function_call(
|
|
||||||
scope, global, state, lib, this_ptr, name, args, constants, *hashes, pos, *capture,
|
|
||||||
level,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.check_return_value(result, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate an expression.
|
|
||||||
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 {
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
||||||
|
|
||||||
match expr {
|
|
||||||
Expr::DynamicConstant(_, _)
|
|
||||||
| Expr::IntegerConstant(_, _)
|
|
||||||
| Expr::StringConstant(_, _)
|
|
||||||
| Expr::CharConstant(_, _)
|
|
||||||
| Expr::BoolConstant(_, _)
|
|
||||||
| Expr::Unit(_) => Self::eval_constant_expr(expr),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Expr::FloatConstant(_, _) => Self::eval_constant_expr(expr),
|
|
||||||
|
|
||||||
// Variable
|
|
||||||
Expr::Variable(_, _, _) => {
|
|
||||||
self.eval_variable_expr(scope, global, state, lib, this_ptr, expr, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statement block
|
|
||||||
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
||||||
Expr::Stmt(x) => {
|
Expr::Stmt(x) => {
|
||||||
self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level)
|
self.eval_stmt_block(scope, global, state, lib, this_ptr, x, true, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs[idx_expr]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(_, _, _) => {
|
Expr::Index(_, _, _) => {
|
||||||
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
|
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs.dot_rhs
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(_, _, _) => {
|
Expr::Dot(_, _, _) => {
|
||||||
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
|
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `... ${...} ...`
|
|
||||||
Expr::InterpolatedString(_, _) => {
|
|
||||||
self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Array(_, _) => {
|
|
||||||
self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Map(_, _) => {
|
|
||||||
self.eval_literal_expr(scope, global, state, lib, this_ptr, expr, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function call
|
|
||||||
Expr::FnCall(x, pos) => {
|
Expr::FnCall(x, pos) => {
|
||||||
self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level)
|
self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple expressions
|
|
||||||
Expr::And(_, _) | Expr::Or(_, _) | Expr::Custom(_, _) => {
|
|
||||||
self.eval_simple_expr(scope, global, state, lib, this_ptr, expr, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3422,19 +3323,22 @@ impl Engine {
|
|||||||
|
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult {
|
fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult {
|
||||||
if let Ok(ref mut r) = result {
|
match result {
|
||||||
// Concentrate all empty strings into one instance to save memory
|
Ok(ref mut r) => {
|
||||||
if let Dynamic(crate::types::dynamic::Union::Str(s, _, _)) = r {
|
// Concentrate all empty strings into one instance to save memory
|
||||||
if s.is_empty() {
|
if let Dynamic(Union::Str(s, _, _)) = r {
|
||||||
if !s.ptr_eq(&self.empty_string) {
|
if s.is_empty() {
|
||||||
*s = self.const_empty_string();
|
if !s.ptr_eq(&self.empty_string) {
|
||||||
|
*s = self.const_empty_string();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.check_data_size(&r, pos)?;
|
self.check_data_size(&r, pos)?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
Loading…
Reference in New Issue
Block a user