Skip to content

Commit

Permalink
Harden legacy macros, add support for existsOne macro (#1064)
Browse files Browse the repository at this point in the history
  • Loading branch information
TristonianJones authored Nov 7, 2024
1 parent 8ad600b commit f8ecaa2
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 4 deletions.
15 changes: 15 additions & 0 deletions parser/macro.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -280,6 +285,7 @@ var (
AllMacro,
ExistsMacro,
ExistsOneMacro,
ExistsOneMacroNew,
MapMacro,
MapFilterMacro,
FilterMacro,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand Down
38 changes: 34 additions & 4 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,13 @@ var testCases = []testInfo{
m^#2:*expr.Expr_IdentExpr#.f^#3:*expr.Expr_SelectExpr#
)^#4:has#`,
},
{
I: `has(m)`,
E: `ERROR: <input>:1:5: invalid argument to has() macro
| has(m)
| ....^`,
},

{
I: `m.exists(v, f)`,
P: `__comprehension__(
Expand Down Expand Up @@ -495,7 +502,7 @@ var testCases = []testInfo{
)^#11:all#`,
},
{
I: `m.exists_one(v, f)`,
I: `m.existsOne(v, f)`,
P: `__comprehension__(
// Variable
v,
Expand All @@ -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: <input>:1:14: iteration variable overwrites accumulator variable
| [].existsOne(__result__, __result__)
| .............^`,
},
{
I: `m.map(v, f)`,
Expand Down Expand Up @@ -553,7 +566,12 @@ var testCases = []testInfo{
f^#4:*expr.Expr_IdentExpr#
)^#11:map#`,
},

{
I: `m.map(__result__, __result__)`,
E: `ERROR: <input>:1:7: iteration variable overwrites accumulator variable
| m.map(__result__, __result__)
| ......^`,
},
{
I: `m.map(v, p, f)`,
P: `__comprehension__(
Expand Down Expand Up @@ -618,6 +636,18 @@ var testCases = []testInfo{
p^#4:*expr.Expr_IdentExpr#
)^#13:filter#`,
},
{
I: `m.filter(__result__, false)`,
E: `ERROR: <input>:1:10: iteration variable overwrites accumulator variable
| m.filter(__result__, false)
| .........^`,
},
{
I: `m.filter(a.b, false)`,
E: `ERROR: <input>:1:11: argument is not an identifier
| m.filter(a.b, false)
| ..........^`,
},

// Tests from C++ parser
{
Expand Down

0 comments on commit f8ecaa2

Please sign in to comment.