From b72131bcd6496f2e1b22eb7c7f19540d79c3d7a3 Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Wed, 4 Mar 2020 09:15:19 +0300 Subject: [PATCH] Add support for trailing comma in arrays and maps --- parser/parser.go | 35 ++++++++++++++++++++++++++--------- parser/parser_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index a97ec8ddc..baf1d9643 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -373,7 +373,22 @@ func (p *parser) parseClosure() Node { } func (p *parser) parseArrayExpression(token Token) Node { - nodes := p.parseList("[", "]") + nodes := make([]Node, 0) + + p.expect(Bracket, "[") + for !p.current.Is(Bracket, "]") && p.err == nil { + if len(nodes) > 0 { + p.expect(Operator, ",") + if p.current.Is(Bracket, "]") { + goto end + } + } + node := p.parseExpression(0) + nodes = append(nodes, node) + } +end: + p.expect(Bracket, "]") + return &ArrayNode{Base: Loc(token.Location), Nodes: nodes} } @@ -384,6 +399,12 @@ func (p *parser) parseMapExpression(token Token) Node { for !p.current.Is(Bracket, "}") && p.err == nil { if len(nodes) > 0 { p.expect(Operator, ",") + if p.current.Is(Bracket, "}") { + goto end + } + if p.current.Is(Operator, ",") { + p.error("unexpected token %v", p.current) + } } var key Node @@ -407,6 +428,7 @@ func (p *parser) parseMapExpression(token Token) Node { nodes = append(nodes, &PairNode{Base: Loc(token.Location), Key: key, Value: node}) } +end: p.expect(Bracket, "}") return &MapNode{Base: Loc(token.Location), Pairs: nodes} @@ -526,21 +548,16 @@ func isAlphabetic(r rune) bool { } func (p *parser) parseArguments() []Node { - return p.parseList("(", ")") -} - -func (p *parser) parseList(start, end string) []Node { - p.expect(Bracket, start) - + p.expect(Bracket, "(") nodes := make([]Node, 0) - for !p.current.Is(Bracket, end) && p.err == nil { + for !p.current.Is(Bracket, ")") && p.err == nil { if len(nodes) > 0 { p.expect(Operator, ",") } node := p.parseExpression(0) nodes = append(nodes, node) } + p.expect(Bracket, ")") - p.expect(Bracket, end) return nodes } diff --git a/parser/parser_test.go b/parser/parser_test.go index 5b37ad7b1..ddb886058 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -142,6 +142,10 @@ func TestParse(t *testing.T) { "{foo:1, bar:2}", &ast.MapNode{Pairs: []ast.Node{&ast.PairNode{Key: &ast.StringNode{Value: "foo"}, Value: &ast.IntegerNode{Value: 1}}, &ast.PairNode{Key: &ast.StringNode{Value: "bar"}, Value: &ast.IntegerNode{Value: 2}}}}, }, + { + "{foo:1, bar:2, }", + &ast.MapNode{Pairs: []ast.Node{&ast.PairNode{Key: &ast.StringNode{Value: "foo"}, Value: &ast.IntegerNode{Value: 1}}, &ast.PairNode{Key: &ast.StringNode{Value: "bar"}, Value: &ast.IntegerNode{Value: 2}}}}, + }, { `{"a": 1, 'b': 2}`, &ast.MapNode{Pairs: []ast.Node{&ast.PairNode{Key: &ast.StringNode{Value: "a"}, Value: &ast.IntegerNode{Value: 1}}, &ast.PairNode{Key: &ast.StringNode{Value: "b"}, Value: &ast.IntegerNode{Value: 2}}}}, @@ -214,6 +218,14 @@ func TestParse(t *testing.T) { "array[:]", &ast.SliceNode{Node: &ast.IdentifierNode{Value: "array"}}, }, + { + "[]", + &ast.ArrayNode{}, + }, + { + "[1, 2, 3,]", + &ast.ArrayNode{Nodes: []ast.Node{&ast.IntegerNode{Value: 1}, &ast.IntegerNode{Value: 2}, &ast.IntegerNode{Value: 3}}}, + }, } for _, test := range parseTests { actual, err := parser.Parse(test.input) @@ -274,6 +286,26 @@ a map key must be a quoted string, a number, a identifier, or an expression encl cannot use pointer accessor outside closure (1:1) | .foo | ^ + +[1, 2, 3,,] +unexpected token Operator(",") (1:10) + | [1, 2, 3,,] + | .........^ + +[,] +unexpected token Operator(",") (1:2) + | [,] + | .^ + +{,} +a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(",")) (1:2) + | {,} + | .^ + +{foo:1, bar:2, ,} +unexpected token Operator(",") (1:16) + | {foo:1, bar:2, ,} + | ...............^ ` func TestParse_error(t *testing.T) {