Build Module type plus engine hooks.
This commit is contained in:
parent
c03b162b7e
commit
38e717a838
@ -68,9 +68,9 @@ fn main() {
|
|||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
let mut main_ast = AST::new();
|
let mut main_ast: AST = Default::default();
|
||||||
let mut ast_u = AST::new();
|
let mut ast_u: AST = Default::default();
|
||||||
let mut ast = AST::new();
|
let mut ast: AST = Default::default();
|
||||||
|
|
||||||
println!("Rhai REPL tool");
|
println!("Rhai REPL tool");
|
||||||
println!("==============");
|
println!("==============");
|
||||||
|
@ -205,7 +205,7 @@ impl fmt::Display for Dynamic {
|
|||||||
Union::Float(value) => write!(f, "{}", value),
|
Union::Float(value) => write!(f, "{}", value),
|
||||||
Union::Array(value) => write!(f, "{:?}", value),
|
Union::Array(value) => write!(f, "{:?}", value),
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
Union::Module(value) => write!(f, "#{:?}", value),
|
Union::Module(value) => write!(f, "{:?}", value),
|
||||||
Union::Variant(_) => write!(f, "?"),
|
Union::Variant(_) => write!(f, "?"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,7 +223,7 @@ impl fmt::Debug for Dynamic {
|
|||||||
Union::Float(value) => write!(f, "{:?}", value),
|
Union::Float(value) => write!(f, "{:?}", value),
|
||||||
Union::Array(value) => write!(f, "{:?}", value),
|
Union::Array(value) => write!(f, "{:?}", value),
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
Union::Module(value) => write!(f, "#{:?}", value),
|
Union::Module(value) => write!(f, "{:?}", value),
|
||||||
Union::Variant(_) => write!(f, "<dynamic>"),
|
Union::Variant(_) => write!(f, "<dynamic>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
src/api.rs
19
src/api.rs
@ -15,6 +15,7 @@ use crate::stdlib::{
|
|||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
mem,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -797,10 +798,10 @@ impl Engine {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
|
|
||||||
ast.0
|
ast.statements()
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, &mut state, ast.1.as_ref(), stmt, 0)
|
self.eval_stmt(scope, &mut state, ast.fn_lib(), stmt, 0)
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::Return(out, _) => Ok(out),
|
EvalAltResult::Return(out, _) => Ok(out),
|
||||||
@ -862,10 +863,10 @@ impl Engine {
|
|||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
|
|
||||||
ast.0
|
ast.statements()
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, &mut state, ast.1.as_ref(), stmt, 0)
|
self.eval_stmt(scope, &mut state, ast.fn_lib(), stmt, 0)
|
||||||
})
|
})
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -921,7 +922,7 @@ impl Engine {
|
|||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let mut arg_values = args.into_vec();
|
let mut arg_values = args.into_vec();
|
||||||
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
let mut args: Vec<_> = arg_values.iter_mut().collect();
|
||||||
let fn_lib = ast.1.as_ref();
|
let fn_lib = ast.fn_lib();
|
||||||
let pos = Position::none();
|
let pos = Position::none();
|
||||||
|
|
||||||
let fn_def = fn_lib
|
let fn_def = fn_lib
|
||||||
@ -955,15 +956,17 @@ impl Engine {
|
|||||||
pub fn optimize_ast(
|
pub fn optimize_ast(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
ast: AST,
|
mut ast: AST,
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> AST {
|
) -> AST {
|
||||||
let fn_lib = ast
|
let fn_lib = ast
|
||||||
.1
|
.fn_lib()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, fn_def)| fn_def.as_ref().clone())
|
.map(|(_, fn_def)| fn_def.as_ref().clone())
|
||||||
.collect();
|
.collect();
|
||||||
optimize_into_ast(self, scope, ast.0, fn_lib, optimization_level)
|
|
||||||
|
let stmt = mem::take(ast.statements_mut());
|
||||||
|
optimize_into_ast(self, scope, stmt, fn_lib, optimization_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override default action of `print` (print to stdout using `println!`)
|
/// Override default action of `print` (print to stdout using `println!`)
|
||||||
|
@ -6,7 +6,7 @@ use crate::error::ParseErrorType;
|
|||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage};
|
use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage};
|
||||||
use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt};
|
use crate::parser::{Expr, FnDef, ModuleRef, ReturnType, Stmt, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
@ -159,7 +159,7 @@ impl State {
|
|||||||
/// and number of parameters are considered equivalent.
|
/// and number of parameters are considered equivalent.
|
||||||
///
|
///
|
||||||
/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_def`.
|
/// The key of the `HashMap` is a `u64` hash calculated by the function `calc_fn_def`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct FunctionsLib(
|
pub struct FunctionsLib(
|
||||||
#[cfg(feature = "sync")] HashMap<u64, Arc<FnDef>>,
|
#[cfg(feature = "sync")] HashMap<u64, Arc<FnDef>>,
|
||||||
#[cfg(not(feature = "sync"))] HashMap<u64, Rc<FnDef>>,
|
#[cfg(not(feature = "sync"))] HashMap<u64, Rc<FnDef>>,
|
||||||
@ -168,7 +168,7 @@ pub struct FunctionsLib(
|
|||||||
impl FunctionsLib {
|
impl FunctionsLib {
|
||||||
/// Create a new `FunctionsLib`.
|
/// Create a new `FunctionsLib`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
FunctionsLib(HashMap::new())
|
Default::default()
|
||||||
}
|
}
|
||||||
/// Create a new `FunctionsLib` from a collection of `FnDef`.
|
/// Create a new `FunctionsLib` from a collection of `FnDef`.
|
||||||
pub fn from_vec(vec: Vec<FnDef>) -> Self {
|
pub fn from_vec(vec: Vec<FnDef>) -> Self {
|
||||||
@ -289,10 +289,10 @@ impl Default for Engine {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// Create the new scripting Engine
|
// Create the new scripting Engine
|
||||||
let mut engine = Self {
|
let mut engine = Self {
|
||||||
packages: Vec::new(),
|
packages: Default::default(),
|
||||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT),
|
functions: HashMap::with_capacity(FUNCTIONS_COUNT),
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: Default::default(),
|
||||||
type_names: HashMap::new(),
|
type_names: Default::default(),
|
||||||
|
|
||||||
// default print/debug implementations
|
// default print/debug implementations
|
||||||
print: Box::new(default_print),
|
print: Box::new(default_print),
|
||||||
@ -380,10 +380,9 @@ fn search_scope<'a>(
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
if let Some(modules) = modules {
|
if let Some(modules) = modules {
|
||||||
let mut drain = modules.iter();
|
let (id, root_pos) = modules.get(0); // First module
|
||||||
let (id, root_pos) = drain.next().unwrap(); // First module
|
|
||||||
|
|
||||||
let mut module = if let Some(index) = index {
|
let module = if let Some(index) = index {
|
||||||
scope
|
scope
|
||||||
.get_mut(scope.len() - index.get())
|
.get_mut(scope.len() - index.get())
|
||||||
.0
|
.0
|
||||||
@ -395,26 +394,11 @@ fn search_scope<'a>(
|
|||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?
|
||||||
};
|
};
|
||||||
|
|
||||||
for (id, id_pos) in drain {
|
Ok((
|
||||||
module = module
|
module.get_qualified_variable_mut(name, modules.as_ref(), pos)?,
|
||||||
.get_mut(id)
|
// Module variables are constant
|
||||||
.and_then(|v| v.downcast_mut::<Module>())
|
ScopeEntryType::Constant,
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?;
|
))
|
||||||
}
|
|
||||||
|
|
||||||
let result = module
|
|
||||||
.get_mut(name)
|
|
||||||
.map(|v| (v, ScopeEntryType::Constant))
|
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?;
|
|
||||||
|
|
||||||
if result.0.is::<Module>() {
|
|
||||||
Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
|
||||||
name.into(),
|
|
||||||
pos,
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let index = if let Some(index) = index {
|
let index = if let Some(index) = index {
|
||||||
scope.len() - index.get()
|
scope.len() - index.get()
|
||||||
@ -439,10 +423,10 @@ impl Engine {
|
|||||||
/// Use the `load_package` method to load packages of functions.
|
/// Use the `load_package` method to load packages of functions.
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
Self {
|
Self {
|
||||||
packages: Vec::new(),
|
packages: Default::default(),
|
||||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: Default::default(),
|
||||||
type_names: HashMap::new(),
|
type_names: Default::default(),
|
||||||
print: Box::new(|_| {}),
|
print: Box::new(|_| {}),
|
||||||
debug: Box::new(|_| {}),
|
debug: Box::new(|_| {}),
|
||||||
|
|
||||||
@ -595,8 +579,7 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(
|
.zip(
|
||||||
// Actually consume the arguments instead of cloning them
|
// Actually consume the arguments instead of cloning them
|
||||||
args.into_iter()
|
args.into_iter().map(|v| mem::take(*v)),
|
||||||
.map(|v| mem::replace(*v, Default::default())),
|
|
||||||
)
|
)
|
||||||
.map(|(name, value)| (name.clone(), ScopeEntryType::Normal, value)),
|
.map(|(name, value)| (name.clone(), ScopeEntryType::Normal, value)),
|
||||||
);
|
);
|
||||||
@ -626,8 +609,7 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(
|
.zip(
|
||||||
// Actually consume the arguments instead of cloning them
|
// Actually consume the arguments instead of cloning them
|
||||||
args.into_iter()
|
args.into_iter().map(|v| mem::take(*v)),
|
||||||
.map(|v| mem::replace(*v, Default::default())),
|
|
||||||
)
|
)
|
||||||
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
||||||
);
|
);
|
||||||
@ -715,20 +697,14 @@ impl Engine {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// If new functions are defined within the eval string, it is an error
|
// If new functions are defined within the eval string, it is an error
|
||||||
if ast.1.len() > 0 {
|
if ast.fn_lib().len() > 0 {
|
||||||
return Err(Box::new(EvalAltResult::ErrorParsing(
|
return Err(Box::new(EvalAltResult::ErrorParsing(
|
||||||
ParseErrorType::WrongFnDefinition.into_err(pos),
|
ParseErrorType::WrongFnDefinition.into_err(pos),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
let statements = mem::take(ast.statements_mut());
|
||||||
{
|
ast = AST::new(statements, fn_lib.clone());
|
||||||
ast.1 = Arc::new(fn_lib.clone());
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
{
|
|
||||||
ast.1 = Rc::new(fn_lib.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the AST
|
// Evaluate the AST
|
||||||
self.eval_ast_with_scope_raw(scope, &ast)
|
self.eval_ast_with_scope_raw(scope, &ast)
|
||||||
@ -1496,8 +1472,8 @@ impl Engine {
|
|||||||
.try_cast::<String>()
|
.try_cast::<String>()
|
||||||
{
|
{
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
module.insert("kitty".to_string(), "foo".to_string().into());
|
module.set_variable("kitty", "foo".to_string());
|
||||||
module.insert("path".to_string(), path.into());
|
module.set_variable("path", path);
|
||||||
|
|
||||||
// TODO - avoid copying module name in inner block?
|
// TODO - avoid copying module name in inner block?
|
||||||
let mod_name = name.as_ref().clone();
|
let mod_name = name.as_ref().clone();
|
||||||
|
@ -128,7 +128,7 @@ pub fn by_ref<T: Clone + 'static>(data: &mut Dynamic) -> &mut T {
|
|||||||
pub fn by_value<T: Clone + 'static>(data: &mut Dynamic) -> T {
|
pub fn by_value<T: Clone + 'static>(data: &mut Dynamic) -> T {
|
||||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||||
mem::replace(data, Default::default()).cast::<T>()
|
mem::take(data).cast::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This macro counts the number of arguments via recursion.
|
/// This macro counts the number of arguments via recursion.
|
||||||
|
@ -109,5 +109,8 @@ pub use engine::Map;
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub use parser::FLOAT;
|
pub use parser::FLOAT;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub use module::Module;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
pub use optimize::OptimizationLevel;
|
pub use optimize::OptimizationLevel;
|
||||||
|
137
src/module.rs
137
src/module.rs
@ -1,35 +1,134 @@
|
|||||||
//! Module defining external-loaded modules for Rhai.
|
//! Module defining external-loaded modules for Rhai.
|
||||||
|
|
||||||
use crate::any::Dynamic;
|
use crate::any::{Dynamic, Variant};
|
||||||
|
use crate::engine::{FnAny, FunctionsLib};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{collections::HashMap, fmt, string::String};
|
||||||
collections::HashMap,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
string::String,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An imported module.
|
/// An imported module, which may contain variables, sub-modules,
|
||||||
|
/// external Rust functions, and script-defined functions.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_module` feature.
|
/// Not available under the `no_module` feature.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Default)]
|
||||||
pub struct Module(HashMap<String, Dynamic>);
|
pub struct Module {
|
||||||
|
/// Sub-modules.
|
||||||
|
modules: HashMap<String, Module>,
|
||||||
|
/// Module variables, including sub-modules.
|
||||||
|
variables: HashMap<String, Dynamic>,
|
||||||
|
/// External Rust functions.
|
||||||
|
functions: HashMap<u64, Box<FnAny>>,
|
||||||
|
/// Script-defined functions.
|
||||||
|
lib: FunctionsLib,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Module {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"<module {:?}, functions={}, lib={}>",
|
||||||
|
self.variables,
|
||||||
|
self.functions.len(),
|
||||||
|
self.lib.len()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Module {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
// `Module` implements `Clone` so it can fit inside a `Dynamic`
|
||||||
|
// but we should never actually clone it.
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Create a new module.
|
/// Create a new module.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(HashMap::new())
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Module {
|
/// Does a variable exist in the module?
|
||||||
type Target = HashMap<String, Dynamic>;
|
pub fn contains_variable(&self, name: &str) -> bool {
|
||||||
fn deref(&self) -> &Self::Target {
|
self.variables.contains_key(name)
|
||||||
&self.0
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Module {
|
/// Get the value of a module variable.
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
pub fn get_variable_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
&mut self.0
|
self.get_variable(name).and_then(|v| v.try_cast::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a module variable.
|
||||||
|
pub fn get_variable(&self, name: &str) -> Option<Dynamic> {
|
||||||
|
self.variables.get(name).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to a module variable.
|
||||||
|
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||||
|
self.variables.get_mut(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a variable into the module.
|
||||||
|
///
|
||||||
|
/// If there is an existing variable of the same name, it is replaced.
|
||||||
|
pub fn set_variable<K: Into<String>, T: Into<Dynamic>>(&mut self, name: K, value: T) {
|
||||||
|
self.variables.insert(name.into(), value.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to a modules-qualified variable.
|
||||||
|
pub(crate) fn get_qualified_variable_mut(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
modules: &StaticVec<(String, Position)>,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(self
|
||||||
|
.get_qualified_module_mut(modules)?
|
||||||
|
.get_variable_mut(name)
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does a sub-module exist in the module?
|
||||||
|
pub fn contains_sub_module(&self, name: &str) -> bool {
|
||||||
|
self.modules.contains_key(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a sub-module.
|
||||||
|
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
|
||||||
|
self.modules.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to a sub-module.
|
||||||
|
pub fn get_sub_module_mut(&mut self, name: &str) -> Option<&mut Module> {
|
||||||
|
self.modules.get_mut(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a sub-module into the module.
|
||||||
|
///
|
||||||
|
/// If there is an existing sub-module of the same name, it is replaced.
|
||||||
|
pub fn set_sub_module<K: Into<String>>(&mut self, name: K, sub_module: Module) {
|
||||||
|
self.modules.insert(name.into(), sub_module.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to a modules chain.
|
||||||
|
/// The first module is always skipped and assumed to be the same as `self`.
|
||||||
|
pub(crate) fn get_qualified_module_mut(
|
||||||
|
&mut self,
|
||||||
|
modules: &StaticVec<(String, Position)>,
|
||||||
|
) -> Result<&mut Module, Box<EvalAltResult>> {
|
||||||
|
let mut drain = modules.iter();
|
||||||
|
drain.next().unwrap(); // Skip first module
|
||||||
|
|
||||||
|
let mut module = self;
|
||||||
|
|
||||||
|
for (id, id_pos) in drain {
|
||||||
|
module = module
|
||||||
|
.get_sub_module_mut(id)
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,7 @@ use crate::token::Position;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
rc::Rc,
|
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
sync::Arc,
|
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -747,16 +745,13 @@ pub fn optimize_into_ast(
|
|||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
|
|
||||||
AST(
|
AST::new(
|
||||||
match level {
|
match level {
|
||||||
OptimizationLevel::None => statements,
|
OptimizationLevel::None => statements,
|
||||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||||
optimize(statements, engine, &scope, &fn_lib, level)
|
optimize(statements, engine, &scope, &fn_lib, level)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
#[cfg(feature = "sync")]
|
lib,
|
||||||
Arc::new(lib),
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
Rc::new(lib),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ pub trait Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Type to store all functions in the package.
|
/// Type to store all functions in the package.
|
||||||
|
#[derive(Default)]
|
||||||
pub struct PackageStore {
|
pub struct PackageStore {
|
||||||
/// All functions, keyed by a hash created from the function name and parameter types.
|
/// All functions, keyed by a hash created from the function name and parameter types.
|
||||||
pub functions: HashMap<u64, Box<FnAny>>,
|
pub functions: HashMap<u64, Box<FnAny>>,
|
||||||
@ -58,10 +59,7 @@ pub struct PackageStore {
|
|||||||
impl PackageStore {
|
impl PackageStore {
|
||||||
/// Create a new `PackageStore`.
|
/// Create a new `PackageStore`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Default::default()
|
||||||
functions: HashMap::new(),
|
|
||||||
type_iterators: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,17 +51,44 @@ pub type ModuleRef = Option<Box<StaticVec<(String, Position)>>>;
|
|||||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||||
///
|
///
|
||||||
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct AST(
|
pub struct AST(
|
||||||
pub(crate) Vec<Stmt>,
|
/// Global statements.
|
||||||
#[cfg(feature = "sync")] pub(crate) Arc<FunctionsLib>,
|
Vec<Stmt>,
|
||||||
#[cfg(not(feature = "sync"))] pub(crate) Rc<FunctionsLib>,
|
/// Script-defined functions, wrapped in an `Arc` for shared access.
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
Arc<FunctionsLib>,
|
||||||
|
/// Script-defined functions, wrapped in an `Rc` for shared access.
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
Rc<FunctionsLib>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl AST {
|
impl AST {
|
||||||
/// Create a new `AST`.
|
/// Create a new `AST`.
|
||||||
pub fn new() -> Self {
|
pub fn new(statements: Vec<Stmt>, fn_lib: FunctionsLib) -> Self {
|
||||||
Default::default()
|
#[cfg(feature = "sync")]
|
||||||
|
{
|
||||||
|
Self(statements, Arc::new(fn_lib))
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
{
|
||||||
|
Self(statements, Rc::new(fn_lib))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the statements.
|
||||||
|
pub(crate) fn statements(&self) -> &Vec<Stmt> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the statements.
|
||||||
|
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the script-defined functions.
|
||||||
|
pub(crate) fn fn_lib(&self) -> &FunctionsLib {
|
||||||
|
self.1.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
||||||
@ -148,18 +175,6 @@ impl AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AST {
|
|
||||||
fn default() -> Self {
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
{
|
|
||||||
Self(vec![], Arc::new(FunctionsLib::new()))
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
{
|
|
||||||
Self(vec![], Rc::new(FunctionsLib::new()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Add<Self> for &AST {
|
impl Add<Self> for &AST {
|
||||||
type Output = AST;
|
type Output = AST;
|
||||||
|
|
||||||
@ -191,13 +206,13 @@ pub enum ReturnType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// A type that encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct Stack(Vec<(String, ScopeEntryType)>);
|
struct Stack(Vec<(String, ScopeEntryType)>);
|
||||||
|
|
||||||
impl Stack {
|
impl Stack {
|
||||||
/// Create a new `Stack`.
|
/// Create a new `Stack`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(Vec::new())
|
Default::default()
|
||||||
}
|
}
|
||||||
/// Find a variable by name in the `Stack`, searching in reverse.
|
/// Find a variable by name in the `Stack`, searching in reverse.
|
||||||
/// The return value is the offset to be deducted from `Stack::len`,
|
/// The return value is the offset to be deducted from `Stack::len`,
|
||||||
|
12
src/scope.rs
12
src/scope.rs
@ -60,7 +60,7 @@ pub struct Entry<'a> {
|
|||||||
/// allowing for automatic _shadowing_.
|
/// allowing for automatic _shadowing_.
|
||||||
///
|
///
|
||||||
/// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Scope<'a>(Vec<Entry<'a>>);
|
pub struct Scope<'a>(Vec<Entry<'a>>);
|
||||||
|
|
||||||
impl<'a> Scope<'a> {
|
impl<'a> Scope<'a> {
|
||||||
@ -77,7 +77,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(Vec::new())
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Empty the Scope.
|
/// Empty the Scope.
|
||||||
@ -177,7 +177,7 @@ impl<'a> Scope<'a> {
|
|||||||
name,
|
name,
|
||||||
EntryType::Module,
|
EntryType::Module,
|
||||||
Dynamic(Union::Module(Box::new(value))),
|
Dynamic(Union::Module(Box::new(value))),
|
||||||
true,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,12 +422,6 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Scope<'_> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Scope::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
||||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||||
self.0
|
self.0
|
||||||
|
@ -122,7 +122,7 @@ impl fmt::Display for Position {
|
|||||||
|
|
||||||
impl fmt::Debug for Position {
|
impl fmt::Debug for Position {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "({}:{})", self.line, self.pos)
|
write!(f, "{}:{}", self.line, self.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
src/utils.rs
24
src/utils.rs
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
mem,
|
mem,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -44,7 +45,7 @@ pub(crate) fn calc_fn_def(fn_name: &str, num_params: usize) -> u64 {
|
|||||||
///
|
///
|
||||||
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
||||||
/// This simplified implementation here is to avoid pulling in another crate.
|
/// This simplified implementation here is to avoid pulling in another crate.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Default)]
|
||||||
pub struct StaticVec<T: Default + Clone> {
|
pub struct StaticVec<T: Default + Clone> {
|
||||||
/// Total number of values held.
|
/// Total number of values held.
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -57,16 +58,7 @@ pub struct StaticVec<T: Default + Clone> {
|
|||||||
impl<T: Default + Clone> StaticVec<T> {
|
impl<T: Default + Clone> StaticVec<T> {
|
||||||
/// Create a new `StaticVec`.
|
/// Create a new `StaticVec`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Default::default()
|
||||||
len: 0,
|
|
||||||
list: [
|
|
||||||
Default::default(),
|
|
||||||
Default::default(),
|
|
||||||
Default::default(),
|
|
||||||
Default::default(),
|
|
||||||
],
|
|
||||||
more: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Push a new value to the end of this `StaticVec`.
|
/// Push a new value to the end of this `StaticVec`.
|
||||||
pub fn push<X: Into<T>>(&mut self, value: X) {
|
pub fn push<X: Into<T>>(&mut self, value: X) {
|
||||||
@ -86,7 +78,7 @@ impl<T: Default + Clone> StaticVec<T> {
|
|||||||
let result = if self.len <= 0 {
|
let result = if self.len <= 0 {
|
||||||
panic!("nothing to pop!")
|
panic!("nothing to pop!")
|
||||||
} else if self.len <= self.list.len() {
|
} else if self.len <= self.list.len() {
|
||||||
mem::replace(self.list.get_mut(self.len - 1).unwrap(), Default::default())
|
mem::take(self.list.get_mut(self.len - 1).unwrap())
|
||||||
} else {
|
} else {
|
||||||
self.more.pop().unwrap()
|
self.more.pop().unwrap()
|
||||||
};
|
};
|
||||||
@ -126,3 +118,11 @@ impl<T: Default + Clone> StaticVec<T> {
|
|||||||
self.list[..num].iter().chain(self.more.iter())
|
self.list[..num].iter().chain(self.more.iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Clone + fmt::Debug> fmt::Debug for StaticVec<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "[ ")?;
|
||||||
|
self.iter().try_for_each(|v| write!(f, "{:?}, ", v))?;
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1 +1,33 @@
|
|||||||
use rhai::{EvalAltResult, Scope, INT};
|
#![cfg(not(feature = "no_module"))]
|
||||||
|
use rhai::{EvalAltResult, Module, Scope, INT};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module() {
|
||||||
|
let mut module = Module::new();
|
||||||
|
module.set_variable("kitty", 42 as INT);
|
||||||
|
|
||||||
|
assert!(module.contains_variable("kitty"));
|
||||||
|
assert_eq!(module.get_variable_value::<INT>("kitty").unwrap(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sub_module() {
|
||||||
|
let mut module = Module::new();
|
||||||
|
|
||||||
|
let mut sub_module = Module::new();
|
||||||
|
|
||||||
|
let mut sub_module2 = Module::new();
|
||||||
|
sub_module2.set_variable("kitty", 42 as INT);
|
||||||
|
|
||||||
|
sub_module.set_sub_module("world", sub_module2);
|
||||||
|
module.set_sub_module("hello", sub_module);
|
||||||
|
|
||||||
|
assert!(module.contains_sub_module("hello"));
|
||||||
|
let m = module.get_sub_module("hello").unwrap();
|
||||||
|
|
||||||
|
assert!(m.contains_sub_module("world"));
|
||||||
|
let m2 = m.get_sub_module("world").unwrap();
|
||||||
|
|
||||||
|
assert!(m2.contains_variable("kitty"));
|
||||||
|
assert_eq!(m2.get_variable_value::<INT>("kitty").unwrap(), 42);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user