Refactor code base and split into more module files.

This commit is contained in:
Stephen Chung 2020-05-05 12:24:13 +08:00
parent 143861747d
commit c03b162b7e
8 changed files with 186 additions and 143 deletions

View File

@ -1,6 +1,7 @@
//! Helper module which defines the `Any` trait to to allow dynamic value handling. //! 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; use crate::parser::INT;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]

View File

@ -3,19 +3,20 @@
use crate::any::{Dynamic, Union}; use crate::any::{Dynamic, Union};
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::error::ParseErrorType; use crate::error::ParseErrorType;
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};
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;
use crate::utils::{calc_fn_def, StaticVec};
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
boxed::Box, boxed::Box,
collections::HashMap, collections::HashMap,
format, format,
hash::{Hash, Hasher},
iter::once, iter::once,
mem, mem,
num::NonZeroUsize, num::NonZeroUsize,
@ -26,12 +27,6 @@ use crate::stdlib::{
vec::Vec, 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. /// An dynamic array of `Dynamic` values.
/// ///
/// Not available under the `no_index` feature. /// Not available under the `no_index` feature.
@ -42,32 +37,6 @@ pub type Array = Vec<Dynamic>;
/// Not available under the `no_object` feature. /// Not available under the `no_object` feature.
pub type Map = HashMap<String, Dynamic>; pub type Map = HashMap<String, Dynamic>;
/// An imported module.
///
/// Not available under the `no_module` feature.
#[derive(Debug, Clone)]
pub struct Module(HashMap<String, Dynamic>);
impl Module {
/// Create a new module.
pub fn new() -> Self {
Self(HashMap::new())
}
}
impl Deref for Module {
type Target = HashMap<String, Dynamic>;
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]; pub type FnCallArgs<'a> = [&'a mut Dynamic];
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
@ -166,78 +135,8 @@ impl<T: Into<Dynamic>> From<T> 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<T: Default + Clone> {
/// 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<T>,
}
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(),
}
}
/// Push a new value to the end of this `StaticVec`.
pub fn push<X: Into<T>>(&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. /// 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 { pub struct State {
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. /// 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. /// 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<Item = TypeId>) -> 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 /// Print/debug to stdout
fn default_print(s: &str) { fn default_print(s: &str) {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -503,12 +375,13 @@ fn default_print(s: &str) {
fn search_scope<'a>( fn search_scope<'a>(
scope: &'a mut Scope, scope: &'a mut Scope,
name: &str, name: &str,
modules: &Option<Box<StaticVec<(String, Position)>>>, modules: &ModuleRef,
index: Option<NonZeroUsize>, index: Option<NonZeroUsize>,
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 (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 { let mut module = if let Some(index) = index {
scope scope
@ -522,9 +395,7 @@ 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 x in 1..modules.len() { for (id, id_pos) in drain {
let (id, id_pos) = modules.get(x);
module = module module = module
.get_mut(id) .get_mut(id)
.and_then(|v| v.downcast_mut::<Module>()) .and_then(|v| v.downcast_mut::<Module>())

View File

@ -3,9 +3,10 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::any::{Dynamic, Variant}; use crate::any::{Dynamic, Variant};
use crate::engine::{calc_fn_spec, Engine, FnCallArgs}; use crate::engine::{Engine, FnCallArgs};
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::token::Position; use crate::token::Position;
use crate::utils::calc_fn_spec;
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString}; use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};

View File

@ -76,6 +76,7 @@ mod error;
mod fn_call; mod fn_call;
mod fn_func; mod fn_func;
mod fn_register; mod fn_register;
mod module;
mod optimize; mod optimize;
pub mod packages; pub mod packages;
mod parser; mod parser;
@ -83,9 +84,10 @@ mod result;
mod scope; mod scope;
mod stdlib; mod stdlib;
mod token; mod token;
mod utils;
pub use any::Dynamic; 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 error::{ParseError, ParseErrorType};
pub use fn_call::FuncArgs; pub use fn_call::FuncArgs;
pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; pub use fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn};
@ -93,6 +95,7 @@ pub use parser::{AST, INT};
pub use result::EvalAltResult; pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
pub use token::Position; pub use token::Position;
pub use utils::calc_fn_spec as calc_fn_hash;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use fn_func::Func; pub use fn_func::Func;

35
src/module.rs Normal file
View File

@ -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<String, Dynamic>);
impl Module {
/// Create a new module.
pub fn new() -> Self {
Self(HashMap::new())
}
}
impl Deref for Module {
type Target = HashMap<String, Dynamic>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Module {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@ -1,11 +1,12 @@
//! Main module defining the lexer and parser. //! Main module defining the lexer and parser.
use crate::any::{Dynamic, Union}; 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::error::{LexError, ParseError, ParseErrorType};
use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token, TokenIterator}; use crate::token::{Position, Token, TokenIterator};
use crate::utils::{calc_fn_def, StaticVec};
use crate::stdlib::{ use crate::stdlib::{
borrow::Cow, borrow::Cow,
@ -42,6 +43,9 @@ pub type FLOAT = f64;
type PERR = ParseErrorType; 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<Box<StaticVec<(String, Position)>>>; 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.
@ -1079,7 +1083,7 @@ fn parse_primary<'a>(
vec.push((*id, pos)); vec.push((*id, pos));
modules = Some(Box::new(vec)); 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); index = stack.find_sub_scope(&root.0);
} }
@ -1243,7 +1247,7 @@ fn make_dot_expr(
} }
// lhs.module::id - syntax error // lhs.module::id - syntax error
(_, Expr::Variable(_, Some(modules), _, _)) => { (_, 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.dot_lhs.dot_rhs
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot( (lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(

View File

@ -1,7 +1,7 @@
//! Module that defines the `Scope` type representing a function call-stack scope. //! Module that defines the `Scope` type representing a function call-stack scope.
use crate::any::{Dynamic, Union, Variant}; use crate::any::{Dynamic, Union, Variant};
use crate::engine::Module; use crate::module::Module;
use crate::parser::{map_dynamic_to_expr, Expr}; use crate::parser::{map_dynamic_to_expr, Expr};
use crate::token::Position; use crate::token::Position;

128
src/utils.rs Normal file
View File

@ -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<Item = TypeId>) -> 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<T: Default + Clone> {
/// 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<T>,
}
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(),
}
}
/// Push a new value to the end of this `StaticVec`.
pub fn push<X: Into<T>>(&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<Item = &T> {
let num = if self.len >= self.list.len() {
self.list.len()
} else {
self.len
};
self.list[..num].iter().chain(self.more.iter())
}
}