Skip to content

Commit b2422fc

Browse files
committed
Add # operator support
1 parent a3582e9 commit b2422fc

File tree

4 files changed

+96
-24
lines changed

4 files changed

+96
-24
lines changed

parser/lexer/lexer_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ var lexTests = []lexTest{
4747
},
4848
},
4949
{
50-
"a and orb().val",
50+
"a and orb().val #.",
5151
[]Token{
5252
{Kind: Identifier, Value: "a"},
5353
{Kind: Operator, Value: "and"},
@@ -56,6 +56,8 @@ var lexTests = []lexTest{
5656
{Kind: Bracket, Value: ")"},
5757
{Kind: Operator, Value: "."},
5858
{Kind: Identifier, Value: "val"},
59+
{Kind: Operator, Value: "#"},
60+
{Kind: Operator, Value: "."},
5961
{Kind: EOF},
6062
},
6163
},

parser/lexer/state.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ func root(l *lexer) stateFn {
2828
l.emit(Bracket)
2929
case strings.ContainsRune(")]}", r):
3030
l.emit(Bracket)
31-
case strings.ContainsRune(",?!:%#&*+-/<=>^|", r):
31+
case strings.ContainsRune(",?!:%&*+-/<=>^|", r):
3232
l.backup()
3333
return operator
34+
case r == '#':
35+
l.emit(Operator)
3436
case r == '.':
3537
l.backup()
3638
return dot

parser/parser.go

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type operator struct {
2525
associativity associativity
2626
}
2727

28+
type builtin struct {
29+
arity int
30+
}
31+
2832
var unaryOperators = map[string]operator{
2933
"not": {50, left},
3034
"!": {50, left},
@@ -58,14 +62,14 @@ var binaryOperators = map[string]operator{
5862
"**": {70, right},
5963
}
6064

61-
var builtins = map[string]bool{
62-
"len": true,
63-
"all": true,
64-
"none": true,
65-
"any": true,
66-
"one": true,
67-
"filter": true,
68-
"map": true,
65+
var builtins = map[string]builtin{
66+
"len": {1},
67+
"all": {2},
68+
"none": {2},
69+
"any": {2},
70+
"one": {2},
71+
"filter": {2},
72+
"map": {2},
6973
}
7074

7175
type parser struct {
@@ -95,7 +99,7 @@ func Parse(input string) (*Tree, error) {
9599

96100
node := p.parseExpression(0)
97101

98-
if !p.isEOF() {
102+
if !p.current.Is(EOF) {
99103
p.error("unexpected token %v", p.current)
100104
}
101105

@@ -121,7 +125,8 @@ func (p *parser) error(format string, args ...interface{}) {
121125
func (p *parser) next() {
122126
p.pos++
123127
if p.pos >= len(p.tokens) {
124-
panic("unexpected end of expression")
128+
p.error("unexpected end of expression")
129+
return
125130
}
126131
p.current = p.tokens[p.pos]
127132
}
@@ -134,10 +139,6 @@ func (p *parser) expect(kind Kind, values ...string) {
134139
p.error("unexpected token %v", p.current)
135140
}
136141

137-
func (p *parser) isEOF() bool {
138-
return p.current.Is(EOF)
139-
}
140-
141142
// parse functions
142143

143144
func (p *parser) parseExpression(precedence int) Node {
@@ -216,6 +217,17 @@ func (p *parser) parsePrimary() Node {
216217
return p.parsePostfixExpression(expr)
217218
}
218219

220+
if token.Is(Operator, "#") {
221+
p.next()
222+
node := &PointerNode{Base: Loc(token.Location)}
223+
return p.parsePostfixExpression(node)
224+
}
225+
226+
if token.Is(Operator, ".") {
227+
node := &PointerNode{Base: Loc(token.Location)}
228+
return p.parsePostfixExpression(node)
229+
}
230+
219231
return p.parsePrimaryExpression()
220232
}
221233

@@ -305,18 +317,54 @@ func (p *parser) parsePrimaryExpression() Node {
305317
func (p *parser) parseIdentifierExpression(token Token) Node {
306318
var node Node
307319
if p.current.Is(Bracket, "(") {
308-
arguments := p.parseArguments()
309-
if _, ok := builtins[token.Value]; ok {
310-
node = &BuiltinNode{Base: Loc(token.Location), Name: token.Value, Arguments: arguments}
320+
var arguments []Node
321+
322+
if b, ok := builtins[token.Value]; ok {
323+
p.expect(Bracket, "(")
324+
// TODO: Add builtins signatures.
325+
if b.arity == 1 {
326+
arguments = make([]Node, 1)
327+
arguments[0] = p.parseExpression(0)
328+
} else if b.arity == 2 {
329+
arguments = make([]Node, 2)
330+
arguments[0] = p.parseExpression(0)
331+
p.expect(Operator, ",")
332+
arguments[1] = p.parseClosure()
333+
}
334+
p.expect(Bracket, ")")
335+
336+
node = &BuiltinNode{
337+
Base: Loc(token.Location),
338+
Name: token.Value,
339+
Arguments: arguments,
340+
}
311341
} else {
312-
node = &FunctionNode{Base: Loc(token.Location), Name: token.Value, Arguments: arguments}
342+
arguments = p.parseArguments()
343+
node = &FunctionNode{
344+
Base: Loc(token.Location),
345+
Name: token.Value,
346+
Arguments: arguments,
347+
}
313348
}
314349
} else {
315350
node = &IdentifierNode{Base: Loc(token.Location), Value: token.Value}
316351
}
317352
return node
318353
}
319354

355+
func (p *parser) parseClosure() Node {
356+
token := p.current
357+
p.expect(Bracket, "{")
358+
359+
node := p.parseExpression(0)
360+
361+
p.expect(Bracket, "}")
362+
return &ClosureNode{
363+
Base: Loc(token.Location),
364+
Node: node,
365+
}
366+
}
367+
320368
func (p *parser) parseArrayExpression(token Token) Node {
321369
nodes := p.parseList("[", "]")
322370
return &ArrayNode{Base: Loc(token.Location), Nodes: nodes}
@@ -403,10 +451,22 @@ func (p *parser) parsePostfixExpression(node Node) Node {
403451
} else if token.Value == "[" {
404452
p.next()
405453
arg := p.parseExpression(0)
406-
node = &IndexNode{
407-
Base: Loc(token.Location),
408-
Node: node,
409-
Index: arg,
454+
if p.current.Is(Operator, ":") {
455+
p.next()
456+
from := arg
457+
to := p.parseExpression(0)
458+
node = &SliceNode{
459+
Base: Loc(p.current.Location),
460+
Node: node,
461+
From: from,
462+
To: to,
463+
}
464+
} else {
465+
node = &IndexNode{
466+
Base: Loc(token.Location),
467+
Node: node,
468+
Index: arg,
469+
}
410470
}
411471
p.expect(Bracket, "]")
412472
} else {

parser/parser_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,14 @@ func TestParse(t *testing.T) {
186186
"all(Tickets, {.Price > 0})",
187187
&ast.BuiltinNode{Name: "all", Arguments: []ast.Node{&ast.IdentifierNode{Value: "Tickets"}, &ast.ClosureNode{Node: &ast.BinaryNode{Operator: ">", Left: &ast.PropertyNode{Node: &ast.PointerNode{}, Property: "Price"}, Right: &ast.IntegerNode{Value: 0}}}}},
188188
},
189+
{
190+
"one(Tickets, {#.Price > 0})",
191+
&ast.BuiltinNode{Name: "one", Arguments: []ast.Node{&ast.IdentifierNode{Value: "Tickets"}, &ast.ClosureNode{Node: &ast.BinaryNode{Operator: ">", Left: &ast.PropertyNode{Node: &ast.PointerNode{}, Property: "Price"}, Right: &ast.IntegerNode{Value: 0}}}}},
192+
},
193+
{
194+
"filter(Prices, {# > 100})",
195+
&ast.BuiltinNode{Name: "filter", Arguments: []ast.Node{&ast.IdentifierNode{Value: "Prices"}, &ast.ClosureNode{Node: &ast.BinaryNode{Operator: ">", Left: &ast.PointerNode{}, Right: &ast.IntegerNode{Value: 100}}}}},
196+
},
189197
{
190198
"array[1:2]",
191199
&ast.SliceNode{Node: &ast.IdentifierNode{Value: "array"}, From: &ast.IntegerNode{Value: 1}, To: &ast.IntegerNode{Value: 2}},

0 commit comments

Comments
 (0)