13 mapping map[string]interface{}
14 types map[string]tomlType
17 // A list of keys in the order that they appear in the TOML data.
20 // the full key for the current hash in scope
23 // the base key name for everything except hashes
26 // rough approximation of line number
29 // A map of 'key.group.names' to whether they were created implicitly.
30 implicits map[string]bool
33 type parseError string
35 func (pe parseError) Error() string {
39 func parse(data string) (p *parser, err error) {
41 if r := recover(); r != nil {
43 if err, ok = r.(parseError); ok {
51 mapping: make(map[string]interface{}),
52 types: make(map[string]tomlType),
54 ordered: make([]Key, 0),
55 implicits: make(map[string]bool),
59 if item.typ == itemEOF {
68 func (p *parser) panicf(format string, v ...interface{}) {
69 msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
70 p.approxLine, p.current(), fmt.Sprintf(format, v...))
71 panic(parseError(msg))
74 func (p *parser) next() item {
76 if it.typ == itemError {
77 p.panicf("%s", it.val)
82 func (p *parser) bug(format string, v ...interface{}) {
83 panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
86 func (p *parser) expect(typ itemType) item {
88 p.assertEqual(typ, it.typ)
92 func (p *parser) assertEqual(expected, got itemType) {
94 p.bug("Expected '%s' but got '%s'.", expected, got)
98 func (p *parser) topLevel(item item) {
100 case itemCommentStart:
101 p.approxLine = item.line
105 p.approxLine = kg.line
108 for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
109 key = append(key, p.keyString(kg))
111 p.assertEqual(itemTableEnd, kg.typ)
113 p.establishContext(key, false)
114 p.setType("", tomlHash)
115 p.ordered = append(p.ordered, key)
116 case itemArrayTableStart:
118 p.approxLine = kg.line
121 for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
122 key = append(key, p.keyString(kg))
124 p.assertEqual(itemArrayTableEnd, kg.typ)
126 p.establishContext(key, true)
127 p.setType("", tomlArrayHash)
128 p.ordered = append(p.ordered, key)
131 p.approxLine = kname.line
132 p.currentKey = p.keyString(kname)
134 val, typ := p.value(p.next())
135 p.setValue(p.currentKey, val)
136 p.setType(p.currentKey, typ)
137 p.ordered = append(p.ordered, p.context.add(p.currentKey))
140 p.bug("Unexpected type at top level: %s", item.typ)
144 // Gets a string for a key (or part of a key in a table name).
145 func (p *parser) keyString(it item) string {
149 case itemString, itemMultilineString,
150 itemRawString, itemRawMultilineString:
154 p.bug("Unexpected key type: %s", it.typ)
159 // value translates an expected value from the lexer into a Go value wrapped
160 // as an empty interface.
161 func (p *parser) value(it item) (interface{}, tomlType) {
164 return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
165 case itemMultilineString:
166 trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
167 return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
169 return it.val, p.typeOfPrimitive(it)
170 case itemRawMultilineString:
171 return stripFirstNewline(it.val), p.typeOfPrimitive(it)
175 return true, p.typeOfPrimitive(it)
177 return false, p.typeOfPrimitive(it)
179 p.bug("Expected boolean value, but got '%s'.", it.val)
181 if !numUnderscoresOK(it.val) {
182 p.panicf("Invalid integer %q: underscores must be surrounded by digits",
185 val := strings.Replace(it.val, "_", "", -1)
186 num, err := strconv.ParseInt(val, 10, 64)
188 // Distinguish integer values. Normally, it'd be a bug if the lexer
189 // provides an invalid integer, but it's possible that the number is
190 // out of range of valid values (which the lexer cannot determine).
191 // So mark the former as a bug but the latter as a legitimate user
193 if e, ok := err.(*strconv.NumError); ok &&
194 e.Err == strconv.ErrRange {
196 p.panicf("Integer '%s' is out of the range of 64-bit "+
197 "signed integers.", it.val)
199 p.bug("Expected integer value, but got '%s'.", it.val)
202 return num, p.typeOfPrimitive(it)
204 parts := strings.FieldsFunc(it.val, func(r rune) bool {
211 for _, part := range parts {
212 if !numUnderscoresOK(part) {
213 p.panicf("Invalid float %q: underscores must be "+
214 "surrounded by digits", it.val)
217 if !numPeriodsOK(it.val) {
218 // As a special case, numbers like '123.' or '1.e2',
219 // which are valid as far as Go/strconv are concerned,
220 // must be rejected because TOML says that a fractional
221 // part consists of '.' followed by 1+ digits.
222 p.panicf("Invalid float %q: '.' must be followed "+
223 "by one or more digits", it.val)
225 val := strings.Replace(it.val, "_", "", -1)
226 num, err := strconv.ParseFloat(val, 64)
228 if e, ok := err.(*strconv.NumError); ok &&
229 e.Err == strconv.ErrRange {
231 p.panicf("Float '%s' is out of the range of 64-bit "+
232 "IEEE-754 floating-point numbers.", it.val)
234 p.panicf("Invalid float value: %q", it.val)
237 return num, p.typeOfPrimitive(it)
242 for _, format := range []string{
243 "2006-01-02T15:04:05Z07:00",
244 "2006-01-02T15:04:05",
247 t, err = time.ParseInLocation(format, it.val, time.Local)
254 p.panicf("Invalid TOML Datetime: %q.", it.val)
256 return t, p.typeOfPrimitive(it)
258 array := make([]interface{}, 0)
259 types := make([]tomlType, 0)
261 for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
262 if it.typ == itemCommentStart {
267 val, typ := p.value(it)
268 array = append(array, val)
269 types = append(types, typ)
271 return array, p.typeOfArray(types)
272 case itemInlineTableStart:
274 hash = make(map[string]interface{})
275 outerContext = p.context
276 outerKey = p.currentKey
279 p.context = append(p.context, p.currentKey)
281 for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
282 if it.typ != itemKeyStart {
283 p.bug("Expected key start but instead found %q, around line %d",
284 it.val, p.approxLine)
286 if it.typ == itemCommentStart {
293 p.approxLine = k.line
294 kname := p.keyString(k)
298 val, typ := p.value(p.next())
299 // make sure we keep metadata up to date
300 p.setType(kname, typ)
301 p.ordered = append(p.ordered, p.context.add(p.currentKey))
304 p.context = outerContext
305 p.currentKey = outerKey
306 return hash, tomlHash
308 p.bug("Unexpected value type: %s", it.typ)
312 // numUnderscoresOK checks whether each underscore in s is surrounded by
313 // characters that are not underscores.
314 func numUnderscoresOK(s string) bool {
316 for _, r := range s {
329 // numPeriodsOK checks whether every period in s is followed by a digit.
330 func numPeriodsOK(s string) bool {
332 for _, r := range s {
333 if period && !isDigit(r) {
341 // establishContext sets the current context of the parser,
342 // where the context is either a hash or an array of hashes. Which one is
343 // set depends on the value of the `array` parameter.
345 // Establishing the context also makes sure that the key isn't a duplicate, and
346 // will create implicit hashes automatically.
347 func (p *parser) establishContext(key Key, array bool) {
350 // Always start at the top level and drill down for our context.
351 hashContext := p.mapping
352 keyContext := make(Key, 0)
354 // We only need implicit hashes for key[0:-1]
355 for _, k := range key[0 : len(key)-1] {
356 _, ok = hashContext[k]
357 keyContext = append(keyContext, k)
359 // No key? Make an implicit hash and move on.
361 p.addImplicit(keyContext)
362 hashContext[k] = make(map[string]interface{})
365 // If the hash context is actually an array of tables, then set
366 // the hash context to the last element in that array.
368 // Otherwise, it better be a table, since this MUST be a key group (by
369 // virtue of it not being the last element in a key).
370 switch t := hashContext[k].(type) {
371 case []map[string]interface{}:
372 hashContext = t[len(t)-1]
373 case map[string]interface{}:
376 p.panicf("Key '%s' was already created as a hash.", keyContext)
380 p.context = keyContext
382 // If this is the first element for this array, then allocate a new
383 // list of tables for it.
385 if _, ok := hashContext[k]; !ok {
386 hashContext[k] = make([]map[string]interface{}, 0, 5)
389 // Add a new table. But make sure the key hasn't already been used
390 // for something else.
391 if hash, ok := hashContext[k].([]map[string]interface{}); ok {
392 hashContext[k] = append(hash, make(map[string]interface{}))
394 p.panicf("Key '%s' was already created and cannot be used as "+
395 "an array.", keyContext)
398 p.setValue(key[len(key)-1], make(map[string]interface{}))
400 p.context = append(p.context, key[len(key)-1])
403 // setValue sets the given key to the given value in the current context.
404 // It will make sure that the key hasn't already been defined, account for
405 // implicit key groups.
406 func (p *parser) setValue(key string, value interface{}) {
407 var tmpHash interface{}
411 keyContext := make(Key, 0)
412 for _, k := range p.context {
413 keyContext = append(keyContext, k)
414 if tmpHash, ok = hash[k]; !ok {
415 p.bug("Context for key '%s' has not been established.", keyContext)
417 switch t := tmpHash.(type) {
418 case []map[string]interface{}:
419 // The context is a table of hashes. Pick the most recent table
420 // defined as the current hash.
422 case map[string]interface{}:
425 p.bug("Expected hash to have type 'map[string]interface{}', but "+
426 "it has '%T' instead.", tmpHash)
429 keyContext = append(keyContext, key)
431 if _, ok := hash[key]; ok {
432 // Typically, if the given key has already been set, then we have
433 // to raise an error since duplicate keys are disallowed. However,
434 // it's possible that a key was previously defined implicitly. In this
435 // case, it is allowed to be redefined concretely. (See the
436 // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
438 // But we have to make sure to stop marking it as an implicit. (So that
439 // another redefinition provokes an error.)
441 // Note that since it has already been defined (as a hash), we don't
442 // want to overwrite it. So our business is done.
443 if p.isImplicit(keyContext) {
444 p.removeImplicit(keyContext)
448 // Otherwise, we have a concrete key trying to override a previous
449 // key, which is *always* wrong.
450 p.panicf("Key '%s' has already been defined.", keyContext)
455 // setType sets the type of a particular value at a given key.
456 // It should be called immediately AFTER setValue.
458 // Note that if `key` is empty, then the type given will be applied to the
459 // current context (which is either a table or an array of tables).
460 func (p *parser) setType(key string, typ tomlType) {
461 keyContext := make(Key, 0, len(p.context)+1)
462 for _, k := range p.context {
463 keyContext = append(keyContext, k)
465 if len(key) > 0 { // allow type setting for hashes
466 keyContext = append(keyContext, key)
468 p.types[keyContext.String()] = typ
471 // addImplicit sets the given Key as having been created implicitly.
472 func (p *parser) addImplicit(key Key) {
473 p.implicits[key.String()] = true
476 // removeImplicit stops tagging the given key as having been implicitly
478 func (p *parser) removeImplicit(key Key) {
479 p.implicits[key.String()] = false
482 // isImplicit returns true if the key group pointed to by the key was created
484 func (p *parser) isImplicit(key Key) bool {
485 return p.implicits[key.String()]
488 // current returns the full key name of the current context.
489 func (p *parser) current() string {
490 if len(p.currentKey) == 0 {
491 return p.context.String()
493 if len(p.context) == 0 {
496 return fmt.Sprintf("%s.%s", p.context, p.currentKey)
499 func stripFirstNewline(s string) string {
500 if len(s) == 0 || s[0] != '\n' {
506 func stripEscapedWhitespace(s string) string {
507 esc := strings.Split(s, "\\\n")
509 for i := 1; i < len(esc); i++ {
510 esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
513 return strings.Join(esc, "")
516 func (p *parser) replaceEscapes(str string) string {
522 c, size := utf8.DecodeRune(s[r:])
524 replaced = append(replaced, c)
529 p.bug("Escape sequence at end of string.")
534 p.bug("Expected valid escape code after \\, but got %q.", s[r])
537 replaced = append(replaced, rune(0x0008))
540 replaced = append(replaced, rune(0x0009))
543 replaced = append(replaced, rune(0x000A))
546 replaced = append(replaced, rune(0x000C))
549 replaced = append(replaced, rune(0x000D))
552 replaced = append(replaced, rune(0x0022))
555 replaced = append(replaced, rune(0x005C))
558 // At this point, we know we have a Unicode escape of the form
559 // `uXXXX` at [r, r+5). (Because the lexer guarantees this
561 escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
562 replaced = append(replaced, escaped)
565 // At this point, we know we have a Unicode escape of the form
566 // `uXXXX` at [r, r+9). (Because the lexer guarantees this
568 escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
569 replaced = append(replaced, escaped)
573 return string(replaced)
576 func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
578 hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
580 p.bug("Could not parse '%s' as a hexadecimal number, but the "+
581 "lexer claims it's OK: %s", s, err)
583 if !utf8.ValidRune(rune(hex)) {
584 p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
589 func isStringType(ty itemType) bool {
590 return ty == itemString || ty == itemMultilineString ||
591 ty == itemRawString || ty == itemRawMultilineString