From c03b162b7e01f467d34482b414b0a34585414ea7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 5 May 2020 12:24:13 +0800 Subject: [PATCH] Refactor code base and split into more module files. --- src/any.rs | 3 +- src/engine.rs | 143 +++------------------------------------------ src/fn_register.rs | 3 +- src/lib.rs | 5 +- src/module.rs | 35 +++++++++++ src/parser.rs | 10 +++- src/scope.rs | 2 +- src/utils.rs | 128 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 186 insertions(+), 143 deletions(-) create mode 100644 src/module.rs create mode 100644 src/utils.rs diff --git a/src/any.rs b/src/any.rs index aee3da60..b87f2d50 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,6 +1,7 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. -use crate::engine::{Array, Map, Module}; +use crate::engine::{Array, Map}; +use crate::module::Module; use crate::parser::INT; #[cfg(not(feature = "no_float"))] diff --git a/src/engine.rs b/src/engine.rs index cdcdbc72..6a1853d2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,19 +3,20 @@ use crate::any::{Dynamic, Union}; use crate::calc_fn_hash; 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::result::EvalAltResult; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::Position; +use crate::utils::{calc_fn_def, StaticVec}; use crate::stdlib::{ any::TypeId, boxed::Box, collections::HashMap, format, - hash::{Hash, Hasher}, iter::once, mem, num::NonZeroUsize, @@ -26,12 +27,6 @@ use crate::stdlib::{ vec::Vec, }; -#[cfg(not(feature = "no_std"))] -use crate::stdlib::collections::hash_map::DefaultHasher; - -#[cfg(feature = "no_std")] -use ahash::AHasher; - /// An dynamic array of `Dynamic` values. /// /// Not available under the `no_index` feature. @@ -42,32 +37,6 @@ pub type Array = Vec; /// Not available under the `no_object` feature. pub type Map = HashMap; -/// An imported module. -/// -/// Not available under the `no_module` feature. -#[derive(Debug, Clone)] -pub struct Module(HashMap); - -impl Module { - /// Create a new module. - pub fn new() -> Self { - Self(HashMap::new()) - } -} - -impl Deref for Module { - type Target = HashMap; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Module { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - pub type FnCallArgs<'a> = [&'a mut Dynamic]; #[cfg(feature = "sync")] @@ -166,78 +135,8 @@ impl> From for Target<'_> { } } -/// A type to hold a number of values in static storage for speed, -/// and any spill-overs in a `Vec`. -#[derive(Debug, Clone)] -pub struct StaticVec { - /// Total number of values held. - len: usize, - /// Static storage. 4 slots should be enough for most cases - i.e. four levels of indirection. - list: [T; 4], - /// Dynamic storage. For spill-overs. - more: Vec, -} - -impl StaticVec { - /// Create a new `StaticVec`. - pub fn new() -> Self { - Self { - len: 0, - list: [ - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ], - more: Vec::new(), - } - } - /// Push a new value to the end of this `StaticVec`. - pub fn push>(&mut self, value: X) { - if self.len >= self.list.len() { - self.more.push(value.into()); - } else { - self.list[self.len] = value.into(); - } - self.len += 1; - } - /// Pop a value from the end of this `StaticVec`. - /// - /// # Panics - /// - /// Panics if the `StaticVec` is empty. - pub fn pop(&mut self) -> 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()) - } else { - self.more.pop().unwrap() - }; - - self.len -= 1; - - result - } - /// Get the number of items in this `StaticVec`. - pub fn len(&self) -> usize { - self.len - } - pub fn get(&self, index: usize) -> &T { - if index >= self.len { - panic!("index OOB in StaticVec"); - } - - if index < self.list.len() { - self.list.get(index).unwrap() - } else { - self.more.get(index - self.list.len()).unwrap() - } - } -} - /// A type that holds all the current states of the Engine. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct State { /// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. /// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned. @@ -466,33 +365,6 @@ fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { } } -/// Calculate a `u64` hash key from a function name and parameter types. -/// -/// Parameter types are passed in via `TypeId` values from an iterator -/// which can come from any source. -pub fn calc_fn_spec(fn_name: &str, params: impl Iterator) -> u64 { - #[cfg(feature = "no_std")] - let mut s: AHasher = Default::default(); - #[cfg(not(feature = "no_std"))] - let mut s = DefaultHasher::new(); - - s.write(fn_name.as_bytes()); - params.for_each(|t| t.hash(&mut s)); - s.finish() -} - -/// Calculate a `u64` hash key from a function name and number of parameters (without regard to types). -pub(crate) fn calc_fn_def(fn_name: &str, params: usize) -> u64 { - #[cfg(feature = "no_std")] - let mut s: AHasher = Default::default(); - #[cfg(not(feature = "no_std"))] - let mut s = DefaultHasher::new(); - - s.write(fn_name.as_bytes()); - s.write_usize(params); - s.finish() -} - /// Print/debug to stdout fn default_print(s: &str) { #[cfg(not(feature = "no_std"))] @@ -503,12 +375,13 @@ fn default_print(s: &str) { fn search_scope<'a>( scope: &'a mut Scope, name: &str, - modules: &Option>>, + modules: &ModuleRef, index: Option, pos: Position, ) -> Result<(&'a mut Dynamic, ScopeEntryType), Box> { if let Some(modules) = modules { - let (id, root_pos) = modules.get(0); // First module + let mut drain = modules.iter(); + let (id, root_pos) = drain.next().unwrap(); // First module let mut module = if let Some(index) = index { scope @@ -522,9 +395,7 @@ fn search_scope<'a>( .ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))? }; - for x in 1..modules.len() { - let (id, id_pos) = modules.get(x); - + for (id, id_pos) in drain { module = module .get_mut(id) .and_then(|v| v.downcast_mut::()) diff --git a/src/fn_register.rs b/src/fn_register.rs index 9368d6a3..197cf6cc 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -3,9 +3,10 @@ #![allow(non_snake_case)] use crate::any::{Dynamic, Variant}; -use crate::engine::{calc_fn_spec, Engine, FnCallArgs}; +use crate::engine::{Engine, FnCallArgs}; use crate::result::EvalAltResult; use crate::token::Position; +use crate::utils::calc_fn_spec; use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString}; diff --git a/src/lib.rs b/src/lib.rs index e13eaf0f..04e4ebb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ mod error; mod fn_call; mod fn_func; mod fn_register; +mod module; mod optimize; pub mod packages; mod parser; @@ -83,9 +84,10 @@ mod result; mod scope; mod stdlib; mod token; +mod utils; pub use any::Dynamic; -pub use engine::{calc_fn_spec as calc_fn_hash, Engine}; +pub use engine::Engine; pub use error::{ParseError, ParseErrorType}; pub use fn_call::FuncArgs; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; @@ -93,6 +95,7 @@ pub use parser::{AST, INT}; pub use result::EvalAltResult; pub use scope::Scope; pub use token::Position; +pub use utils::calc_fn_spec as calc_fn_hash; #[cfg(not(feature = "no_function"))] pub use fn_func::Func; diff --git a/src/module.rs b/src/module.rs new file mode 100644 index 00000000..4b62097b --- /dev/null +++ b/src/module.rs @@ -0,0 +1,35 @@ +//! Module defining external-loaded modules for Rhai. + +use crate::any::Dynamic; + +use crate::stdlib::{ + collections::HashMap, + ops::{Deref, DerefMut}, + string::String, +}; + +/// An imported module. +/// +/// Not available under the `no_module` feature. +#[derive(Debug, Clone)] +pub struct Module(HashMap); + +impl Module { + /// Create a new module. + pub fn new() -> Self { + Self(HashMap::new()) + } +} + +impl Deref for Module { + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Module { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/src/parser.rs b/src/parser.rs index ef31c3f9..707b9809 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,11 +1,12 @@ //! Main module defining the lexer and parser. use crate::any::{Dynamic, Union}; -use crate::engine::{calc_fn_def, Engine, FunctionsLib, StaticVec}; +use crate::engine::{Engine, FunctionsLib}; use crate::error::{LexError, ParseError, ParseErrorType}; use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::token::{Position, Token, TokenIterator}; +use crate::utils::{calc_fn_def, StaticVec}; use crate::stdlib::{ borrow::Cow, @@ -42,6 +43,9 @@ pub type FLOAT = f64; type PERR = ParseErrorType; +/// A chain of module names to qualify a variable or function call. +/// A `StaticVec` is used because most module-level access contains only one level, +/// and it is wasteful to always allocate a `Vec` with one element. pub type ModuleRef = Option>>; /// Compiled AST (abstract syntax tree) of a Rhai script. @@ -1079,7 +1083,7 @@ fn parse_primary<'a>( vec.push((*id, pos)); modules = Some(Box::new(vec)); - let root = modules.as_ref().unwrap().get(0); + let root = modules.as_ref().unwrap().iter().next().unwrap(); index = stack.find_sub_scope(&root.0); } @@ -1243,7 +1247,7 @@ fn make_dot_expr( } // lhs.module::id - syntax error (_, Expr::Variable(_, Some(modules), _, _)) => { - return Err(PERR::PropertyExpected.into_err(modules.get(0).1)) + return Err(PERR::PropertyExpected.into_err(modules.iter().next().unwrap().1)) } // lhs.dot_lhs.dot_rhs (lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot( diff --git a/src/scope.rs b/src/scope.rs index 394997bf..da90f72a 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,7 +1,7 @@ //! Module that defines the `Scope` type representing a function call-stack scope. use crate::any::{Dynamic, Union, Variant}; -use crate::engine::Module; +use crate::module::Module; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 00000000..4f51c7aa --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,128 @@ +//! Module containing various utility types and functions. + +use crate::stdlib::{ + any::TypeId, + hash::{Hash, Hasher}, + mem, + vec::Vec, +}; + +#[cfg(not(feature = "no_std"))] +use crate::stdlib::collections::hash_map::DefaultHasher; + +#[cfg(feature = "no_std")] +use ahash::AHasher; + +/// Calculate a `u64` hash key from a function name and parameter types. +/// +/// Parameter types are passed in via `TypeId` values from an iterator +/// which can come from any source. +pub fn calc_fn_spec(fn_name: &str, params: impl Iterator) -> u64 { + #[cfg(feature = "no_std")] + let mut s: AHasher = Default::default(); + #[cfg(not(feature = "no_std"))] + let mut s = DefaultHasher::new(); + + s.write(fn_name.as_bytes()); + params.for_each(|t| t.hash(&mut s)); + s.finish() +} + +/// Calculate a `u64` hash key from a function name and number of parameters (without regard to types). +pub(crate) fn calc_fn_def(fn_name: &str, num_params: usize) -> u64 { + #[cfg(feature = "no_std")] + let mut s: AHasher = Default::default(); + #[cfg(not(feature = "no_std"))] + let mut s = DefaultHasher::new(); + + s.write(fn_name.as_bytes()); + s.write_usize(num_params); + s.finish() +} + +/// A type to hold a number of values in static storage for speed, and any spill-overs in a `Vec`. +/// +/// 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)] +pub struct StaticVec { + /// Total number of values held. + len: usize, + /// Static storage. 4 slots should be enough for most cases - i.e. four levels of indirection. + list: [T; 4], + /// Dynamic storage. For spill-overs. + more: Vec, +} + +impl StaticVec { + /// Create a new `StaticVec`. + pub fn new() -> Self { + Self { + len: 0, + list: [ + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ], + more: Vec::new(), + } + } + /// Push a new value to the end of this `StaticVec`. + pub fn push>(&mut self, value: X) { + if self.len >= self.list.len() { + self.more.push(value.into()); + } else { + self.list[self.len] = value.into(); + } + self.len += 1; + } + /// Pop a value from the end of this `StaticVec`. + /// + /// # Panics + /// + /// Panics if the `StaticVec` is empty. + pub fn pop(&mut self) -> 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()) + } else { + self.more.pop().unwrap() + }; + + self.len -= 1; + + result + } + /// Get the number of items in this `StaticVec`. + pub fn len(&self) -> usize { + self.len + } + /// Get an item at a particular index. + /// + /// # Panics + /// + /// Panics if the index is out of bounds. + pub fn get(&self, index: usize) -> &T { + if index >= self.len { + panic!("index OOB in StaticVec"); + } + + if index < self.list.len() { + self.list.get(index).unwrap() + } else { + self.more.get(index - self.list.len()).unwrap() + } + } + /// Get an iterator to entries in the `StaticVec`. + pub fn iter(&self) -> impl Iterator { + let num = if self.len >= self.list.len() { + self.list.len() + } else { + self.len + }; + + self.list[..num].iter().chain(self.more.iter()) + } +}