github.com/expr-lang/expr@v1.16.9/parser/parser_test.go (about)

     1  package parser_test
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/expr-lang/expr/internal/testify/assert"
     9  	"github.com/expr-lang/expr/internal/testify/require"
    10  
    11  	. "github.com/expr-lang/expr/ast"
    12  	"github.com/expr-lang/expr/parser"
    13  )
    14  
    15  func TestParse(t *testing.T) {
    16  	tests := []struct {
    17  		input string
    18  		want  Node
    19  	}{
    20  		{
    21  			"a",
    22  			&IdentifierNode{Value: "a"},
    23  		},
    24  		{
    25  			`"str"`,
    26  			&StringNode{Value: "str"},
    27  		},
    28  		{
    29  			"`hello\nworld`",
    30  			&StringNode{Value: `hello
    31  world`},
    32  		},
    33  		{
    34  			"3",
    35  			&IntegerNode{Value: 3},
    36  		},
    37  		{
    38  			"0xFF",
    39  			&IntegerNode{Value: 255},
    40  		},
    41  		{
    42  			"0x6E",
    43  			&IntegerNode{Value: 110},
    44  		},
    45  		{
    46  			"0X63",
    47  			&IntegerNode{Value: 99},
    48  		},
    49  		{
    50  			"0o600",
    51  			&IntegerNode{Value: 384},
    52  		},
    53  		{
    54  			"0O45",
    55  			&IntegerNode{Value: 37},
    56  		},
    57  		{
    58  			"0b10",
    59  			&IntegerNode{Value: 2},
    60  		},
    61  		{
    62  			"0B101011",
    63  			&IntegerNode{Value: 43},
    64  		},
    65  		{
    66  			"10_000_000",
    67  			&IntegerNode{Value: 10_000_000},
    68  		},
    69  		{
    70  			"2.5",
    71  			&FloatNode{Value: 2.5},
    72  		},
    73  		{
    74  			"1e9",
    75  			&FloatNode{Value: 1e9},
    76  		},
    77  		{
    78  			"true",
    79  			&BoolNode{Value: true},
    80  		},
    81  		{
    82  			"false",
    83  			&BoolNode{Value: false},
    84  		},
    85  		{
    86  			"nil",
    87  			&NilNode{},
    88  		},
    89  		{
    90  			"-3",
    91  			&UnaryNode{Operator: "-",
    92  				Node: &IntegerNode{Value: 3}},
    93  		},
    94  		{
    95  			"-2^2",
    96  			&UnaryNode{
    97  				Operator: "-",
    98  				Node: &BinaryNode{
    99  					Operator: "^",
   100  					Left:     &IntegerNode{Value: 2},
   101  					Right:    &IntegerNode{Value: 2},
   102  				},
   103  			},
   104  		},
   105  		{
   106  			"1 - 2",
   107  			&BinaryNode{Operator: "-",
   108  				Left:  &IntegerNode{Value: 1},
   109  				Right: &IntegerNode{Value: 2}},
   110  		},
   111  		{
   112  			"(1 - 2) * 3",
   113  			&BinaryNode{
   114  				Operator: "*",
   115  				Left: &BinaryNode{
   116  					Operator: "-",
   117  					Left:     &IntegerNode{Value: 1},
   118  					Right:    &IntegerNode{Value: 2},
   119  				},
   120  				Right: &IntegerNode{Value: 3},
   121  			},
   122  		},
   123  		{
   124  			"a or b or c",
   125  			&BinaryNode{Operator: "or",
   126  				Left: &BinaryNode{Operator: "or",
   127  					Left:  &IdentifierNode{Value: "a"},
   128  					Right: &IdentifierNode{Value: "b"}},
   129  				Right: &IdentifierNode{Value: "c"}},
   130  		},
   131  		{
   132  			"a or b and c",
   133  			&BinaryNode{Operator: "or",
   134  				Left: &IdentifierNode{Value: "a"},
   135  				Right: &BinaryNode{Operator: "and",
   136  					Left:  &IdentifierNode{Value: "b"},
   137  					Right: &IdentifierNode{Value: "c"}}},
   138  		},
   139  		{
   140  			"(a or b) and c",
   141  			&BinaryNode{Operator: "and",
   142  				Left: &BinaryNode{Operator: "or",
   143  					Left:  &IdentifierNode{Value: "a"},
   144  					Right: &IdentifierNode{Value: "b"}},
   145  				Right: &IdentifierNode{Value: "c"}},
   146  		},
   147  		{
   148  			"2**4-1",
   149  			&BinaryNode{Operator: "-",
   150  				Left: &BinaryNode{Operator: "**",
   151  					Left:  &IntegerNode{Value: 2},
   152  					Right: &IntegerNode{Value: 4}},
   153  				Right: &IntegerNode{Value: 1}},
   154  		},
   155  		{
   156  			"foo(bar())",
   157  			&CallNode{Callee: &IdentifierNode{Value: "foo"},
   158  				Arguments: []Node{&CallNode{Callee: &IdentifierNode{Value: "bar"},
   159  					Arguments: []Node{}}}},
   160  		},
   161  		{
   162  			`foo("arg1", 2, true)`,
   163  			&CallNode{Callee: &IdentifierNode{Value: "foo"},
   164  				Arguments: []Node{&StringNode{Value: "arg1"},
   165  					&IntegerNode{Value: 2},
   166  					&BoolNode{Value: true}}},
   167  		},
   168  		{
   169  			"foo.bar",
   170  			&MemberNode{Node: &IdentifierNode{Value: "foo"},
   171  				Property: &StringNode{Value: "bar"}},
   172  		},
   173  		{
   174  			"foo['all']",
   175  			&MemberNode{Node: &IdentifierNode{Value: "foo"},
   176  				Property: &StringNode{Value: "all"}},
   177  		},
   178  		{
   179  			"foo.bar()",
   180  			&CallNode{Callee: &MemberNode{Node: &IdentifierNode{Value: "foo"},
   181  				Property: &StringNode{Value: "bar"}, Method: true},
   182  				Arguments: []Node{}},
   183  		},
   184  		{
   185  			`foo.bar("arg1", 2, true)`,
   186  			&CallNode{Callee: &MemberNode{Node: &IdentifierNode{Value: "foo"},
   187  				Property: &StringNode{Value: "bar"}, Method: true},
   188  				Arguments: []Node{&StringNode{Value: "arg1"},
   189  					&IntegerNode{Value: 2},
   190  					&BoolNode{Value: true}}},
   191  		},
   192  		{
   193  			"foo[3]",
   194  			&MemberNode{Node: &IdentifierNode{Value: "foo"},
   195  				Property: &IntegerNode{Value: 3}},
   196  		},
   197  		{
   198  			"true ? true : false",
   199  			&ConditionalNode{Cond: &BoolNode{Value: true},
   200  				Exp1: &BoolNode{Value: true},
   201  				Exp2: &BoolNode{}},
   202  		},
   203  		{
   204  			"a?[b]:c",
   205  			&ConditionalNode{Cond: &IdentifierNode{Value: "a"},
   206  				Exp1: &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "b"}}},
   207  				Exp2: &IdentifierNode{Value: "c"}},
   208  		},
   209  		{
   210  			"a.b().c().d[33]",
   211  			&MemberNode{
   212  				Node: &MemberNode{
   213  					Node: &CallNode{
   214  						Callee: &MemberNode{
   215  							Node: &CallNode{
   216  								Callee: &MemberNode{
   217  									Node: &IdentifierNode{
   218  										Value: "a",
   219  									},
   220  									Property: &StringNode{
   221  										Value: "b",
   222  									},
   223  									Method: true,
   224  								},
   225  								Arguments: []Node{},
   226  							},
   227  							Property: &StringNode{
   228  								Value: "c",
   229  							},
   230  							Method: true,
   231  						},
   232  						Arguments: []Node{},
   233  					},
   234  					Property: &StringNode{
   235  						Value: "d",
   236  					},
   237  				},
   238  				Property: &IntegerNode{Value: 33}},
   239  		},
   240  		{
   241  			"'a' == 'b'",
   242  			&BinaryNode{Operator: "==",
   243  				Left:  &StringNode{Value: "a"},
   244  				Right: &StringNode{Value: "b"}},
   245  		},
   246  		{
   247  			"+0 != -0",
   248  			&BinaryNode{Operator: "!=",
   249  				Left: &UnaryNode{Operator: "+",
   250  					Node: &IntegerNode{}},
   251  				Right: &UnaryNode{Operator: "-",
   252  					Node: &IntegerNode{}}},
   253  		},
   254  		{
   255  			"[a, b, c]",
   256  			&ArrayNode{Nodes: []Node{&IdentifierNode{Value: "a"},
   257  				&IdentifierNode{Value: "b"},
   258  				&IdentifierNode{Value: "c"}}},
   259  		},
   260  		{
   261  			"{foo:1, bar:2}",
   262  			&MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "foo"},
   263  				Value: &IntegerNode{Value: 1}},
   264  				&PairNode{Key: &StringNode{Value: "bar"},
   265  					Value: &IntegerNode{Value: 2}}}},
   266  		},
   267  		{
   268  			"{foo:1, bar:2, }",
   269  			&MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "foo"},
   270  				Value: &IntegerNode{Value: 1}},
   271  				&PairNode{Key: &StringNode{Value: "bar"},
   272  					Value: &IntegerNode{Value: 2}}}},
   273  		},
   274  		{
   275  			`{"a": 1, 'b': 2}`,
   276  			&MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "a"},
   277  				Value: &IntegerNode{Value: 1}},
   278  				&PairNode{Key: &StringNode{Value: "b"},
   279  					Value: &IntegerNode{Value: 2}}}},
   280  		},
   281  		{
   282  			"[1].foo",
   283  			&MemberNode{Node: &ArrayNode{Nodes: []Node{&IntegerNode{Value: 1}}},
   284  				Property: &StringNode{Value: "foo"}},
   285  		},
   286  		{
   287  			"{foo:1}.bar",
   288  			&MemberNode{Node: &MapNode{Pairs: []Node{&PairNode{Key: &StringNode{Value: "foo"},
   289  				Value: &IntegerNode{Value: 1}}}},
   290  				Property: &StringNode{Value: "bar"}},
   291  		},
   292  		{
   293  			"len(foo)",
   294  			&BuiltinNode{
   295  				Name: "len",
   296  				Arguments: []Node{
   297  					&IdentifierNode{Value: "foo"},
   298  				},
   299  			},
   300  		},
   301  		{
   302  			`foo matches "foo"`,
   303  			&BinaryNode{
   304  				Operator: "matches",
   305  				Left:     &IdentifierNode{Value: "foo"},
   306  				Right:    &StringNode{Value: "foo"}},
   307  		},
   308  		{
   309  			`foo not matches "foo"`,
   310  			&UnaryNode{
   311  				Operator: "not",
   312  				Node: &BinaryNode{
   313  					Operator: "matches",
   314  					Left:     &IdentifierNode{Value: "foo"},
   315  					Right:    &StringNode{Value: "foo"}}},
   316  		},
   317  		{
   318  			`foo matches regex`,
   319  			&BinaryNode{
   320  				Operator: "matches",
   321  				Left:     &IdentifierNode{Value: "foo"},
   322  				Right:    &IdentifierNode{Value: "regex"}},
   323  		},
   324  		{
   325  			`foo contains "foo"`,
   326  			&BinaryNode{
   327  				Operator: "contains",
   328  				Left:     &IdentifierNode{Value: "foo"},
   329  				Right:    &StringNode{Value: "foo"}},
   330  		},
   331  		{
   332  			`foo not contains "foo"`,
   333  			&UnaryNode{
   334  				Operator: "not",
   335  				Node: &BinaryNode{Operator: "contains",
   336  					Left:  &IdentifierNode{Value: "foo"},
   337  					Right: &StringNode{Value: "foo"}}},
   338  		},
   339  		{
   340  			`foo startsWith "foo"`,
   341  			&BinaryNode{Operator: "startsWith",
   342  				Left:  &IdentifierNode{Value: "foo"},
   343  				Right: &StringNode{Value: "foo"}},
   344  		},
   345  		{
   346  			`foo endsWith "foo"`,
   347  			&BinaryNode{Operator: "endsWith",
   348  				Left:  &IdentifierNode{Value: "foo"},
   349  				Right: &StringNode{Value: "foo"}},
   350  		},
   351  		{
   352  			"1..9",
   353  			&BinaryNode{Operator: "..",
   354  				Left:  &IntegerNode{Value: 1},
   355  				Right: &IntegerNode{Value: 9}},
   356  		},
   357  		{
   358  			"0 in []",
   359  			&BinaryNode{Operator: "in",
   360  				Left:  &IntegerNode{},
   361  				Right: &ArrayNode{Nodes: []Node{}}},
   362  		},
   363  		{
   364  			"not in_var",
   365  			&UnaryNode{Operator: "not",
   366  				Node: &IdentifierNode{Value: "in_var"}},
   367  		},
   368  		{
   369  			"-1 not in [1, 2, 3, 4]",
   370  			&UnaryNode{Operator: "not",
   371  				Node: &BinaryNode{Operator: "in",
   372  					Left: &UnaryNode{Operator: "-", Node: &IntegerNode{Value: 1}},
   373  					Right: &ArrayNode{Nodes: []Node{
   374  						&IntegerNode{Value: 1},
   375  						&IntegerNode{Value: 2},
   376  						&IntegerNode{Value: 3},
   377  						&IntegerNode{Value: 4},
   378  					}}}},
   379  		},
   380  		{
   381  			"1*8 not in [1, 2, 3, 4]",
   382  			&UnaryNode{Operator: "not",
   383  				Node: &BinaryNode{Operator: "in",
   384  					Left: &BinaryNode{Operator: "*",
   385  						Left:  &IntegerNode{Value: 1},
   386  						Right: &IntegerNode{Value: 8},
   387  					},
   388  					Right: &ArrayNode{Nodes: []Node{
   389  						&IntegerNode{Value: 1},
   390  						&IntegerNode{Value: 2},
   391  						&IntegerNode{Value: 3},
   392  						&IntegerNode{Value: 4},
   393  					}}}},
   394  		},
   395  		{
   396  			"2==2 ? false : 3 not in [1, 2, 5]",
   397  			&ConditionalNode{
   398  				Cond: &BinaryNode{
   399  					Operator: "==",
   400  					Left:     &IntegerNode{Value: 2},
   401  					Right:    &IntegerNode{Value: 2},
   402  				},
   403  				Exp1: &BoolNode{Value: false},
   404  				Exp2: &UnaryNode{
   405  					Operator: "not",
   406  					Node: &BinaryNode{
   407  						Operator: "in",
   408  						Left:     &IntegerNode{Value: 3},
   409  						Right: &ArrayNode{Nodes: []Node{
   410  							&IntegerNode{Value: 1},
   411  							&IntegerNode{Value: 2},
   412  							&IntegerNode{Value: 5},
   413  						}}}}},
   414  		},
   415  		{
   416  			"'foo' + 'bar' not matches 'foobar'",
   417  			&UnaryNode{Operator: "not",
   418  				Node: &BinaryNode{Operator: "matches",
   419  					Left: &BinaryNode{Operator: "+",
   420  						Left:  &StringNode{Value: "foo"},
   421  						Right: &StringNode{Value: "bar"}},
   422  					Right: &StringNode{Value: "foobar"}}},
   423  		},
   424  		{
   425  			"all(Tickets, #)",
   426  			&BuiltinNode{
   427  				Name: "all",
   428  				Arguments: []Node{
   429  					&IdentifierNode{Value: "Tickets"},
   430  					&ClosureNode{
   431  						Node: &PointerNode{},
   432  					}}},
   433  		},
   434  		{
   435  			"all(Tickets, {.Price > 0})",
   436  			&BuiltinNode{
   437  				Name: "all",
   438  				Arguments: []Node{
   439  					&IdentifierNode{Value: "Tickets"},
   440  					&ClosureNode{
   441  						Node: &BinaryNode{
   442  							Operator: ">",
   443  							Left: &MemberNode{Node: &PointerNode{},
   444  								Property: &StringNode{Value: "Price"}},
   445  							Right: &IntegerNode{Value: 0}}}}},
   446  		},
   447  		{
   448  			"one(Tickets, {#.Price > 0})",
   449  			&BuiltinNode{
   450  				Name: "one",
   451  				Arguments: []Node{
   452  					&IdentifierNode{Value: "Tickets"},
   453  					&ClosureNode{
   454  						Node: &BinaryNode{
   455  							Operator: ">",
   456  							Left: &MemberNode{
   457  								Node:     &PointerNode{},
   458  								Property: &StringNode{Value: "Price"},
   459  							},
   460  							Right: &IntegerNode{Value: 0}}}}},
   461  		},
   462  		{
   463  			"filter(Prices, {# > 100})",
   464  			&BuiltinNode{Name: "filter",
   465  				Arguments: []Node{&IdentifierNode{Value: "Prices"},
   466  					&ClosureNode{Node: &BinaryNode{Operator: ">",
   467  						Left:  &PointerNode{},
   468  						Right: &IntegerNode{Value: 100}}}}},
   469  		},
   470  		{
   471  			"array[1:2]",
   472  			&SliceNode{Node: &IdentifierNode{Value: "array"},
   473  				From: &IntegerNode{Value: 1},
   474  				To:   &IntegerNode{Value: 2}},
   475  		},
   476  		{
   477  			"array[:2]",
   478  			&SliceNode{Node: &IdentifierNode{Value: "array"},
   479  				To: &IntegerNode{Value: 2}},
   480  		},
   481  		{
   482  			"array[1:]",
   483  			&SliceNode{Node: &IdentifierNode{Value: "array"},
   484  				From: &IntegerNode{Value: 1}},
   485  		},
   486  		{
   487  			"array[:]",
   488  			&SliceNode{Node: &IdentifierNode{Value: "array"}},
   489  		},
   490  		{
   491  			"[]",
   492  			&ArrayNode{},
   493  		},
   494  		{
   495  			"foo ?? bar",
   496  			&BinaryNode{Operator: "??",
   497  				Left:  &IdentifierNode{Value: "foo"},
   498  				Right: &IdentifierNode{Value: "bar"}},
   499  		},
   500  		{
   501  			"foo ?? bar ?? baz",
   502  			&BinaryNode{Operator: "??",
   503  				Left: &BinaryNode{Operator: "??",
   504  					Left:  &IdentifierNode{Value: "foo"},
   505  					Right: &IdentifierNode{Value: "bar"}},
   506  				Right: &IdentifierNode{Value: "baz"}},
   507  		},
   508  		{
   509  			"foo ?? (bar || baz)",
   510  			&BinaryNode{Operator: "??",
   511  				Left: &IdentifierNode{Value: "foo"},
   512  				Right: &BinaryNode{Operator: "||",
   513  					Left:  &IdentifierNode{Value: "bar"},
   514  					Right: &IdentifierNode{Value: "baz"}}},
   515  		},
   516  		{
   517  			"foo || bar ?? baz",
   518  			&BinaryNode{Operator: "||",
   519  				Left: &IdentifierNode{Value: "foo"},
   520  				Right: &BinaryNode{Operator: "??",
   521  					Left:  &IdentifierNode{Value: "bar"},
   522  					Right: &IdentifierNode{Value: "baz"}}},
   523  		},
   524  		{
   525  			"foo ?? bar()",
   526  			&BinaryNode{Operator: "??",
   527  				Left:  &IdentifierNode{Value: "foo"},
   528  				Right: &CallNode{Callee: &IdentifierNode{Value: "bar"}}},
   529  		},
   530  		{
   531  			"true | ok()",
   532  			&CallNode{
   533  				Callee: &IdentifierNode{Value: "ok"},
   534  				Arguments: []Node{
   535  					&BoolNode{Value: true}}}},
   536  		{
   537  			`let foo = a + b; foo + c`,
   538  			&VariableDeclaratorNode{
   539  				Name: "foo",
   540  				Value: &BinaryNode{Operator: "+",
   541  					Left:  &IdentifierNode{Value: "a"},
   542  					Right: &IdentifierNode{Value: "b"}},
   543  				Expr: &BinaryNode{Operator: "+",
   544  					Left:  &IdentifierNode{Value: "foo"},
   545  					Right: &IdentifierNode{Value: "c"}}},
   546  		},
   547  		{
   548  			`map([], #index)`,
   549  			&BuiltinNode{
   550  				Name: "map",
   551  				Arguments: []Node{
   552  					&ArrayNode{},
   553  					&ClosureNode{
   554  						Node: &PointerNode{Name: "index"},
   555  					},
   556  				},
   557  			},
   558  		},
   559  		{
   560  			`::split("a,b,c", ",")`,
   561  			&BuiltinNode{
   562  				Name: "split",
   563  				Arguments: []Node{
   564  					&StringNode{Value: "a,b,c"},
   565  					&StringNode{Value: ","},
   566  				},
   567  			},
   568  		},
   569  		{
   570  			`::split("a,b,c", ",")[0]`,
   571  			&MemberNode{
   572  				Node: &BuiltinNode{
   573  					Name: "split",
   574  					Arguments: []Node{
   575  						&StringNode{Value: "a,b,c"},
   576  						&StringNode{Value: ","},
   577  					},
   578  				},
   579  				Property: &IntegerNode{Value: 0},
   580  			},
   581  		},
   582  		{
   583  			`"hello"[1:3]`,
   584  			&SliceNode{
   585  				Node: &StringNode{Value: "hello"},
   586  				From: &IntegerNode{Value: 1},
   587  				To:   &IntegerNode{Value: 3},
   588  			},
   589  		},
   590  		{
   591  			`1 < 2 > 3`,
   592  			&BinaryNode{
   593  				Operator: "&&",
   594  				Left: &BinaryNode{
   595  					Operator: "<",
   596  					Left:     &IntegerNode{Value: 1},
   597  					Right:    &IntegerNode{Value: 2},
   598  				},
   599  				Right: &BinaryNode{
   600  					Operator: ">",
   601  					Left:     &IntegerNode{Value: 2},
   602  					Right:    &IntegerNode{Value: 3},
   603  				},
   604  			},
   605  		},
   606  		{
   607  			`1 < 2 < 3 < 4`,
   608  			&BinaryNode{
   609  				Operator: "&&",
   610  				Left: &BinaryNode{
   611  					Operator: "&&",
   612  					Left: &BinaryNode{
   613  						Operator: "<",
   614  						Left:     &IntegerNode{Value: 1},
   615  						Right:    &IntegerNode{Value: 2},
   616  					},
   617  					Right: &BinaryNode{
   618  						Operator: "<",
   619  						Left:     &IntegerNode{Value: 2},
   620  						Right:    &IntegerNode{Value: 3},
   621  					},
   622  				},
   623  				Right: &BinaryNode{
   624  					Operator: "<",
   625  					Left:     &IntegerNode{Value: 3},
   626  					Right:    &IntegerNode{Value: 4},
   627  				},
   628  			},
   629  		},
   630  		{
   631  			`1 < 2 < 3 == true`,
   632  			&BinaryNode{
   633  				Operator: "==",
   634  				Left: &BinaryNode{
   635  					Operator: "&&",
   636  					Left: &BinaryNode{
   637  						Operator: "<",
   638  						Left:     &IntegerNode{Value: 1},
   639  						Right:    &IntegerNode{Value: 2},
   640  					},
   641  					Right: &BinaryNode{
   642  						Operator: "<",
   643  						Left:     &IntegerNode{Value: 2},
   644  						Right:    &IntegerNode{Value: 3},
   645  					},
   646  				},
   647  				Right: &BoolNode{Value: true},
   648  			},
   649  		},
   650  	}
   651  	for _, test := range tests {
   652  		t.Run(test.input, func(t *testing.T) {
   653  			actual, err := parser.Parse(test.input)
   654  			require.NoError(t, err)
   655  			assert.Equal(t, Dump(test.want), Dump(actual.Node))
   656  		})
   657  	}
   658  }
   659  
   660  const errorTests = `
   661  foo.
   662  unexpected end of expression (1:4)
   663   | foo.
   664   | ...^
   665  
   666  a+
   667  unexpected token EOF (1:2)
   668   | a+
   669   | .^
   670  
   671  a ? (1+2) c
   672  unexpected token Identifier("c") (1:11)
   673   | a ? (1+2) c
   674   | ..........^
   675  
   676  [a b]
   677  unexpected token Identifier("b") (1:4)
   678   | [a b]
   679   | ...^
   680  
   681  foo.bar(a b)
   682  unexpected token Identifier("b") (1:11)
   683   | foo.bar(a b)
   684   | ..........^
   685  
   686  {-}
   687  a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator("-")) (1:2)
   688   | {-}
   689   | .^
   690  
   691  foo({.bar})
   692  a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(".")) (1:6)
   693   | foo({.bar})
   694   | .....^
   695  
   696  .foo
   697  cannot use pointer accessor outside closure (1:1)
   698   | .foo
   699   | ^
   700  
   701  [1, 2, 3,,]
   702  unexpected token Operator(",") (1:10)
   703   | [1, 2, 3,,]
   704   | .........^
   705  
   706  [,]
   707  unexpected token Operator(",") (1:2)
   708   | [,]
   709   | .^
   710  
   711  {,}
   712  a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(",")) (1:2)
   713   | {,}
   714   | .^
   715  
   716  {foo:1, bar:2, ,}
   717  unexpected token Operator(",") (1:16)
   718   | {foo:1, bar:2, ,}
   719   | ...............^
   720  
   721  foo ?? bar || baz
   722  Operator (||) and coalesce expressions (??) cannot be mixed. Wrap either by parentheses. (1:12)
   723   | foo ?? bar || baz
   724   | ...........^
   725  
   726  0b15
   727  bad number syntax: "0b15" (1:4)
   728   | 0b15
   729   | ...^
   730  
   731  0X10G
   732  bad number syntax: "0X10G" (1:5)
   733   | 0X10G
   734   | ....^
   735  
   736  0o1E
   737  invalid float literal: strconv.ParseFloat: parsing "0o1E": invalid syntax (1:4)
   738   | 0o1E
   739   | ...^
   740  
   741  0b1E
   742  invalid float literal: strconv.ParseFloat: parsing "0b1E": invalid syntax (1:4)
   743   | 0b1E
   744   | ...^
   745  
   746  0b1E+6
   747  bad number syntax: "0b1E+6" (1:6)
   748   | 0b1E+6
   749   | .....^
   750  
   751  0b1E+1
   752  invalid float literal: strconv.ParseFloat: parsing "0b1E+1": invalid syntax (1:6)
   753   | 0b1E+1
   754   | .....^
   755  
   756  0o1E+1
   757  invalid float literal: strconv.ParseFloat: parsing "0o1E+1": invalid syntax (1:6)
   758   | 0o1E+1
   759   | .....^
   760  
   761  1E
   762  invalid float literal: strconv.ParseFloat: parsing "1E": invalid syntax (1:2)
   763   | 1E
   764   | .^
   765  
   766  1 not == [1, 2, 5]
   767  unexpected token Operator("==") (1:7)
   768   | 1 not == [1, 2, 5]
   769   | ......^
   770  `
   771  
   772  func TestParse_error(t *testing.T) {
   773  	tests := strings.Split(strings.Trim(errorTests, "\n"), "\n\n")
   774  	for _, test := range tests {
   775  		input := strings.SplitN(test, "\n", 2)
   776  		if len(input) != 2 {
   777  			t.Errorf("syntax error in test: %q", test)
   778  			break
   779  		}
   780  		_, err := parser.Parse(input[0])
   781  		if err == nil {
   782  			err = fmt.Errorf("<nil>")
   783  		}
   784  		assert.Equal(t, input[1], err.Error(), input[0])
   785  	}
   786  }
   787  
   788  func TestParse_optional_chaining(t *testing.T) {
   789  	parseTests := []struct {
   790  		input    string
   791  		expected Node
   792  	}{
   793  		{
   794  			"foo?.bar.baz",
   795  			&ChainNode{
   796  				Node: &MemberNode{
   797  					Node: &MemberNode{
   798  						Node:     &IdentifierNode{Value: "foo"},
   799  						Property: &StringNode{Value: "bar"},
   800  						Optional: true,
   801  					},
   802  					Property: &StringNode{Value: "baz"},
   803  				},
   804  			},
   805  		},
   806  		{
   807  			"foo.bar?.baz",
   808  			&ChainNode{
   809  				Node: &MemberNode{
   810  					Node: &MemberNode{
   811  						Node:     &IdentifierNode{Value: "foo"},
   812  						Property: &StringNode{Value: "bar"},
   813  					},
   814  					Property: &StringNode{Value: "baz"},
   815  					Optional: true,
   816  				},
   817  			},
   818  		},
   819  		{
   820  			"foo?.bar?.baz",
   821  			&ChainNode{
   822  				Node: &MemberNode{
   823  					Node: &MemberNode{
   824  						Node:     &IdentifierNode{Value: "foo"},
   825  						Property: &StringNode{Value: "bar"},
   826  						Optional: true,
   827  					},
   828  					Property: &StringNode{Value: "baz"},
   829  					Optional: true,
   830  				},
   831  			},
   832  		},
   833  		{
   834  			"!foo?.bar.baz",
   835  			&UnaryNode{
   836  				Operator: "!",
   837  				Node: &ChainNode{
   838  					Node: &MemberNode{
   839  						Node: &MemberNode{
   840  							Node:     &IdentifierNode{Value: "foo"},
   841  							Property: &StringNode{Value: "bar"},
   842  							Optional: true,
   843  						},
   844  						Property: &StringNode{Value: "baz"},
   845  					},
   846  				},
   847  			},
   848  		},
   849  		{
   850  			"foo.bar[a?.b]?.baz",
   851  			&ChainNode{
   852  				Node: &MemberNode{
   853  					Node: &MemberNode{
   854  						Node: &MemberNode{
   855  							Node:     &IdentifierNode{Value: "foo"},
   856  							Property: &StringNode{Value: "bar"},
   857  						},
   858  						Property: &ChainNode{
   859  							Node: &MemberNode{
   860  								Node:     &IdentifierNode{Value: "a"},
   861  								Property: &StringNode{Value: "b"},
   862  								Optional: true,
   863  							},
   864  						},
   865  					},
   866  					Property: &StringNode{Value: "baz"},
   867  					Optional: true,
   868  				},
   869  			},
   870  		},
   871  		{
   872  			"foo.bar?.[0]",
   873  			&ChainNode{
   874  				Node: &MemberNode{
   875  					Node: &MemberNode{
   876  						Node:     &IdentifierNode{Value: "foo"},
   877  						Property: &StringNode{Value: "bar"},
   878  					},
   879  					Property: &IntegerNode{Value: 0},
   880  					Optional: true,
   881  				},
   882  			},
   883  		},
   884  	}
   885  	for _, test := range parseTests {
   886  		actual, err := parser.Parse(test.input)
   887  		if err != nil {
   888  			t.Errorf("%s:\n%v", test.input, err)
   889  			continue
   890  		}
   891  		assert.Equal(t, Dump(test.expected), Dump(actual.Node), test.input)
   892  	}
   893  }
   894  
   895  func TestParse_pipe_operator(t *testing.T) {
   896  	input := "arr | map(.foo) | len() | Foo()"
   897  	expect := &CallNode{
   898  		Callee: &IdentifierNode{Value: "Foo"},
   899  		Arguments: []Node{
   900  			&BuiltinNode{
   901  				Name: "len",
   902  				Arguments: []Node{
   903  					&BuiltinNode{
   904  						Name: "map",
   905  						Arguments: []Node{
   906  							&IdentifierNode{Value: "arr"},
   907  							&ClosureNode{
   908  								Node: &MemberNode{
   909  									Node:     &PointerNode{},
   910  									Property: &StringNode{Value: "foo"},
   911  								}}}}}}}}
   912  
   913  	actual, err := parser.Parse(input)
   914  	require.NoError(t, err)
   915  	assert.Equal(t, Dump(expect), Dump(actual.Node))
   916  }