Reverse tail call optimization.

This commit is contained in:
Stephen Chung 2022-01-06 12:31:46 +08:00
parent bc6bf6c6ba
commit ed89817d0c

View File

@ -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