1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package proto
25
26 import (
27 "bytes"
28 "errors"
29 "fmt"
30 "io"
31 "runtime"
32 "strconv"
33 "strings"
34 "text/scanner"
35 )
36
37
38 type Parser struct {
39 debug bool
40 scanner *scanner.Scanner
41 buf *nextValues
42 scannerErrors []error
43 }
44
45
46 type nextValues struct {
47 pos scanner.Position
48 tok token
49 lit string
50 }
51
52
53 func NewParser(r io.Reader) *Parser {
54 s := new(scanner.Scanner)
55 s.Init(r)
56 s.Mode = scanner.ScanIdents | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanRawStrings | scanner.ScanComments
57 p := &Parser{scanner: s}
58 s.Error = p.handleScanError
59 return p
60 }
61
62
63 func (p *Parser) handleScanError(s *scanner.Scanner, msg string) {
64 p.scannerErrors = append(p.scannerErrors,
65 fmt.Errorf("go scanner error at %v = %v", s.Position, msg))
66 }
67
68
69
70
71 func (p *Parser) ignoreIllegalEscapesWhile(block func()) {
72
73 p.scanner.Error = func(s *scanner.Scanner, msg string) {
74
75 if strings.Contains(msg, "char escape") {
76 return
77 }
78 p.handleScanError(s, msg)
79 }
80 block()
81
82 p.scanner.Error = p.handleScanError
83 }
84
85
86 func (p *Parser) Parse() (*Proto, error) {
87 proto := new(Proto)
88 if p.scanner.Filename != "" {
89 proto.Filename = p.scanner.Filename
90 }
91 parseError := proto.parse(p)
92
93 if len(p.scannerErrors) > 0 {
94 buf := new(bytes.Buffer)
95 for _, each := range p.scannerErrors {
96 fmt.Fprintln(buf, each)
97 }
98 return proto, errors.New(buf.String())
99 }
100 return proto, parseError
101 }
102
103
104 func (p *Parser) Filename(f string) {
105 p.scanner.Filename = f
106 }
107
108 const stringWithSingleQuote = "'"
109
110
111 func (p *Parser) next() (pos scanner.Position, tok token, lit string) {
112 if p.buf != nil {
113
114 vals := *p.buf
115 p.buf = nil
116 return vals.pos, vals.tok, vals.lit
117 }
118 ch := p.scanner.Scan()
119 if ch == scanner.EOF {
120 return p.scanner.Position, tEOF, ""
121 }
122 lit = p.scanner.TokenText()
123
124 if stringWithSingleQuote == lit {
125 return p.nextSingleQuotedString()
126 }
127 return p.scanner.Position, asToken(lit), lit
128 }
129
130
131 func (p *Parser) nextSingleQuotedString() (pos scanner.Position, tok token, lit string) {
132 var ch rune
133 p.ignoreErrorsWhile(func() { ch = p.scanner.Scan() })
134 if ch == scanner.EOF {
135 return p.scanner.Position, tEOF, ""
136 }
137
138 lit = p.scanner.TokenText()
139 if stringWithSingleQuote == lit {
140
141 return p.scanner.Position, tIDENT, "''"
142 }
143
144
145 for {
146 p.ignoreErrorsWhile(func() { ch = p.scanner.Scan() })
147
148 if ch == scanner.EOF {
149 return p.scanner.Position, tEOF, ""
150 }
151
152 partial := p.scanner.TokenText()
153 if partial == "'" {
154 break
155 }
156 lit += partial
157 }
158
159 if stringWithSingleQuote != p.scanner.TokenText() {
160 p.unexpected(lit, "'", p)
161 }
162 return p.scanner.Position, tIDENT, fmt.Sprintf("'%s'", lit)
163 }
164
165 func (p *Parser) ignoreErrorsWhile(block func()) {
166
167 p.scanner.Error = func(s *scanner.Scanner, msg string) { return }
168 block()
169
170 p.scanner.Error = p.handleScanError
171 }
172
173
174 func (p *Parser) nextPut(pos scanner.Position, tok token, lit string) {
175 p.buf = &nextValues{pos, tok, lit}
176 }
177
178 func (p *Parser) unexpected(found, expected string, obj interface{}) error {
179 debug := ""
180 if p.debug {
181 _, file, line, _ := runtime.Caller(1)
182 debug = fmt.Sprintf(" at %s:%d (with %#v)", file, line, obj)
183 }
184 return fmt.Errorf("%v: found %q but expected [%s]%s", p.scanner.Position, found, expected, debug)
185 }
186
187 func (p *Parser) nextInteger() (i int, err error) {
188 _, tok, lit := p.next()
189 if "-" == lit {
190 i, err = p.nextInteger()
191 return i * -1, err
192 }
193 if tok != tNUMBER {
194 return 0, errors.New("non integer")
195 }
196 if strings.HasPrefix(lit, "0x") || strings.HasPrefix(lit, "0X") {
197
198 i64, err := strconv.ParseInt(lit, 0, 64)
199 return int(i64), err
200 }
201 i, err = strconv.Atoi(lit)
202 return
203 }
204
205
206 func (p *Parser) nextIdentifier() (pos scanner.Position, tok token, lit string) {
207 pos, tok, lit = p.nextIdent(false)
208 if tDOT == tok {
209
210 pos, tok, lit = p.nextIdent(false)
211 lit = "." + lit
212 }
213 return
214 }
215
216
217
218
219
220
221
222 func (p *Parser) nextTypeName() (pos scanner.Position, tok token, lit string) {
223 pos, tok, lit = p.next()
224 startPos := pos
225 fullLit := lit
226
227 if tDOT == tok {
228 pos, tok, lit = p.next()
229 fullLit = fmt.Sprintf(".%s", lit)
230 }
231
232 for {
233 r := p.peekNonWhitespace()
234 if '.' != r {
235 break
236 }
237 p.next()
238 pos, tok, lit = p.next()
239 fullLit = fmt.Sprintf("%s.%s", fullLit, lit)
240 tok = tIDENT
241 }
242 return startPos, tok, fullLit
243 }
244
245 func (p *Parser) nextIdent(keywordStartAllowed bool) (pos scanner.Position, tok token, lit string) {
246 pos, tok, lit = p.next()
247 if tIDENT != tok {
248
249 if !(isKeyword(tok) && keywordStartAllowed) {
250 return
251 }
252
253 }
254 startPos := pos
255 fullLit := lit
256
257 for {
258 r := p.peekNonWhitespace()
259 if '.' != r {
260 break
261 }
262 p.next()
263 pos, tok, lit := p.next()
264 if tIDENT != tok && !isKeyword(tok) {
265 p.nextPut(pos, tok, lit)
266 break
267 }
268 fullLit = fmt.Sprintf("%s.%s", fullLit, lit)
269 }
270 return startPos, tIDENT, fullLit
271 }
272
273 func (p *Parser) peekNonWhitespace() rune {
274 r := p.scanner.Peek()
275 if r == scanner.EOF {
276 return r
277 }
278 if isWhitespace(r) {
279
280 p.scanner.Next()
281 return p.peekNonWhitespace()
282 }
283 return r
284 }
285
View as plain text