Remove modules from Scope and use separate stack.
This commit is contained in:
parent
f3bde843cb
commit
aac04a5e53
@ -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
|
||||
------------
|
||||
|
11
src/any.rs
11
src/any.rs
@ -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>(),
|
||||
|
17
src/api.rs
17
src/api.rs
@ -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,
|
||||
|
251
src/engine.rs
251
src/engine.rs
@ -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())
|
||||
|
@ -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 {
|
||||
|
196
src/module.rs
196
src/module.rs
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)),
|
||||
|
83
src/scope.rs
83
src/scope.rs
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
));
|
||||
|
@ -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(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user