|
1 // Copyright 2018 Frank Schroeder. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style |
|
3 // license that can be found in the LICENSE file. |
|
4 |
|
5 package properties |
|
6 |
|
7 import ( |
|
8 "fmt" |
|
9 "runtime" |
|
10 ) |
|
11 |
|
12 type parser struct { |
|
13 lex *lexer |
|
14 } |
|
15 |
|
16 func parse(input string) (properties *Properties, err error) { |
|
17 p := &parser{lex: lex(input)} |
|
18 defer p.recover(&err) |
|
19 |
|
20 properties = NewProperties() |
|
21 key := "" |
|
22 comments := []string{} |
|
23 |
|
24 for { |
|
25 token := p.expectOneOf(itemComment, itemKey, itemEOF) |
|
26 switch token.typ { |
|
27 case itemEOF: |
|
28 goto done |
|
29 case itemComment: |
|
30 comments = append(comments, token.val) |
|
31 continue |
|
32 case itemKey: |
|
33 key = token.val |
|
34 if _, ok := properties.m[key]; !ok { |
|
35 properties.k = append(properties.k, key) |
|
36 } |
|
37 } |
|
38 |
|
39 token = p.expectOneOf(itemValue, itemEOF) |
|
40 if len(comments) > 0 { |
|
41 properties.c[key] = comments |
|
42 comments = []string{} |
|
43 } |
|
44 switch token.typ { |
|
45 case itemEOF: |
|
46 properties.m[key] = "" |
|
47 goto done |
|
48 case itemValue: |
|
49 properties.m[key] = token.val |
|
50 } |
|
51 } |
|
52 |
|
53 done: |
|
54 return properties, nil |
|
55 } |
|
56 |
|
57 func (p *parser) errorf(format string, args ...interface{}) { |
|
58 format = fmt.Sprintf("properties: Line %d: %s", p.lex.lineNumber(), format) |
|
59 panic(fmt.Errorf(format, args...)) |
|
60 } |
|
61 |
|
62 func (p *parser) expect(expected itemType) (token item) { |
|
63 token = p.lex.nextItem() |
|
64 if token.typ != expected { |
|
65 p.unexpected(token) |
|
66 } |
|
67 return token |
|
68 } |
|
69 |
|
70 func (p *parser) expectOneOf(expected ...itemType) (token item) { |
|
71 token = p.lex.nextItem() |
|
72 for _, v := range expected { |
|
73 if token.typ == v { |
|
74 return token |
|
75 } |
|
76 } |
|
77 p.unexpected(token) |
|
78 panic("unexpected token") |
|
79 } |
|
80 |
|
81 func (p *parser) unexpected(token item) { |
|
82 p.errorf(token.String()) |
|
83 } |
|
84 |
|
85 // recover is the handler that turns panics into returns from the top level of Parse. |
|
86 func (p *parser) recover(errp *error) { |
|
87 e := recover() |
|
88 if e != nil { |
|
89 if _, ok := e.(runtime.Error); ok { |
|
90 panic(e) |
|
91 } |
|
92 *errp = e.(error) |
|
93 } |
|
94 return |
|
95 } |