Skip to content

Commit 6c865ec

Browse files
committed
[lexical-scoping] Parsing let-expression.
1 parent 3f054ab commit 6c865ec

File tree

5 files changed

+168
-14
lines changed

5 files changed

+168
-14
lines changed

pkg/parsing/astnodetype_string.go

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/parsing/lexer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ const (
8787
TOKExpref
8888
TOKAnd
8989
TOKNot
90+
TOKLet
91+
TOKIn
92+
TOKVarref
9093
TOKEOF
9194
)
9295

pkg/parsing/parser.go

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const (
3737
ASTSubexpression
3838
ASTSlice
3939
ASTValueProjection
40+
ASTLetExpression
41+
ASTVariable
42+
ASTBindings
43+
ASTBinding
4044
)
4145

4246
// ASTNode represents the abstract syntax tree of a JMESPath expression.
@@ -83,6 +87,7 @@ func (node ASTNode) PrettyPrint(indent int) string {
8387

8488
var bindingPowers = map[TokType]int{
8589
TOKEOF: 0,
90+
TOKVarref: 0,
8691
TOKUnquotedIdentifier: 0,
8792
TOKQuotedIdentifier: 0,
8893
TOKRbracket: 0,
@@ -140,6 +145,10 @@ func (p *Parser) Parse(expression string) (ASTNode, error) {
140145
if err != nil {
141146
return ASTNode{}, err
142147
}
148+
return p.parseTokens(tokens)
149+
}
150+
151+
func (p *Parser) parseTokens(tokens []token) (ASTNode, error) {
143152
p.tokens = tokens
144153
parsed, err := p.parseExpression(0)
145154
if err != nil {
@@ -303,16 +312,19 @@ func (p *Parser) led(tokenType TokType, node ASTNode) (ASTNode, error) {
303312
Value: tokenType,
304313
Children: []ASTNode{node, right},
305314
}, nil
306-
case TOKEQ, TOKNE, TOKGT, TOKGTE, TOKLT, TOKLTE:
307-
right, err := p.parseExpression(bindingPowers[tokenType])
308-
if err != nil {
309-
return ASTNode{}, err
315+
case TOKEQ:
316+
{
317+
if node.NodeType != ASTVariable {
318+
return p.parseComparatorExpression(node, tokenType)
319+
}
320+
right, err := p.parseExpression(bindingPowers[TOKEQ])
321+
return ASTNode{
322+
NodeType: ASTBinding,
323+
Children: []ASTNode{node, right},
324+
}, err
310325
}
311-
return ASTNode{
312-
NodeType: ASTComparator,
313-
Value: tokenType,
314-
Children: []ASTNode{node, right},
315-
}, nil
326+
case TOKNE, TOKGT, TOKGTE, TOKLT, TOKLTE:
327+
return p.parseComparatorExpression(node, tokenType)
316328
case TOKLbracket:
317329
tokenType := p.current()
318330
var right ASTNode
@@ -345,6 +357,44 @@ func (p *Parser) led(tokenType TokType, node ASTNode) (ASTNode, error) {
345357

346358
func (p *Parser) nud(token token) (ASTNode, error) {
347359
switch token.tokenType {
360+
case TOKLet:
361+
{
362+
var bindings []ASTNode
363+
for p.current() != TOKIn {
364+
binding, err := p.parseExpression(0)
365+
if err != nil {
366+
return ASTNode{}, err
367+
}
368+
if p.current() == TOKComma {
369+
if err := p.match(TOKComma); err != nil {
370+
return ASTNode{}, err
371+
}
372+
}
373+
bindings = append(bindings, binding)
374+
}
375+
if err := p.match(TOKIn); err != nil {
376+
return ASTNode{}, err
377+
}
378+
expression, err := p.parseExpression(0)
379+
if err != nil {
380+
return ASTNode{}, err
381+
}
382+
return ASTNode{
383+
NodeType: ASTLetExpression,
384+
Children: []ASTNode{
385+
{
386+
NodeType: ASTBindings,
387+
Children: bindings,
388+
},
389+
expression,
390+
},
391+
}, nil
392+
}
393+
case TOKVarref:
394+
return ASTNode{
395+
NodeType: ASTVariable,
396+
Value: token.value,
397+
}, nil
348398
case TOKJSONLiteral:
349399
var parsed interface{}
350400
err := json.Unmarshal([]byte(token.value), &parsed)
@@ -596,6 +646,18 @@ func (p *Parser) parseProjectionRHS(bindingPower int) (ASTNode, error) {
596646
}
597647
}
598648

649+
func (p *Parser) parseComparatorExpression(left ASTNode, tokenType TokType) (ASTNode, error) {
650+
right, err := p.parseExpression(bindingPowers[tokenType])
651+
if err != nil {
652+
return ASTNode{}, err
653+
}
654+
return ASTNode{
655+
NodeType: ASTComparator,
656+
Value: tokenType,
657+
Children: []ASTNode{left, right},
658+
}, nil
659+
}
660+
599661
func (p *Parser) lookahead(number int) TokType {
600662
return p.lookaheadToken(number).tokenType
601663
}

pkg/parsing/parser_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,88 @@ import (
77
"github.com/stretchr/testify/assert"
88
)
99

10+
func TestParsingVariable(t *testing.T) {
11+
assert := assert.New(t)
12+
tokens := []token{
13+
{tokenType: TOKVarref, value: "foo", position: 20, length: 3},
14+
{tokenType: TOKEOF, position: 19},
15+
}
16+
17+
var prettyPrintedLookup = `ASTVariable {
18+
value: "foo"
19+
}
20+
`
21+
p := NewParser()
22+
parsed, _ := p.parseTokens(tokens)
23+
assert.Equal(prettyPrintedLookup, parsed.PrettyPrint(0))
24+
}
25+
26+
func TestParsingVariableBinding(t *testing.T) {
27+
assert := assert.New(t)
28+
tokens := []token{
29+
{tokenType: TOKVarref, value: "foo", position: 4, length: 4},
30+
{tokenType: TOKEQ, value: "=", position: 9, length: 1},
31+
{tokenType: TOKUnquotedIdentifier, value: "foo", position: 11, length: 3},
32+
{tokenType: TOKEOF, position: 19},
33+
}
34+
35+
var prettyPrintedLookup = `ASTBinding {
36+
children: {
37+
ASTVariable {
38+
value: "foo"
39+
}
40+
ASTField {
41+
value: "foo"
42+
}
43+
}
44+
}
45+
`
46+
p := NewParser()
47+
parsed, _ := p.parseTokens(tokens)
48+
assert.Equal(prettyPrintedLookup, parsed.PrettyPrint(0))
49+
}
50+
51+
func TestParsingLetExpression(t *testing.T) {
52+
// let $foo = foo in @
53+
// 012345678901234567890123
54+
// 1 2
55+
assert := assert.New(t)
56+
tokens := []token{
57+
{tokenType: TOKLet, value: "let", position: 0, length: 3},
58+
{tokenType: TOKVarref, value: "foo", position: 4, length: 4},
59+
{tokenType: TOKEQ, value: "=", position: 9, length: 1},
60+
{tokenType: TOKUnquotedIdentifier, value: "foo", position: 11, length: 3},
61+
{tokenType: TOKIn, value: "in", position: 15, length: 2},
62+
{tokenType: TOKCurrent, value: "@", position: 18, length: 1},
63+
{tokenType: TOKEOF, position: 19},
64+
}
65+
66+
expected := `ASTLetExpression {
67+
children: {
68+
ASTBindings {
69+
children: {
70+
ASTBinding {
71+
children: {
72+
ASTVariable {
73+
value: "foo"
74+
}
75+
ASTField {
76+
value: "foo"
77+
}
78+
}
79+
}
80+
}
81+
}
82+
ASTCurrentNode {
83+
}
84+
}
85+
}
86+
`
87+
p := NewParser()
88+
parsed, _ := p.parseTokens(tokens)
89+
assert.Equal(expected, parsed.PrettyPrint(0))
90+
}
91+
1092
var parsingErrorTests = []struct {
1193
expression string
1294
msg string

pkg/parsing/toktype_string.go

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)