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 input = String::new();
|
||||
let mut main_ast = AST::new();
|
||||
let mut ast_u = AST::new();
|
||||
let mut ast = AST::new();
|
||||
let mut main_ast: AST = Default::default();
|
||||
let mut ast_u: AST = Default::default();
|
||||
let mut ast: AST = Default::default();
|
||||
|
||||
println!("Rhai REPL tool");
|
||||
println!("==============");
|
||||
|
@ -205,7 +205,7 @@ impl fmt::Display for Dynamic {
|
||||
Union::Float(value) => write!(f, "{}", value),
|
||||
Union::Array(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, "?"),
|
||||
}
|
||||
}
|
||||
@ -223,7 +223,7 @@ impl fmt::Debug for Dynamic {
|
||||
Union::Float(value) => write!(f, "{:?}", value),
|
||||
Union::Array(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>"),
|
||||
}
|
||||
}
|
||||
|
19
src/api.rs
19
src/api.rs
@ -15,6 +15,7 @@ use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
mem,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
@ -797,10 +798,10 @@ impl Engine {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
let mut state = State::new();
|
||||
|
||||
ast.0
|
||||
ast.statements()
|
||||
.iter()
|
||||
.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 {
|
||||
EvalAltResult::Return(out, _) => Ok(out),
|
||||
@ -862,10 +863,10 @@ impl Engine {
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let mut state = State::new();
|
||||
|
||||
ast.0
|
||||
ast.statements()
|
||||
.iter()
|
||||
.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(
|
||||
|err| match *err {
|
||||
@ -921,7 +922,7 @@ impl Engine {
|
||||
) -> Result<T, Box<EvalAltResult>> {
|
||||
let mut arg_values = args.into_vec();
|
||||
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 fn_def = fn_lib
|
||||
@ -955,15 +956,17 @@ impl Engine {
|
||||
pub fn optimize_ast(
|
||||
&self,
|
||||
scope: &Scope,
|
||||
ast: AST,
|
||||
mut ast: AST,
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> AST {
|
||||
let fn_lib = ast
|
||||
.1
|
||||
.fn_lib()
|
||||
.iter()
|
||||
.map(|(_, fn_def)| fn_def.as_ref().clone())
|
||||
.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!`)
|
||||
|
@ -6,7 +6,7 @@ use crate::error::ParseErrorType;
|
||||
use crate::module::Module;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
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::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::Position;
|
||||
@ -159,7 +159,7 @@ impl State {
|
||||
/// and number of parameters are considered equivalent.
|
||||
///
|
||||
/// 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(
|
||||
#[cfg(feature = "sync")] HashMap<u64, Arc<FnDef>>,
|
||||
#[cfg(not(feature = "sync"))] HashMap<u64, Rc<FnDef>>,
|
||||
@ -168,7 +168,7 @@ pub struct FunctionsLib(
|
||||
impl FunctionsLib {
|
||||
/// Create a new `FunctionsLib`.
|
||||
pub fn new() -> Self {
|
||||
FunctionsLib(HashMap::new())
|
||||
Default::default()
|
||||
}
|
||||
/// Create a new `FunctionsLib` from a collection of `FnDef`.
|
||||
pub fn from_vec(vec: Vec<FnDef>) -> Self {
|
||||
@ -289,10 +289,10 @@ impl Default for Engine {
|
||||
fn default() -> Self {
|
||||
// Create the new scripting Engine
|
||||
let mut engine = Self {
|
||||
packages: Vec::new(),
|
||||
packages: Default::default(),
|
||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT),
|
||||
type_iterators: HashMap::new(),
|
||||
type_names: HashMap::new(),
|
||||
type_iterators: Default::default(),
|
||||
type_names: Default::default(),
|
||||
|
||||
// default print/debug implementations
|
||||
print: Box::new(default_print),
|
||||
@ -380,10 +380,9 @@ fn search_scope<'a>(
|
||||
pos: Position,
|
||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||
if let Some(modules) = modules {
|
||||
let mut drain = modules.iter();
|
||||
let (id, root_pos) = drain.next().unwrap(); // First module
|
||||
let (id, root_pos) = modules.get(0); // First module
|
||||
|
||||
let mut module = if let Some(index) = index {
|
||||
let module = if let Some(index) = index {
|
||||
scope
|
||||
.get_mut(scope.len() - index.get())
|
||||
.0
|
||||
@ -395,26 +394,11 @@ fn search_scope<'a>(
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?
|
||||
};
|
||||
|
||||
for (id, id_pos) in drain {
|
||||
module = module
|
||||
.get_mut(id)
|
||||
.and_then(|v| v.downcast_mut::<Module>())
|
||||
.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)
|
||||
}
|
||||
Ok((
|
||||
module.get_qualified_variable_mut(name, modules.as_ref(), pos)?,
|
||||
// Module variables are constant
|
||||
ScopeEntryType::Constant,
|
||||
))
|
||||
} else {
|
||||
let index = if let Some(index) = index {
|
||||
scope.len() - index.get()
|
||||
@ -439,10 +423,10 @@ impl Engine {
|
||||
/// Use the `load_package` method to load packages of functions.
|
||||
pub fn new_raw() -> Self {
|
||||
Self {
|
||||
packages: Vec::new(),
|
||||
packages: Default::default(),
|
||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
||||
type_iterators: HashMap::new(),
|
||||
type_names: HashMap::new(),
|
||||
type_iterators: Default::default(),
|
||||
type_names: Default::default(),
|
||||
print: Box::new(|_| {}),
|
||||
debug: Box::new(|_| {}),
|
||||
|
||||
@ -595,8 +579,7 @@ impl Engine {
|
||||
.iter()
|
||||
.zip(
|
||||
// Actually consume the arguments instead of cloning them
|
||||
args.into_iter()
|
||||
.map(|v| mem::replace(*v, Default::default())),
|
||||
args.into_iter().map(|v| mem::take(*v)),
|
||||
)
|
||||
.map(|(name, value)| (name.clone(), ScopeEntryType::Normal, value)),
|
||||
);
|
||||
@ -626,8 +609,7 @@ impl Engine {
|
||||
.iter()
|
||||
.zip(
|
||||
// Actually consume the arguments instead of cloning them
|
||||
args.into_iter()
|
||||
.map(|v| mem::replace(*v, Default::default())),
|
||||
args.into_iter().map(|v| mem::take(*v)),
|
||||
)
|
||||
.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 ast.1.len() > 0 {
|
||||
if ast.fn_lib().len() > 0 {
|
||||
return Err(Box::new(EvalAltResult::ErrorParsing(
|
||||
ParseErrorType::WrongFnDefinition.into_err(pos),
|
||||
)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
{
|
||||
ast.1 = Arc::new(fn_lib.clone());
|
||||
}
|
||||
#[cfg(not(feature = "sync"))]
|
||||
{
|
||||
ast.1 = Rc::new(fn_lib.clone());
|
||||
}
|
||||
let statements = mem::take(ast.statements_mut());
|
||||
ast = AST::new(statements, fn_lib.clone());
|
||||
|
||||
// Evaluate the AST
|
||||
self.eval_ast_with_scope_raw(scope, &ast)
|
||||
@ -1496,8 +1472,8 @@ impl Engine {
|
||||
.try_cast::<String>()
|
||||
{
|
||||
let mut module = Module::new();
|
||||
module.insert("kitty".to_string(), "foo".to_string().into());
|
||||
module.insert("path".to_string(), path.into());
|
||||
module.set_variable("kitty", "foo".to_string());
|
||||
module.set_variable("path", path);
|
||||
|
||||
// TODO - avoid copying module name in inner block?
|
||||
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 {
|
||||
// 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.
|
||||
mem::replace(data, Default::default()).cast::<T>()
|
||||
mem::take(data).cast::<T>()
|
||||
}
|
||||
|
||||
/// This macro counts the number of arguments via recursion.
|
||||
|
@ -109,5 +109,8 @@ pub use engine::Map;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub use parser::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub use module::Module;
|
||||
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
pub use optimize::OptimizationLevel;
|
||||
|
137
src/module.rs
137
src/module.rs
@ -1,35 +1,134 @@
|
||||
//! 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::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
string::String,
|
||||
};
|
||||
use crate::stdlib::{collections::HashMap, fmt, 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.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module(HashMap<String, Dynamic>);
|
||||
#[derive(Default)]
|
||||
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 {
|
||||
/// Create a new module.
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Module {
|
||||
type Target = HashMap<String, Dynamic>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
/// Does a variable exist in the module?
|
||||
pub fn contains_variable(&self, name: &str) -> bool {
|
||||
self.variables.contains_key(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Module {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
/// Get the value of a module variable.
|
||||
pub fn get_variable_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||
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::{
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
@ -747,16 +745,13 @@ pub fn optimize_into_ast(
|
||||
.collect(),
|
||||
);
|
||||
|
||||
AST(
|
||||
AST::new(
|
||||
match level {
|
||||
OptimizationLevel::None => statements,
|
||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||
optimize(statements, engine, &scope, &fn_lib, level)
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "sync")]
|
||||
Arc::new(lib),
|
||||
#[cfg(not(feature = "sync"))]
|
||||
Rc::new(lib),
|
||||
lib,
|
||||
)
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ pub trait Package {
|
||||
}
|
||||
|
||||
/// Type to store all functions in the package.
|
||||
#[derive(Default)]
|
||||
pub struct PackageStore {
|
||||
/// All functions, keyed by a hash created from the function name and parameter types.
|
||||
pub functions: HashMap<u64, Box<FnAny>>,
|
||||
@ -58,10 +59,7 @@ pub struct PackageStore {
|
||||
impl PackageStore {
|
||||
/// Create a new `PackageStore`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
functions: HashMap::new(),
|
||||
type_iterators: HashMap::new(),
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,17 +51,44 @@ pub type ModuleRef = Option<Box<StaticVec<(String, Position)>>>;
|
||||
/// 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`.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct AST(
|
||||
pub(crate) Vec<Stmt>,
|
||||
#[cfg(feature = "sync")] pub(crate) Arc<FunctionsLib>,
|
||||
#[cfg(not(feature = "sync"))] pub(crate) Rc<FunctionsLib>,
|
||||
/// Global statements.
|
||||
Vec<Stmt>,
|
||||
/// 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 {
|
||||
/// Create a new `AST`.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
pub fn new(statements: Vec<Stmt>, fn_lib: FunctionsLib) -> Self {
|
||||
#[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
|
||||
@ -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 {
|
||||
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.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct Stack(Vec<(String, ScopeEntryType)>);
|
||||
|
||||
impl Stack {
|
||||
/// Create a new `Stack`.
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
Default::default()
|
||||
}
|
||||
/// Find a variable by name in the `Stack`, searching in reverse.
|
||||
/// 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_.
|
||||
///
|
||||
/// 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>>);
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
@ -77,7 +77,7 @@ impl<'a> Scope<'a> {
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Empty the Scope.
|
||||
@ -177,7 +177,7 @@ impl<'a> Scope<'a> {
|
||||
name,
|
||||
EntryType::Module,
|
||||
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> {
|
||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||
self.0
|
||||
|
@ -122,7 +122,7 @@ impl fmt::Display for Position {
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
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::{
|
||||
any::TypeId,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
mem,
|
||||
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 simplified implementation here is to avoid pulling in another crate.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct StaticVec<T: Default + Clone> {
|
||||
/// Total number of values held.
|
||||
len: usize,
|
||||
@ -57,16 +58,7 @@ pub struct StaticVec<T: Default + Clone> {
|
||||
impl<T: Default + Clone> StaticVec<T> {
|
||||
/// Create a new `StaticVec`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
len: 0,
|
||||
list: [
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
],
|
||||
more: Vec::new(),
|
||||
}
|
||||
Default::default()
|
||||
}
|
||||
/// Push a new value to the end of this `StaticVec`.
|
||||
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 {
|
||||
panic!("nothing to pop!")
|
||||
} 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 {
|
||||
self.more.pop().unwrap()
|
||||
};
|
||||
@ -126,3 +118,11 @@ impl<T: Default + Clone> StaticVec<T> {
|
||||
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