Build Module type plus engine hooks.

This commit is contained in:
Stephen Chung 2020-05-05 15:00:10 +08:00
parent c03b162b7e
commit 38e717a838
14 changed files with 249 additions and 134 deletions

View File

@ -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!("==============");

View File

@ -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>"),
} }
} }

View File

@ -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!`)

View File

@ -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();

View File

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

View File

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

View File

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

View File

@ -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),
) )
} }

View File

@ -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(),
}
} }
} }

View File

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

View File

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

View File

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

View File

@ -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, "]")
}
}

View File

@ -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);
}