From f9429c06f9189c9a7e9824d851d8bd3e56df45a3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 23 Mar 2021 18:25:40 +0800 Subject: [PATCH] Use template for object map literal. --- src/ast.rs | 23 +++++++++++------------ src/engine.rs | 12 +++++------- src/optimize.rs | 10 +++++----- src/parser.rs | 4 +++- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index f420ae7a..3651db11 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -26,9 +26,6 @@ use crate::{stdlib::str::FromStr, FLOAT}; #[cfg(not(feature = "no_index"))] use crate::Array; -#[cfg(not(feature = "no_object"))] -use crate::Map; - /// A type representing the access mode of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { @@ -1535,7 +1532,10 @@ pub enum Expr { /// [ expr, ... ] Array(Box>, Position), /// #{ name:expr, ... } - Map(Box>, Position), + Map( + Box<(StaticVec<(Ident, Expr)>, BTreeMap)>, + Position, + ), /// () Unit(Position), /// Variable access - (optional index, optional (hash, modules), variable name) @@ -1594,11 +1594,10 @@ impl Expr { #[cfg(not(feature = "no_object"))] Self::Map(x, _) if self.is_constant() => { - let mut map = Map::new(); - map.extend( - x.iter() - .map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())), - ); + let mut map = x.1.clone(); + x.0.iter().for_each(|(k, v)| { + *map.get_mut(&k.name).unwrap() = v.get_constant_value().unwrap() + }); Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly)) } @@ -1677,7 +1676,7 @@ impl Expr { match self { Self::Array(x, _) => x.iter().all(Self::is_pure), - Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure), + Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure), Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => { x.lhs.is_pure() && x.rhs.is_pure() @@ -1717,7 +1716,7 @@ impl Expr { Self::Array(x, _) => x.iter().all(Self::is_constant), // An map literal is constant if all items are constant - Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant), + Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), _ => false, } @@ -1804,7 +1803,7 @@ impl Expr { } } Self::Map(x, _) => { - for (_, e) in x.as_ref() { + for (_, e) in &x.0 { if !e.walk(path, on_node) { return false; } diff --git a/src/engine.rs b/src/engine.rs index 4d1a9541..a0864b8e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1686,13 +1686,11 @@ impl Engine { #[cfg(not(feature = "no_object"))] Expr::Map(x, _) => { - let mut map = Map::new(); - for (Ident { name: key, .. }, expr) in x.as_ref() { - map.insert( - key.clone(), - self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .flatten(), - ); + let mut map = x.1.clone(); + for (Ident { name: key, .. }, expr) in &x.0 { + *map.get_mut(key).unwrap() = self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .flatten(); } Ok(Dynamic(Union::Map(Box::new(map), AccessMode::ReadWrite))) } diff --git a/src/optimize.rs b/src/optimize.rs index 37ac11ae..383ca44f 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -615,12 +615,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { #[cfg(not(feature = "no_object"))] Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) { // map.string - (Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => { + (Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { let prop = &p.2.name; // 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 == prop) + *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| &x.name == prop) .map(|(_, mut expr)| { expr.set_position(*pos); expr }) .unwrap_or_else(|| Expr::Unit(*pos)); } @@ -645,11 +645,11 @@ 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.0.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) + *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name == *s) .map(|(_, mut expr)| { expr.set_position(*pos); expr }) .unwrap_or_else(|| Expr::Unit(*pos)); } @@ -681,7 +681,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { } // #{ key:value, .. } #[cfg(not(feature = "no_object"))] - Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), + Expr::Map(x, _) => x.0.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)), // lhs && rhs Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) { // true && rhs -> rhs diff --git a/src/parser.rs b/src/parser.rs index de7a507e..c4ab18cb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -690,6 +690,7 @@ fn parse_map_literal( settings.pos = eat_token(input, Token::MapStart); let mut map: StaticVec<(Ident, Expr)> = Default::default(); + let mut template: BTreeMap = Default::default(); loop { const MISSING_RBRACE: &str = "to end this object map literal"; @@ -760,6 +761,7 @@ fn parse_map_literal( let expr = parse_expr(input, state, lib, settings.level_up())?; let name = state.get_interned_string(name); + template.insert(name.clone(), Default::default()); map.push((Ident { name, pos }, expr)); match input.peek().unwrap() { @@ -784,7 +786,7 @@ fn parse_map_literal( } } - Ok(Expr::Map(Box::new(map), settings.pos)) + Ok(Expr::Map(Box::new((map, template)), settings.pos)) } /// Parse a switch expression.