OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / pelletier / go-toml / parser.go
1 // TOML Parser.
2
3 package toml
4
5 import (
6         "errors"
7         "fmt"
8         "reflect"
9         "regexp"
10         "strconv"
11         "strings"
12         "time"
13 )
14
15 type tomlParser struct {
16         flowIdx       int
17         flow          []token
18         tree          *Tree
19         currentTable  []string
20         seenTableKeys []string
21 }
22
23 type tomlParserStateFn func() tomlParserStateFn
24
25 // Formats and panics an error message based on a token
26 func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) {
27         panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
28 }
29
30 func (p *tomlParser) run() {
31         for state := p.parseStart; state != nil; {
32                 state = state()
33         }
34 }
35
36 func (p *tomlParser) peek() *token {
37         if p.flowIdx >= len(p.flow) {
38                 return nil
39         }
40         return &p.flow[p.flowIdx]
41 }
42
43 func (p *tomlParser) assume(typ tokenType) {
44         tok := p.getToken()
45         if tok == nil {
46                 p.raiseError(tok, "was expecting token %s, but token stream is empty", tok)
47         }
48         if tok.typ != typ {
49                 p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok)
50         }
51 }
52
53 func (p *tomlParser) getToken() *token {
54         tok := p.peek()
55         if tok == nil {
56                 return nil
57         }
58         p.flowIdx++
59         return tok
60 }
61
62 func (p *tomlParser) parseStart() tomlParserStateFn {
63         tok := p.peek()
64
65         // end of stream, parsing is finished
66         if tok == nil {
67                 return nil
68         }
69
70         switch tok.typ {
71         case tokenDoubleLeftBracket:
72                 return p.parseGroupArray
73         case tokenLeftBracket:
74                 return p.parseGroup
75         case tokenKey:
76                 return p.parseAssign
77         case tokenEOF:
78                 return nil
79         default:
80                 p.raiseError(tok, "unexpected token")
81         }
82         return nil
83 }
84
85 func (p *tomlParser) parseGroupArray() tomlParserStateFn {
86         startToken := p.getToken() // discard the [[
87         key := p.getToken()
88         if key.typ != tokenKeyGroupArray {
89                 p.raiseError(key, "unexpected token %s, was expecting a table array key", key)
90         }
91
92         // get or create table array element at the indicated part in the path
93         keys, err := parseKey(key.val)
94         if err != nil {
95                 p.raiseError(key, "invalid table array key: %s", err)
96         }
97         p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries
98         destTree := p.tree.GetPath(keys)
99         var array []*Tree
100         if destTree == nil {
101                 array = make([]*Tree, 0)
102         } else if target, ok := destTree.([]*Tree); ok && target != nil {
103                 array = destTree.([]*Tree)
104         } else {
105                 p.raiseError(key, "key %s is already assigned and not of type table array", key)
106         }
107         p.currentTable = keys
108
109         // add a new tree to the end of the table array
110         newTree := newTree()
111         newTree.position = startToken.Position
112         array = append(array, newTree)
113         p.tree.SetPath(p.currentTable, "", false, array)
114
115         // remove all keys that were children of this table array
116         prefix := key.val + "."
117         found := false
118         for ii := 0; ii < len(p.seenTableKeys); {
119                 tableKey := p.seenTableKeys[ii]
120                 if strings.HasPrefix(tableKey, prefix) {
121                         p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...)
122                 } else {
123                         found = (tableKey == key.val)
124                         ii++
125                 }
126         }
127
128         // keep this key name from use by other kinds of assignments
129         if !found {
130                 p.seenTableKeys = append(p.seenTableKeys, key.val)
131         }
132
133         // move to next parser state
134         p.assume(tokenDoubleRightBracket)
135         return p.parseStart
136 }
137
138 func (p *tomlParser) parseGroup() tomlParserStateFn {
139         startToken := p.getToken() // discard the [
140         key := p.getToken()
141         if key.typ != tokenKeyGroup {
142                 p.raiseError(key, "unexpected token %s, was expecting a table key", key)
143         }
144         for _, item := range p.seenTableKeys {
145                 if item == key.val {
146                         p.raiseError(key, "duplicated tables")
147                 }
148         }
149
150         p.seenTableKeys = append(p.seenTableKeys, key.val)
151         keys, err := parseKey(key.val)
152         if err != nil {
153                 p.raiseError(key, "invalid table array key: %s", err)
154         }
155         if err := p.tree.createSubTree(keys, startToken.Position); err != nil {
156                 p.raiseError(key, "%s", err)
157         }
158         p.assume(tokenRightBracket)
159         p.currentTable = keys
160         return p.parseStart
161 }
162
163 func (p *tomlParser) parseAssign() tomlParserStateFn {
164         key := p.getToken()
165         p.assume(tokenEqual)
166
167         value := p.parseRvalue()
168         var tableKey []string
169         if len(p.currentTable) > 0 {
170                 tableKey = p.currentTable
171         } else {
172                 tableKey = []string{}
173         }
174
175         // find the table to assign, looking out for arrays of tables
176         var targetNode *Tree
177         switch node := p.tree.GetPath(tableKey).(type) {
178         case []*Tree:
179                 targetNode = node[len(node)-1]
180         case *Tree:
181                 targetNode = node
182         default:
183                 p.raiseError(key, "Unknown table type for path: %s",
184                         strings.Join(tableKey, "."))
185         }
186
187         // assign value to the found table
188         keyVals, err := parseKey(key.val)
189         if err != nil {
190                 p.raiseError(key, "%s", err)
191         }
192         if len(keyVals) != 1 {
193                 p.raiseError(key, "Invalid key")
194         }
195         keyVal := keyVals[0]
196         localKey := []string{keyVal}
197         finalKey := append(tableKey, keyVal)
198         if targetNode.GetPath(localKey) != nil {
199                 p.raiseError(key, "The following key was defined twice: %s",
200                         strings.Join(finalKey, "."))
201         }
202         var toInsert interface{}
203
204         switch value.(type) {
205         case *Tree, []*Tree:
206                 toInsert = value
207         default:
208                 toInsert = &tomlValue{value: value, position: key.Position}
209         }
210         targetNode.values[keyVal] = toInsert
211         return p.parseStart
212 }
213
214 var numberUnderscoreInvalidRegexp *regexp.Regexp
215
216 func cleanupNumberToken(value string) (string, error) {
217         if numberUnderscoreInvalidRegexp.MatchString(value) {
218                 return "", errors.New("invalid use of _ in number")
219         }
220         cleanedVal := strings.Replace(value, "_", "", -1)
221         return cleanedVal, nil
222 }
223
224 func (p *tomlParser) parseRvalue() interface{} {
225         tok := p.getToken()
226         if tok == nil || tok.typ == tokenEOF {
227                 p.raiseError(tok, "expecting a value")
228         }
229
230         switch tok.typ {
231         case tokenString:
232                 return tok.val
233         case tokenTrue:
234                 return true
235         case tokenFalse:
236                 return false
237         case tokenInteger:
238                 cleanedVal, err := cleanupNumberToken(tok.val)
239                 if err != nil {
240                         p.raiseError(tok, "%s", err)
241                 }
242                 val, err := strconv.ParseInt(cleanedVal, 10, 64)
243                 if err != nil {
244                         p.raiseError(tok, "%s", err)
245                 }
246                 return val
247         case tokenFloat:
248                 cleanedVal, err := cleanupNumberToken(tok.val)
249                 if err != nil {
250                         p.raiseError(tok, "%s", err)
251                 }
252                 val, err := strconv.ParseFloat(cleanedVal, 64)
253                 if err != nil {
254                         p.raiseError(tok, "%s", err)
255                 }
256                 return val
257         case tokenDate:
258                 val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC)
259                 if err != nil {
260                         p.raiseError(tok, "%s", err)
261                 }
262                 return val
263         case tokenLeftBracket:
264                 return p.parseArray()
265         case tokenLeftCurlyBrace:
266                 return p.parseInlineTable()
267         case tokenEqual:
268                 p.raiseError(tok, "cannot have multiple equals for the same key")
269         case tokenError:
270                 p.raiseError(tok, "%s", tok)
271         }
272
273         p.raiseError(tok, "never reached")
274
275         return nil
276 }
277
278 func tokenIsComma(t *token) bool {
279         return t != nil && t.typ == tokenComma
280 }
281
282 func (p *tomlParser) parseInlineTable() *Tree {
283         tree := newTree()
284         var previous *token
285 Loop:
286         for {
287                 follow := p.peek()
288                 if follow == nil || follow.typ == tokenEOF {
289                         p.raiseError(follow, "unterminated inline table")
290                 }
291                 switch follow.typ {
292                 case tokenRightCurlyBrace:
293                         p.getToken()
294                         break Loop
295                 case tokenKey:
296                         if !tokenIsComma(previous) && previous != nil {
297                                 p.raiseError(follow, "comma expected between fields in inline table")
298                         }
299                         key := p.getToken()
300                         p.assume(tokenEqual)
301                         value := p.parseRvalue()
302                         tree.Set(key.val, "", false, value)
303                 case tokenComma:
304                         if previous == nil {
305                                 p.raiseError(follow, "inline table cannot start with a comma")
306                         }
307                         if tokenIsComma(previous) {
308                                 p.raiseError(follow, "need field between two commas in inline table")
309                         }
310                         p.getToken()
311                 default:
312                         p.raiseError(follow, "unexpected token type in inline table: %s", follow.typ.String())
313                 }
314                 previous = follow
315         }
316         if tokenIsComma(previous) {
317                 p.raiseError(previous, "trailing comma at the end of inline table")
318         }
319         return tree
320 }
321
322 func (p *tomlParser) parseArray() interface{} {
323         var array []interface{}
324         arrayType := reflect.TypeOf(nil)
325         for {
326                 follow := p.peek()
327                 if follow == nil || follow.typ == tokenEOF {
328                         p.raiseError(follow, "unterminated array")
329                 }
330                 if follow.typ == tokenRightBracket {
331                         p.getToken()
332                         break
333                 }
334                 val := p.parseRvalue()
335                 if arrayType == nil {
336                         arrayType = reflect.TypeOf(val)
337                 }
338                 if reflect.TypeOf(val) != arrayType {
339                         p.raiseError(follow, "mixed types in array")
340                 }
341                 array = append(array, val)
342                 follow = p.peek()
343                 if follow == nil || follow.typ == tokenEOF {
344                         p.raiseError(follow, "unterminated array")
345                 }
346                 if follow.typ != tokenRightBracket && follow.typ != tokenComma {
347                         p.raiseError(follow, "missing comma")
348                 }
349                 if follow.typ == tokenComma {
350                         p.getToken()
351                 }
352         }
353         // An array of Trees is actually an array of inline
354         // tables, which is a shorthand for a table array. If the
355         // array was not converted from []interface{} to []*Tree,
356         // the two notations would not be equivalent.
357         if arrayType == reflect.TypeOf(newTree()) {
358                 tomlArray := make([]*Tree, len(array))
359                 for i, v := range array {
360                         tomlArray[i] = v.(*Tree)
361                 }
362                 return tomlArray
363         }
364         return array
365 }
366
367 func parseToml(flow []token) *Tree {
368         result := newTree()
369         result.position = Position{1, 1}
370         parser := &tomlParser{
371                 flowIdx:       0,
372                 flow:          flow,
373                 tree:          result,
374                 currentTable:  make([]string, 0),
375                 seenTableKeys: make([]string, 0),
376         }
377         parser.run()
378         return result
379 }
380
381 func init() {
382         numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d]|_$|^_)`)
383 }