Skip to content

Commit c7658ac

Browse files
authored
Add optional array access like foo?.[42] (#550)
1 parent 89478f3 commit c7658ac

File tree

3 files changed

+45
-0
lines changed

3 files changed

+45
-0
lines changed

expr_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,18 @@ func TestExpr(t *testing.T) {
11841184
`"hello"[1:3]`,
11851185
"el",
11861186
},
1187+
{
1188+
`[1, 2, 3]?.[0]`,
1189+
1,
1190+
},
1191+
{
1192+
`[[1, 2], 3, 4]?.[0]?.[1]`,
1193+
2,
1194+
},
1195+
{
1196+
`[nil, 3, 4]?.[0]?.[1]`,
1197+
nil,
1198+
},
11871199
}
11881200

11891201
for _, tt := range tests {
@@ -1281,6 +1293,16 @@ func TestExpr_optional_chaining_nested_chains(t *testing.T) {
12811293
assert.Equal(t, "baz", got)
12821294
}
12831295

1296+
func TestExpr_optional_chaining_array(t *testing.T) {
1297+
env := map[string]any{}
1298+
program, err := expr.Compile("foo?.[1]?.[2]?.[3]", expr.Env(env), expr.AllowUndefinedVariables())
1299+
require.NoError(t, err)
1300+
1301+
got, err := expr.Run(program, env)
1302+
require.NoError(t, err)
1303+
assert.Equal(t, nil, got)
1304+
}
1305+
12841306
func TestExpr_eval_with_env(t *testing.T) {
12851307
_, err := expr.Eval("true", expr.Env(map[string]any{}))
12861308
assert.Error(t, err)

parser/parser.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,10 +575,16 @@ end:
575575
func (p *parser) parsePostfixExpression(node Node) Node {
576576
postfixToken := p.current
577577
for (postfixToken.Is(Operator) || postfixToken.Is(Bracket)) && p.err == nil {
578+
optional := postfixToken.Value == "?."
579+
parseToken:
578580
if postfixToken.Value == "." || postfixToken.Value == "?." {
579581
p.next()
580582

581583
propertyToken := p.current
584+
if optional && propertyToken.Is(Bracket, "[") {
585+
postfixToken = propertyToken
586+
goto parseToken
587+
}
582588
p.next()
583589

584590
if propertyToken.Kind != Identifier &&
@@ -662,8 +668,12 @@ func (p *parser) parsePostfixExpression(node Node) Node {
662668
node = &MemberNode{
663669
Node: node,
664670
Property: from,
671+
Optional: optional,
665672
}
666673
node.SetLocation(postfixToken.Location)
674+
if optional {
675+
node = &ChainNode{Node: node}
676+
}
667677
p.expect(Bracket, "]")
668678
}
669679
}

parser/parser_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,19 @@ func TestParse_optional_chaining(t *testing.T) {
747747
},
748748
},
749749
},
750+
{
751+
"foo.bar?.[0]",
752+
&ChainNode{
753+
Node: &MemberNode{
754+
Node: &MemberNode{
755+
Node: &IdentifierNode{Value: "foo"},
756+
Property: &StringNode{Value: "bar"},
757+
},
758+
Property: &IntegerNode{Value: 0},
759+
Optional: true,
760+
},
761+
},
762+
},
750763
}
751764
for _, test := range parseTests {
752765
actual, err := parser.Parse(test.input)

0 commit comments

Comments
 (0)