diff --git a/src/engine.rs b/src/engine.rs index 4fd38ab6..f38cb8c4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -893,7 +893,7 @@ impl Engine { match dot_lhs { // id.??? or id[???] - Expr::Variable(id, pos) => { + Expr::Variable(id, _, pos) => { let (target, typ) = search_scope(scope, id, *pos)?; // Constants cannot be modified @@ -1129,7 +1129,7 @@ impl Engine { Expr::FloatConstant(f, _) => Ok((*f).into()), Expr::StringConstant(s, _) => Ok(s.to_string().into()), Expr::CharConstant(c, _) => Ok((*c).into()), - Expr::Variable(id, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()), + Expr::Variable(id, _, pos) => search_scope(scope, id, *pos).map(|(v, _)| v.clone()), Expr::Property(_, _) => panic!("unexpected property."), // Statement block @@ -1141,7 +1141,7 @@ impl Engine { match lhs.as_ref() { // name = rhs - Expr::Variable(name, pos) => match scope.get(name) { + Expr::Variable(name, _, pos) => match scope.get(name) { None => { return Err(Box::new(EvalAltResult::ErrorVariableNotFound( name.to_string(), diff --git a/src/optimize.rs b/src/optimize.rs index 3f00e711..f61bef36 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -368,12 +368,12 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { //id = id2 = expr2 Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) { // var = var = expr2 -> var = expr2 - (Expr::Variable(var, _), Expr::Variable(var2, _)) if var == var2 => { + (Expr::Variable(var, sp, _), Expr::Variable(var2, sp2, _)) if var == var2 && sp == sp2 => { // Assignment to the same variable - fold state.set_dirty(); Expr::Assignment( - Box::new(Expr::Variable(var, pos)), + Box::new(Expr::Variable(var, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos, ) @@ -397,13 +397,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { #[cfg(not(feature = "no_object"))] Expr::Dot(lhs, rhs, pos) => match (*lhs, *rhs) { // map.string - (Expr::Map(items, pos), Expr::Property(s, _)) - if items.iter().all(|(_, x, _)| x.is_pure()) => - { + (Expr::Map(items, pos), Expr::Property(s, _)) if items.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(); - items.into_iter().find(|(name, _, _)| name == s.as_ref()) + items.into_iter().find(|(name, _, _)| name == &s) .map(|(_, expr, _)| expr.set_position(pos)) .unwrap_or_else(|| Expr::Unit(pos)) } @@ -428,13 +426,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { items.remove(i as usize).set_position(pos) } // map[string] - (Expr::Map(items, pos), Expr::StringConstant(s, _)) - if items.iter().all(|(_, x, _)| x.is_pure()) => - { + (Expr::Map(items, pos), Expr::StringConstant(s, _)) if items.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(); - items.into_iter().find(|(name, _, _)| name == s.as_ref()) + items.into_iter().find(|(name, _, _)| name == &s) .map(|(_, expr, _)| expr.set_position(pos)) .unwrap_or_else(|| Expr::Unit(pos)) } @@ -470,7 +466,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { // "xxx" in "xxxxx" (Expr::StringConstant(lhs, pos), Expr::StringConstant(rhs, _)) => { state.set_dirty(); - if rhs.contains(lhs.as_ref()) { + if rhs.contains(&lhs) { Expr::True(pos) } else { Expr::False(pos) @@ -613,7 +609,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos), // constant-name - Expr::Variable(name, pos) if state.contains_constant(&name) => { + Expr::Variable(name, _, pos) if state.contains_constant(&name) => { state.set_dirty(); // Replace constant with value diff --git a/src/parser.rs b/src/parser.rs index 36cf4fd0..fd58cff0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,7 +12,6 @@ use crate::stdlib::{ boxed::Box, char, collections::HashMap, - fmt::Display, format, iter::Peekable, ops::Add, @@ -196,11 +195,11 @@ pub enum Stmt { /// loop { stmt } Loop(Box), /// for id in expr { stmt } - For(Cow<'static, str>, Box, Box), + For(String, Box, Box), /// let id = expr - Let(Cow<'static, str>, Option>, Position), + Let(String, Option>, Position), /// const id = expr - Const(Cow<'static, str>, Box, Position), + Const(String, Box, Position), /// { stmt; ... } Block(Vec, Position), /// { stmt } @@ -280,14 +279,16 @@ pub enum Expr { /// Character constant. CharConstant(char, Position), /// String constant. - StringConstant(Cow<'static, str>, Position), + StringConstant(String, Position), /// Variable access. - Variable(Cow<'static, str>, Position), + Variable(String, usize, Position), /// Property access. - Property(Cow<'static, str>, Position), + Property(String, Position), /// { stmt } Stmt(Box, Position), /// func(expr, ... ) + /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls + /// and the function names are predictable, so no need to allocate a new `String`. FunctionCall(Cow<'static, str>, Vec, Option, Position), /// expr = expr Assignment(Box, Box, Position), @@ -325,7 +326,7 @@ impl Expr { #[cfg(not(feature = "no_float"))] Self::FloatConstant(f, _) => (*f).into(), Self::CharConstant(c, _) => (*c).into(), - Self::StringConstant(s, _) => s.to_string().into(), + Self::StringConstant(s, _) => s.clone().into(), Self::True(_) => true.into(), Self::False(_) => false.into(), Self::Unit(_) => ().into(), @@ -382,7 +383,7 @@ impl Expr { | Self::StringConstant(_, pos) | Self::Array(_, pos) | Self::Map(_, pos) - | Self::Variable(_, pos) + | Self::Variable(_, _, pos) | Self::Property(_, pos) | Self::Stmt(_, pos) | Self::FunctionCall(_, _, _, pos) @@ -408,7 +409,7 @@ impl Expr { | Self::StringConstant(_, pos) | Self::Array(_, pos) | Self::Map(_, pos) - | Self::Variable(_, pos) + | Self::Variable(_, _, pos) | Self::Property(_, pos) | Self::Stmt(_, pos) | Self::FunctionCall(_, _, _, pos) @@ -439,7 +440,7 @@ impl Expr { Self::Stmt(stmt, _) => stmt.is_pure(), - Self::Variable(_, _) => true, + Self::Variable(_, _, _) => true, expr => expr.is_constant(), } @@ -498,7 +499,7 @@ impl Expr { _ => false, }, - Self::Variable(_, _) | Self::Property(_, _) => match token { + Self::Variable(_, _, _) | Self::Property(_, _) => match token { Token::LeftBracket | Token::LeftParen => true, _ => false, }, @@ -508,7 +509,7 @@ impl Expr { /// Convert a `Variable` into a `Property`. All other variants are untouched. pub(crate) fn into_property(self) -> Self { match self { - Self::Variable(id, pos) => Self::Property(id, pos), + Self::Variable(id, _, pos) => Self::Property(id, pos), _ => self, } } @@ -567,8 +568,8 @@ fn parse_paren_expr<'a>( } /// Parse a function call. -fn parse_call_expr<'a, S: Into> + Display>( - id: S, +fn parse_call_expr<'a>( + id: String, input: &mut Peekable>, begin: Position, allow_stmt_expr: bool, @@ -916,8 +917,8 @@ fn parse_primary<'a>( #[cfg(not(feature = "no_float"))] Token::FloatConstant(x) => Expr::FloatConstant(x, pos), Token::CharConstant(c) => Expr::CharConstant(c, pos), - Token::StringConst(s) => Expr::StringConstant(s.into(), pos), - Token::Identifier(s) => Expr::Variable(s.into(), pos), + Token::StringConst(s) => Expr::StringConstant(s, pos), + Token::Identifier(s) => Expr::Variable(s, 0, pos), Token::LeftParen => parse_paren_expr(input, pos, allow_stmt_expr)?, #[cfg(not(feature = "no_index"))] Token::LeftBracket => parse_array_literal(input, pos, allow_stmt_expr)?, @@ -943,7 +944,7 @@ fn parse_primary<'a>( root_expr = match (root_expr, token) { // Function call - (Expr::Variable(id, pos), Token::LeftParen) + (Expr::Variable(id, _, pos), Token::LeftParen) | (Expr::Property(id, pos), Token::LeftParen) => { parse_call_expr(id, input, pos, allow_stmt_expr)? } @@ -1078,7 +1079,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position, is_index: bool) -> Expr idx_pos, ), // lhs.id - (lhs, rhs @ Expr::Variable(_, _)) | (lhs, rhs @ Expr::Property(_, _)) => { + (lhs, rhs @ Expr::Variable(_, _, _)) | (lhs, rhs @ Expr::Property(_, _)) => { let lhs = if is_index { lhs.into_property() } else { lhs }; Expr::Dot(Box::new(lhs), Box::new(rhs.into_property()), op_pos) } @@ -1479,7 +1480,7 @@ fn parse_for<'a>( let expr = parse_expr(input, allow_stmt_expr)?; let body = parse_block(input, true, allow_stmt_expr)?; - Ok(Stmt::For(name.into(), Box::new(expr), Box::new(body))) + Ok(Stmt::For(name, Box::new(expr), Box::new(body))) } /// Parse a variable definition statement. @@ -1505,10 +1506,10 @@ fn parse_let<'a>( match var_type { // let name = expr - ScopeEntryType::Normal => Ok(Stmt::Let(name.into(), Some(Box::new(init_value)), pos)), + ScopeEntryType::Normal => Ok(Stmt::Let(name, Some(Box::new(init_value)), pos)), // const name = { expr:constant } ScopeEntryType::Constant if init_value.is_constant() => { - Ok(Stmt::Const(name.into(), Box::new(init_value), pos)) + Ok(Stmt::Const(name, Box::new(init_value), pos)) } // const name = expr - error ScopeEntryType::Constant => { @@ -1517,7 +1518,7 @@ fn parse_let<'a>( } } else { // let name - Ok(Stmt::Let(name.into(), None, pos)) + Ok(Stmt::Let(name, None, pos)) } } @@ -1840,7 +1841,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((*value).into(), pos)), + Union::Str(value) => Some(Expr::StringConstant((*value).clone(), pos)), Union::Bool(true) => Some(Expr::True(pos)), Union::Bool(false) => Some(Expr::False(pos)), #[cfg(not(feature = "no_index"))]