blob: 4d5874b65bca8b032675ed52e205f36418b7623b [file]
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"fmt"
"reflect"
"sort"
"strings"
"testing"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/debug"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/test"
)
var testCases = []testInfo{
{
I: `"A"`,
P: `"A"^#1:*expr.Constant_StringValue#`,
},
{
I: `true`,
P: `true^#1:*expr.Constant_BoolValue#`,
},
{
I: `false`,
P: `false^#1:*expr.Constant_BoolValue#`,
},
{
I: `0`,
P: `0^#1:*expr.Constant_Int64Value#`,
},
{
I: `42`,
P: `42^#1:*expr.Constant_Int64Value#`,
},
{
I: `0xF`,
P: `15^#1:*expr.Constant_Int64Value#`,
},
{
I: `0u`,
P: `0u^#1:*expr.Constant_Uint64Value#`,
},
{
I: `23u`,
P: `23u^#1:*expr.Constant_Uint64Value#`,
},
{
I: `24u`,
P: `24u^#1:*expr.Constant_Uint64Value#`,
},
{
I: `0xFu`,
P: `15u^#1:*expr.Constant_Uint64Value#`,
},
{
I: `-1`,
P: `-1^#1:*expr.Constant_Int64Value#`,
},
{
I: `4--4`,
P: `_-_(
4^#1:*expr.Constant_Int64Value#,
-4^#3:*expr.Constant_Int64Value#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `4--4.1`,
P: `_-_(
4^#1:*expr.Constant_Int64Value#,
-4.1^#3:*expr.Constant_DoubleValue#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `b"abc"`,
P: `b"abc"^#1:*expr.Constant_BytesValue#`,
},
{
I: `23.39`,
P: `23.39^#1:*expr.Constant_DoubleValue#`,
},
{
I: `!a`,
P: `!_(
a^#2:*expr.Expr_IdentExpr#
)^#1:*expr.Expr_CallExpr#`,
},
{
I: `null`,
P: `null^#1:*expr.Constant_NullValue#`,
},
{
I: `a`,
P: `a^#1:*expr.Expr_IdentExpr#`,
},
{
I: `a?b:c`,
P: `_?_:_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#,
c^#4:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a || b`,
P: `_||_(
a^#1:*expr.Expr_IdentExpr#,
b^#2:*expr.Expr_IdentExpr#
)^#3:*expr.Expr_CallExpr#`,
},
{
I: `a || b || c || d || e || f `,
P: ` _||_(
_||_(
_||_(
a^#1:*expr.Expr_IdentExpr#,
b^#2:*expr.Expr_IdentExpr#
)^#3:*expr.Expr_CallExpr#,
c^#4:*expr.Expr_IdentExpr#
)^#5:*expr.Expr_CallExpr#,
_||_(
_||_(
d^#6:*expr.Expr_IdentExpr#,
e^#8:*expr.Expr_IdentExpr#
)^#9:*expr.Expr_CallExpr#,
f^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#
)^#7:*expr.Expr_CallExpr#`,
},
{
I: `a && b`,
P: `_&&_(
a^#1:*expr.Expr_IdentExpr#,
b^#2:*expr.Expr_IdentExpr#
)^#3:*expr.Expr_CallExpr#`,
},
{
I: `a && b && c && d && e && f && g`,
P: `_&&_(
_&&_(
_&&_(
a^#1:*expr.Expr_IdentExpr#,
b^#2:*expr.Expr_IdentExpr#
)^#3:*expr.Expr_CallExpr#,
_&&_(
c^#4:*expr.Expr_IdentExpr#,
d^#6:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#
)^#5:*expr.Expr_CallExpr#,
_&&_(
_&&_(
e^#8:*expr.Expr_IdentExpr#,
f^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#,
g^#12:*expr.Expr_IdentExpr#
)^#13:*expr.Expr_CallExpr#
)^#9:*expr.Expr_CallExpr#`,
},
{
I: `a && b && c && d || e && f && g && h`,
P: `_||_(
_&&_(
_&&_(
a^#1:*expr.Expr_IdentExpr#,
b^#2:*expr.Expr_IdentExpr#
)^#3:*expr.Expr_CallExpr#,
_&&_(
c^#4:*expr.Expr_IdentExpr#,
d^#6:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#
)^#5:*expr.Expr_CallExpr#,
_&&_(
_&&_(
e^#8:*expr.Expr_IdentExpr#,
f^#9:*expr.Expr_IdentExpr#
)^#10:*expr.Expr_CallExpr#,
_&&_(
g^#11:*expr.Expr_IdentExpr#,
h^#13:*expr.Expr_IdentExpr#
)^#14:*expr.Expr_CallExpr#
)^#12:*expr.Expr_CallExpr#
)^#15:*expr.Expr_CallExpr#`,
},
{
I: `a + b`,
P: `_+_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a - b`,
P: `_-_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a * b`,
P: `_*_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a / b`,
P: `_/_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a % b`,
P: `_%_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a in b`,
P: `@in(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a == b`,
P: `_==_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a != b`,
P: ` _!=_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a > b`,
P: `_>_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a >= b`,
P: `_>=_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a < b`,
P: `_<_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a <= b`,
P: `_<=_(
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a.b`,
P: `a^#1:*expr.Expr_IdentExpr#.b^#2:*expr.Expr_SelectExpr#`,
},
{
I: `a.b.c`,
P: `a^#1:*expr.Expr_IdentExpr#.b^#2:*expr.Expr_SelectExpr#.c^#3:*expr.Expr_SelectExpr#`,
},
{
I: `a[b]`,
P: `_[_](
a^#1:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `foo{ }`,
P: `foo{}^#1:*expr.Expr_StructExpr#`,
},
{
I: `foo{ a:b }`,
P: `foo{
a:b^#3:*expr.Expr_IdentExpr#^#2:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `foo{ a:b, c:d }`,
P: `foo{
a:b^#3:*expr.Expr_IdentExpr#^#2:*expr.Expr_CreateStruct_Entry#,
c:d^#5:*expr.Expr_IdentExpr#^#4:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `{}`,
P: `{}^#1:*expr.Expr_StructExpr#`,
},
{
I: `{a:b, c:d}`,
P: `{
a^#3:*expr.Expr_IdentExpr#:b^#4:*expr.Expr_IdentExpr#^#2:*expr.Expr_CreateStruct_Entry#,
c^#6:*expr.Expr_IdentExpr#:d^#7:*expr.Expr_IdentExpr#^#5:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `[]`,
P: `[]^#1:*expr.Expr_ListExpr#`,
},
{
I: `[a]`,
P: `[
a^#2:*expr.Expr_IdentExpr#
]^#1:*expr.Expr_ListExpr#`,
},
{
I: `[a, b, c]`,
P: `[
a^#2:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#,
c^#4:*expr.Expr_IdentExpr#
]^#1:*expr.Expr_ListExpr#`,
},
{
I: `(a)`,
P: `a^#1:*expr.Expr_IdentExpr#`,
},
{
I: `((a))`,
P: `a^#1:*expr.Expr_IdentExpr#`,
},
{
I: `a()`,
P: `a()^#1:*expr.Expr_CallExpr#`,
},
{
I: `a(b)`,
P: `a(
b^#2:*expr.Expr_IdentExpr#
)^#1:*expr.Expr_CallExpr#`,
},
{
I: `a(b, c)`,
P: `a(
b^#2:*expr.Expr_IdentExpr#,
c^#3:*expr.Expr_IdentExpr#
)^#1:*expr.Expr_CallExpr#`,
},
{
I: `a.b()`,
P: `a^#1:*expr.Expr_IdentExpr#.b()^#2:*expr.Expr_CallExpr#`,
},
{
I: `a.b(c)`,
P: `a^#1:*expr.Expr_IdentExpr#.b(
c^#3:*expr.Expr_IdentExpr#
)^#2:*expr.Expr_CallExpr#`,
L: `a^#1[1,0]#.b(
c^#3[1,4]#
)^#2[1,3]#`,
},
// Parse error tests
{
I: `0xFFFFFFFFFFFFFFFFF`,
E: `ERROR: <input>:1:1: invalid int literal
| 0xFFFFFFFFFFFFFFFFF
| ^`,
},
{
I: `0xFFFFFFFFFFFFFFFFFu`,
E: `ERROR: <input>:1:1: invalid uint literal
| 0xFFFFFFFFFFFFFFFFFu
| ^`,
},
{
I: `1.99e90000009`,
E: `ERROR: <input>:1:1: invalid double literal
| 1.99e90000009
| ^`,
},
{
I: `*@a | b`,
E: `ERROR: <input>:1:1: Syntax error: extraneous input '*' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| *@a | b
| ^
ERROR: <input>:1:2: Syntax error: token recognition error at: '@'
| *@a | b
| .^
ERROR: <input>:1:5: Syntax error: token recognition error at: '| '
| *@a | b
| ....^
ERROR: <input>:1:7: Syntax error: extraneous input 'b' expecting <EOF>
| *@a | b
| ......^`,
},
{
I: `a | b`,
E: `ERROR: <input>:1:3: Syntax error: token recognition error at: '| '
| a | b
| ..^
ERROR: <input>:1:5: Syntax error: extraneous input 'b' expecting <EOF>
| a | b
| ....^`,
},
// Macro tests
{
I: `has(m.f)`,
P: `m^#2:*expr.Expr_IdentExpr#.f~test-only~^#4:*expr.Expr_SelectExpr#`,
L: `m^#2[1,4]#.f~test-only~^#4[1,3]#`,
M: `has(
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__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
false^#5:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
!_(
@result^#6:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#
)^#8:*expr.Expr_CallExpr#,
// LoopStep
_||_(
@result^#9:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#10:*expr.Expr_CallExpr#,
// Result
@result^#11:*expr.Expr_IdentExpr#)^#12:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.exists(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#12:exists#`,
},
{
I: `m.all(v, f)`,
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
true^#5:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
@result^#6:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#,
// LoopStep
_&&_(
@result^#8:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#9:*expr.Expr_CallExpr#,
// Result
@result^#10:*expr.Expr_IdentExpr#)^#11:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.all(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#11:all#`,
},
{
I: `m.existsOne(v, f)`,
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
0^#5:*expr.Constant_Int64Value#,
// LoopCondition
true^#6:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
f^#4:*expr.Expr_IdentExpr#,
_+_(
@result^#7:*expr.Expr_IdentExpr#,
1^#8:*expr.Constant_Int64Value#
)^#9:*expr.Expr_CallExpr#,
@result^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#,
// Result
_==_(
@result^#12:*expr.Expr_IdentExpr#,
1^#13:*expr.Constant_Int64Value#
)^#14:*expr.Expr_CallExpr#)^#15:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.existsOne(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#15:existsOne#`,
},
{
I: `[].existsOne(__result__, __result__)`,
E: `ERROR: <input>:1:14: iteration variable overwrites accumulator variable
| [].existsOne(__result__, __result__)
| .............^`,
},
{
I: `m.map(v, f)`,
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
[]^#5:*expr.Expr_ListExpr#,
// LoopCondition
true^#6:*expr.Constant_BoolValue#,
// LoopStep
_+_(
@result^#7:*expr.Expr_IdentExpr#,
[
f^#4:*expr.Expr_IdentExpr#
]^#8:*expr.Expr_ListExpr#
)^#9:*expr.Expr_CallExpr#,
// Result
@result^#10:*expr.Expr_IdentExpr#)^#11:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.map(
v^#3:*expr.Expr_IdentExpr#,
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__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
[]^#6:*expr.Expr_ListExpr#,
// LoopCondition
true^#7:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
p^#4:*expr.Expr_IdentExpr#,
_+_(
@result^#8:*expr.Expr_IdentExpr#,
[
f^#5:*expr.Expr_IdentExpr#
]^#9:*expr.Expr_ListExpr#
)^#10:*expr.Expr_CallExpr#,
@result^#11:*expr.Expr_IdentExpr#
)^#12:*expr.Expr_CallExpr#,
// Result
@result^#13:*expr.Expr_IdentExpr#)^#14:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.map(
v^#3:*expr.Expr_IdentExpr#,
p^#4:*expr.Expr_IdentExpr#,
f^#5:*expr.Expr_IdentExpr#
)^#14:map#`,
},
{
I: `m.filter(v, p)`,
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
[]^#5:*expr.Expr_ListExpr#,
// LoopCondition
true^#6:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
p^#4:*expr.Expr_IdentExpr#,
_+_(
@result^#7:*expr.Expr_IdentExpr#,
[
v^#3:*expr.Expr_IdentExpr#
]^#8:*expr.Expr_ListExpr#
)^#9:*expr.Expr_CallExpr#,
@result^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#,
// Result
@result^#12:*expr.Expr_IdentExpr#)^#13:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.filter(
v^#3:*expr.Expr_IdentExpr#,
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
{
I: "x * 2",
P: `_*_(
x^#1:*expr.Expr_IdentExpr#,
2^#3:*expr.Constant_Int64Value#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: "x * 2u",
P: `_*_(
x^#1:*expr.Expr_IdentExpr#,
2u^#3:*expr.Constant_Uint64Value#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: "x * 2.0",
P: `_*_(
x^#1:*expr.Expr_IdentExpr#,
2^#3:*expr.Constant_DoubleValue#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `"\u2764"`,
P: "\"\u2764\"^#1:*expr.Constant_StringValue#",
},
{
I: "\"\u2764\"",
P: "\"\u2764\"^#1:*expr.Constant_StringValue#",
},
{
I: `! false`,
P: `!_(
false^#2:*expr.Constant_BoolValue#
)^#1:*expr.Expr_CallExpr#`,
},
{
I: `-a`,
P: `-_(
a^#2:*expr.Expr_IdentExpr#
)^#1:*expr.Expr_CallExpr#`,
},
{
I: `a.b(5)`,
P: `a^#1:*expr.Expr_IdentExpr#.b(
5^#3:*expr.Constant_Int64Value#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `a[3]`,
P: `_[_](
a^#1:*expr.Expr_IdentExpr#,
3^#3:*expr.Constant_Int64Value#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `SomeMessage{foo: 5, bar: "xyz"}`,
P: `SomeMessage{
foo:5^#3:*expr.Constant_Int64Value#^#2:*expr.Expr_CreateStruct_Entry#,
bar:"xyz"^#5:*expr.Constant_StringValue#^#4:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `[3, 4, 5]`,
P: `[
3^#2:*expr.Constant_Int64Value#,
4^#3:*expr.Constant_Int64Value#,
5^#4:*expr.Constant_Int64Value#
]^#1:*expr.Expr_ListExpr#`,
},
{
I: `[3, 4, 5,]`,
P: `[
3^#2:*expr.Constant_Int64Value#,
4^#3:*expr.Constant_Int64Value#,
5^#4:*expr.Constant_Int64Value#
]^#1:*expr.Expr_ListExpr#`,
},
{
I: `{foo: 5, bar: "xyz"}`,
P: `{
foo^#3:*expr.Expr_IdentExpr#:5^#4:*expr.Constant_Int64Value#^#2:*expr.Expr_CreateStruct_Entry#,
bar^#6:*expr.Expr_IdentExpr#:"xyz"^#7:*expr.Constant_StringValue#^#5:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `{foo: 5, bar: "xyz", }`,
P: `{
foo^#3:*expr.Expr_IdentExpr#:5^#4:*expr.Constant_Int64Value#^#2:*expr.Expr_CreateStruct_Entry#,
bar^#6:*expr.Expr_IdentExpr#:"xyz"^#7:*expr.Constant_StringValue#^#5:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `a > 5 && a < 10`,
P: `_&&_(
_>_(
a^#1:*expr.Expr_IdentExpr#,
5^#3:*expr.Constant_Int64Value#
)^#2:*expr.Expr_CallExpr#,
_<_(
a^#4:*expr.Expr_IdentExpr#,
10^#6:*expr.Constant_Int64Value#
)^#5:*expr.Expr_CallExpr#
)^#7:*expr.Expr_CallExpr#`,
},
{
I: `a < 5 || a > 10`,
P: `_||_(
_<_(
a^#1:*expr.Expr_IdentExpr#,
5^#3:*expr.Constant_Int64Value#
)^#2:*expr.Expr_CallExpr#,
_>_(
a^#4:*expr.Expr_IdentExpr#,
10^#6:*expr.Constant_Int64Value#
)^#5:*expr.Expr_CallExpr#
)^#7:*expr.Expr_CallExpr#`,
},
{
I: `{`,
E: `ERROR: <input>:1:2: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '}', '(', '.', ',', '-', '!', '?', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| {
| .^`,
},
// Tests from Java parser
{
I: `[] + [1,2,3,] + [4]`,
P: `_+_(
_+_(
[]^#1:*expr.Expr_ListExpr#,
[
1^#4:*expr.Constant_Int64Value#,
2^#5:*expr.Constant_Int64Value#,
3^#6:*expr.Constant_Int64Value#
]^#3:*expr.Expr_ListExpr#
)^#2:*expr.Expr_CallExpr#,
[
4^#9:*expr.Constant_Int64Value#
]^#8:*expr.Expr_ListExpr#
)^#7:*expr.Expr_CallExpr#`,
},
{
I: `{1:2u, 2:3u}`,
P: `{
1^#3:*expr.Constant_Int64Value#:2u^#4:*expr.Constant_Uint64Value#^#2:*expr.Expr_CreateStruct_Entry#,
2^#6:*expr.Constant_Int64Value#:3u^#7:*expr.Constant_Uint64Value#^#5:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `TestAllTypes{single_int32: 1, single_int64: 2}`,
P: `TestAllTypes{
single_int32:1^#3:*expr.Constant_Int64Value#^#2:*expr.Expr_CreateStruct_Entry#,
single_int64:2^#5:*expr.Constant_Int64Value#^#4:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `TestAllTypes(){}`,
E: `ERROR: <input>:1:15: Syntax error: mismatched input '{' expecting <EOF>
| TestAllTypes(){}
| ..............^`,
},
{
I: `TestAllTypes{}()`,
E: `ERROR: <input>:1:15: Syntax error: mismatched input '(' expecting <EOF>
| TestAllTypes{}()
| ..............^`,
},
{
I: `size(x) == x.size()`,
P: `_==_(
size(
x^#2:*expr.Expr_IdentExpr#
)^#1:*expr.Expr_CallExpr#,
x^#4:*expr.Expr_IdentExpr#.size()^#5:*expr.Expr_CallExpr#
)^#3:*expr.Expr_CallExpr#`,
},
{
I: `1 + $`,
E: `ERROR: <input>:1:5: Syntax error: token recognition error at: '$'
| 1 + $
| ....^
ERROR: <input>:1:6: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| 1 + $
| .....^`,
},
{
I: `1 + 2
3 +`,
E: `ERROR: <input>:2:1: Syntax error: mismatched input '3' expecting <EOF>
| 3 +
| ^`,
},
{
I: `"\""`,
P: `"\""^#1:*expr.Constant_StringValue#`,
},
{
I: `[1,3,4][0]`,
P: `_[_](
[
1^#2:*expr.Constant_Int64Value#,
3^#3:*expr.Constant_Int64Value#,
4^#4:*expr.Constant_Int64Value#
]^#1:*expr.Expr_ListExpr#,
0^#6:*expr.Constant_Int64Value#
)^#5:*expr.Expr_CallExpr#`,
},
{
I: `1.all(2, 3)`,
E: `ERROR: <input>:1:7: argument must be a simple name
| 1.all(2, 3)
| ......^`,
},
{
I: `x["a"].single_int32 == 23`,
P: `_==_(
_[_](
x^#1:*expr.Expr_IdentExpr#,
"a"^#3:*expr.Constant_StringValue#
)^#2:*expr.Expr_CallExpr#.single_int32^#4:*expr.Expr_SelectExpr#,
23^#6:*expr.Constant_Int64Value#
)^#5:*expr.Expr_CallExpr#`,
},
{
I: `x.single_nested_message != null`,
P: `_!=_(
x^#1:*expr.Expr_IdentExpr#.single_nested_message^#2:*expr.Expr_SelectExpr#,
null^#4:*expr.Constant_NullValue#
)^#3:*expr.Expr_CallExpr#`,
},
{
I: `false && !true || false ? 2 : 3`,
P: `_?_:_(
_||_(
_&&_(
false^#1:*expr.Constant_BoolValue#,
!_(
true^#3:*expr.Constant_BoolValue#
)^#2:*expr.Expr_CallExpr#
)^#4:*expr.Expr_CallExpr#,
false^#5:*expr.Constant_BoolValue#
)^#6:*expr.Expr_CallExpr#,
2^#8:*expr.Constant_Int64Value#,
3^#9:*expr.Constant_Int64Value#
)^#7:*expr.Expr_CallExpr#`,
},
{
I: `b"abc" + B"def"`,
P: `_+_(
b"abc"^#1:*expr.Constant_BytesValue#,
b"def"^#3:*expr.Constant_BytesValue#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `1 + 2 * 3 - 1 / 2 == 6 % 1`,
P: `_==_(
_-_(
_+_(
1^#1:*expr.Constant_Int64Value#,
_*_(
2^#3:*expr.Constant_Int64Value#,
3^#5:*expr.Constant_Int64Value#
)^#4:*expr.Expr_CallExpr#
)^#2:*expr.Expr_CallExpr#,
_/_(
1^#7:*expr.Constant_Int64Value#,
2^#9:*expr.Constant_Int64Value#
)^#8:*expr.Expr_CallExpr#
)^#6:*expr.Expr_CallExpr#,
_%_(
6^#11:*expr.Constant_Int64Value#,
1^#13:*expr.Constant_Int64Value#
)^#12:*expr.Expr_CallExpr#
)^#10:*expr.Expr_CallExpr#`,
},
{
I: `1 + +`,
E: `ERROR: <input>:1:5: Syntax error: mismatched input '+' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| 1 + +
| ....^
ERROR: <input>:1:6: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| 1 + +
| .....^`,
},
{
I: `"abc" + "def"`,
P: `_+_(
"abc"^#1:*expr.Constant_StringValue#,
"def"^#3:*expr.Constant_StringValue#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: `{"a": 1}."a"`,
E: `ERROR: <input>:1:10: Syntax error: no viable alternative at input '."a"'
| {"a": 1}."a"
| .........^`,
},
{
I: `"\xC3\XBF"`,
P: `"ÿ"^#1:*expr.Constant_StringValue#`,
},
{
I: `"\303\277"`,
P: `"ÿ"^#1:*expr.Constant_StringValue#`,
},
{
I: `"hi\u263A \u263Athere"`,
P: `"hi☺ ☺there"^#1:*expr.Constant_StringValue#`,
},
{
I: `"\U000003A8\?"`,
P: `"Ψ?"^#1:*expr.Constant_StringValue#`,
},
{
I: `"\a\b\f\n\r\t\v'\"\\\? Legal escapes"`,
P: `"\a\b\f\n\r\t\v'\"\\? Legal escapes"^#1:*expr.Constant_StringValue#`,
},
{
I: `"\xFh"`,
E: `ERROR: <input>:1:1: Syntax error: token recognition error at: '"\xFh'
| "\xFh"
| ^
ERROR: <input>:1:6: Syntax error: token recognition error at: '"'
| "\xFh"
| .....^
ERROR: <input>:1:7: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| "\xFh"
| ......^`,
},
{
I: `"\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>"`,
E: `ERROR: <input>:1:1: Syntax error: token recognition error at: '"\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>'
| "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>"
| ^
ERROR: <input>:1:42: Syntax error: token recognition error at: '"'
| "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>"
| .........................................^
ERROR: <input>:1:43: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| "\a\b\f\n\r\t\v\'\"\\\? Illegal escape \>"
| ..........................................^`,
},
{
I: `"😁" in ["😁", "😑", "😦"]`,
P: `@in(
"😁"^#1:*expr.Constant_StringValue#,
[
"😁"^#4:*expr.Constant_StringValue#,
"😑"^#5:*expr.Constant_StringValue#,
"😦"^#6:*expr.Constant_StringValue#
]^#3:*expr.Expr_ListExpr#
)^#2:*expr.Expr_CallExpr#`,
},
{
I: ` '😁' in ['😁', '😑', '😦']
&& in.😁`,
E: `ERROR: <input>:2:7: Syntax error: extraneous input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| && in.😁
| ......^
ERROR: <input>:2:10: Syntax error: token recognition error at: '😁'
| && in.😁
| .........^
ERROR: <input>:2:11: Syntax error: no viable alternative at input '.'
| && in.😁
| ..........^`,
},
{
I: "as",
E: `ERROR: <input>:1:1: reserved identifier: as
| as
| ^`,
},
{
I: "break",
E: `ERROR: <input>:1:1: reserved identifier: break
| break
| ^`,
},
{
I: "const",
E: `ERROR: <input>:1:1: reserved identifier: const
| const
| ^`,
},
{
I: "continue",
E: `ERROR: <input>:1:1: reserved identifier: continue
| continue
| ^`,
},
{
I: "else",
E: `ERROR: <input>:1:1: reserved identifier: else
| else
| ^`,
},
{
I: "for",
E: `ERROR: <input>:1:1: reserved identifier: for
| for
| ^`,
},
{
I: "function",
E: `ERROR: <input>:1:1: reserved identifier: function
| function
| ^`,
},
{
I: "if",
E: `ERROR: <input>:1:1: reserved identifier: if
| if
| ^`,
},
{
I: "import",
E: `ERROR: <input>:1:1: reserved identifier: import
| import
| ^`,
},
{
I: "in",
E: `ERROR: <input>:1:1: Syntax error: mismatched input 'in' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| in
| ^
ERROR: <input>:1:3: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| in
| ..^`,
},
{
I: "let",
E: `ERROR: <input>:1:1: reserved identifier: let
| let
| ^`,
},
{
I: "loop",
E: `ERROR: <input>:1:1: reserved identifier: loop
| loop
| ^`,
},
{
I: "package",
E: `ERROR: <input>:1:1: reserved identifier: package
| package
| ^`,
},
{
I: "namespace",
E: `ERROR: <input>:1:1: reserved identifier: namespace
| namespace
| ^`,
},
{
I: "return",
E: `ERROR: <input>:1:1: reserved identifier: return
| return
| ^`,
},
{
I: "var",
E: `ERROR: <input>:1:1: reserved identifier: var
| var
| ^`,
},
{
I: "void",
E: `ERROR: <input>:1:1: reserved identifier: void
| void
| ^`,
},
{
I: "while",
E: `ERROR: <input>:1:1: reserved identifier: while
| while
| ^`,
},
{
I: "[1, 2, 3].map(var, var * var)",
E: `ERROR: <input>:1:15: reserved identifier: var
| [1, 2, 3].map(var, var * var)
| ..............^
ERROR: <input>:1:15: argument is not an identifier
| [1, 2, 3].map(var, var * var)
| ..............^
ERROR: <input>:1:20: reserved identifier: var
| [1, 2, 3].map(var, var * var)
| ...................^
ERROR: <input>:1:26: reserved identifier: var
| [1, 2, 3].map(var, var * var)
| .........................^`,
},
{
I: "func{{a}}",
E: `ERROR: <input>:1:6: Syntax error: extraneous input '{' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER}
| func{{a}}
| .....^
ERROR: <input>:1:8: Syntax error: mismatched input '}' expecting ':'
| func{{a}}
| .......^
ERROR: <input>:1:9: Syntax error: extraneous input '}' expecting <EOF>
| func{{a}}
| ........^`,
},
{
I: "msg{:a}",
E: `ERROR: <input>:1:5: Syntax error: extraneous input ':' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER}
| msg{:a}
| ....^
ERROR: <input>:1:7: Syntax error: mismatched input '}' expecting ':'
| msg{:a}
| ......^`,
},
{
I: "{a}",
E: `ERROR: <input>:1:3: Syntax error: mismatched input '}' expecting ':'
| {a}
| ..^`,
},
{
I: "{:a}",
E: `ERROR: <input>:1:2: Syntax error: extraneous input ':' expecting {'[', '{', '}', '(', '.', ',', '-', '!', '?', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| {:a}
| .^
ERROR: <input>:1:4: Syntax error: mismatched input '}' expecting ':'
| {:a}
| ...^`,
},
{
I: "ind[a{b}]",
E: `ERROR: <input>:1:8: Syntax error: mismatched input '}' expecting ':'
| ind[a{b}]
| .......^`,
},
{
I: `--`,
E: `ERROR: <input>:1:3: Syntax error: no viable alternative at input '-'
| --
| ..^
ERROR: <input>:1:3: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| --
| ..^`,
},
{
I: `?`,
E: `ERROR: <input>:1:1: Syntax error: mismatched input '?' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| ?
| ^
ERROR: <input>:1:2: Syntax error: mismatched input '<EOF>' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| ?
| .^`,
},
{
I: `a ? b ((?))`,
E: `ERROR: <input>:1:9: Syntax error: mismatched input '?' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| a ? b ((?))
| ........^
ERROR: <input>:1:10: Syntax error: mismatched input ')' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| a ? b ((?))
| .........^
ERROR: <input>:1:12: Syntax error: error recovery attempt limit exceeded: 4
| a ? b ((?))
| ...........^`,
},
{
I: `[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['too many']]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`,
E: "ERROR: <input>:-1:0: expression recursion limit exceeded: 32",
},
{
I: `-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
--3-[-1--1--1--1---1-1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1-À1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1
--1--0--1--1--1--3-[-1--1--1--1---1--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1--1
--1---1--1--1--0--1--1--1--1--0--3--1--1--0--1`,
E: `ERROR: <input>:-1:0: expression recursion limit exceeded: 32
ERROR: <input>:3:33: Syntax error: extraneous input '/' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| --3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
| ................................^
ERROR: <input>:8:33: Syntax error: extraneous input '/' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| --3-[-1--1--1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1
| ................................^
ERROR: <input>:11:17: Syntax error: token recognition error at: 'À'
| --1--1---1--1-À1--0--1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
| ................^
ERROR: <input>:14:23: Syntax error: extraneous input '/' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}
| --1--1---1--1--1--0-/1--1--1--1--0--2--1--1--0--1--1--1--1--0--1--1--1--3-[-1--1
| ......................^`,
}, {
I: `ó ¢
ó 0 
0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\"`,
E: `ERROR: <input>:-1:0: error recovery token lookahead limit exceeded: 4
ERROR: <input>:1:1: Syntax error: token recognition error at: 'ó'
| ó ¢
| ^
ERROR: <input>:1:2: Syntax error: token recognition error at: ' '
| ó ¢
| .^
ERROR: <input>:1:3: Syntax error: token recognition error at: '¢'
| ó ¢
| ..^
ERROR: <input>:2:3: Syntax error: token recognition error at: 'ó'
| ó 0 
| ..^
ERROR: <input>:2:4: Syntax error: token recognition error at: ' '
| ó 0 
| ...^
ERROR: <input>:2:6: Syntax error: token recognition error at: ' '
| ó 0 
| .....^
ERROR: <input>:3:3: Syntax error: token recognition error at: ''
| 0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\"
| ..^
ERROR: <input>:3:4: Syntax error: mismatched input '0' expecting <EOF>
| 0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\"
| ...^
ERROR: <input>:3:11: Syntax error: token recognition error at: '\'
| 0"""\""\"""\""\"""\""\"""\""\"""\"\"""\""\"""\""\"""\""\"""\"!\"""\""\"""\""\"
| ..........^`,
},
// Macro Calls Tests
{
I: `x.filter(y, y.filter(z, z > 0))`,
P: `__comprehension__(
// Variable
y,
// Target
x^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
[]^#19:*expr.Expr_ListExpr#,
// LoopCondition
true^#20:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
__comprehension__(
// Variable
z,
// Target
y^#4:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
[]^#10:*expr.Expr_ListExpr#,
// LoopCondition
true^#11:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
_>_(
z^#7:*expr.Expr_IdentExpr#,
0^#9:*expr.Constant_Int64Value#
)^#8:*expr.Expr_CallExpr#,
_+_(
@result^#12:*expr.Expr_IdentExpr#,
[
z^#6:*expr.Expr_IdentExpr#
]^#13:*expr.Expr_ListExpr#
)^#14:*expr.Expr_CallExpr#,
@result^#15:*expr.Expr_IdentExpr#
)^#16:*expr.Expr_CallExpr#,
// Result
@result^#17:*expr.Expr_IdentExpr#)^#18:*expr.Expr_ComprehensionExpr#,
_+_(
@result^#21:*expr.Expr_IdentExpr#,
[
y^#3:*expr.Expr_IdentExpr#
]^#22:*expr.Expr_ListExpr#
)^#23:*expr.Expr_CallExpr#,
@result^#24:*expr.Expr_IdentExpr#
)^#25:*expr.Expr_CallExpr#,
// Result
@result^#26:*expr.Expr_IdentExpr#)^#27:*expr.Expr_ComprehensionExpr#`,
M: `x^#1:*expr.Expr_IdentExpr#.filter(
y^#3:*expr.Expr_IdentExpr#,
^#18:filter#
)^#27:filter#,
y^#4:*expr.Expr_IdentExpr#.filter(
z^#6:*expr.Expr_IdentExpr#,
_>_(
z^#7:*expr.Expr_IdentExpr#,
0^#9:*expr.Constant_Int64Value#
)^#8:*expr.Expr_CallExpr#
)^#18:filter#`,
},
{
I: `has(a.b).filter(c, c)`,
P: `__comprehension__(
// Variable
c,
// Target
a^#2:*expr.Expr_IdentExpr#.b~test-only~^#4:*expr.Expr_SelectExpr#,
// Accumulator
@result,
// Init
[]^#8:*expr.Expr_ListExpr#,
// LoopCondition
true^#9:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
c^#7:*expr.Expr_IdentExpr#,
_+_(
@result^#10:*expr.Expr_IdentExpr#,
[
c^#6:*expr.Expr_IdentExpr#
]^#11:*expr.Expr_ListExpr#
)^#12:*expr.Expr_CallExpr#,
@result^#13:*expr.Expr_IdentExpr#
)^#14:*expr.Expr_CallExpr#,
// Result
@result^#15:*expr.Expr_IdentExpr#)^#16:*expr.Expr_ComprehensionExpr#`,
M: `^#4:has#.filter(
c^#6:*expr.Expr_IdentExpr#,
c^#7:*expr.Expr_IdentExpr#
)^#16:filter#,
has(
a^#2:*expr.Expr_IdentExpr#.b^#3:*expr.Expr_SelectExpr#
)^#4:has#`,
},
{
I: `x.filter(y, y.exists(z, has(z.a)) && y.exists(z, has(z.b)))`,
P: `__comprehension__(
// Variable
y,
// Target
x^#1:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
[]^#35:*expr.Expr_ListExpr#,
// LoopCondition
true^#36:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
_&&_(
__comprehension__(
// Variable
z,
// Target
y^#4:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
false^#11:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
!_(
@result^#12:*expr.Expr_IdentExpr#
)^#13:*expr.Expr_CallExpr#
)^#14:*expr.Expr_CallExpr#,
// LoopStep
_||_(
@result^#15:*expr.Expr_IdentExpr#,
z^#8:*expr.Expr_IdentExpr#.a~test-only~^#10:*expr.Expr_SelectExpr#
)^#16:*expr.Expr_CallExpr#,
// Result
@result^#17:*expr.Expr_IdentExpr#)^#18:*expr.Expr_ComprehensionExpr#,
__comprehension__(
// Variable
z,
// Target
y^#19:*expr.Expr_IdentExpr#,
// Accumulator
@result,
// Init
false^#26:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
!_(
@result^#27:*expr.Expr_IdentExpr#
)^#28:*expr.Expr_CallExpr#
)^#29:*expr.Expr_CallExpr#,
// LoopStep
_||_(
@result^#30:*expr.Expr_IdentExpr#,
z^#23:*expr.Expr_IdentExpr#.b~test-only~^#25:*expr.Expr_SelectExpr#
)^#31:*expr.Expr_CallExpr#,
// Result
@result^#32:*expr.Expr_IdentExpr#)^#33:*expr.Expr_ComprehensionExpr#
)^#34:*expr.Expr_CallExpr#,
_+_(
@result^#37:*expr.Expr_IdentExpr#,
[
y^#3:*expr.Expr_IdentExpr#
]^#38:*expr.Expr_ListExpr#
)^#39:*expr.Expr_CallExpr#,
@result^#40:*expr.Expr_IdentExpr#
)^#41:*expr.Expr_CallExpr#,
// Result
@result^#42:*expr.Expr_IdentExpr#)^#43:*expr.Expr_ComprehensionExpr#`,
M: `x^#1:*expr.Expr_IdentExpr#.filter(
y^#3:*expr.Expr_IdentExpr#,
_&&_(
^#18:exists#,
^#33:exists#
)^#34:*expr.Expr_CallExpr#
)^#43:filter#,
y^#19:*expr.Expr_IdentExpr#.exists(
z^#21:*expr.Expr_IdentExpr#,
^#25:has#
)^#33:exists#,
has(
z^#23:*expr.Expr_IdentExpr#.b^#24:*expr.Expr_SelectExpr#
)^#25:has#,
y^#4:*expr.Expr_IdentExpr#.exists(
z^#6:*expr.Expr_IdentExpr#,
^#10:has#
)^#18:exists#,
has(
z^#8:*expr.Expr_IdentExpr#.a^#9:*expr.Expr_SelectExpr#
)^#10:has#`,
},
{
I: `(has(a.b) || has(c.d)).string()`,
P: `_||_(
a^#2:*expr.Expr_IdentExpr#.b~test-only~^#4:*expr.Expr_SelectExpr#,
c^#6:*expr.Expr_IdentExpr#.d~test-only~^#8:*expr.Expr_SelectExpr#
)^#9:*expr.Expr_CallExpr#.string()^#10:*expr.Expr_CallExpr#`,
M: `has(
c^#6:*expr.Expr_IdentExpr#.d^#7:*expr.Expr_SelectExpr#
)^#8:has#,
has(
a^#2:*expr.Expr_IdentExpr#.b^#3:*expr.Expr_SelectExpr#
)^#4:has#`,
},
{
I: `has(a.b).asList().exists(c, c)`,
P: `__comprehension__(
// Variable
c,
// Target
a^#2:*expr.Expr_IdentExpr#.b~test-only~^#4:*expr.Expr_SelectExpr#.asList()^#5:*expr.Expr_CallExpr#,
// Accumulator
@result,
// Init
false^#9:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
!_(
@result^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#
)^#12:*expr.Expr_CallExpr#,
// LoopStep
_||_(
@result^#13:*expr.Expr_IdentExpr#,
c^#8:*expr.Expr_IdentExpr#
)^#14:*expr.Expr_CallExpr#,
// Result
@result^#15:*expr.Expr_IdentExpr#)^#16:*expr.Expr_ComprehensionExpr#`,
M: `^#4:has#.asList()^#5:*expr.Expr_CallExpr#.exists(
c^#7:*expr.Expr_IdentExpr#,
c^#8:*expr.Expr_IdentExpr#
)^#16:exists#,
has(
a^#2:*expr.Expr_IdentExpr#.b^#3:*expr.Expr_SelectExpr#
)^#4:has#`,
},
{
I: `[has(a.b), has(c.d)].exists(e, e)`,
P: `__comprehension__(
// Variable
e,
// Target
[
a^#3:*expr.Expr_IdentExpr#.b~test-only~^#5:*expr.Expr_SelectExpr#,
c^#7:*expr.Expr_IdentExpr#.d~test-only~^#9:*expr.Expr_SelectExpr#
]^#1:*expr.Expr_ListExpr#,
// Accumulator
@result,
// Init
false^#13:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
!_(
@result^#14:*expr.Expr_IdentExpr#
)^#15:*expr.Expr_CallExpr#
)^#16:*expr.Expr_CallExpr#,
// LoopStep
_||_(
@result^#17:*expr.Expr_IdentExpr#,
e^#12:*expr.Expr_IdentExpr#
)^#18:*expr.Expr_CallExpr#,
// Result
@result^#19:*expr.Expr_IdentExpr#)^#20:*expr.Expr_ComprehensionExpr#`,
M: `[
^#5:has#,
^#9:has#
]^#1:*expr.Expr_ListExpr#.exists(
e^#11:*expr.Expr_IdentExpr#,
e^#12:*expr.Expr_IdentExpr#
)^#20:exists#,
has(
c^#7:*expr.Expr_IdentExpr#.d^#8:*expr.Expr_SelectExpr#
)^#9:has#,
has(
a^#3:*expr.Expr_IdentExpr#.b^#4:*expr.Expr_SelectExpr#
)^#5:has#`,
},
{
I: `y!=y!=y!=y!=y!=y!=y!=y!=y!=-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y
!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y
!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y
!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y
!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y
!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y!=-y!=-y-y!=-y`,
E: `ERROR: <input>:-1:0: max recursion depth exceeded`,
},
{
// More than 32 nested list creation statements
I: `[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[['not fine']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`,
E: `ERROR: <input>:-1:0: expression recursion limit exceeded: 32`,
},
{
// More than 32 arithmetic operations.
I: `1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
+ 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20
+ 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30
+ 31 + 32 + 33 + 34`,
E: `ERROR: <input>:-1:0: max recursion depth exceeded`,
},
{
// More than 32 field selections
I: `a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H`,
E: `ERROR: <input>:-1:0: max recursion depth exceeded`,
},
{
// More than 32 index operations
I: `a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]
[21][22][23][24][25][26][27][28][29][30][31][32][33]`,
E: `ERROR: <input>:-1:0: max recursion depth exceeded`,
},
{
// More than 32 relation operators
I: `a < 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < 11
< 12 < 13 < 14 < 15 < 16 < 17 < 18 < 19 < 20 < 21
< 22 < 23 < 24 < 25 < 26 < 27 < 28 < 29 < 30 < 31
< 32 < 33`,
E: `ERROR: <input>:-1:0: max recursion depth exceeded`,
},
{
// More than 32 index / relation operators. Note, the recursion count is the
// maximum recursion level on the left or right side index expression (20) plus
// the number of relation operators (13)
I: `a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20] !=
a[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]`,
E: `ERROR: <input>:-1:0: max recursion depth exceeded`,
},
{
I: `self.true == 1`,
E: `ERROR: <input>:1:6: Syntax error: mismatched input 'true' expecting IDENTIFIER
| self.true == 1
| .....^`,
},
{
I: `a.?b && a[?b]`,
E: `ERROR: <input>:1:2: unsupported syntax '.?'
| a.?b && a[?b]
| .^
ERROR: <input>:1:10: unsupported syntax '[?'
| a.?b && a[?b]
| .........^`,
},
{
I: `a.?b[?0] && a[?c]`,
Opts: []Option{EnableOptionalSyntax(true)},
P: `_&&_(
_[?_](
_?._(
a^#1:*expr.Expr_IdentExpr#,
"b"^#2:*expr.Constant_StringValue#
)^#3:*expr.Expr_CallExpr#,
0^#5:*expr.Constant_Int64Value#
)^#4:*expr.Expr_CallExpr#,
_[?_](
a^#6:*expr.Expr_IdentExpr#,
c^#8:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#
)^#9:*expr.Expr_CallExpr#`,
},
{
I: `{?'key': value}`,
Opts: []Option{EnableOptionalSyntax(true)},
P: `{
?"key"^#3:*expr.Constant_StringValue#:value^#4:*expr.Expr_IdentExpr#^#2:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `[?a, ?b]`,
Opts: []Option{EnableOptionalSyntax(true)},
P: `[
a^#2:*expr.Expr_IdentExpr#,
b^#3:*expr.Expr_IdentExpr#
]^#1:*expr.Expr_ListExpr#`,
},
{
I: `[?a[?b]]`,
Opts: []Option{EnableOptionalSyntax(true)},
P: `[
_[?_](
a^#2:*expr.Expr_IdentExpr#,
b^#4:*expr.Expr_IdentExpr#
)^#3:*expr.Expr_CallExpr#
]^#1:*expr.Expr_ListExpr#`,
},
{
I: `[?a, ?b]`,
E: `
ERROR: <input>:1:2: unsupported syntax '?'
| [?a, ?b]
| .^
ERROR: <input>:1:6: unsupported syntax '?'
| [?a, ?b]
| .....^`,
},
{
I: `Msg{?field: value}`,
Opts: []Option{EnableOptionalSyntax(true)},
P: `Msg{
?field:value^#3:*expr.Expr_IdentExpr#^#2:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: `Msg{?field: value} && {?'key': value}`,
E: `
ERROR: <input>:1:5: unsupported syntax '?'
| Msg{?field: value} && {?'key': value}
| ....^
ERROR: <input>:1:24: unsupported syntax '?'
| Msg{?field: value} && {?'key': value}
| .......................^`,
},
{
I: "a.`b-c`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
P: `a^#1:*expr.Expr_IdentExpr#.b-c^#2:*expr.Expr_SelectExpr#`,
},
{I: "a.`b c`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
P: `a^#1:*expr.Expr_IdentExpr#.b c^#2:*expr.Expr_SelectExpr#`,
},
{
I: "a.`b.c`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
P: `a^#1:*expr.Expr_IdentExpr#.b.c^#2:*expr.Expr_SelectExpr#`,
},
{
I: "a.`in`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
P: `a^#1:*expr.Expr_IdentExpr#.in^#2:*expr.Expr_SelectExpr#`,
},
{
I: "a.`/foo`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
P: `a^#1:*expr.Expr_IdentExpr#./foo^#2:*expr.Expr_SelectExpr#`,
},
{
I: "Message{`in`: true}",
Opts: []Option{EnableIdentEscapeSyntax(true)},
P: `Message{
in:true^#3:*expr.Constant_BoolValue#^#2:*expr.Expr_CreateStruct_Entry#
}^#1:*expr.Expr_StructExpr#`,
},
{
I: "`b-c`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
E: "ERROR: <input>:1:1: Syntax error: mismatched input '`b-c`' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}\n" +
"| `b-c`\n" +
"| ^",
},
{
I: "`b-c`()",
Opts: []Option{EnableIdentEscapeSyntax(true)},
E: "ERROR: <input>:1:1: Syntax error: extraneous input '`b-c`' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}\n" +
"| `b-c`()\n" +
"| ^\n" +
"ERROR: <input>:1:7: Syntax error: mismatched input ')' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}\n" +
"| `b-c`()\n" +
"| ......^",
},
{
I: "a.`$b`",
Opts: []Option{EnableIdentEscapeSyntax(true)},
E: "ERROR: <input>:1:3: Syntax error: token recognition error at: '`$'\n" +
"| a.`$b`\n" +
"| ..^\n" +
"ERROR: <input>:1:6: Syntax error: token recognition error at: '`'\n" +
"| a.`$b`\n" +
"| .....^",
},
{
I: "a.`b.c`()",
Opts: []Option{EnableIdentEscapeSyntax(true)},
E: "ERROR: <input>:1:8: Syntax error: mismatched input '(' expecting <EOF>\n" +
"| a.`b.c`()\n" +
"| .......^\n",
},
{
I: "a.`b-c`",
Opts: []Option{EnableIdentEscapeSyntax(false)},
E: "ERROR: <input>:1:3: unsupported syntax: '`'\n" +
"| a.`b-c`\n" +
"| ..^",
},
{
I: "a.`b.c`",
Opts: []Option{EnableIdentEscapeSyntax(false)},
E: "ERROR: <input>:1:3: unsupported syntax: '`'\n" +
"| a.`b.c`\n" +
"| ..^\n",
},
{
I: "a.`in`",
Opts: []Option{EnableIdentEscapeSyntax(false)},
E: "ERROR: <input>:1:3: unsupported syntax: '`'\n" +
"| a.`in`\n" +
"| ..^",
},
{
I: "a.`/foo`",
Opts: []Option{EnableIdentEscapeSyntax(false)},
E: "ERROR: <input>:1:3: unsupported syntax: '`'\n" +
"| a.`/foo`\n" +
"| ..^",
},
{
I: "Message{`in`: true}",
Opts: []Option{EnableIdentEscapeSyntax(false)},
E: "ERROR: <input>:1:9: unsupported syntax: '`'\n" +
"| Message{`in`: true}\n" +
"| ........^",
},
{
I: `noop_macro(123)`,
Opts: []Option{
Macros(NewGlobalVarArgMacro("noop_macro",
func(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
return nil, nil
})),
},
P: `noop_macro(
123^#2:*expr.Constant_Int64Value#
)^#1:*expr.Expr_CallExpr#`,
},
{
I: `x{?.`,
Opts: []Option{
ErrorRecoveryLookaheadTokenLimit(10),
ErrorRecoveryLimit(10),
},
E: `
ERROR: <input>:1:3: unsupported syntax '?'
| x{?.
| ..^
ERROR: <input>:1:4: Syntax error: mismatched input '.' expecting {IDENTIFIER, ESC_IDENTIFIER}
| x{?.
| ...^`,
},
{
I: `x{.`,
E: `
ERROR: <input>:1:3: Syntax error: mismatched input '.' expecting {'}', ',', '?', IDENTIFIER, ESC_IDENTIFIER}
| x{.
| ..^`,
},
{
I: `'3# < 10" '& tru ^^`,
Opts: []Option{ErrorReportingLimit(2)},
E: `
ERROR: <input>:1:12: Syntax error: token recognition error at: '& '
| '3# < 10" '& tru ^^
| ...........^
ERROR: <input>:1:18: Syntax error: token recognition error at: '^'
| '3# < 10" '& tru ^^
| .................^
ERROR: <input>:1:19: Syntax error: More than 2 syntax errors
| '3# < 10" '& tru ^^
| ..................^
`,
},
{
I: `'\udead' == '\ufffd'`,
E: `
ERROR: <input>:1:1: invalid unicode code point
| '\udead' == '\ufffd'
| ^`,
},
// Macro tests for old accumulator name
{
I: `m.exists(v, f)`,
Opts: []Option{
EnableHiddenAccumulatorName(false),
},
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
__result__,
// Init
false^#5:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
!_(
__result__^#6:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#
)^#8:*expr.Expr_CallExpr#,
// LoopStep
_||_(
__result__^#9:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#10:*expr.Expr_CallExpr#,
// Result
__result__^#11:*expr.Expr_IdentExpr#)^#12:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.exists(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#12:exists#`,
},
{
I: `m.all(v, f)`,
Opts: []Option{
EnableHiddenAccumulatorName(false),
},
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
__result__,
// Init
true^#5:*expr.Constant_BoolValue#,
// LoopCondition
@not_strictly_false(
__result__^#6:*expr.Expr_IdentExpr#
)^#7:*expr.Expr_CallExpr#,
// LoopStep
_&&_(
__result__^#8:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#9:*expr.Expr_CallExpr#,
// Result
__result__^#10:*expr.Expr_IdentExpr#)^#11:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.all(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#11:all#`,
},
{
I: `m.existsOne(v, f)`,
Opts: []Option{
EnableHiddenAccumulatorName(false),
},
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
__result__,
// Init
0^#5:*expr.Constant_Int64Value#,
// LoopCondition
true^#6:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
f^#4:*expr.Expr_IdentExpr#,
_+_(
__result__^#7:*expr.Expr_IdentExpr#,
1^#8:*expr.Constant_Int64Value#
)^#9:*expr.Expr_CallExpr#,
__result__^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#,
// Result
_==_(
__result__^#12:*expr.Expr_IdentExpr#,
1^#13:*expr.Constant_Int64Value#
)^#14:*expr.Expr_CallExpr#)^#15:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.existsOne(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#15:existsOne#`,
},
{
I: `m.map(v, f)`,
Opts: []Option{
EnableHiddenAccumulatorName(false),
},
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
__result__,
// Init
[]^#5:*expr.Expr_ListExpr#,
// LoopCondition
true^#6:*expr.Constant_BoolValue#,
// LoopStep
_+_(
__result__^#7:*expr.Expr_IdentExpr#,
[
f^#4:*expr.Expr_IdentExpr#
]^#8:*expr.Expr_ListExpr#
)^#9:*expr.Expr_CallExpr#,
// Result
__result__^#10:*expr.Expr_IdentExpr#)^#11:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.map(
v^#3:*expr.Expr_IdentExpr#,
f^#4:*expr.Expr_IdentExpr#
)^#11:map#`,
},
{
I: `m.map(v, p, f)`,
Opts: []Option{
EnableHiddenAccumulatorName(false),
},
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
__result__,
// Init
[]^#6:*expr.Expr_ListExpr#,
// LoopCondition
true^#7:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
p^#4:*expr.Expr_IdentExpr#,
_+_(
__result__^#8:*expr.Expr_IdentExpr#,
[
f^#5:*expr.Expr_IdentExpr#
]^#9:*expr.Expr_ListExpr#
)^#10:*expr.Expr_CallExpr#,
__result__^#11:*expr.Expr_IdentExpr#
)^#12:*expr.Expr_CallExpr#,
// Result
__result__^#13:*expr.Expr_IdentExpr#)^#14:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.map(
v^#3:*expr.Expr_IdentExpr#,
p^#4:*expr.Expr_IdentExpr#,
f^#5:*expr.Expr_IdentExpr#
)^#14:map#`,
},
{
I: `m.filter(v, p)`,
Opts: []Option{
EnableHiddenAccumulatorName(false),
},
P: `__comprehension__(
// Variable
v,
// Target
m^#1:*expr.Expr_IdentExpr#,
// Accumulator
__result__,
// Init
[]^#5:*expr.Expr_ListExpr#,
// LoopCondition
true^#6:*expr.Constant_BoolValue#,
// LoopStep
_?_:_(
p^#4:*expr.Expr_IdentExpr#,
_+_(
__result__^#7:*expr.Expr_IdentExpr#,
[
v^#3:*expr.Expr_IdentExpr#
]^#8:*expr.Expr_ListExpr#
)^#9:*expr.Expr_CallExpr#,
__result__^#10:*expr.Expr_IdentExpr#
)^#11:*expr.Expr_CallExpr#,
// Result
__result__^#12:*expr.Expr_IdentExpr#)^#13:*expr.Expr_ComprehensionExpr#`,
M: `m^#1:*expr.Expr_IdentExpr#.filter(
v^#3:*expr.Expr_IdentExpr#,
p^#4:*expr.Expr_IdentExpr#
)^#13:filter#`,
},
}
type testInfo struct {
// I contains the input expression to be parsed.
I string
// P contains the type/id adorned debug output of the expression tree.
P string
// E contains the expected error output for a failed parse, or "" if the parse is expected to be successful.
E string
// L contains the expected source adorned debug output of the expression tree.
L string
// M contains the expected adorned debug output of the macro calls map
M string
// Opts contains the list of options to be configured with the parser before parsing the expression.
Opts []Option
}
type metadata interface {
GetLocation(exprID int64) (common.Location, bool)
}
type kindAndIDAdorner struct {
sourceInfo *ast.SourceInfo
}
func (k *kindAndIDAdorner) GetMetadata(elem any) string {
switch e := elem.(type) {
case ast.Expr:
if macroCall, found := k.sourceInfo.GetMacroCall(e.ID()); found {
return fmt.Sprintf("^#%d:%s#", e.ID(), macroCall.AsCall().FunctionName())
}
var valType string
switch e.Kind() {
case ast.CallKind:
valType = "*expr.Expr_CallExpr"
case ast.ComprehensionKind:
valType = "*expr.Expr_ComprehensionExpr"
case ast.IdentKind:
valType = "*expr.Expr_IdentExpr"
case ast.LiteralKind:
lit := e.AsLiteral()
switch lit.(type) {
case types.Bool:
valType = "*expr.Constant_BoolValue"
case types.Bytes:
valType = "*expr.Constant_BytesValue"
case types.Double:
valType = "*expr.Constant_DoubleValue"
case types.Int:
valType = "*expr.Constant_Int64Value"
case types.Null:
valType = "*expr.Constant_NullValue"
case types.String:
valType = "*expr.Constant_StringValue"
case types.Uint:
valType = "*expr.Constant_Uint64Value"
default:
valType = reflect.TypeOf(lit).String()
}
case ast.ListKind:
valType = "*expr.Expr_ListExpr"
case ast.MapKind, ast.StructKind:
valType = "*expr.Expr_StructExpr"
case ast.SelectKind:
valType = "*expr.Expr_SelectExpr"
}
return fmt.Sprintf("^#%d:%s#", e.ID(), valType)
case ast.EntryExpr:
return fmt.Sprintf("^#%d:%s#", e.ID(), "*expr.Expr_CreateStruct_Entry")
}
return ""
}
type locationAdorner struct {
sourceInfo *ast.SourceInfo
}
var _ metadata = &locationAdorner{}
func (l *locationAdorner) GetLocation(exprID int64) (common.Location, bool) {
loc := l.sourceInfo.GetStartLocation(exprID)
return loc, loc != common.NoLocation
}
func (l *locationAdorner) GetMetadata(elem any) string {
var elemID int64
switch elem := elem.(type) {
case ast.Expr:
elemID = elem.ID()
case ast.EntryExpr:
elemID = elem.ID()
}
location, _ := l.GetLocation(elemID)
return fmt.Sprintf("^#%d[%d,%d]#", elemID, location.Line(), location.Column())
}
func convertMacroCallsToString(source *ast.SourceInfo) string {
macroCalls := source.MacroCalls()
keys := make([]int64, len(macroCalls))
adornedStrings := make([]string, len(macroCalls))
i := 0
for k := range macroCalls {
keys[i] = k
i++
}
fac := ast.NewExprFactory()
// Sort the keys in descending order to create a stable ordering for tests and improve readability.
sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] })
i = 0
for _, key := range keys {
call := macroCalls[int64(key)].AsCall()
var callWithID ast.Expr
if call.IsMemberFunction() {
callWithID = fac.NewMemberCall(int64(key), call.FunctionName(), call.Target(), call.Args()...)
} else {
callWithID = fac.NewCall(int64(key), call.FunctionName(), call.Args()...)
}
adornedStrings[i] = debug.ToAdornedDebugString(
callWithID,
&kindAndIDAdorner{sourceInfo: source})
i++
}
return strings.Join(adornedStrings, ",\n")
}
func TestParse(t *testing.T) {
defaultParser := newTestParser(t)
for i, tst := range testCases {
name := fmt.Sprintf("%d %s", i, tst.I)
// Local variable required as the closure will reference the value for the last
// 'tst' value rather than the local 'tc' instance declared within the loop.
tc := tst
t.Run(name, func(t *testing.T) {
// Runs the tests in parallel to ensure that there are no data races
// due to shared mutable state across tests.
t.Parallel()
p := defaultParser
if len(tc.Opts) > 0 {
p = newTestParser(t, tc.Opts...)
}
src := common.NewTextSource(tc.I)
parsed, errors := p.Parse(src)
if len(errors.GetErrors()) > 0 {
actualErr := errors.ToDisplayString()
if tc.E == "" {
t.Fatalf("Unexpected errors: %v", actualErr)
} else if !test.Compare(actualErr, tc.E) {
t.Fatal(test.DiffMessage("Error mismatch", actualErr, tc.E))
}
return
} else if tc.E != "" {
t.Fatalf("Expected error not thrown: '%s'", tc.E)
}
failureDisplayMethod := fmt.Sprintf("Parse(\"%s\")", tc.I)
actualWithKind := debug.ToAdornedDebugString(parsed.Expr(), &kindAndIDAdorner{})
if !test.Compare(actualWithKind, tc.P) {
t.Fatal(test.DiffMessage(fmt.Sprintf("Structure - %s", failureDisplayMethod), actualWithKind, tc.P))
}
if tc.L != "" {
actualWithLocation := debug.ToAdornedDebugString(parsed.Expr(), &locationAdorner{parsed.SourceInfo()})
if !test.Compare(actualWithLocation, tc.L) {
t.Fatal(test.DiffMessage(fmt.Sprintf("Location - %s", failureDisplayMethod), actualWithLocation, tc.L))
}
}
if tc.M != "" {
actualAdornedMacroCalls := convertMacroCallsToString(parsed.SourceInfo())
if !test.Compare(actualAdornedMacroCalls, tc.M) {
t.Fatal(test.DiffMessage(fmt.Sprintf("Macro Calls - %s", failureDisplayMethod), actualAdornedMacroCalls, tc.M))
}
}
// Verify there are no unused IDs in the source info.
astIDs := parsed.IDs()
unusedIDs := []int64{}
for id := range parsed.SourceInfo().OffsetRanges() {
if !astIDs[id] {
unusedIDs = append(unusedIDs, id)
}
}
if len(unusedIDs) > 0 {
t.Errorf("SourceInfo has offset range for IDs %v, but no such nodes exists in AST: %s",
unusedIDs, debug.ToDebugStringWithIDs(parsed.Expr()))
}
// Verify that source info offset ranges are shifted when the source is prepended with whitespace.
padding := strings.Repeat(" \n", 10)
padSrc := &RelativeSource{
Source: common.NewTextSource(padding + src.Content()),
localSrc: src,
absLoc: common.NewLocation(11, 0),
}
padded, padErrs := p.Parse(padSrc)
if len(padErrs.GetErrors()) > 0 {
t.Fatalf("Unexpected errors with padded source: %v", padErrs.ToDisplayString())
}
for id, origRange := range parsed.SourceInfo().OffsetRanges() {
padRange, found := padded.SourceInfo().GetOffsetRange(id)
if !found {
t.Errorf("ID %d not found in padded source info", id)
continue
}
want := ast.OffsetRange{Start: origRange.Start + 100, Stop: origRange.Stop + 100}
if padRange != want {
t.Errorf("ID %d offset range mismatch: got %v, want %v", id, padRange, want)
}
}
})
}
}
func TestExpressionSizeCodePointLimit(t *testing.T) {
p, err := NewParser(Macros(AllMacros...), ExpressionSizeCodePointLimit((2)))
if err != nil {
t.Fatal(err)
}
src := common.NewTextSource("foo")
_, errs := p.Parse(src)
if got, want := len(errs.GetErrors()), 1; got != want {
t.Fatalf("got %d errors, want %d errors: %s", got, want, errs.ToDisplayString())
}
if got, want := errs.GetErrors()[0].Message, "expression code point size exceeds limit: size: 3, limit 2"; got != want {
t.Fatalf("got %q, want %q: %s", got, want, errs.GetErrors()[0].ToDisplayString(src))
}
}
func TestParserOptionErrors(t *testing.T) {
if _, err := NewParser(Macros(AllMacros...), MaxRecursionDepth(-2)); err == nil {
t.Fatalf("got %q, want %q", err, "max recursion depth must be greater than or equal to -1: -2")
}
if _, err := NewParser(ErrorRecoveryLimit(-2)); err == nil {
t.Fatalf("got %q, want %q", err, "error recovery limit must be greater than or equal to -1: -2")
}
if _, err := NewParser(ErrorRecoveryLookaheadTokenLimit(0)); err == nil {
t.Fatalf("got %q, want %q", err, "error recovery lookahead token limit must be at least 1: 0")
}
if _, err := NewParser(ErrorReportingLimit(0)); err == nil {
t.Fatalf("got %q, want %q", err, "error reporting limit must be greater than 0: -2")
}
if _, err := NewParser(ExpressionSizeCodePointLimit(-2)); err == nil {
t.Fatalf("got %q, want %q", err, "expression size code point limit must be greater than or equal to -1: -2")
}
}
func BenchmarkParse(b *testing.B) {
p, err := NewParser(
Macros(AllMacros...),
MaxRecursionDepth(32),
ErrorRecoveryLimit(4),
ErrorRecoveryLookaheadTokenLimit(4),
PopulateMacroCalls(true),
)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, testCase := range testCases {
p.Parse(common.NewTextSource(testCase.I))
}
}
}
func BenchmarkParseParallel(b *testing.B) {
p, err := NewParser(
Macros(AllMacros...),
MaxRecursionDepth(32),
ErrorRecoveryLimit(4),
ErrorRecoveryLookaheadTokenLimit(4),
PopulateMacroCalls(true),
)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
for _, testCase := range testCases {
p.Parse(common.NewTextSource(testCase.I))
}
}
})
}
func TestParseErrorData(t *testing.T) {
p := newTestParser(t)
src := common.NewTextSource(`a.?b`)
_, iss := p.Parse(src)
if len(iss.GetErrors()) != 1 {
t.Fatalf("Check() of a bad expression did produce a single error: %v", iss.ToDisplayString())
}
celErr := iss.GetErrors()[0]
if celErr.ExprID != 2 {
t.Errorf("got exprID %v, wanted 2", celErr.ExprID)
}
if !strings.Contains(celErr.Message, "unsupported syntax") {
t.Errorf("got message %v, wanted unsupported syntax", celErr.Message)
}
}
func newTestParser(t *testing.T, options ...Option) *Parser {
t.Helper()
defaultOpts := []Option{
Macros(AllMacros...),
MaxRecursionDepth(32),
ErrorRecoveryLimit(4),
ErrorRecoveryLookaheadTokenLimit(4),
PopulateMacroCalls(true),
}
opts := append([]Option{}, defaultOpts...)
opts = append(opts, options...)
p, err := NewParser(opts...)
if err != nil {
t.Fatalf("NewParser() failed: %v", err)
}
return p
}
// RelativeSource represents an embedded source element within a larger source.
type RelativeSource struct {
common.Source
localSrc common.Source
absLoc common.Location
}
// Content returns the embedded source snippet.
func (rel *RelativeSource) Content() string {
return rel.localSrc.Content()
}
// OffsetLocation returns the absolute location given the relative offset, if found.
func (rel *RelativeSource) OffsetLocation(offset int32) (common.Location, bool) {
absOffset, found := rel.Source.LocationOffset(rel.absLoc)
if !found {
return common.NoLocation, false
}
return rel.Source.OffsetLocation(absOffset + offset)
}