Use interned strings to prepare for match statement.

This commit is contained in:
Stephen Chung 2020-11-12 22:36:13 +08:00
parent 8e5a53bc0d
commit 69a0f044f4
4 changed files with 94 additions and 66 deletions

View File

@ -16,7 +16,7 @@ use crate::FLOAT;
use crate::engine::Array; use crate::engine::Array;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::engine::{make_getter, make_setter, Map}; use crate::engine::Map;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
use crate::engine::Imports; use crate::engine::Imports;
@ -919,9 +919,9 @@ pub enum Expr {
/// FnPtr constant. /// FnPtr constant.
FnPointer(Box<IdentX>), FnPointer(Box<IdentX>),
/// Variable access - (optional index, optional modules, hash, variable name) /// Variable access - (optional index, optional modules, hash, variable name)
Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, Ident)>), Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, IdentX)>),
/// Property access - (getter, setter), prop /// Property access - (getter, setter), prop
Property(Box<((String, String), IdentX)>), Property(Box<((ImmutableString, ImmutableString), IdentX)>),
/// { stmt } /// { stmt }
Stmt(Box<StaticVec<Stmt>>, Position), Stmt(Box<StaticVec<Stmt>>, Position),
/// Wrapped expression - should not be optimized away. /// Wrapped expression - should not be optimized away.
@ -1239,21 +1239,6 @@ impl Expr {
Self::Custom(_, _) => false, Self::Custom(_, _) => false,
} }
} }
/// Convert a `Variable` into a `Property`. All other variants are untouched.
#[cfg(not(feature = "no_object"))]
#[inline]
pub(crate) fn into_property(self) -> Self {
match self {
Self::Variable(x) if x.1.is_none() => {
let ident = x.3;
let getter = make_getter(&ident.name);
let setter = make_setter(&ident.name);
Self::Property(Box::new(((getter, setter), ident.into())))
}
_ => self,
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -650,7 +650,7 @@ pub fn search_imports(
state: &mut State, state: &mut State,
namespace: &NamespaceRef, namespace: &NamespaceRef,
) -> Result<Shared<Module>, Box<EvalAltResult>> { ) -> Result<Shared<Module>, Box<EvalAltResult>> {
let Ident { name: root, pos } = &namespace[0]; let IdentX { name: root, pos } = &namespace[0];
// Qualified - check if the root module is directly indexed // Qualified - check if the root module is directly indexed
let index = if state.always_search { let index = if state.always_search {
@ -794,7 +794,7 @@ impl Engine {
match expr { match expr {
Expr::Variable(v) => match v.as_ref() { Expr::Variable(v) => match v.as_ref() {
// Qualified variable // Qualified variable
(_, Some(modules), hash_var, Ident { name, pos }) => { (_, Some(modules), hash_var, IdentX { name, pos }) => {
let module = search_imports(mods, state, modules)?; let module = search_imports(mods, state, modules)?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| { let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err { match *err {
@ -826,13 +826,13 @@ impl Engine {
this_ptr: &'s mut Option<&mut Dynamic>, this_ptr: &'s mut Option<&mut Dynamic>,
expr: &'a Expr, expr: &'a Expr,
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> { ) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
let (index, _, _, Ident { name, pos }) = match expr { let (index, _, _, IdentX { name, pos }) = match expr {
Expr::Variable(v) => v.as_ref(), Expr::Variable(v) => v.as_ref(),
_ => unreachable!(), _ => unreachable!(),
}; };
// Check if the variable is `this` // Check if the variable is `this`
if name == KEYWORD_THIS { if name.as_str() == KEYWORD_THIS {
if let Some(val) = this_ptr { if let Some(val) = this_ptr {
return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos)); return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos));
} else { } else {
@ -871,7 +871,7 @@ impl Engine {
// Find the variable in the scope // Find the variable in the scope
scope scope
.get_index(name) .get_index(name)
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.into(), *pos))? .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos))?
.0 .0
}; };
@ -1234,7 +1234,7 @@ impl Engine {
match lhs { match lhs {
// id.??? or id[???] // id.??? or id[???]
Expr::Variable(x) => { Expr::Variable(x) => {
let Ident { let IdentX {
name: var_name, name: var_name,
pos: var_pos, pos: var_pos,
} = &x.3; } = &x.3;

View File

@ -1,6 +1,6 @@
//! Module defining external-loaded modules for Rhai. //! Module defining external-loaded modules for Rhai.
use crate::ast::{FnAccess, Ident}; use crate::ast::{FnAccess, IdentX};
use crate::dynamic::{Dynamic, Variant}; use crate::dynamic::{Dynamic, Variant};
use crate::fn_native::{ use crate::fn_native::{
shared_make_mut, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, shared_make_mut, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn,
@ -1528,7 +1528,7 @@ impl Module {
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Clone, Eq, PartialEq, Default, Hash)] #[derive(Clone, Eq, PartialEq, Default, Hash)]
pub struct NamespaceRef(StaticVec<Ident>, Option<NonZeroUsize>); pub struct NamespaceRef(StaticVec<IdentX>, Option<NonZeroUsize>);
impl fmt::Debug for NamespaceRef { impl fmt::Debug for NamespaceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -1543,7 +1543,7 @@ impl fmt::Debug for NamespaceRef {
} }
impl Deref for NamespaceRef { impl Deref for NamespaceRef {
type Target = StaticVec<Ident>; type Target = StaticVec<IdentX>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -1558,15 +1558,15 @@ impl DerefMut for NamespaceRef {
impl fmt::Display for NamespaceRef { impl fmt::Display for NamespaceRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for Ident { name, .. } in self.0.iter() { for IdentX { name, .. } in self.0.iter() {
write!(f, "{}{}", name, Token::DoubleColon.syntax())?; write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
} }
Ok(()) Ok(())
} }
} }
impl From<StaticVec<Ident>> for NamespaceRef { impl From<StaticVec<IdentX>> for NamespaceRef {
fn from(modules: StaticVec<Ident>) -> Self { fn from(modules: StaticVec<IdentX>) -> Self {
Self(modules, None) Self(modules, None)
} }
} }

View File

@ -64,6 +64,8 @@ type FunctionsLib = HashMap<u64, ScriptFnDef, StraightHasherBuilder>;
struct ParseState<'e> { struct ParseState<'e> {
/// Reference to the scripting `Engine`. /// Reference to the scripting `Engine`.
engine: &'e Engine, engine: &'e Engine,
/// Interned strings.
strings: HashMap<String, ImmutableString>,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope. /// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(String, ScopeEntryType)>, stack: Vec<(String, ScopeEntryType)>,
/// Tracks a list of external variables (variables that are not explicitly declared in the scope). /// Tracks a list of external variables (variables that are not explicitly declared in the scope).
@ -108,6 +110,7 @@ impl<'e> ParseState<'e> {
externals: Default::default(), externals: Default::default(),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
allow_capture: true, allow_capture: true,
strings: Default::default(),
stack: Default::default(), stack: Default::default(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: Default::default(), modules: Default::default(),
@ -163,6 +166,18 @@ impl<'e> ParseState<'e> {
.find(|(_, n)| *n == name) .find(|(_, n)| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1)) .and_then(|(i, _)| NonZeroUsize::new(i + 1))
} }
/// Get an interned string, creating one if it is not yet interned.
pub fn get_interned_string(&mut self, text: String) -> ImmutableString {
if !self.strings.contains_key(&text) {
let value: ImmutableString = text.clone().into();
let result = value.clone();
self.strings.insert(text, value);
result
} else {
self.strings.get(&text).unwrap().clone()
}
}
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@ -208,6 +223,24 @@ impl ParseSettings {
} }
} }
} }
impl Expr {
/// Convert a `Variable` into a `Property`. All other variants are untouched.
#[cfg(not(feature = "no_object"))]
#[inline]
fn into_property(self, state: &mut ParseState) -> Self {
match self {
Self::Variable(x) if x.1.is_none() => {
let ident = x.3;
let getter = state.get_interned_string(make_getter(&ident.name));
let setter = state.get_interned_string(make_setter(&ident.name));
Self::Property(Box::new(((getter, setter), ident.into())))
}
_ => self,
}
}
}
/// Consume a particular token, checking that it is the expected one. /// Consume a particular token, checking that it is the expected one.
fn eat_token(input: &mut TokenStream, token: Token) -> Position { fn eat_token(input: &mut TokenStream, token: Token) -> Position {
let (t, pos) = input.next().unwrap(); let (t, pos) = input.next().unwrap();
@ -268,7 +301,7 @@ fn parse_fn_call(
input: &mut TokenStream, input: &mut TokenStream,
state: &mut ParseState, state: &mut ParseState,
lib: &mut FunctionsLib, lib: &mut FunctionsLib,
id: String, id: ImmutableString,
capture: bool, capture: bool,
mut namespace: Option<Box<NamespaceRef>>, mut namespace: Option<Box<NamespaceRef>>,
settings: ParseSettings, settings: ParseSettings,
@ -314,7 +347,7 @@ fn parse_fn_call(
return Ok(Expr::FnCall( return Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: id.into(), name: id.to_string().into(),
capture, capture,
namespace, namespace,
hash: hash_script, hash: hash_script,
@ -361,7 +394,7 @@ fn parse_fn_call(
return Ok(Expr::FnCall( return Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: id.into(), name: id.to_string().into(),
capture, capture,
namespace, namespace,
hash: hash_script, hash: hash_script,
@ -784,7 +817,10 @@ fn parse_primary(
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => Expr::FloatConstant(FloatWrapper(x), settings.pos), Token::FloatConstant(x) => Expr::FloatConstant(FloatWrapper(x), settings.pos),
Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos),
Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new(s, settings.pos))), Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new(
state.get_interned_string(s),
settings.pos,
))),
// Function call // Function call
Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
@ -793,7 +829,8 @@ fn parse_primary(
{ {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos)))) let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
Expr::Variable(Box::new((None, None, 0, var_name_def)))
} }
// Module qualification // Module qualification
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -803,18 +840,21 @@ fn parse_primary(
{ {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos)))) let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
Expr::Variable(Box::new((None, None, 0, var_name_def)))
} }
// Normal variable access // Normal variable access
Token::Identifier(s) => { Token::Identifier(s) => {
let index = state.access_var(&s, settings.pos); let index = state.access_var(&s, settings.pos);
Expr::Variable(Box::new((index, None, 0, Ident::new(s, settings.pos)))) let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
Expr::Variable(Box::new((index, None, 0, var_name_def)))
} }
// Function call is allowed to have reserved keyword // Function call is allowed to have reserved keyword
Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => {
if is_keyword_function(&s) { if is_keyword_function(&s) {
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos)))) let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
Expr::Variable(Box::new((None, None, 0, var_name_def)))
} else { } else {
return Err(PERR::Reserved(s).into_err(settings.pos)); return Err(PERR::Reserved(s).into_err(settings.pos));
} }
@ -829,7 +869,8 @@ fn parse_primary(
))) )))
.into_err(settings.pos)); .into_err(settings.pos));
} else { } else {
Expr::Variable(Box::new((None, None, 0, Ident::new(s, settings.pos)))) let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos);
Expr::Variable(Box::new((None, None, 0, var_name_def)))
} }
} }
@ -888,13 +929,13 @@ fn parse_primary(
.into_err(pos)); .into_err(pos));
} }
let (_, modules, _, Ident { name, pos }) = *x; let (_, modules, _, IdentX { name, pos }) = *x;
settings.pos = pos; settings.pos = pos;
parse_fn_call(input, state, lib, name, true, modules, settings.level_up())? parse_fn_call(input, state, lib, name, true, modules, settings.level_up())?
} }
// Function call // Function call
(Expr::Variable(x), Token::LeftParen) => { (Expr::Variable(x), Token::LeftParen) => {
let (_, modules, _, Ident { name, pos }) = *x; let (_, modules, _, IdentX { name, pos }) = *x;
settings.pos = pos; settings.pos = pos;
parse_fn_call(input, state, lib, name, false, modules, settings.level_up())? parse_fn_call(input, state, lib, name, false, modules, settings.level_up())?
} }
@ -912,7 +953,8 @@ fn parse_primary(
modules = Some(Box::new(m)); modules = Some(Box::new(m));
} }
Expr::Variable(Box::new((index, modules, 0, Ident::new(id2, pos2)))) let var_name_def = IdentX::new(state.get_interned_string(id2), pos2);
Expr::Variable(Box::new((index, modules, 0, var_name_def)))
} }
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
return Err(PERR::Reserved(id2).into_err(pos2)); return Err(PERR::Reserved(id2).into_err(pos2));
@ -936,7 +978,7 @@ fn parse_primary(
match &mut root_expr { match &mut root_expr {
// Cache the hash key for namespace-qualified variables // Cache the hash key for namespace-qualified variables
Expr::Variable(x) if x.1.is_some() => { Expr::Variable(x) if x.1.is_some() => {
let (_, modules, hash, Ident { name, .. }) = x.as_mut(); let (_, modules, hash, IdentX { name, .. }) = x.as_mut();
let namespace = modules.as_mut().unwrap(); let namespace = modules.as_mut().unwrap();
// Qualifiers + variable name // Qualifiers + variable name
@ -1102,7 +1144,7 @@ fn make_assignment_stmt<'a>(
index, index,
_, _,
_, _,
Ident { IdentX {
name, name,
pos: name_pos, pos: name_pos,
}, },
@ -1113,7 +1155,7 @@ fn make_assignment_stmt<'a>(
} }
// Constant values cannot be assigned to // Constant values cannot be assigned to
ScopeEntryType::Constant => { ScopeEntryType::Constant => {
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos)) Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
} }
} }
} }
@ -1129,7 +1171,7 @@ fn make_assignment_stmt<'a>(
index, index,
_, _,
_, _,
Ident { IdentX {
name, name,
pos: name_pos, pos: name_pos,
}, },
@ -1140,7 +1182,7 @@ fn make_assignment_stmt<'a>(
} }
// Constant values cannot be assigned to // Constant values cannot be assigned to
ScopeEntryType::Constant => { ScopeEntryType::Constant => {
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos)) Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
} }
} }
} }
@ -1200,20 +1242,25 @@ fn parse_op_assignment_stmt(
/// Make a dot expression. /// Make a dot expression.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> { fn make_dot_expr(
state: &mut ParseState,
lhs: Expr,
rhs: Expr,
op_pos: Position,
) -> Result<Expr, ParseError> {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
// idx_lhs[idx_expr].rhs // idx_lhs[idx_expr].rhs
// Attach dot chain to the bottom level of indexing chain // Attach dot chain to the bottom level of indexing chain
(Expr::Index(mut x, pos), rhs) => { (Expr::Index(mut x, pos), rhs) => {
x.rhs = make_dot_expr(x.rhs, rhs, op_pos)?; x.rhs = make_dot_expr(state, x.rhs, rhs, op_pos)?;
Expr::Index(x, pos) Expr::Index(x, pos)
} }
// lhs.id // lhs.id
(lhs, Expr::Variable(x)) if x.1.is_none() => { (lhs, Expr::Variable(x)) if x.1.is_none() => {
let ident = x.3; let ident = x.3;
let getter = make_getter(&ident.name); let getter = state.get_interned_string(make_getter(&ident.name));
let setter = make_setter(&ident.name); let setter = state.get_interned_string(make_setter(&ident.name));
let rhs = Expr::Property(Box::new(((getter, setter), ident.into()))); let rhs = Expr::Property(Box::new(((getter, setter), ident)));
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
} }
@ -1229,7 +1276,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
(lhs, Expr::Dot(x, pos)) => { (lhs, Expr::Dot(x, pos)) => {
let rhs = Expr::Dot( let rhs = Expr::Dot(
Box::new(BinaryExpr { Box::new(BinaryExpr {
lhs: x.lhs.into_property(), lhs: x.lhs.into_property(state),
rhs: x.rhs, rhs: x.rhs,
}), }),
pos, pos,
@ -1240,7 +1287,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
(lhs, Expr::Index(x, pos)) => { (lhs, Expr::Index(x, pos)) => {
let rhs = Expr::Index( let rhs = Expr::Index(
Box::new(BinaryExpr { Box::new(BinaryExpr {
lhs: x.lhs.into_property(), lhs: x.lhs.into_property(state),
rhs: x.rhs, rhs: x.rhs,
}), }),
pos, pos,
@ -1579,7 +1626,7 @@ fn parse_binary_op(
Token::Period => { Token::Period => {
let rhs = args.pop().unwrap(); let rhs = args.pop().unwrap();
let current_lhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap();
make_dot_expr(current_lhs, rhs, pos)? make_dot_expr(state, current_lhs, rhs, pos)?
} }
Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => { Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => {
@ -1647,12 +1694,8 @@ fn parse_custom_syntax(
MARKER_IDENT => match input.next().unwrap() { MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => { (Token::Identifier(s), pos) => {
segments.push(s.clone()); segments.push(s.clone());
exprs.push(Expr::Variable(Box::new(( let var_name_def = IdentX::new(state.get_interned_string(s), pos);
None, exprs.push(Expr::Variable(Box::new((None, None, 0, var_name_def))));
None,
0,
Ident::new(s, pos),
))));
} }
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
return Err(PERR::Reserved(s).into_err(pos)); return Err(PERR::Reserved(s).into_err(pos));
@ -1950,14 +1993,14 @@ fn parse_let(
// let name = expr // let name = expr
ScopeEntryType::Normal => { ScopeEntryType::Normal => {
state.stack.push((name.clone(), ScopeEntryType::Normal)); state.stack.push((name.clone(), ScopeEntryType::Normal));
let ident = Ident::new(name, pos); let var_def = Ident::new(name, pos);
Ok(Stmt::Let(Box::new(ident), init_expr, export, token_pos)) Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos))
} }
// const name = { expr:constant } // const name = { expr:constant }
ScopeEntryType::Constant => { ScopeEntryType::Constant => {
state.stack.push((name.clone(), ScopeEntryType::Constant)); state.stack.push((name.clone(), ScopeEntryType::Constant));
let ident = Ident::new(name, pos); let var_def = Ident::new(name, pos);
Ok(Stmt::Const(Box::new(ident), init_expr, export, token_pos)) Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos))
} }
} }
} }
@ -2507,7 +2550,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
externals.iter().for_each(|x| { externals.iter().for_each(|x| {
args.push(Expr::Variable(Box::new((None, None, 0, x.clone())))); args.push(Expr::Variable(Box::new((None, None, 0, x.clone().into()))));
}); });
#[cfg(feature = "no_closure")] #[cfg(feature = "no_closure")]