-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser_combinators.go
88 lines (77 loc) · 2.29 KB
/
parser_combinators.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package main
import (
"errors"
"log"
)
// primary -> NUMBER | "(" expr ")" TODO
func parsePrimary(pr *parser) *astNode {
log.Print("inside primary")
token := pr.next()
if pr.eof() {
return nil
}
switch token.tokenType {
case Number: // NUMBER
return newASTNode(token, nilASTNode, nilASTNode, evalNumber)
case LeftBrace: // "(" expr ")"
ast := parseExpr(pr)
pr.next() // consuming ")"
if pr.eof() { // Because we found eof while expecting ) for consumption
pr.lastErr = errors.New(") expected")
return nil
}
return ast
}
return nil
}
// unary -> (- | !) unary | priamry
func parseUnary(pr *parser) *astNode {
log.Print("inside unary")
token := pr.peek()
if pr.eof() {
return nil
}
if token.tokenType == Minus || token.tokenType == Not {
pr.next() // consume minus or not
return newASTNode(token, nilASTNode, parseUnary(pr), evalUnary)
}
return parsePrimary(pr)
}
// exponent -> unary ( ("**") unary )*
func parseExponent(pr *parser) *astNode {
return parseCombinator(pr, evalInfix, parseUnary, RaisePower)
}
// factor -> exponent ( ( "/" | "*" | "%" ) exponent)*
func parseFactor(pr *parser) *astNode {
return parseCombinator(pr, evalInfix, parseExponent, Mod, Asterisk, Slash)
}
// term -> factor (( "+" | "-" ) factor)*
func parseTerm(pr *parser) *astNode {
return parseCombinator(pr, evalInfix, parseFactor, Plus, Minus)
}
// comparison -> term (( ">" | ">=" | "<" | "<=" ) term)*
func parseComp(pr *parser) *astNode {
return parseCombinator(pr, evalInfix, parseTerm, LT, GT, LTEQ, GTEQ)
}
// expr -> comparison ( ( "==" | "!=" ) comparison )*
func parseExpr(pr *parser) *astNode {
return parseCombinator(pr, evalInfix, parseComp, Eq, NotEq)
}
func parseCombinator(pr *parser, evalr evaluator, nonterminalParser parserFn, matchableTokens ...TokenType) *astNode {
var tokenMap = make(map[TokenType]struct{}, len(matchableTokens))
for _, token := range matchableTokens {
tokenMap[token] = struct{}{}
}
var fn parserFn
fn = func(pr *parser) *astNode {
parent := nonterminalParser(pr) // parsing first part of the grammar e.g. in grammar E -> F ( operator E )* parses F
token := pr.peek()
_, ok := tokenMap[token.tokenType]
if !pr.eof() && ok {
pr.next() // consume token
return newASTNode(token, parent, fn(pr), evalr)
}
return parent
}
return fn(pr)
}