Use hashed lookup for module-qualified functions and variables.

This commit is contained in:
Stephen Chung
2020-05-08 00:19:08 +08:00
parent 7f6ce29447
commit 5f12391ec6
8 changed files with 268 additions and 121 deletions

View File

@@ -1,8 +1,10 @@
//! Main module defining the lexer and parser.
use crate::any::{Dynamic, Union};
use crate::calc_fn_hash;
use crate::engine::{Engine, FunctionsLib};
use crate::error::{LexError, ParseError, ParseErrorType};
use crate::module::ModuleRef;
use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token, TokenIterator};
@@ -14,7 +16,7 @@ use crate::stdlib::{
char,
collections::HashMap,
format,
iter::Peekable,
iter::{empty, Peekable},
num::NonZeroUsize,
ops::{Add, Deref, DerefMut},
rc::Rc,
@@ -44,11 +46,6 @@ 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<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`.
@@ -357,8 +354,13 @@ pub enum Expr {
CharConstant(char, Position),
/// String constant.
StringConstant(String, Position),
/// Variable access - (variable name, optional modules, optional index, position)
Variable(Box<String>, ModuleRef, Option<NonZeroUsize>, Position),
/// Variable access - (variable name, optional modules, hash, optional index, position)
Variable(
Box<String>,
Option<Box<ModuleRef>>,
Option<NonZeroUsize>,
Position,
),
/// Property access.
Property(String, Position),
/// { stmt }
@@ -368,7 +370,7 @@ pub enum Expr {
/// and the function names are predictable, so no need to allocate a new `String`.
FnCall(
Box<Cow<'static, str>>,
ModuleRef,
Option<Box<ModuleRef>>,
Box<Vec<Expr>>,
Option<Box<Dynamic>>,
Position,
@@ -675,7 +677,7 @@ fn parse_call_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
stack: &mut Stack,
id: String,
modules: ModuleRef,
modules: Option<Box<ModuleRef>>,
begin: Position,
allow_stmt_expr: bool,
) -> Result<Expr, Box<ParseError>> {
@@ -1110,12 +1112,11 @@ fn parse_primary<'a>(
if let Some(ref mut modules) = modules {
modules.push((*id, pos));
} else {
index = stack.find_module(id.as_ref());
let mut vec = StaticVec::new();
vec.push((*id, pos));
modules = Some(Box::new(vec));
let root = modules.as_ref().unwrap().iter().next().unwrap();
index = stack.find_module(&root.0);
modules = Some(Box::new(vec.into()));
}
Expr::Variable(Box::new(id2), modules, index, pos2)
@@ -1133,6 +1134,15 @@ fn parse_primary<'a>(
}
}
match &mut root_expr {
// Calculate hash key for module-qualified variables
Expr::Variable(id, Some(modules), _, _) => {
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
modules.set_key(hash);
}
_ => (),
}
Ok(root_expr)
}
@@ -1315,7 +1325,7 @@ fn make_dot_expr(
}
// lhs.module::id - syntax error
(_, Expr::Variable(_, Some(modules), _, _)) => {
return Err(PERR::PropertyExpected.into_err(modules.iter().next().unwrap().1))
return Err(PERR::PropertyExpected.into_err(modules.get(0).1))
}
// lhs.dot_lhs.dot_rhs
(lhs, Expr::Dot(dot_lhs, dot_rhs, dot_pos)) => Expr::Dot(