Remove modules from Scope and use separate stack.

This commit is contained in:
Stephen Chung 2020-06-27 23:56:24 +08:00
parent f3bde843cb
commit aac04a5e53
11 changed files with 434 additions and 334 deletions

View File

@ -12,6 +12,7 @@ Breaking changes
* The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument.
* Functions defined in script now differentiates between using method-call style and normal function-call style.
The method-call style will bind the object to the `this` parameter instead of consuming the first parameter.
* Imported modules are no longer stored in the `Scope`. `Scope::push_module` is removed.
New features
------------

View File

@ -1,7 +1,6 @@
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
use crate::fn_native::{FnPtr, SendSync};
use crate::module::Module;
use crate::parser::{ImmutableString, INT};
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
@ -138,7 +137,6 @@ pub enum Union {
Array(Box<Array>),
#[cfg(not(feature = "no_object"))]
Map(Box<Map>),
Module(Box<Module>),
#[cfg(not(feature = "no_function"))]
FnPtr(FnPtr),
Variant(Box<Box<dyn Variant>>),
@ -177,7 +175,6 @@ impl Dynamic {
Union::Array(_) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_object"))]
Union::Map(_) => TypeId::of::<Map>(),
Union::Module(_) => TypeId::of::<Module>(),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
Union::Variant(value) => (***value).type_id(),
@ -198,7 +195,6 @@ impl Dynamic {
Union::Array(_) => "array",
#[cfg(not(feature = "no_object"))]
Union::Map(_) => "map",
Union::Module(_) => "sub-scope",
#[cfg(not(feature = "no_function"))]
Union::FnPtr(_) => "Fn",
@ -224,7 +220,6 @@ impl fmt::Display for Dynamic {
Union::Array(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
Union::Module(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => fmt::Display::fmt(value, f),
@ -250,7 +245,6 @@ impl fmt::Debug for Dynamic {
Union::Array(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
Union::Module(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => fmt::Display::fmt(value, f),
@ -276,7 +270,6 @@ impl Clone for Dynamic {
Union::Array(ref value) => Self(Union::Array(value.clone())),
#[cfg(not(feature = "no_object"))]
Union::Map(ref value) => Self(Union::Map(value.clone())),
Union::Module(ref value) => Self(Union::Module(value.clone())),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
Union::Variant(ref value) => (***value).clone_into_dynamic(),
@ -407,7 +400,6 @@ impl Dynamic {
Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => unsafe_try_cast(value),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
@ -452,7 +444,6 @@ impl Dynamic {
Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
@ -480,7 +471,6 @@ impl Dynamic {
Union::Array(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::Module(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
@ -507,7 +497,6 @@ impl Dynamic {
Union::Array(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::Module(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),

View File

@ -2,7 +2,7 @@
use crate::any::{Dynamic, Variant};
use crate::engine::{
get_script_function_by_signature, make_getter, make_setter, Engine, State, FN_IDX_GET,
get_script_function_by_signature, make_getter, make_setter, Engine, Imports, State, FN_IDX_GET,
FN_IDX_SET,
};
use crate::error::ParseError;
@ -973,7 +973,8 @@ impl Engine {
scope: &mut Scope,
ast: &AST,
) -> Result<T, Box<EvalAltResult>> {
let (result, _) = self.eval_ast_with_scope_raw(scope, ast)?;
let mut mods = Imports::new();
let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?;
let return_type = self.map_type_name(result.type_name());
@ -986,17 +987,18 @@ impl Engine {
}
/// Evaluate an `AST` with own scope.
pub(crate) fn eval_ast_with_scope_raw(
pub(crate) fn eval_ast_with_scope_raw<'a>(
&self,
scope: &mut Scope,
ast: &AST,
mods: &mut Imports,
ast: &'a AST,
) -> Result<(Dynamic, u64), Box<EvalAltResult>> {
let mut state = State::new();
ast.statements()
.iter()
.try_fold(().into(), |_, stmt| {
self.eval_stmt(scope, &mut state, ast.lib(), &mut None, stmt, 0)
self.eval_stmt(scope, mods, &mut state, ast.lib(), &mut None, stmt, 0)
})
.or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out),
@ -1058,11 +1060,12 @@ impl Engine {
ast: &AST,
) -> Result<(), Box<EvalAltResult>> {
let mut state = State::new();
let mut mods = Default::default();
ast.statements()
.iter()
.try_fold(().into(), |_, stmt| {
self.eval_stmt(scope, &mut state, ast.lib(), &mut None, stmt, 0)
self.eval_stmt(scope, &mut mods, &mut state, ast.lib(), &mut None, stmt, 0)
})
.map_or_else(
|err| match *err {
@ -1201,10 +1204,12 @@ impl Engine {
})?;
let mut state = State::new();
let mut mods = Imports::new();
let args = args.as_mut();
self.call_script_fn(
scope,
&mut mods,
&mut state,
ast.lib(),
&mut None,

View File

@ -19,6 +19,7 @@ use crate::parser::FLOAT;
use crate::stdlib::{
any::TypeId,
borrow::Cow,
boxed::Box,
collections::HashMap,
format,
@ -40,6 +41,9 @@ pub type Array = Vec<Dynamic>;
#[cfg(not(feature = "no_object"))]
pub type Map = HashMap<ImmutableString, Dynamic>;
/// A stack of imported modules.
pub type Imports<'a> = Vec<(Cow<'a, str>, Module)>;
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 16;
@ -308,7 +312,7 @@ impl Default for Engine {
#[cfg(any(feature = "no_module", feature = "no_std", target_arch = "wasm32",))]
module_resolver: None,
type_names: HashMap::new(),
type_names: Default::default(),
// default print/debug implementations
print: Box::new(default_print),
@ -392,6 +396,7 @@ fn default_print(s: &str) {
/// Search for a variable within the scope
fn search_scope<'s, 'a>(
scope: &'s mut Scope,
mods: &'s mut Imports,
state: &mut State,
this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr,
@ -420,17 +425,16 @@ fn search_scope<'s, 'a>(
};
let module = if let Some(index) = index {
scope
.get_mut(scope.len() - index.get())
.0
.downcast_mut::<Module>()
.unwrap()
let offset = mods.len() - index.get();
&mut mods.get_mut(offset).unwrap().1
} else {
// Find the root module in the scope
let (id, root_pos) = modules.get(0);
scope
.find_module_internal(id)
mods.iter_mut()
.rev()
.find(|(n, _)| n == id)
.map(|(_, m)| m)
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?
};
@ -473,7 +477,7 @@ impl Engine {
global_module: Default::default(),
module_resolver: None,
type_names: HashMap::new(),
type_names: Default::default(),
print: Box::new(|_| {}),
debug: Box::new(|_| {}),
progress: None,
@ -658,6 +662,7 @@ impl Engine {
pub(crate) fn call_fn_raw(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
fn_name: &str,
@ -753,6 +758,7 @@ impl Engine {
return Ok((
self.call_script_fn(
scope,
mods,
state,
lib,
&mut Some(first[0]),
@ -765,7 +771,7 @@ impl Engine {
));
} else {
let result = self.call_script_fn(
scope, state, lib, &mut None, fn_name, fn_def, args, level,
scope, mods, state, lib, &mut None, fn_name, fn_def, args, level,
)?;
// Restore the original reference
@ -889,6 +895,7 @@ impl Engine {
pub(crate) fn call_script_fn(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
@ -900,7 +907,8 @@ impl Engine {
let orig_scope_level = state.scope_level;
state.scope_level += 1;
let scope_len = scope.len();
let prev_scope_len = scope.len();
let prev_mods_len = mods.len();
// Put arguments into scope as variables
// Actually consume the arguments instead of cloning them
@ -917,7 +925,7 @@ impl Engine {
// Evaluate the function at one higher level of call depth
let result = self
.eval_stmt(scope, state, lib, this_ptr, &fn_def.body, level + 1)
.eval_stmt(scope, mods, state, lib, this_ptr, &fn_def.body, level + 1)
.or_else(|err| match *err {
// Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x),
@ -936,7 +944,8 @@ impl Engine {
});
// Remove all local variables
scope.rewind(scope_len);
scope.rewind(prev_scope_len);
mods.truncate(prev_mods_len);
state.scope_level = orig_scope_level;
result
@ -1009,9 +1018,10 @@ impl Engine {
// Normal function call
_ => {
let mut scope = Scope::new();
let mut mods = Imports::new();
self.call_fn_raw(
&mut scope, state, lib, fn_name, hashes, args, is_ref, is_method, def_val,
level,
&mut scope, &mut mods, state, lib, fn_name, hashes, args, is_ref, is_method,
def_val, level,
)
}
}
@ -1022,6 +1032,7 @@ impl Engine {
fn eval_script_expr(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
script: &Dynamic,
@ -1047,7 +1058,7 @@ impl Engine {
let ast = AST::new(statements, lib.clone());
// Evaluate the AST
let (result, operations) = self.eval_ast_with_scope_raw(scope, &ast)?;
let (result, operations) = self.eval_ast_with_scope_raw(scope, mods, &ast)?;
state.operations += operations;
self.inc_operations(state)?;
@ -1356,6 +1367,7 @@ impl Engine {
fn eval_dot_index_chain(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
@ -1371,7 +1383,9 @@ impl Engine {
let idx_values = &mut StaticVec::new();
self.eval_indexed_chain(scope, state, lib, this_ptr, dot_rhs, idx_values, 0, level)?;
self.eval_indexed_chain(
scope, mods, state, lib, this_ptr, dot_rhs, idx_values, 0, level,
)?;
match dot_lhs {
// id.??? or id[???]
@ -1381,11 +1395,10 @@ impl Engine {
self.inc_operations(state)
.map_err(|err| EvalAltResult::new_position(err, *var_pos))?;
let (target, _, typ, pos) = search_scope(scope, state, this_ptr, dot_lhs)?;
let (target, _, typ, pos) = search_scope(scope, mods, state, this_ptr, dot_lhs)?;
// Constants cannot be modified
match typ {
ScopeEntryType::Module => unreachable!(),
ScopeEntryType::Constant if new_val.is_some() => {
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
var_name.to_string(),
@ -1410,7 +1423,7 @@ impl Engine {
}
// {expr}.??? or {expr}[???]
expr => {
let val = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let obj_ptr = &mut val.into();
self.eval_dot_index_chain_helper(
state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val,
@ -1429,6 +1442,7 @@ impl Engine {
fn eval_indexed_chain(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
@ -1442,10 +1456,11 @@ impl Engine {
match expr {
Expr::FnCall(x) if x.1.is_none() => {
let arg_values = x
.3
.iter()
.map(|arg_expr| self.eval_expr(scope, state, lib, this_ptr, arg_expr, level))
let arg_values =
x.3.iter()
.map(|arg_expr| {
self.eval_expr(scope, mods, state, lib, this_ptr, arg_expr, level)
})
.collect::<Result<StaticVec<Dynamic>, _>>()?;
idx_values.push(Dynamic::from(arg_values));
@ -1458,15 +1473,17 @@ impl Engine {
// Evaluate in left-to-right order
let lhs_val = match lhs {
Expr::Property(_) => Default::default(), // Store a placeholder in case of a property
_ => self.eval_expr(scope, state, lib, this_ptr, lhs, level)?,
_ => self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?,
};
// Push in reverse order
self.eval_indexed_chain(scope, state, lib, this_ptr, rhs, idx_values, size, level)?;
self.eval_indexed_chain(
scope, mods, state, lib, this_ptr, rhs, idx_values, size, level,
)?;
idx_values.push(lhs_val);
}
_ => idx_values.push(self.eval_expr(scope, state, lib, this_ptr, expr, level)?),
_ => idx_values.push(self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?),
}
Ok(())
@ -1581,6 +1598,7 @@ impl Engine {
fn eval_in_expr(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
@ -1591,8 +1609,8 @@ impl Engine {
self.inc_operations(state)
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
let lhs_value = self.eval_expr(scope, state, lib, this_ptr, lhs, level)?;
let rhs_value = self.eval_expr(scope, state, lib, this_ptr, rhs, level)?;
let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?;
let rhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?;
match rhs_value {
#[cfg(not(feature = "no_index"))]
@ -1614,8 +1632,8 @@ impl Engine {
let (r, _) = self
.call_fn_raw(
&mut scope, state, lib, op, hashes, args, false, false, def_value,
level,
&mut scope, mods, state, lib, op, hashes, args, false, false,
def_value, level,
)
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
if r.as_bool().unwrap_or(false) {
@ -1648,6 +1666,7 @@ impl Engine {
fn eval_expr(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
@ -1658,7 +1677,7 @@ impl Engine {
.map_err(|err| EvalAltResult::new_position(err, expr.position()))?;
let result = match expr {
Expr::Expr(x) => self.eval_expr(scope, state, lib, this_ptr, x.as_ref(), level),
Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x.as_ref(), level),
Expr::IntegerConstant(x) => Ok(x.0.into()),
#[cfg(not(feature = "no_float"))]
@ -1673,19 +1692,21 @@ impl Engine {
}
}
Expr::Variable(_) => {
let (val, _, _, _) = search_scope(scope, state, this_ptr, expr)?;
let (val, _, _, _) = search_scope(scope, mods, state, this_ptr, expr)?;
Ok(val.clone())
}
Expr::Property(_) => unreachable!(),
// Statement block
Expr::Stmt(x) => self.eval_stmt(scope, state, lib, this_ptr, &x.0, level),
Expr::Stmt(x) => self.eval_stmt(scope, mods, state, lib, this_ptr, &x.0, level),
// var op= rhs
Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => {
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
let mut rhs_val = self.eval_expr(scope, state, lib, this_ptr, rhs_expr, level)?;
let (lhs_ptr, name, typ, pos) = search_scope(scope, state, this_ptr, lhs_expr)?;
let mut rhs_val =
self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?;
let (lhs_ptr, name, typ, pos) =
search_scope(scope, mods, state, this_ptr, lhs_expr)?;
self.inc_operations(state)
.map_err(|err| EvalAltResult::new_position(err, pos))?;
@ -1732,15 +1753,14 @@ impl Engine {
}
Ok(Default::default())
}
// A module cannot be assigned to
ScopeEntryType::Module => unreachable!(),
}
}
// lhs op= rhs
Expr::Assignment(x) => {
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
let mut rhs_val = self.eval_expr(scope, state, lib, this_ptr, rhs_expr, level)?;
let mut rhs_val =
self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?;
let new_val = Some(if op.is_empty() {
// Normal assignment
@ -1750,7 +1770,7 @@ impl Engine {
let op = &op[..op.len() - 1]; // extract operator without =
let hash = calc_fn_hash(empty(), op, 2, empty());
let args = &mut [
&mut self.eval_expr(scope, state, lib, this_ptr, lhs_expr, level)?,
&mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?,
&mut rhs_val,
];
self.exec_fn_call(state, lib, op, true, hash, args, false, false, None, level)
@ -1764,12 +1784,12 @@ impl Engine {
// idx_lhs[idx_expr] op= rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(_) => self.eval_dot_index_chain(
scope, state, lib, this_ptr, lhs_expr, level, new_val,
scope, mods, state, lib, this_ptr, lhs_expr, level, new_val,
),
// dot_lhs.dot_rhs op= rhs
#[cfg(not(feature = "no_object"))]
Expr::Dot(_) => self.eval_dot_index_chain(
scope, state, lib, this_ptr, lhs_expr, level, new_val,
scope, mods, state, lib, this_ptr, lhs_expr, level, new_val,
),
// Error assignment to constant
expr if expr.is_constant() => {
@ -1788,19 +1808,19 @@ impl Engine {
// lhs[idx_expr]
#[cfg(not(feature = "no_index"))]
Expr::Index(_) => {
self.eval_dot_index_chain(scope, state, lib, this_ptr, expr, level, None)
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
}
// lhs.dot_rhs
#[cfg(not(feature = "no_object"))]
Expr::Dot(_) => {
self.eval_dot_index_chain(scope, state, lib, this_ptr, expr, level, None)
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
}
#[cfg(not(feature = "no_index"))]
Expr::Array(x) => Ok(Dynamic(Union::Array(Box::new(
x.0.iter()
.map(|item| self.eval_expr(scope, state, lib, this_ptr, item, level))
.map(|item| self.eval_expr(scope, mods, state, lib, this_ptr, item, level))
.collect::<Result<Vec<_>, _>>()?,
)))),
@ -1808,7 +1828,7 @@ impl Engine {
Expr::Map(x) => Ok(Dynamic(Union::Map(Box::new(
x.0.iter()
.map(|((key, _), expr)| {
self.eval_expr(scope, state, lib, this_ptr, expr, level)
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
.map(|val| (key.clone(), val))
})
.collect::<Result<HashMap<_, _>, _>>()?,
@ -1827,7 +1847,8 @@ impl Engine {
if !self.has_override(lib, (hash_fn, *hash)) {
// Fn - only in function call style
let expr = args_expr.get(0);
let arg_value = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
let arg_value =
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
return arg_value
.take_immutable_string()
.map(|s| FnPtr::from(s).into())
@ -1849,9 +1870,10 @@ impl Engine {
// eval - only in function call style
let prev_len = scope.len();
let expr = args_expr.get(0);
let script = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
let script =
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let result = self
.eval_script_expr(scope, state, lib, &script)
.eval_script_expr(scope, mods, state, lib, &script)
.map_err(|err| EvalAltResult::new_position(err, expr.position()));
if scope.len() != prev_len {
@ -1882,16 +1904,12 @@ impl Engine {
.iter()
.skip(1)
.map(|expr| {
self.eval_expr(scope, state, lib, this_ptr, expr, level)
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
})
.collect::<Result<_, _>>()?;
let (target, _, typ, pos) = search_scope(scope, state, this_ptr, lhs)?;
match typ {
ScopeEntryType::Module => unreachable!(),
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
}
let (target, _, _, pos) =
search_scope(scope, mods, state, this_ptr, lhs)?;
self.inc_operations(state)
.map_err(|err| EvalAltResult::new_position(err, pos))?;
@ -1905,7 +1923,7 @@ impl Engine {
arg_values = args_expr
.iter()
.map(|expr| {
self.eval_expr(scope, state, lib, this_ptr, expr, level)
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
})
.collect::<Result<_, _>>()?;
@ -1929,21 +1947,28 @@ impl Engine {
let mut arg_values = args_expr
.iter()
.map(|expr| self.eval_expr(scope, state, lib, this_ptr, expr, level))
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
.collect::<Result<StaticVec<_>, _>>()?;
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
let (id, root_pos) = modules.get(0); // First module
let module = if let Some(index) = modules.index() {
scope
.get_mut(scope.len() - index.get())
.0
.downcast_mut::<Module>()
.unwrap()
let index = if state.always_search {
None
} else {
scope.find_module_internal(id).ok_or_else(|| {
modules.index()
};
let module = if let Some(index) = index {
let offset = mods.len() - index.get();
&mut mods.get_mut(offset).unwrap().1
} else {
mods.iter_mut()
.rev()
.find(|(n, _)| n == id)
.map(|(_, m)| m)
.ok_or_else(|| {
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
})?
};
@ -1975,8 +2000,9 @@ impl Engine {
let args = args.as_mut();
let fn_def = f.get_fn_def();
let mut scope = Scope::new();
let mut mods = Imports::new();
self.call_script_fn(
&mut scope, state, lib, &mut None, name, fn_def, args, level,
&mut scope, &mut mods, state, lib, &mut None, name, fn_def, args, level,
)
.map_err(|err| EvalAltResult::new_position(err, *pos))
}
@ -1993,19 +2019,19 @@ impl Engine {
}
}
Expr::In(x) => self.eval_in_expr(scope, state, lib, this_ptr, &x.0, &x.1, level),
Expr::In(x) => self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.0, &x.1, level),
Expr::And(x) => {
let (lhs, rhs, _) = x.as_ref();
Ok((self
.eval_expr(scope, state, lib, this_ptr, lhs, level)?
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?
.as_bool()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
})?
&& // Short-circuit using &&
self
.eval_expr(scope, state, lib, this_ptr, rhs, level)?
.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?
.as_bool()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
@ -2016,14 +2042,14 @@ impl Engine {
Expr::Or(x) => {
let (lhs, rhs, _) = x.as_ref();
Ok((self
.eval_expr(scope, state, lib, this_ptr, lhs, level)?
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?
.as_bool()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
})?
|| // Short-circuit using ||
self
.eval_expr(scope, state, lib, this_ptr, rhs, level)?
.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?
.as_bool()
.map_err(|_| {
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
@ -2045,6 +2071,7 @@ impl Engine {
pub(crate) fn eval_stmt(
&self,
scope: &mut Scope,
mods: &mut Imports,
state: &mut State,
lib: &Module,
this_ptr: &mut Option<&mut Dynamic>,
@ -2060,7 +2087,7 @@ impl Engine {
// Expression as statement
Stmt::Expr(expr) => {
let result = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
let result = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
Ok(match expr.as_ref() {
// If it is a simple assignment, erase the result at the root
@ -2071,14 +2098,16 @@ impl Engine {
// Block scope
Stmt::Block(x) => {
let prev_len = scope.len();
let prev_scope_len = scope.len();
let prev_mods_len = mods.len();
state.scope_level += 1;
let result = x.0.iter().try_fold(Default::default(), |_, stmt| {
self.eval_stmt(scope, state, lib, this_ptr, stmt, level)
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
});
scope.rewind(prev_len);
scope.rewind(prev_scope_len);
mods.truncate(prev_mods_len);
state.scope_level -= 1;
// The impact of an eval statement goes away at the end of a block
@ -2092,14 +2121,14 @@ impl Engine {
Stmt::IfThenElse(x) => {
let (expr, if_block, else_block) = x.as_ref();
self.eval_expr(scope, state, lib, this_ptr, expr, level)?
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool()
.map_err(|_| Box::new(EvalAltResult::ErrorLogicGuard(expr.position())))
.and_then(|guard_val| {
if guard_val {
self.eval_stmt(scope, state, lib, this_ptr, if_block, level)
self.eval_stmt(scope, mods, state, lib, this_ptr, if_block, level)
} else if let Some(stmt) = else_block {
self.eval_stmt(scope, state, lib, this_ptr, stmt, level)
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
} else {
Ok(Default::default())
}
@ -2111,17 +2140,21 @@ impl Engine {
let (expr, body) = x.as_ref();
match self
.eval_expr(scope, state, lib, this_ptr, expr, level)?
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool()
{
Ok(true) => match self.eval_stmt(scope, state, lib, this_ptr, body, level) {
Ok(true) => {
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (),
EvalAltResult::ErrorLoopBreak(true, _) => return Ok(Default::default()),
EvalAltResult::ErrorLoopBreak(true, _) => {
return Ok(Default::default())
}
_ => return Err(err),
},
},
}
}
Ok(false) => return Ok(Default::default()),
Err(_) => {
return Err(Box::new(EvalAltResult::ErrorLogicGuard(expr.position())))
@ -2131,7 +2164,7 @@ impl Engine {
// Loop statement
Stmt::Loop(body) => loop {
match self.eval_stmt(scope, state, lib, this_ptr, body, level) {
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -2144,7 +2177,7 @@ impl Engine {
// For loop
Stmt::For(x) => {
let (name, expr, stmt) = x.as_ref();
let iter_type = self.eval_expr(scope, state, lib, this_ptr, expr, level)?;
let iter_type = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
let tid = iter_type.type_id();
if let Some(func) = self
@ -2163,7 +2196,7 @@ impl Engine {
self.inc_operations(state)
.map_err(|err| EvalAltResult::new_position(err, stmt.position()))?;
match self.eval_stmt(scope, state, lib, this_ptr, stmt, level) {
match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) {
Ok(_) => (),
Err(err) => match *err {
EvalAltResult::ErrorLoopBreak(false, _) => (),
@ -2190,7 +2223,15 @@ impl Engine {
// Return value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
Err(Box::new(EvalAltResult::Return(
self.eval_expr(scope, state, lib, this_ptr, x.1.as_ref().unwrap(), level)?,
self.eval_expr(
scope,
mods,
state,
lib,
this_ptr,
x.1.as_ref().unwrap(),
level,
)?,
(x.0).1,
)))
}
@ -2202,8 +2243,15 @@ impl Engine {
// Throw value
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Exception => {
let val =
self.eval_expr(scope, state, lib, this_ptr, x.1.as_ref().unwrap(), level)?;
let val = self.eval_expr(
scope,
mods,
state,
lib,
this_ptr,
x.1.as_ref().unwrap(),
level,
)?;
Err(Box::new(EvalAltResult::ErrorRuntime(
val.take_string().unwrap_or_else(|_| "".into()),
(x.0).1,
@ -2220,8 +2268,15 @@ impl Engine {
// Let statement
Stmt::Let(x) if x.1.is_some() => {
let ((var_name, _), expr) = x.as_ref();
let val =
self.eval_expr(scope, state, lib, this_ptr, expr.as_ref().unwrap(), level)?;
let val = self.eval_expr(
scope,
mods,
state,
lib,
this_ptr,
expr.as_ref().unwrap(),
level,
)?;
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push_dynamic_value(var_name, ScopeEntryType::Normal, val, false);
Ok(Default::default())
@ -2237,7 +2292,7 @@ impl Engine {
// Const statement
Stmt::Const(x) if x.1.is_constant() => {
let ((var_name, _), expr) = x.as_ref();
let val = self.eval_expr(scope, state, lib, this_ptr, &expr, level)?;
let val = self.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?;
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
scope.push_dynamic_value(var_name, ScopeEntryType::Constant, val, true);
Ok(Default::default())
@ -2256,15 +2311,15 @@ impl Engine {
}
if let Some(path) = self
.eval_expr(scope, state, lib, this_ptr, &expr, level)?
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
.try_cast::<ImmutableString>()
{
#[cfg(not(feature = "no_module"))]
{
if let Some(resolver) = &self.module_resolver {
let module = resolver.resolve(self, &path, expr.position())?;
let mod_name = unsafe_cast_var_name_to_lifetime(name, &state);
scope.push_module_internal(mod_name, module);
let mut module = resolver.resolve(self, &path, expr.position())?;
module.index_all_sub_modules();
mods.push((name.clone().into(), module));
state.modules += 1;
@ -2288,11 +2343,7 @@ impl Engine {
Stmt::Export(list) => {
for ((id, id_pos), rename) in list.iter() {
// Mark scope variables as public
if let Some(index) = scope
.get_index(id)
.map(|(i, _)| i)
.or_else(|| scope.get_module_index(id))
{
if let Some(index) = scope.get_index(id).map(|(i, _)| i) {
let alias = rename
.as_ref()
.map(|(n, _)| n.clone())

View File

@ -124,6 +124,17 @@ impl fmt::Debug for CallableFunction {
}
}
impl fmt::Display for CallableFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pure(_) => write!(f, "NativePureFunction"),
Self::Method(_) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"),
CallableFunction::Script(s) => fmt::Display::fmt(s, f),
}
}
}
impl CallableFunction {
/// Is this a pure native Rust function?
pub fn is_pure(&self) -> bool {

View File

@ -2,7 +2,7 @@
use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash;
use crate::engine::{make_getter, make_setter, Engine, FN_IDX_GET, FN_IDX_SET};
use crate::engine::{make_getter, make_setter, Engine, Imports, FN_IDX_GET, FN_IDX_SET};
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, SendSync};
use crate::parser::{
FnAccess,
@ -10,13 +10,14 @@ use crate::parser::{
ScriptFnDef, AST,
};
use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::scope::{Entry as ScopeEntry, Scope};
use crate::token::{Position, Token};
use crate::utils::{StaticVec, StraightHasherBuilder};
use crate::stdlib::{
any::TypeId,
boxed::Box,
cell::RefCell,
collections::HashMap,
fmt,
iter::empty,
@ -24,6 +25,7 @@ use crate::stdlib::{
num::NonZeroUsize,
ops::{Deref, DerefMut},
string::{String, ToString},
sync::RwLock,
vec,
vec::Vec,
};
@ -35,7 +37,7 @@ pub type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
/// external Rust functions, and script-defined functions.
///
/// Not available under the `no_module` feature.
#[derive(Clone, Default)]
#[derive(Default)]
pub struct Module {
/// Sub-modules.
modules: HashMap<String, Module>,
@ -59,19 +61,47 @@ pub struct Module {
/// Flattened collection of all external Rust functions, native or scripted,
/// including those in sub-modules.
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
/// Is the module indexed?
indexed: bool,
}
impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"<module vars={:?}, functions={}>",
self.variables,
self.functions.len(),
"Module(\n modules: {}\n vars: {}\n functions: {}\n)",
self.modules
.keys()
.map(|k| k.as_str())
.collect::<Vec<_>>()
.join(", "),
self.variables
.iter()
.map(|(k, v)| format!("{}={:?}", k, v))
.collect::<Vec<_>>()
.join(", "),
self.functions
.values()
.map(|(_, _, _, f)| f.to_string())
.collect::<Vec<_>>()
.join(", "),
)
}
}
impl Clone for Module {
fn clone(&self) -> Self {
// Only clone the index at the top level
Self {
all_variables: self.all_variables.clone(),
all_functions: self.all_functions.clone(),
indexed: self.indexed,
..self.do_clone(false)
}
}
}
impl Module {
/// Create a new module.
///
@ -106,6 +136,24 @@ impl Module {
}
}
/// Clone the module, optionally skipping the index.
fn do_clone(&self, clone_index: bool) -> Self {
Self {
modules: if clone_index {
self.modules.clone()
} else {
self.modules
.iter()
.map(|(k, m)| (k.clone(), m.do_clone(clone_index)))
.collect()
},
variables: self.variables.clone(),
functions: self.functions.clone(),
type_iterators: self.type_iterators.clone(),
..Default::default()
}
}
/// Does a variable exist in the module?
///
/// # Examples
@ -166,6 +214,7 @@ impl Module {
/// ```
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) {
self.variables.insert(name.into(), Dynamic::from(value));
self.indexed = false;
}
/// Get a mutable reference to a modules-qualified variable.
@ -197,6 +246,7 @@ impl Module {
fn_def.into(),
),
);
self.indexed = false;
}
/// Does a sub-module exist in the module?
@ -263,6 +313,7 @@ impl Module {
/// ```
pub fn set_sub_module(&mut self, name: impl Into<String>, sub_module: Module) {
self.modules.insert(name.into(), sub_module.into());
self.indexed = false;
}
/// Does the particular Rust function exist in the module?
@ -302,6 +353,8 @@ impl Module {
self.functions
.insert(hash_fn, (name, access, params, func.into()));
self.indexed = false;
hash_fn
}
@ -855,32 +908,27 @@ impl Module {
/// ```
#[cfg(not(feature = "no_module"))]
pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn<Self> {
let mut mods = Imports::new();
// Run the script
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
// Create new module
let mut module = Module::new();
scope.into_iter().for_each(
|ScopeEntry {
typ, value, alias, ..
}| {
match typ {
scope
.into_iter()
.for_each(|ScopeEntry { value, alias, .. }| {
// Variables with an alias left in the scope become module variables
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
if alias.is_some() {
module.variables.insert(*alias.unwrap(), value);
}
});
// Modules left in the scope become sub-modules
ScopeEntryType::Module if alias.is_some() => {
module
.modules
.insert(*alias.unwrap(), value.cast::<Module>());
}
// Variables and modules with no alias are private and not exported
_ => (),
}
},
);
mods.into_iter().for_each(|(alias, m)| {
module.modules.insert(alias.to_string(), m);
});
module.merge(ast.lib());
@ -945,6 +993,10 @@ impl Module {
}
}
if self.indexed {
return;
}
let mut variables = Vec::new();
let mut functions = Vec::new();
@ -952,6 +1004,7 @@ impl Module {
self.all_variables = variables.into_iter().collect();
self.all_functions = functions.into_iter().collect();
self.indexed = true;
}
/// Does a type iterator exist in the module?
@ -962,6 +1015,7 @@ impl Module {
/// Set a type iterator into the module.
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) {
self.type_iterators.insert(typ, func);
self.indexed = false;
}
/// Get the specified type iterator.
@ -1055,6 +1109,8 @@ mod file {
/// Module resolution service that loads module script files from the file system.
///
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
///
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
/// allow specification of a base directory with module path used as a relative path offset
/// to the base directory. The script file is then forced to be in a specified extension
@ -1073,10 +1129,16 @@ mod file {
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
/// ```
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)]
#[derive(Debug)]
pub struct FileModuleResolver {
path: PathBuf,
extension: String,
#[cfg(not(feature = "sync"))]
cache: RefCell<HashMap<PathBuf, AST>>,
#[cfg(feature = "sync")]
cache: RwLock<HashMap<PathBuf, AST>>,
}
impl Default for FileModuleResolver {
@ -1129,6 +1191,7 @@ mod file {
Self {
path: path.into(),
extension: extension.into(),
cache: Default::default(),
}
}
@ -1173,12 +1236,45 @@ mod file {
file_path.push(path);
file_path.set_extension(&self.extension); // Force extension
// Compile it
let scope = Default::default();
// See if it is cached
let (module, ast) = {
#[cfg(not(feature = "sync"))]
let c = self.cache.borrow();
#[cfg(feature = "sync")]
let c = self.cache.read().unwrap();
match c.get(&file_path) {
Some(ast) => (
Module::eval_ast_as_new(scope, ast, engine)
.map_err(|err| err.new_position(pos))?,
None,
),
None => {
// Load the file and compile it if not found
let ast = engine
.compile_file(file_path)
.compile_file(file_path.clone())
.map_err(|err| err.new_position(pos))?;
Module::eval_ast_as_new(Scope::new(), &ast, engine).map_err(|err| err.new_position(pos))
(
Module::eval_ast_as_new(scope, &ast, engine)
.map_err(|err| err.new_position(pos))?,
Some(ast),
)
}
}
};
if let Some(ast) = ast {
// Put it into the cache
#[cfg(not(feature = "sync"))]
self.cache.borrow_mut().insert(file_path, ast);
#[cfg(feature = "sync")]
self.cache.write().unwrap().insert(file_path, ast);
}
Ok(module)
}
}
}
@ -1222,7 +1318,7 @@ mod stat {
/// let mut resolver = StaticModuleResolver::new();
///
/// let module = Module::new();
/// resolver.insert("hello".to_string(), module);
/// resolver.insert("hello", module);
///
/// let mut engine = Engine::new();
/// engine.set_module_resolver(Some(resolver));
@ -1232,17 +1328,43 @@ mod stat {
}
}
impl Deref for StaticModuleResolver {
type Target = HashMap<String, Module>;
fn deref(&self) -> &Self::Target {
&self.0
impl StaticModuleResolver {
/// Add a module keyed by its path.
pub fn insert<S: Into<String>>(&mut self, path: S, mut module: Module) {
module.index_all_sub_modules();
self.0.insert(path.into(), module);
}
/// Remove a module given its path.
pub fn remove(&mut self, path: &str) -> Option<Module> {
self.0.remove(path)
}
impl DerefMut for StaticModuleResolver {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
/// Does the path exist?
pub fn contains_path(&self, path: &str) -> bool {
self.0.contains_key(path)
}
/// Get an iterator of all the modules.
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.0.iter().map(|(k, v)| (k.as_str(), v))
}
/// Get a mutable iterator of all the modules.
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Module)> {
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
}
/// Get an iterator of all the module paths.
pub fn paths(&self) -> impl Iterator<Item = &str> {
self.0.keys().map(String::as_str)
}
/// Get an iterator of all the modules.
pub fn values(&self) -> impl Iterator<Item = &Module> {
self.0.values()
}
/// Get a mutable iterator of all the modules.
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Module> {
self.0.values_mut()
}
/// Remove all modules.
pub fn clear(&mut self) {
self.0.clear();
}
}

View File

@ -1,6 +1,6 @@
use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::{Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::engine::{Engine, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr, ReturnType, ScriptFnDef, Stmt, AST};
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
@ -122,6 +122,7 @@ fn call_fn_with_constant_arguments(
.engine
.call_fn_raw(
&mut Scope::new(),
&mut Imports::new(),
&mut Default::default(),
state.lib,
fn_name,

View File

@ -15,11 +15,11 @@ use crate::stdlib::{
boxed::Box,
char,
collections::HashMap,
format,
fmt, format,
iter::empty,
mem,
num::NonZeroUsize,
ops::{Add, Deref, DerefMut},
ops::Add,
string::{String, ToString},
vec,
vec::Vec,
@ -202,6 +202,25 @@ pub struct ScriptFnDef {
pub pos: Position,
}
impl fmt::Display for ScriptFnDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}({})",
match self.access {
FnAccess::Public => "",
FnAccess::Private => "private ",
},
self.name,
self.params
.iter()
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join(",")
)
}
}
/// `return`/`throw` statement.
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
pub enum ReturnType {
@ -214,9 +233,11 @@ pub enum ReturnType {
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
struct ParseState {
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(String, ScopeEntryType)>,
pub stack: Vec<(String, ScopeEntryType)>,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
pub modules: Vec<String>,
/// Maximum levels of expression nesting.
max_expr_depth: usize,
pub max_expr_depth: usize,
/// Maximum length of a string.
pub max_string_size: usize,
/// Maximum length of an array.
@ -245,15 +266,12 @@ impl ParseState {
/// The return value is the offset to be deducted from `Stack::len`,
/// i.e. the top element of the `ParseState` is offset 1.
/// Return zero when the variable name is not found in the `ParseState`.
pub fn find(&self, name: &str) -> Option<NonZeroUsize> {
pub fn find_var(&self, name: &str) -> Option<NonZeroUsize> {
self.stack
.iter()
.rev()
.enumerate()
.find(|(_, (n, typ))| match typ {
ScopeEntryType::Normal | ScopeEntryType::Constant => *n == name,
ScopeEntryType::Module => false,
})
.find(|(_, (n, _))| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
}
/// Find a module by name in the `ParseState`, searching in reverse.
@ -261,32 +279,15 @@ impl ParseState {
/// i.e. the top element of the `ParseState` is offset 1.
/// Return zero when the variable name is not found in the `ParseState`.
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
self.stack
self.modules
.iter()
.rev()
.enumerate()
.find(|(_, (n, typ))| match typ {
ScopeEntryType::Module => *n == name,
ScopeEntryType::Normal | ScopeEntryType::Constant => false,
})
.find(|(_, n)| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1))
}
}
impl Deref for ParseState {
type Target = Vec<(String, ScopeEntryType)>;
fn deref(&self) -> &Self::Target {
&self.stack
}
}
impl DerefMut for ParseState {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.stack
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
/// A type that encapsulates all the settings for a particular parsing function.
struct ParseSettings {
@ -1279,7 +1280,7 @@ fn parse_primary(
Token::CharConstant(c) => Expr::CharConstant(Box::new((c, settings.pos))),
Token::StringConst(s) => Expr::StringConstant(Box::new((s.into(), settings.pos))),
Token::Identifier(s) => {
let index = state.find(&s);
let index = state.find_var(&s);
Expr::Variable(Box::new(((s, settings.pos), None, 0, index)))
}
Token::LeftParen => parse_paren_expr(input, state, settings.level_up())?,
@ -1469,7 +1470,7 @@ fn make_assignment_stmt<'a>(
// var (indexed) = rhs
Expr::Variable(x) => {
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.len() - index.unwrap().get())].1 {
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
@ -1477,7 +1478,6 @@ fn make_assignment_stmt<'a>(
ScopeEntryType::Constant => {
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
}
ScopeEntryType::Module => unreachable!(),
}
}
// xxx[???] = rhs, xxx.??? = rhs
@ -1489,7 +1489,7 @@ fn make_assignment_stmt<'a>(
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
Expr::Variable(x) => {
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.len() - index.unwrap().get())].1 {
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
@ -1497,7 +1497,6 @@ fn make_assignment_stmt<'a>(
ScopeEntryType::Constant => {
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
}
ScopeEntryType::Module => unreachable!(),
}
}
// expr[???] = rhs, expr.??? = rhs
@ -2017,13 +2016,13 @@ fn parse_for(
ensure_not_statement_expr(input, "a boolean")?;
let expr = parse_expr(input, state, settings.level_up())?;
let prev_len = state.len();
state.push((name.clone(), ScopeEntryType::Normal));
let prev_len = state.stack.len();
state.stack.push((name.clone(), ScopeEntryType::Normal));
settings.is_breakable = true;
let body = parse_block(input, state, settings.level_up())?;
state.truncate(prev_len);
state.stack.truncate(prev_len);
Ok(Stmt::For(Box::new((name, expr, body))))
}
@ -2064,34 +2063,30 @@ fn parse_let(
match var_type {
// let name = expr
ScopeEntryType::Normal => {
state.push((name.clone(), ScopeEntryType::Normal));
state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), Some(init_value)))))
}
// const name = { expr:constant }
ScopeEntryType::Constant if init_value.is_constant() => {
state.push((name.clone(), ScopeEntryType::Constant));
state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), init_value))))
}
// const name = expr: error
ScopeEntryType::Constant => {
Err(PERR::ForbiddenConstantExpr(name).into_err(init_value.position()))
}
// Variable cannot be a module
ScopeEntryType::Module => unreachable!(),
}
} else {
// let name
match var_type {
ScopeEntryType::Normal => {
state.push((name.clone(), ScopeEntryType::Normal));
state.stack.push((name.clone(), ScopeEntryType::Normal));
Ok(Stmt::Let(Box::new(((name, pos), None))))
}
ScopeEntryType::Constant => {
state.push((name.clone(), ScopeEntryType::Constant));
state.stack.push((name.clone(), ScopeEntryType::Constant));
Ok(Stmt::Const(Box::new(((name, pos), Expr::Unit(pos)))))
}
// Variable cannot be a module
ScopeEntryType::Module => unreachable!(),
}
}
}
@ -2127,7 +2122,7 @@ fn parse_import(
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
};
state.push((name.clone(), ScopeEntryType::Module));
state.modules.push(name.clone());
Ok(Stmt::Import(Box::new((expr, (name, settings.pos)))))
}
@ -2214,7 +2209,7 @@ fn parse_block(
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut statements = StaticVec::new();
let prev_len = state.len();
let prev_len = state.stack.len();
while !match_token(input, Token::RightBrace)? {
// Parse statements inside the block
@ -2254,7 +2249,7 @@ fn parse_block(
}
}
state.truncate(prev_len);
state.stack.truncate(prev_len);
Ok(Stmt::Block(Box::new((statements, settings.pos))))
}
@ -2389,7 +2384,7 @@ fn parse_fn(
(Token::RightParen, _) => (),
_ => match input.next().unwrap() {
(Token::Identifier(s), pos) => {
state.push((s.clone(), ScopeEntryType::Normal));
state.stack.push((s.clone(), ScopeEntryType::Normal));
params.push((s, pos))
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),

View File

@ -1,7 +1,6 @@
//! Module that defines the `Scope` type representing a function call-stack scope.
use crate::any::{Dynamic, Union, Variant};
use crate::module::Module;
use crate::any::{Dynamic, Variant};
use crate::parser::{map_dynamic_to_expr, Expr};
use crate::token::Position;
@ -14,9 +13,6 @@ pub enum EntryType {
Normal,
/// Immutable constant value.
Constant,
/// Name of a module, allowing member access with the :: operator.
/// This is for internal use only.
Module,
}
/// An entry in the Scope.
@ -171,32 +167,6 @@ impl<'a> Scope<'a> {
self.push_dynamic_value(name, EntryType::Normal, value, false);
}
/// Add (push) a new module to the Scope.
///
/// Modules are used for accessing member variables, functions and plugins under a namespace.
#[cfg(not(feature = "no_module"))]
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
self.push_module_internal(name, value);
}
/// Add (push) a new module to the Scope.
///
/// Modules are used for accessing member variables, functions and plugins under a namespace.
pub(crate) fn push_module_internal<K: Into<Cow<'a, str>>>(
&mut self,
name: K,
mut value: Module,
) {
value.index_all_sub_modules();
self.push_dynamic_value(
name,
EntryType::Module,
Dynamic(Union::Module(Box::new(value))),
false,
);
}
/// Add (push) a new constant to the Scope.
///
/// Constants are immutable and cannot be assigned to. Their values never change.
@ -312,66 +282,26 @@ impl<'a> Scope<'a> {
self.0
.iter()
.rev() // Always search a Scope in reverse order
.any(|Entry { name: key, typ, .. }| match typ {
EntryType::Normal | EntryType::Constant => name == key,
EntryType::Module => false,
})
.any(|Entry { name: key, .. }| name == key)
}
/// Find an entry in the Scope, starting from the last.
///
/// modules are ignored.
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
self.0
.iter()
.enumerate()
.rev() // Always search a Scope in reverse order
.find_map(|(index, Entry { name: key, typ, .. })| match typ {
EntryType::Normal | EntryType::Constant => {
.find_map(|(index, Entry { name: key, typ, .. })| {
if name == key {
Some((index, *typ))
} else {
None
}
}
EntryType::Module => None,
})
}
/// Find a module in the Scope, starting from the last.
pub(crate) fn get_module_index(&self, name: &str) -> Option<usize> {
self.0
.iter()
.enumerate()
.rev() // Always search a Scope in reverse order
.find_map(|(index, Entry { name: key, typ, .. })| match typ {
EntryType::Module => {
if name == key {
Some(index)
} else {
None
}
}
EntryType::Normal | EntryType::Constant => None,
})
}
/// Find a module in the Scope, starting from the last entry.
#[cfg(not(feature = "no_module"))]
pub fn find_module(&mut self, name: &str) -> Option<&mut Module> {
self.find_module_internal(name)
}
/// Find a module in the Scope, starting from the last entry.
pub(crate) fn find_module_internal(&mut self, name: &str) -> Option<&mut Module> {
let index = self.get_module_index(name)?;
self.get_mut(index).0.downcast_mut::<Module>()
}
/// Get the value of an entry in the Scope, starting from the last.
///
/// modules are ignored.
///
/// # Examples
///
/// ```
@ -386,10 +316,7 @@ impl<'a> Scope<'a> {
self.0
.iter()
.rev()
.find(|Entry { name: key, typ, .. }| match typ {
EntryType::Normal | EntryType::Constant => name == key,
EntryType::Module => false,
})
.find(|Entry { name: key, .. }| name == key)
.and_then(|Entry { value, .. }| value.downcast_ref::<T>().cloned())
}
@ -421,8 +348,6 @@ impl<'a> Scope<'a> {
Some((index, EntryType::Normal)) => {
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
}
// modules cannot be modified
Some((_, EntryType::Module)) => unreachable!(),
}
}

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_module"))]
use rhai::{
module_resolvers, Engine, EvalAltResult, Module, ParseError, ParseErrorType, Scope, INT,
module_resolvers::StaticModuleResolver, Engine, EvalAltResult, Module, ParseError,
ParseErrorType, Scope, INT,
};
#[test]
@ -37,22 +38,25 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
let engine = Engine::new();
let mut resolver = StaticModuleResolver::new();
resolver.insert("question", module);
let mut engine = Engine::new();
engine.set_module_resolver(Some(resolver));
let mut scope = Scope::new();
scope.push_module("question", module);
assert_eq!(
engine.eval_expression_with_scope::<INT>(
engine.eval_with_scope::<INT>(
&mut scope,
"question::life::universe::answer + 1"
r#"import "question" as q; q::life::universe::answer + 1"#
)?,
42
);
assert_eq!(
engine.eval_expression_with_scope::<INT>(
engine.eval_with_scope::<INT>(
&mut scope,
"question::life::universe::inc(question::life::universe::answer)"
r#"import "question" as q; q::life::universe::inc(q::life::universe::answer)"#
)?,
42
);
@ -62,7 +66,7 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
#[test]
fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
let mut resolver = module_resolvers::StaticModuleResolver::new();
let mut resolver = StaticModuleResolver::new();
let mut module = Module::new();
module.set_var("answer", 42 as INT);
@ -70,7 +74,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
Ok(x + y + z + w)
});
resolver.insert("hello".to_string(), module);
resolver.insert("hello", module);
let mut engine = Engine::new();
engine.set_module_resolver(Some(resolver));
@ -155,12 +159,10 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let mut resolver = rhai::module_resolvers::StaticModuleResolver::new();
let mut resolver1 = StaticModuleResolver::new();
let mut sub_module = Module::new();
sub_module.set_var("foo", true);
resolver.insert("another module".to_string(), sub_module);
engine.set_module_resolver(Some(resolver));
resolver1.insert("another module", sub_module);
let ast = engine.compile(
r#"
@ -190,44 +192,48 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
export
x as abc,
foo,
hello,
extra as foobar;
hello;
"#,
)?;
engine.set_module_resolver(Some(resolver1));
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
let mut resolver2 = StaticModuleResolver::new();
resolver2.insert("testing", module);
engine.set_module_resolver(Some(resolver2));
let mut scope = Scope::new();
scope.push_module("testing", module);
assert_eq!(
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::abc")?,
engine.eval_with_scope::<INT>(&mut scope, r#"import "testing" as ttt; ttt::abc"#)?,
123
);
assert_eq!(
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::foo")?,
engine.eval_with_scope::<INT>(&mut scope, r#"import "testing" as ttt; ttt::foo"#)?,
42
);
assert!(engine.eval_expression_with_scope::<bool>(&mut scope, "testing::foobar::foo")?);
assert!(engine
.eval_with_scope::<bool>(&mut scope, r#"import "testing" as ttt; ttt::extra::foo"#)?);
assert_eq!(
engine.eval_expression_with_scope::<String>(&mut scope, "testing::hello")?,
engine.eval_with_scope::<String>(&mut scope, r#"import "testing" as ttt; ttt::hello"#)?,
"hello, 42 worlds!"
);
assert_eq!(
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::calc(999)")?,
engine.eval_with_scope::<INT>(&mut scope, r#"import "testing" as ttt; ttt::calc(999)"#)?,
1000
);
assert_eq!(
engine.eval_expression_with_scope::<INT>(
engine.eval_with_scope::<INT>(
&mut scope,
"testing::add_len(testing::foo, testing::hello)"
r#"import "testing" as ttt; ttt::add_len(ttt::foo, ttt::hello)"#
)?,
59
);
assert!(matches!(
*engine
.eval_expression_with_scope::<()>(&mut scope, "testing::hidden()")
.eval_with_scope::<()>(&mut scope, r#"import "testing" as ttt; ttt::hidden()"#)
.expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "hidden"
));

View File

@ -38,19 +38,13 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } }")?;
assert_eq!(
format!("{:?}", ast),
"AST([], <module vars={}, functions=0>)"
);
assert!(format!("{:?}", ast).starts_with("AST([], Module("));
engine.set_optimization_level(OptimizationLevel::Full);
let ast = engine.compile("if 1 == 2 { 42 }")?;
assert_eq!(
format!("{:?}", ast),
"AST([], <module vars={}, functions=0>)"
);
assert!(format!("{:?}", ast).starts_with("AST([], Module("));
Ok(())
}