diff --git a/src/optimize.rs b/src/optimize.rs index fef42473..89825480 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -385,25 +385,51 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { // id = expr expr => Expr::Assignment(id, Box::new(optimize_expr(expr, state)), pos), }, + // lhs.rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(lhs, rhs, pos) => Expr::Dot( - Box::new(optimize_expr(*lhs, state)), - Box::new(optimize_expr(*rhs, state)), - pos, - ), + 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()) => + { + // 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()) + .map(|(_, expr, _)| expr.set_position(pos)) + .unwrap_or_else(|| Expr::Unit(pos)) + } + // lhs.rhs + (lhs, rhs) => Expr::Dot( + Box::new(optimize_expr(lhs, state)), + Box::new(optimize_expr(rhs, state)), + pos, + ) + } // lhs[rhs] #[cfg(not(feature = "no_index"))] Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) { // array[int] - (Expr::Array(mut items, _), Expr::IntegerConstant(i, _)) - if i >= 0 && (i as usize) < items.len() && items.iter().all(|x| x.is_pure()) => + (Expr::Array(mut items, pos), Expr::IntegerConstant(i, _)) + if i >= 0 && (i as usize) < items.len() && items.iter().all(Expr::is_pure) => { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - items.remove(i as usize) + 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()) => + { + // 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()) + .map(|(_, expr, _)| expr.set_position(pos)) + .unwrap_or_else(|| Expr::Unit(pos)) } // string[int] (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) @@ -580,11 +606,11 @@ 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, _) if state.contains_constant(&name) => { + Expr::Variable(name, pos) if state.contains_constant(&name) => { state.set_dirty(); // Replace constant with value - state.find_constant(&name).expect("should find constant in scope!").clone() + state.find_constant(&name).expect("should find constant in scope!").clone().set_position(pos) } // All other expressions - skip diff --git a/src/parser.rs b/src/parser.rs index 21f04e1f..d46acef5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -511,6 +511,33 @@ impl Expr { } } + /// Get the `Position` of the expression. + pub(crate) fn set_position(mut self, new_pos: Position) -> Self { + match &mut self { + Self::IntegerConstant(_, pos) + | Self::FloatConstant(_, pos) + | Self::CharConstant(_, pos) + | Self::StringConstant(_, pos) + | Self::Array(_, pos) + | Self::Map(_, pos) + | Self::Variable(_, pos) + | Self::Property(_, pos) + | Self::Stmt(_, pos) + | Self::FunctionCall(_, _, _, pos) + | Self::And(_, _, pos) + | Self::Or(_, _, pos) + | Self::In(_, _, pos) + | Self::True(pos) + | Self::False(pos) + | Self::Unit(pos) + | Self::Assignment(_, _, pos) + | Self::Dot(_, _, pos) + | Self::Index(_, _, pos) => *pos = new_pos, + } + + self + } + /// Is the expression pure? /// /// A pure expression has no side effects. diff --git a/src/result.rs b/src/result.rs index ce2b1b87..ddb56f0b 100644 --- a/src/result.rs +++ b/src/result.rs @@ -107,15 +107,15 @@ impl EvalAltResult { Self::ErrorArrayBounds(_, index, _) if *index < 0 => { "Array access expects non-negative index" } - Self::ErrorArrayBounds(0, _, _) => "Access of empty array", + Self::ErrorArrayBounds(0, _, _) => "Empty array has nothing to access", Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds", Self::ErrorStringBounds(_, index, _) if *index < 0 => { "Indexing a string expects a non-negative index" } - Self::ErrorStringBounds(0, _, _) => "Indexing of empty string", + Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index", Self::ErrorStringBounds(_, _, _) => "String index out of bounds", - Self::ErrorLogicGuard(_) => "Boolean expression expected", - Self::ErrorFor(_) => "For loop expects array or range", + Self::ErrorLogicGuard(_) => "Boolean value expected", + Self::ErrorFor(_) => "For loop expects an array, object map, or range", Self::ErrorVariableNotFound(_, _) => "Variable not found", Self::ErrorAssignmentToUnknownLHS(_) => { "Assignment to an unsupported left-hand side expression"