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 "fmt"
29 "sort"
30 "text/scanner"
31 )
32
33
34 type Option struct {
35 Position scanner.Position
36 Comment *Comment
37 Name string
38 Constant Literal
39 IsEmbedded bool
40
41 AggregatedConstants []*NamedLiteral
42 InlineComment *Comment
43 Parent Visitee
44 }
45
46
47
48 func (o *Option) parse(p *Parser) error {
49 pos, tok, lit := p.nextIdentifier()
50 if tLEFTPAREN == tok {
51 pos, tok, lit = p.nextIdentifier()
52 if tok != tIDENT {
53 if !isKeyword(tok) {
54 return p.unexpected(lit, "option full identifier", o)
55 }
56 }
57 pos, tok, _ = p.next()
58 if tok != tRIGHTPAREN {
59 return p.unexpected(lit, "option full identifier closing )", o)
60 }
61 o.Name = fmt.Sprintf("(%s)", lit)
62 } else {
63 if tCOMMENT == tok {
64 nc := newComment(pos, lit)
65 if o.Comment != nil {
66 o.Comment.Merge(nc)
67 } else {
68 o.Comment = nc
69 }
70 return o.parse(p)
71 }
72
73 if tIDENT != tok {
74 if !isKeyword(tok) {
75 return p.unexpected(lit, "option identifier", o)
76 }
77 }
78 o.Name = lit
79 }
80 pos, tok, lit = p.next()
81 if tDOT == tok {
82
83 pos, tok, lit = p.nextIdent(true)
84 if tok != tIDENT {
85 if !isKeyword(tok) {
86 return p.unexpected(lit, "option postfix identifier", o)
87 }
88 }
89 o.Name = fmt.Sprintf("%s.%s", o.Name, lit)
90 pos, tok, lit = p.next()
91 }
92 if tEQUALS != tok {
93 return p.unexpected(lit, "option value assignment =", o)
94 }
95 r := p.peekNonWhitespace()
96 var err error
97
98
99 p.ignoreIllegalEscapesWhile(func() {
100 if '{' == r {
101
102 p.next()
103 err = o.parseAggregate(p)
104 } else {
105
106 l := new(Literal)
107 l.Position = pos
108 if e := l.parse(p); e != nil {
109 err = e
110 }
111 o.Constant = *l
112 }
113 })
114 return err
115 }
116
117
118 func (o *Option) inlineComment(c *Comment) {
119 o.InlineComment = c
120 }
121
122
123 func (o *Option) Accept(v Visitor) {
124 v.VisitOption(o)
125 }
126
127
128 func (o *Option) Doc() *Comment {
129 return o.Comment
130 }
131
132
133 type Literal struct {
134 Position scanner.Position
135 Source string
136 IsString bool
137
138
139
140 Comment *Comment
141
142
143 QuoteRune rune
144
145
146 Array []*Literal
147
148
149
150 Map map[string]*Literal
151
152
153
154 OrderedMap LiteralMap
155 }
156
157 var emptyRune rune
158
159
160
161 type LiteralMap []*NamedLiteral
162
163
164 func (m LiteralMap) Get(key string) (*Literal, bool) {
165 for _, each := range m {
166 if each.Name == key {
167
168 return each.Literal, true
169 }
170 }
171 return new(Literal), false
172 }
173
174
175 func (l Literal) SourceRepresentation() string {
176 var buf bytes.Buffer
177 if l.IsString {
178 if l.QuoteRune == emptyRune {
179 buf.WriteRune('"')
180 } else {
181 buf.WriteRune(l.QuoteRune)
182 }
183 }
184 buf.WriteString(l.Source)
185 if l.IsString {
186 if l.QuoteRune == emptyRune {
187 buf.WriteRune('"')
188 } else {
189 buf.WriteRune(l.QuoteRune)
190 }
191 }
192 return buf.String()
193 }
194
195
196 func (l *Literal) parse(p *Parser) error {
197 pos, tok, lit := p.next()
198
199 if isComment(lit) {
200 nc := newComment(pos, lit)
201 if l.Comment == nil {
202 l.Comment = nc
203 } else {
204 l.Comment.Merge(nc)
205 }
206
207 return l.parse(p)
208 }
209 if tok == tLEFTSQUARE {
210
211 array := []*Literal{}
212
213
214
215 r := p.peekNonWhitespace()
216 if ']' == r {
217 pos, _, _ := p.next()
218 l.Array = array
219 l.IsString = false
220 l.Position = pos
221 return nil
222 }
223
224 for {
225 e := new(Literal)
226 if err := e.parse(p); err != nil {
227 return err
228 }
229 array = append(array, e)
230 _, tok, lit := p.next()
231 if tok == tCOMMA {
232 continue
233 }
234 if tok == tRIGHTSQUARE {
235 break
236 }
237 return p.unexpected(lit, ", or ]", l)
238 }
239 l.Array = array
240 l.IsString = false
241 l.Position = pos
242 return nil
243 }
244 if tLEFTCURLY == tok {
245 l.Position, l.Source, l.IsString = pos, "", false
246 constants, err := parseAggregateConstants(p, l)
247 if err != nil {
248 return nil
249 }
250 l.OrderedMap = LiteralMap(constants)
251 return nil
252 }
253 if "-" == lit {
254
255 if err := l.parse(p); err != nil {
256 return err
257 }
258
259 l.Position, l.Source = pos, "-"+l.Source
260 return nil
261 }
262 source := lit
263 iss := isString(lit)
264 if iss {
265 source, l.QuoteRune = unQuote(source)
266 }
267 l.Position, l.Source, l.IsString = pos, source, iss
268
269
270 for {
271 pos, tok, lit := p.next()
272 if isString(lit) {
273 line, _ := unQuote(lit)
274 l.Source += line
275 } else {
276 p.nextPut(pos, tok, lit)
277 break
278 }
279 }
280 return nil
281 }
282
283
284 type NamedLiteral struct {
285 *Literal
286 Name string
287
288 PrintsColon bool
289 }
290
291
292
293 func (o *Option) parseAggregate(p *Parser) error {
294 constants, err := parseAggregateConstants(p, o)
295 literalMap := map[string]*Literal{}
296 for _, each := range constants {
297 literalMap[each.Name] = each.Literal
298 }
299 o.Constant = Literal{Map: literalMap, OrderedMap: constants, Position: o.Position}
300
301
302 o.AggregatedConstants = collectAggregatedConstants(literalMap)
303 return err
304 }
305
306
307
308 func collectAggregatedConstants(m map[string]*Literal) (list []*NamedLiteral) {
309 for k, v := range m {
310 if v.Map != nil {
311 sublist := collectAggregatedConstants(v.Map)
312 for _, each := range sublist {
313 list = append(list, &NamedLiteral{
314 Name: k + "." + each.Name,
315 PrintsColon: true,
316 Literal: each.Literal,
317 })
318 }
319 } else {
320 list = append(list, &NamedLiteral{
321 Name: k,
322 PrintsColon: true,
323 Literal: v,
324 })
325 }
326 }
327
328 sort.Sort(byPosition(list))
329 return
330 }
331
332 type byPosition []*NamedLiteral
333
334 func (b byPosition) Less(i, j int) bool {
335 return b[i].Literal.Position.Line < b[j].Literal.Position.Line
336 }
337 func (b byPosition) Len() int { return len(b) }
338 func (b byPosition) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
339
340 func parseAggregateConstants(p *Parser, container interface{}) (list []*NamedLiteral, err error) {
341 for {
342 pos, tok, lit := p.nextIdentifier()
343 if tRIGHTSQUARE == tok {
344 p.nextPut(pos, tok, lit)
345
346 return
347 }
348 if tRIGHTCURLY == tok {
349 return
350 }
351 if tSEMICOLON == tok {
352
353 continue
354
355 }
356 if tCOMMENT == tok {
357
358
359 continue
360 }
361 if tCOMMA == tok {
362 if len(list) == 0 {
363 err = p.unexpected(lit, "non-empty option aggregate key", container)
364 return
365 }
366 continue
367 }
368 if tIDENT != tok && !isKeyword(tok) {
369 err = p.unexpected(lit, "option aggregate key", container)
370 return
371 }
372
373 if isString(lit) && len(list) > 0 {
374
375 s, _ := unQuote(lit)
376 list[len(list)-1].Source += s
377 continue
378 }
379 key := lit
380 printsColon := false
381
382 pos, tok, lit = p.next()
383 if tCOLON == tok {
384
385 printsColon = true
386 pos, tok, lit = p.next()
387 }
388
389 if tLEFTCURLY == tok {
390 nested, fault := parseAggregateConstants(p, container)
391 if fault != nil {
392 err = fault
393 return
394 }
395
396
397 m := map[string]*Literal{}
398 for _, each := range nested {
399 m[each.Name] = each.Literal
400 }
401 list = append(list, &NamedLiteral{
402 Name: key,
403 PrintsColon: printsColon,
404 Literal: &Literal{Map: m, OrderedMap: LiteralMap(nested)}})
405 continue
406 }
407
408 p.nextPut(pos, tok, lit)
409
410 l := new(Literal)
411 l.Position = pos
412 if err = l.parse(p); err != nil {
413 return
414 }
415 list = append(list, &NamedLiteral{Name: key, Literal: l, PrintsColon: printsColon})
416 }
417 }
418
419 func (o *Option) parent(v Visitee) { o.Parent = v }
420
View as plain text