forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse.go
188 lines (168 loc) · 4.12 KB
/
parse.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied from Go's text/template/parse package and modified for yacc.
// Package yacc parses .y files.
package yacc
import (
"fmt"
"runtime"
)
// Tree is the representation of a single parsed file.
type Tree struct {
Name string // name of the template represented by the tree.
Productions []*ProductionNode
text string // text parsed to create the template (or its parent)
// Parsing only; cleared after parse.
lex *lexer
token [2]item // two-token lookahead for parser.
peekCount int
}
// Parse parses the yacc file text with optional name.
func Parse(name, text string) (t *Tree, err error) {
t = New(name)
t.text = text
err = t.Parse(text)
return
}
// next returns the next token.
func (t *Tree) next() item {
if t.peekCount > 0 {
t.peekCount--
} else {
t.token[0] = t.lex.nextItem()
}
return t.token[t.peekCount]
}
// backup backs the input stream up one token.
func (t *Tree) backup() {
t.peekCount++
}
// peek returns but does not consume the next token.
func (t *Tree) peek() item {
if t.peekCount > 0 {
return t.token[t.peekCount-1]
}
t.peekCount = 1
t.token[0] = t.lex.nextItem()
return t.token[0]
}
// Parsing.
// New allocates a new parse tree with the given name.
func New(name string) *Tree {
return &Tree{
Name: name,
}
}
// errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) {
format = fmt.Sprintf("parse: %s:%d: %s", t.Name, t.lex.lineNumber(), format)
panic(fmt.Errorf(format, args...))
}
// expect consumes the next token and guarantees it has the required type.
func (t *Tree) expect(expected itemType, context string) item {
token := t.next()
if token.typ != expected {
t.unexpected(token, context)
}
return token
}
// unexpected complains about the token and terminates processing.
func (t *Tree) unexpected(token item, context string) {
t.errorf("unexpected %s in %s", token, context)
}
// recover is the handler that turns panics into returns from the top level of Parse.
func (t *Tree) recover(errp *error) {
if e := recover(); e != nil {
if _, ok := e.(runtime.Error); ok {
panic(e)
}
if t != nil {
t.stopParse()
}
*errp = e.(error)
}
}
// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(lex *lexer) {
t.lex = lex
}
// stopParse terminates parsing.
func (t *Tree) stopParse() {
t.lex = nil
}
// Parse parses the yacc string to construct a representation of
// the file for analysis.
func (t *Tree) Parse(text string) (err error) {
defer t.recover(&err)
t.startParse(lex(t.Name, text))
t.text = text
t.parse()
t.stopParse()
return nil
}
// parse is the top-level parser for a file.
// It runs to EOF.
func (t *Tree) parse() {
for {
switch token := t.next(); token.typ {
case itemIdent:
p := newProduction(token.pos, token.val)
t.parseProduction(p)
t.Productions = append(t.Productions, p)
case itemEOF:
return
}
}
}
func (t *Tree) parseProduction(p *ProductionNode) {
const context = "production"
t.expect(itemColon, context)
t.expect(itemNL, context)
expectExpr := true
for {
token := t.next()
switch token.typ {
case itemComment, itemNL:
// ignore
case itemPipe:
if expectExpr {
t.unexpected(token, context)
}
expectExpr = true
default:
t.backup()
if !expectExpr {
return
}
e := newExpression(token.pos)
t.parseExpression(e)
p.Expressions = append(p.Expressions, e)
expectExpr = false
}
}
}
func (t *Tree) parseExpression(e *ExpressionNode) {
const context = "expression"
for {
switch token := t.next(); token.typ {
case itemNL:
peek := t.peek().typ
if peek == itemPipe || peek == itemNL {
return
}
case itemIdent:
e.Items = append(e.Items, Item{token.val, TypToken})
case itemLiteral:
e.Items = append(e.Items, Item{token.val, TypLiteral})
case itemExpr:
e.Command = token.val
t.expect(itemNL, context)
return
case itemPct, itemComment:
// ignore
default:
t.unexpected(token, context)
}
}
}