From 7d1b971b39293a88442e10973dd905eb9f430047 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 13 Nov 2020 10:43:54 +0800 Subject: [PATCH] More interned strings. --- src/ast.rs | 50 +++++++++++++------------- src/engine.rs | 13 ++++--- src/optimize.rs | 24 ++++++------- src/parser.rs | 95 +++++++++++++++++++++++++++---------------------- 4 files changed, 95 insertions(+), 87 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 7170b6e8..268b9126 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -98,10 +98,10 @@ pub struct ScriptFnDef { /// Function access mode. pub access: FnAccess, /// Names of function parameters. - pub params: StaticVec, + pub params: StaticVec, /// Access to external variables. #[cfg(not(feature = "no_closure"))] - pub externals: HashSet, + pub externals: HashSet, } impl fmt::Display for ScriptFnDef { @@ -637,10 +637,10 @@ pub enum Stmt { Import(Expr, Option>, Position), /// export var as var, ... #[cfg(not(feature = "no_module"))] - Export(Vec<(Ident, Option)>, Position), + Export(Vec<(IdentX, Option)>, Position), /// Convert a variable to shared. #[cfg(not(feature = "no_closure"))] - Share(Box), + Share(IdentX), } impl Default for Stmt { @@ -915,9 +915,9 @@ pub enum Expr { /// Character constant. CharConstant(char, Position), /// String constant. - StringConstant(Box), + StringConstant(ImmutableString, Position), /// FnPtr constant. - FnPointer(Box), + FnPointer(ImmutableString, Position), /// Variable access - (optional index, optional modules, hash, variable name) Variable(Box<(Option, Option>, u64, IdentX)>), /// Property access - (getter, setter), prop @@ -971,8 +971,8 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, _) => TypeId::of::(), Self::CharConstant(_, _) => TypeId::of::(), - Self::StringConstant(_) => TypeId::of::(), - Self::FnPointer(_) => TypeId::of::(), + Self::StringConstant(_, _) => TypeId::of::(), + Self::FnPointer(_, _) => TypeId::of::(), Self::True(_) | Self::False(_) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) => { TypeId::of::() } @@ -999,9 +999,9 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(x, _) => x.0.into(), Self::CharConstant(x, _) => (*x).into(), - Self::StringConstant(x) => x.name.clone().into(), - Self::FnPointer(x) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked( - x.name.clone(), + Self::StringConstant(x, _) => x.clone().into(), + Self::FnPointer(x, _) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked( + x.clone(), Default::default(), )))), Self::True(_) => true.into(), @@ -1044,8 +1044,8 @@ impl Expr { Self::IntegerConstant(_, pos) => *pos, Self::CharConstant(_, pos) => *pos, - Self::StringConstant(x) => x.pos, - Self::FnPointer(x) => x.pos, + Self::StringConstant(_, pos) => *pos, + Self::FnPointer(_, pos) => *pos, Self::Array(_, pos) => *pos, Self::Map(_, pos) => *pos, Self::Property(x) => (x.1).pos, @@ -1075,8 +1075,8 @@ impl Expr { Self::IntegerConstant(_, pos) => *pos = new_pos, Self::CharConstant(_, pos) => *pos = new_pos, - Self::StringConstant(x) => x.pos = new_pos, - Self::FnPointer(x) => x.pos = new_pos, + Self::StringConstant(_, pos) => *pos = new_pos, + Self::FnPointer(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos, Self::Variable(x) => (x.3).pos = new_pos, @@ -1134,8 +1134,8 @@ impl Expr { Self::IntegerConstant(_, _) | Self::CharConstant(_, _) - | Self::StringConstant(_) - | Self::FnPointer(_) + | Self::StringConstant(_, _) + | Self::FnPointer(_, _) | Self::True(_) | Self::False(_) | Self::Unit(_) => true, @@ -1148,8 +1148,8 @@ impl Expr { // Check in expression Self::In(x, _) => match (&x.lhs, &x.rhs) { - (Self::StringConstant(_), Self::StringConstant(_)) - | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, + (Self::StringConstant(_, _), Self::StringConstant(_, _)) + | (Self::CharConstant(_, _), Self::StringConstant(_, _)) => true, _ => false, }, @@ -1167,8 +1167,8 @@ impl Expr { Self::IntegerConstant(_, _) | Self::CharConstant(_, _) - | Self::StringConstant(_) - | Self::FnPointer(_) + | Self::StringConstant(_, _) + | Self::FnPointer(_, _) | Self::True(_) | Self::False(_) | Self::Unit(_) => true, @@ -1181,8 +1181,8 @@ impl Expr { // Check in expression Self::In(x, _) => match (&x.lhs, &x.rhs) { - (Self::StringConstant(_), Self::StringConstant(_)) - | (Self::CharConstant(_, _), Self::StringConstant(_)) => true, + (Self::StringConstant(_, _), Self::StringConstant(_, _)) + | (Self::CharConstant(_, _), Self::StringConstant(_, _)) => true, _ => false, }, @@ -1200,7 +1200,7 @@ impl Expr { Self::IntegerConstant(_, _) | Self::CharConstant(_, _) - | Self::FnPointer(_) + | Self::FnPointer(_, _) | Self::In(_, _) | Self::And(_, _) | Self::Or(_, _) @@ -1208,7 +1208,7 @@ impl Expr { | Self::False(_) | Self::Unit(_) => false, - Self::StringConstant(_) + Self::StringConstant(_, _) | Self::Stmt(_, _) | Self::FnCall(_, _) | Self::Dot(_, _) diff --git a/src/engine.rs b/src/engine.rs index aac16b22..569c9bd2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1544,11 +1544,9 @@ impl Engine { Expr::IntegerConstant(x, _) => Ok((*x).into()), #[cfg(not(feature = "no_float"))] Expr::FloatConstant(x, _) => Ok(x.0.into()), - Expr::StringConstant(x) => Ok(x.name.clone().into()), + Expr::StringConstant(x, _) => Ok(x.clone().into()), Expr::CharConstant(x, _) => Ok((*x).into()), - Expr::FnPointer(x) => { - Ok(FnPtr::new_unchecked(x.name.clone(), Default::default()).into()) - } + Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()), Expr::Variable(x) if (x.3).name == KEYWORD_THIS => { if let Some(val) = this_ptr { Ok(val.clone()) @@ -2178,13 +2176,14 @@ impl Engine { // Export statement #[cfg(not(feature = "no_module"))] Stmt::Export(list, _) => { - for (Ident { name, pos: id_pos }, rename) in list.iter() { + for (IdentX { name, pos: id_pos }, rename) in list.iter() { // Mark scope variables as public if let Some(index) = scope.get_index(name).map(|(i, _)| i) { let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name); - scope.add_entry_alias(index, alias.clone()); + scope.add_entry_alias(index, alias.to_string()); } else { - return EvalAltResult::ErrorVariableNotFound(name.into(), *id_pos).into(); + return EvalAltResult::ErrorVariableNotFound(name.to_string(), *id_pos) + .into(); } } Ok(Default::default()) diff --git a/src/optimize.rs b/src/optimize.rs index afe189b8..af617d83 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -502,19 +502,19 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { *expr = result; } // map[string] - (Expr::Map(m, pos), Expr::StringConstant(s)) if m.iter().all(|(_, x)| x.is_pure()) => { + (Expr::Map(m, pos), Expr::StringConstant(s, _)) if m.iter().all(|(_, x)| x.is_pure()) => { // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - *expr = mem::take(m).into_iter().find(|(x, _)| x.name == s.name) + *expr = mem::take(m).into_iter().find(|(x, _)| x.name == *s) .map(|(_, mut expr)| { expr.set_position(*pos); expr }) .unwrap_or_else(|| Expr::Unit(*pos)); } // string[int] - (Expr::StringConstant(s), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.name.chars().count() => { + (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => { // String literal indexing - get the character state.set_dirty(); - *expr = Expr::CharConstant(s.name.chars().nth(*i as usize).unwrap(), s.pos); + *expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos); } // var[rhs] (Expr::Variable(_), rhs) => optimize_expr(rhs, state), @@ -530,22 +530,22 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // lhs in rhs Expr::In(x, _) => match (&mut x.lhs, &mut x.rhs) { // "xxx" in "xxxxx" - (Expr::StringConstant(a), Expr::StringConstant(b)) => { + (Expr::StringConstant(a, pos), Expr::StringConstant(b, _)) => { state.set_dirty(); - *expr = if b.name.contains(a.name.as_str()) { Expr::True(a.pos) } else { Expr::False(a.pos) }; + *expr = if b.contains(a.as_str()) { Expr::True(*pos) } else { Expr::False(*pos) }; } // 'x' in "xxxxx" - (Expr::CharConstant(a, pos), Expr::StringConstant(b)) => { + (Expr::CharConstant(a, pos), Expr::StringConstant(b, _)) => { state.set_dirty(); - *expr = if b.name.contains(*a) { Expr::True(*pos) } else { Expr::False(*pos) }; + *expr = if b.contains(*a) { Expr::True(*pos) } else { Expr::False(*pos) }; } // "xxx" in #{...} - (Expr::StringConstant(a), Expr::Map(b, _)) => { + (Expr::StringConstant(a, pos), Expr::Map(b, _)) => { state.set_dirty(); - *expr = if b.iter().find(|(x, _)| x.name == a.name).is_some() { - Expr::True(a.pos) + *expr = if b.iter().find(|(x, _)| x.name == *a).is_some() { + Expr::True(*pos) } else { - Expr::False(a.pos) + Expr::False(*pos) }; } // 'x' in #{...} diff --git a/src/parser.rs b/src/parser.rs index 68a63177..0d694065 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -66,10 +66,10 @@ struct ParseState<'e> { /// Interned strings. strings: HashMap, /// Encapsulates a local stack with variable names to simulate an actual runtime scope. - stack: Vec<(String, ScopeEntryType)>, + stack: Vec<(ImmutableString, ScopeEntryType)>, /// Tracks a list of external variables (variables that are not explicitly declared in the scope). #[cfg(not(feature = "no_closure"))] - externals: HashMap, + externals: HashMap, /// An indicator that disables variable capturing into externals one single time /// up until the nearest consumed Identifier token. /// If set to false the next call to `access_var` will not capture the variable. @@ -78,7 +78,7 @@ struct ParseState<'e> { allow_capture: bool, /// Encapsulates a local stack with imported module names. #[cfg(not(feature = "no_module"))] - modules: Vec, + modules: Vec, /// Maximum levels of expression nesting. #[cfg(not(feature = "unchecked"))] max_expr_depth: usize, @@ -136,7 +136,7 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_closure"))] if self.allow_capture { if index.is_none() && !self.externals.contains_key(name) { - self.externals.insert(name.to_string(), _pos); + self.externals.insert(name.into(), _pos); } } else { self.allow_capture = true @@ -162,20 +162,24 @@ impl<'e> ParseState<'e> { .iter() .rev() .enumerate() - .find(|(_, n)| *n == name) + .find(|(_, n)| **n == name) .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 { + pub fn get_interned_string(&mut self, text: S) -> ImmutableString + where + S: AsRef + Into, + { #[allow(clippy::map_entry)] - if !self.strings.contains_key(&text) { - let value: ImmutableString = text.clone().into(); + if !self.strings.contains_key(text.as_ref()) { + let value: ImmutableString = text.into(); let result = value.clone(); - self.strings.insert(text, value); + let key = value.to_string(); + self.strings.insert(key, value); result } else { - self.strings.get(&text).unwrap().clone() + self.strings.get(text.as_ref()).unwrap().clone() } } } @@ -456,7 +460,7 @@ fn parse_index_chain( .into_err(*pos)) } Expr::IntegerConstant(_, pos) => match lhs { - Expr::Array(_, _) | Expr::StringConstant(_) => (), + Expr::Array(_, _) | Expr::StringConstant(_, _) => (), Expr::Map(_, _) => { return Err(PERR::MalformedIndexExpr( @@ -490,14 +494,14 @@ fn parse_index_chain( }, // lhs[string] - Expr::StringConstant(x) => match lhs { + Expr::StringConstant(_, pos) => match lhs { Expr::Map(_, _) => (), - Expr::Array(_, _) | Expr::StringConstant(_) => { + Expr::Array(_, _) | Expr::StringConstant(_, _) => { return Err(PERR::MalformedIndexExpr( "Array or string expects numeric index, not a string".into(), ) - .into_err(x.pos)) + .into_err(*pos)) } #[cfg(not(feature = "no_float"))] @@ -817,10 +821,9 @@ fn parse_primary( #[cfg(not(feature = "no_float"))] Token::FloatConstant(x) => Expr::FloatConstant(FloatWrapper(x), settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), - Token::StringConstant(s) => Expr::StringConstant(Box::new(IdentX::new( - state.get_interned_string(s), - settings.pos, - ))), + Token::StringConstant(s) => { + Expr::StringConstant(state.get_interned_string(s), settings.pos) + } // Function call Token::Identifier(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { @@ -1345,19 +1348,19 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), + (Expr::StringConstant(_, _), Expr::StringConstant(_, _)) + | (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (), // 123.456 in "xxxx" #[cfg(not(feature = "no_float"))] - (x @ Expr::FloatConstant(_, _), Expr::StringConstant(_)) => { + (x @ Expr::FloatConstant(_, _), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a float".into(), ) .into_err(x.position())) } // 123 in "xxxx" - (x @ Expr::IntegerConstant(_, _), Expr::StringConstant(_)) => { + (x @ Expr::IntegerConstant(_, _), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a number".into(), ) @@ -1365,32 +1368,32 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result { + (x @ Expr::And(_, _), Expr::StringConstant(_, _)) + | (x @ Expr::Or(_, _), Expr::StringConstant(_, _)) + | (x @ Expr::In(_, _), Expr::StringConstant(_, _)) + | (x @ Expr::True(_), Expr::StringConstant(_, _)) + | (x @ Expr::False(_), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not a boolean".into(), ) .into_err(x.position())) } // [???, ???, ???] in "xxxx" - (x @ Expr::Array(_, _), Expr::StringConstant(_)) => { + (x @ Expr::Array(_, _), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not an array".into(), ) .into_err(x.position())) } // #{...} in "xxxx" - (x @ Expr::Map(_, _), Expr::StringConstant(_)) => { + (x @ Expr::Map(_, _), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not an object map".into(), ) .into_err(x.position())) } // () in "xxxx" - (x @ Expr::Unit(_), Expr::StringConstant(_)) => { + (x @ Expr::Unit(_), Expr::StringConstant(_, _)) => { return Err(PERR::MalformedInExpr( "'in' expression for a string expects a string, not ()".into(), ) @@ -1398,7 +1401,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result (), // 123.456 in #{...} @@ -1664,7 +1667,7 @@ fn parse_custom_syntax( delta if delta > 0 => { state.stack.resize( state.stack.len() + delta as usize, - ("".to_string(), ScopeEntryType::Normal), + ("".into(), ScopeEntryType::Normal), ); } delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(), @@ -1944,8 +1947,9 @@ fn parse_for( ensure_not_statement_expr(input, "a boolean")?; let expr = parse_expr(input, state, lib, settings.level_up())?; + let loop_var = state.get_interned_string(name.clone()); let prev_stack_len = state.stack.len(); - state.stack.push((name.clone(), ScopeEntryType::Normal)); + state.stack.push((loop_var, ScopeEntryType::Normal)); settings.is_breakable = true; let body = parse_block(input, state, lib, settings.level_up())?; @@ -1992,13 +1996,15 @@ fn parse_let( match var_type { // let name = expr ScopeEntryType::Normal => { - state.stack.push((name.clone(), ScopeEntryType::Normal)); + let var_name = state.get_interned_string(name.clone()); + state.stack.push((var_name, ScopeEntryType::Normal)); let var_def = Ident::new(name, pos); Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos)) } // const name = { expr:constant } ScopeEntryType::Constant => { - state.stack.push((name.clone(), ScopeEntryType::Constant)); + let var_name = state.get_interned_string(name.clone()); + state.stack.push((var_name, ScopeEntryType::Constant)); let var_def = Ident::new(name, pos); Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos)) } @@ -2038,6 +2044,7 @@ fn parse_import( (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; + let name = state.get_interned_string(name); state.modules.push(name.clone()); Ok(Stmt::Import( @@ -2091,7 +2098,7 @@ fn parse_export( let rename = if match_token(input, Token::As).0 { match input.next().unwrap() { - (Token::Identifier(s), pos) => Some(Ident::new(s.clone(), pos)), + (Token::Identifier(s), pos) => Some(IdentX::new(state.get_interned_string(s), pos)), (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); } @@ -2102,7 +2109,7 @@ fn parse_export( None }; - exports.push((Ident::new(id, id_pos), rename)); + exports.push((IdentX::new(state.get_interned_string(id), id_pos), rename)); match input.peek().unwrap() { (Token::Comma, _) => { @@ -2464,6 +2471,7 @@ fn parse_fn( match input.next().unwrap() { (Token::RightParen, _) => break, (Token::Identifier(s), pos) => { + let s = state.get_interned_string(s); state.stack.push((s.clone(), ScopeEntryType::Normal)); params.push((s, pos)) } @@ -2538,7 +2546,7 @@ fn parse_fn( /// Creates a curried expression from a list of external variables #[cfg(not(feature = "no_function"))] -fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Position) -> Expr { +fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Position) -> Expr { if externals.is_empty() { return fn_expr; } @@ -2577,7 +2585,7 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po // Statement block let mut statements: StaticVec<_> = Default::default(); // Insert `Share` statements - statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x)))); + statements.extend(externals.into_iter().map(|x| Stmt::Share(x))); // Final expression statements.push(Stmt::Expr(expr)); Expr::Stmt(Box::new(statements), pos) @@ -2606,6 +2614,7 @@ fn parse_anon_fn( match input.next().unwrap() { (Token::Pipe, _) => break, (Token::Identifier(s), pos) => { + let s = state.get_interned_string(s); state.stack.push((s.clone(), ScopeEntryType::Normal)); params.push((s, pos)) } @@ -2656,13 +2665,13 @@ fn parse_anon_fn( // External variables may need to be processed in a consistent order, // so extract them into a list. - let externals: StaticVec = { + let externals: StaticVec = { #[cfg(not(feature = "no_closure"))] { state .externals .iter() - .map(|(k, &v)| Ident::new(k.clone(), v)) + .map(|(k, &v)| IdentX::new(k.clone(), v)) .collect() } #[cfg(feature = "no_closure")] @@ -2706,7 +2715,7 @@ fn parse_anon_fn( mods: Default::default(), }; - let expr = Expr::FnPointer(Box::new(IdentX::new(fn_name, settings.pos))); + let expr = Expr::FnPointer(fn_name, settings.pos); let expr = if cfg!(not(feature = "no_closure")) { make_curry_from_externals(expr, externals, settings.pos) @@ -2860,7 +2869,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { Union::Unit(_) => Some(Expr::Unit(pos)), Union::Int(value) => Some(Expr::IntegerConstant(value, pos)), Union::Char(value) => Some(Expr::CharConstant(value, pos)), - Union::Str(value) => Some(Expr::StringConstant(Box::new(IdentX::new(value, pos)))), + Union::Str(value) => Some(Expr::StringConstant(value, pos)), Union::Bool(true) => Some(Expr::True(pos)), Union::Bool(false) => Some(Expr::False(pos)), #[cfg(not(feature = "no_index"))]