15 type tomlParser struct {
20 seenTableKeys []string
23 type tomlParserStateFn func() tomlParserStateFn
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...))
30 func (p *tomlParser) run() {
31 for state := p.parseStart; state != nil; {
36 func (p *tomlParser) peek() *token {
37 if p.flowIdx >= len(p.flow) {
40 return &p.flow[p.flowIdx]
43 func (p *tomlParser) assume(typ tokenType) {
46 p.raiseError(tok, "was expecting token %s, but token stream is empty", tok)
49 p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok)
53 func (p *tomlParser) getToken() *token {
62 func (p *tomlParser) parseStart() tomlParserStateFn {
65 // end of stream, parsing is finished
71 case tokenDoubleLeftBracket:
72 return p.parseGroupArray
73 case tokenLeftBracket:
80 p.raiseError(tok, "unexpected token")
85 func (p *tomlParser) parseGroupArray() tomlParserStateFn {
86 startToken := p.getToken() // discard the [[
88 if key.typ != tokenKeyGroupArray {
89 p.raiseError(key, "unexpected token %s, was expecting a table array key", key)
92 // get or create table array element at the indicated part in the path
93 keys, err := parseKey(key.val)
95 p.raiseError(key, "invalid table array key: %s", err)
97 p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries
98 destTree := p.tree.GetPath(keys)
101 array = make([]*Tree, 0)
102 } else if target, ok := destTree.([]*Tree); ok && target != nil {
103 array = destTree.([]*Tree)
105 p.raiseError(key, "key %s is already assigned and not of type table array", key)
107 p.currentTable = keys
109 // add a new tree to the end of the table array
111 newTree.position = startToken.Position
112 array = append(array, newTree)
113 p.tree.SetPath(p.currentTable, "", false, array)
115 // remove all keys that were children of this table array
116 prefix := key.val + "."
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:]...)
123 found = (tableKey == key.val)
128 // keep this key name from use by other kinds of assignments
130 p.seenTableKeys = append(p.seenTableKeys, key.val)
133 // move to next parser state
134 p.assume(tokenDoubleRightBracket)
138 func (p *tomlParser) parseGroup() tomlParserStateFn {
139 startToken := p.getToken() // discard the [
141 if key.typ != tokenKeyGroup {
142 p.raiseError(key, "unexpected token %s, was expecting a table key", key)
144 for _, item := range p.seenTableKeys {
146 p.raiseError(key, "duplicated tables")
150 p.seenTableKeys = append(p.seenTableKeys, key.val)
151 keys, err := parseKey(key.val)
153 p.raiseError(key, "invalid table array key: %s", err)
155 if err := p.tree.createSubTree(keys, startToken.Position); err != nil {
156 p.raiseError(key, "%s", err)
158 p.assume(tokenRightBracket)
159 p.currentTable = keys
163 func (p *tomlParser) parseAssign() tomlParserStateFn {
167 value := p.parseRvalue()
168 var tableKey []string
169 if len(p.currentTable) > 0 {
170 tableKey = p.currentTable
172 tableKey = []string{}
175 // find the table to assign, looking out for arrays of tables
177 switch node := p.tree.GetPath(tableKey).(type) {
179 targetNode = node[len(node)-1]
183 p.raiseError(key, "Unknown table type for path: %s",
184 strings.Join(tableKey, "."))
187 // assign value to the found table
188 keyVals, err := parseKey(key.val)
190 p.raiseError(key, "%s", err)
192 if len(keyVals) != 1 {
193 p.raiseError(key, "Invalid key")
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, "."))
202 var toInsert interface{}
204 switch value.(type) {
208 toInsert = &tomlValue{value: value, position: key.Position}
210 targetNode.values[keyVal] = toInsert
214 var numberUnderscoreInvalidRegexp *regexp.Regexp
216 func cleanupNumberToken(value string) (string, error) {
217 if numberUnderscoreInvalidRegexp.MatchString(value) {
218 return "", errors.New("invalid use of _ in number")
220 cleanedVal := strings.Replace(value, "_", "", -1)
221 return cleanedVal, nil
224 func (p *tomlParser) parseRvalue() interface{} {
226 if tok == nil || tok.typ == tokenEOF {
227 p.raiseError(tok, "expecting a value")
238 cleanedVal, err := cleanupNumberToken(tok.val)
240 p.raiseError(tok, "%s", err)
242 val, err := strconv.ParseInt(cleanedVal, 10, 64)
244 p.raiseError(tok, "%s", err)
248 cleanedVal, err := cleanupNumberToken(tok.val)
250 p.raiseError(tok, "%s", err)
252 val, err := strconv.ParseFloat(cleanedVal, 64)
254 p.raiseError(tok, "%s", err)
258 val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC)
260 p.raiseError(tok, "%s", err)
263 case tokenLeftBracket:
264 return p.parseArray()
265 case tokenLeftCurlyBrace:
266 return p.parseInlineTable()
268 p.raiseError(tok, "cannot have multiple equals for the same key")
270 p.raiseError(tok, "%s", tok)
273 p.raiseError(tok, "never reached")
278 func tokenIsComma(t *token) bool {
279 return t != nil && t.typ == tokenComma
282 func (p *tomlParser) parseInlineTable() *Tree {
288 if follow == nil || follow.typ == tokenEOF {
289 p.raiseError(follow, "unterminated inline table")
292 case tokenRightCurlyBrace:
296 if !tokenIsComma(previous) && previous != nil {
297 p.raiseError(follow, "comma expected between fields in inline table")
301 value := p.parseRvalue()
302 tree.Set(key.val, "", false, value)
305 p.raiseError(follow, "inline table cannot start with a comma")
307 if tokenIsComma(previous) {
308 p.raiseError(follow, "need field between two commas in inline table")
312 p.raiseError(follow, "unexpected token type in inline table: %s", follow.typ.String())
316 if tokenIsComma(previous) {
317 p.raiseError(previous, "trailing comma at the end of inline table")
322 func (p *tomlParser) parseArray() interface{} {
323 var array []interface{}
324 arrayType := reflect.TypeOf(nil)
327 if follow == nil || follow.typ == tokenEOF {
328 p.raiseError(follow, "unterminated array")
330 if follow.typ == tokenRightBracket {
334 val := p.parseRvalue()
335 if arrayType == nil {
336 arrayType = reflect.TypeOf(val)
338 if reflect.TypeOf(val) != arrayType {
339 p.raiseError(follow, "mixed types in array")
341 array = append(array, val)
343 if follow == nil || follow.typ == tokenEOF {
344 p.raiseError(follow, "unterminated array")
346 if follow.typ != tokenRightBracket && follow.typ != tokenComma {
347 p.raiseError(follow, "missing comma")
349 if follow.typ == tokenComma {
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)
367 func parseToml(flow []token) *Tree {
369 result.position = Position{1, 1}
370 parser := &tomlParser{
374 currentTable: make([]string, 0),
375 seenTableKeys: make([]string, 0),
382 numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d]|_$|^_)`)