diff --git a/parser/macro.go b/parser/macro.go index dc47b420..dcd23225 100644 --- a/parser/macro.go +++ b/parser/macro.go @@ -259,8 +259,13 @@ var ( // ExistsOneMacro expands "range.exists_one(var, predicate)", which is true if for exactly one // element in range the predicate holds. + // Deprecated: Use ExistsOneMacroNew ExistsOneMacro = NewReceiverMacro(operators.ExistsOne, 2, MakeExistsOne) + // ExistsOneMacroNew expands "range.existsOne(var, predicate)", which is true if for exactly one + // element in range the predicate holds. + ExistsOneMacroNew = NewReceiverMacro("existsOne", 2, MakeExistsOne) + // MapMacro expands "range.map(var, function)" into a comprehension which applies the function // to each element in the range to produce a new list. MapMacro = NewReceiverMacro(operators.Map, 2, MakeMap) @@ -280,6 +285,7 @@ var ( AllMacro, ExistsMacro, ExistsOneMacro, + ExistsOneMacroNew, MapMacro, MapFilterMacro, FilterMacro, @@ -336,6 +342,9 @@ func MakeMap(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common if !found { return nil, eh.NewError(args[0].ID(), "argument is not an identifier") } + if v == AccumulatorName { + return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable") + } var fn ast.Expr var filter ast.Expr @@ -366,6 +375,9 @@ func MakeFilter(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *com if !found { return nil, eh.NewError(args[0].ID(), "argument is not an identifier") } + if v == AccumulatorName { + return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable") + } filter := args[1] init := eh.NewList() @@ -389,6 +401,9 @@ func makeQuantifier(kind quantifierKind, eh ExprHelper, target ast.Expr, args [] if !found { return nil, eh.NewError(args[0].ID(), "argument must be a simple name") } + if v == AccumulatorName { + return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable") + } var init ast.Expr var condition ast.Expr diff --git a/parser/parser_test.go b/parser/parser_test.go index 72bb587f..f174c8fe 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -438,6 +438,13 @@ var testCases = []testInfo{ m^#2:*expr.Expr_IdentExpr#.f^#3:*expr.Expr_SelectExpr# )^#4:has#`, }, + { + I: `has(m)`, + E: `ERROR: :1:5: invalid argument to has() macro + | has(m) + | ....^`, + }, + { I: `m.exists(v, f)`, P: `__comprehension__( @@ -495,7 +502,7 @@ var testCases = []testInfo{ )^#11:all#`, }, { - I: `m.exists_one(v, f)`, + I: `m.existsOne(v, f)`, P: `__comprehension__( // Variable v, @@ -521,10 +528,16 @@ var testCases = []testInfo{ __result__^#12:*expr.Expr_IdentExpr#, 1^#13:*expr.Constant_Int64Value# )^#14:*expr.Expr_CallExpr#)^#15:*expr.Expr_ComprehensionExpr#`, - M: `m^#1:*expr.Expr_IdentExpr#.exists_one( + M: `m^#1:*expr.Expr_IdentExpr#.existsOne( v^#3:*expr.Expr_IdentExpr#, f^#4:*expr.Expr_IdentExpr# - )^#15:exists_one#`, + )^#15:existsOne#`, + }, + { + I: `[].existsOne(__result__, __result__)`, + E: `ERROR: :1:14: iteration variable overwrites accumulator variable + | [].existsOne(__result__, __result__) + | .............^`, }, { I: `m.map(v, f)`, @@ -553,7 +566,12 @@ var testCases = []testInfo{ f^#4:*expr.Expr_IdentExpr# )^#11:map#`, }, - + { + I: `m.map(__result__, __result__)`, + E: `ERROR: :1:7: iteration variable overwrites accumulator variable + | m.map(__result__, __result__) + | ......^`, + }, { I: `m.map(v, p, f)`, P: `__comprehension__( @@ -618,6 +636,18 @@ var testCases = []testInfo{ p^#4:*expr.Expr_IdentExpr# )^#13:filter#`, }, + { + I: `m.filter(__result__, false)`, + E: `ERROR: :1:10: iteration variable overwrites accumulator variable + | m.filter(__result__, false) + | .........^`, + }, + { + I: `m.filter(a.b, false)`, + E: `ERROR: :1:11: argument is not an identifier + | m.filter(a.b, false) + | ..........^`, + }, // Tests from C++ parser {