src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

parse.go (22421B)


      1 package toml
      2 
      3 import (
      4 	"fmt"
      5 	"math"
      6 	"os"
      7 	"strconv"
      8 	"strings"
      9 	"time"
     10 	"unicode/utf8"
     11 
     12 	"github.com/BurntSushi/toml/internal"
     13 )
     14 
     15 type parser struct {
     16 	lx         *lexer
     17 	context    Key      // Full key for the current hash in scope.
     18 	currentKey string   // Base key name for everything except hashes.
     19 	pos        Position // Current position in the TOML file.
     20 	tomlNext   bool
     21 
     22 	ordered []Key // List of keys in the order that they appear in the TOML data.
     23 
     24 	keyInfo   map[string]keyInfo  // Map keyname → info about the TOML key.
     25 	mapping   map[string]any      // Map keyname → key value.
     26 	implicits map[string]struct{} // Record implicit keys (e.g. "key.group.names").
     27 }
     28 
     29 type keyInfo struct {
     30 	pos      Position
     31 	tomlType tomlType
     32 }
     33 
     34 func parse(data string) (p *parser, err error) {
     35 	_, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
     36 
     37 	defer func() {
     38 		if r := recover(); r != nil {
     39 			if pErr, ok := r.(ParseError); ok {
     40 				pErr.input = data
     41 				err = pErr
     42 				return
     43 			}
     44 			panic(r)
     45 		}
     46 	}()
     47 
     48 	// Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString()
     49 	// which mangles stuff. UTF-16 BOM isn't strictly valid, but some tools add
     50 	// it anyway.
     51 	if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16
     52 		data = data[2:]
     53 		//lint:ignore S1017 https://github.com/dominikh/go-tools/issues/1447
     54 	} else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8
     55 		data = data[3:]
     56 	}
     57 
     58 	// Examine first few bytes for NULL bytes; this probably means it's a UTF-16
     59 	// file (second byte in surrogate pair being NULL). Again, do this here to
     60 	// avoid having to deal with UTF-8/16 stuff in the lexer.
     61 	ex := 6
     62 	if len(data) < 6 {
     63 		ex = len(data)
     64 	}
     65 	if i := strings.IndexRune(data[:ex], 0); i > -1 {
     66 		return nil, ParseError{
     67 			Message:  "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8",
     68 			Position: Position{Line: 1, Col: 1, Start: i, Len: 1},
     69 			Line:     1,
     70 			input:    data,
     71 		}
     72 	}
     73 
     74 	p = &parser{
     75 		keyInfo:   make(map[string]keyInfo),
     76 		mapping:   make(map[string]any),
     77 		lx:        lex(data, tomlNext),
     78 		ordered:   make([]Key, 0),
     79 		implicits: make(map[string]struct{}),
     80 		tomlNext:  tomlNext,
     81 	}
     82 	for {
     83 		item := p.next()
     84 		if item.typ == itemEOF {
     85 			break
     86 		}
     87 		p.topLevel(item)
     88 	}
     89 
     90 	return p, nil
     91 }
     92 
     93 func (p *parser) panicErr(it item, err error) {
     94 	panic(ParseError{
     95 		Message:  err.Error(),
     96 		err:      err,
     97 		Position: it.pos.withCol(p.lx.input),
     98 		Line:     it.pos.Len,
     99 		LastKey:  p.current(),
    100 	})
    101 }
    102 
    103 func (p *parser) panicItemf(it item, format string, v ...any) {
    104 	panic(ParseError{
    105 		Message:  fmt.Sprintf(format, v...),
    106 		Position: it.pos.withCol(p.lx.input),
    107 		Line:     it.pos.Len,
    108 		LastKey:  p.current(),
    109 	})
    110 }
    111 
    112 func (p *parser) panicf(format string, v ...any) {
    113 	panic(ParseError{
    114 		Message:  fmt.Sprintf(format, v...),
    115 		Position: p.pos.withCol(p.lx.input),
    116 		Line:     p.pos.Line,
    117 		LastKey:  p.current(),
    118 	})
    119 }
    120 
    121 func (p *parser) next() item {
    122 	it := p.lx.nextItem()
    123 	//fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.pos.Line, it.val)
    124 	if it.typ == itemError {
    125 		if it.err != nil {
    126 			panic(ParseError{
    127 				Message:  it.err.Error(),
    128 				err:      it.err,
    129 				Position: it.pos.withCol(p.lx.input),
    130 				Line:     it.pos.Line,
    131 				LastKey:  p.current(),
    132 			})
    133 		}
    134 
    135 		p.panicItemf(it, "%s", it.val)
    136 	}
    137 	return it
    138 }
    139 
    140 func (p *parser) nextPos() item {
    141 	it := p.next()
    142 	p.pos = it.pos
    143 	return it
    144 }
    145 
    146 func (p *parser) bug(format string, v ...any) {
    147 	panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
    148 }
    149 
    150 func (p *parser) expect(typ itemType) item {
    151 	it := p.next()
    152 	p.assertEqual(typ, it.typ)
    153 	return it
    154 }
    155 
    156 func (p *parser) assertEqual(expected, got itemType) {
    157 	if expected != got {
    158 		p.bug("Expected '%s' but got '%s'.", expected, got)
    159 	}
    160 }
    161 
    162 func (p *parser) topLevel(item item) {
    163 	switch item.typ {
    164 	case itemCommentStart: // # ..
    165 		p.expect(itemText)
    166 	case itemTableStart: // [ .. ]
    167 		name := p.nextPos()
    168 
    169 		var key Key
    170 		for ; name.typ != itemTableEnd && name.typ != itemEOF; name = p.next() {
    171 			key = append(key, p.keyString(name))
    172 		}
    173 		p.assertEqual(itemTableEnd, name.typ)
    174 
    175 		p.addContext(key, false)
    176 		p.setType("", tomlHash, item.pos)
    177 		p.ordered = append(p.ordered, key)
    178 	case itemArrayTableStart: // [[ .. ]]
    179 		name := p.nextPos()
    180 
    181 		var key Key
    182 		for ; name.typ != itemArrayTableEnd && name.typ != itemEOF; name = p.next() {
    183 			key = append(key, p.keyString(name))
    184 		}
    185 		p.assertEqual(itemArrayTableEnd, name.typ)
    186 
    187 		p.addContext(key, true)
    188 		p.setType("", tomlArrayHash, item.pos)
    189 		p.ordered = append(p.ordered, key)
    190 	case itemKeyStart: // key = ..
    191 		outerContext := p.context
    192 		/// Read all the key parts (e.g. 'a' and 'b' in 'a.b')
    193 		k := p.nextPos()
    194 		var key Key
    195 		for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
    196 			key = append(key, p.keyString(k))
    197 		}
    198 		p.assertEqual(itemKeyEnd, k.typ)
    199 
    200 		/// The current key is the last part.
    201 		p.currentKey = key.last()
    202 
    203 		/// All the other parts (if any) are the context; need to set each part
    204 		/// as implicit.
    205 		context := key.parent()
    206 		for i := range context {
    207 			p.addImplicitContext(append(p.context, context[i:i+1]...))
    208 		}
    209 		p.ordered = append(p.ordered, p.context.add(p.currentKey))
    210 
    211 		/// Set value.
    212 		vItem := p.next()
    213 		val, typ := p.value(vItem, false)
    214 		p.setValue(p.currentKey, val)
    215 		p.setType(p.currentKey, typ, vItem.pos)
    216 
    217 		/// Remove the context we added (preserving any context from [tbl] lines).
    218 		p.context = outerContext
    219 		p.currentKey = ""
    220 	default:
    221 		p.bug("Unexpected type at top level: %s", item.typ)
    222 	}
    223 }
    224 
    225 // Gets a string for a key (or part of a key in a table name).
    226 func (p *parser) keyString(it item) string {
    227 	switch it.typ {
    228 	case itemText:
    229 		return it.val
    230 	case itemString, itemStringEsc, itemMultilineString,
    231 		itemRawString, itemRawMultilineString:
    232 		s, _ := p.value(it, false)
    233 		return s.(string)
    234 	default:
    235 		p.bug("Unexpected key type: %s", it.typ)
    236 	}
    237 	panic("unreachable")
    238 }
    239 
    240 var datetimeRepl = strings.NewReplacer(
    241 	"z", "Z",
    242 	"t", "T",
    243 	" ", "T")
    244 
    245 // value translates an expected value from the lexer into a Go value wrapped
    246 // as an empty interface.
    247 func (p *parser) value(it item, parentIsArray bool) (any, tomlType) {
    248 	switch it.typ {
    249 	case itemString:
    250 		return it.val, p.typeOfPrimitive(it)
    251 	case itemStringEsc:
    252 		return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it)
    253 	case itemMultilineString:
    254 		return p.replaceEscapes(it, p.stripEscapedNewlines(stripFirstNewline(it.val))), p.typeOfPrimitive(it)
    255 	case itemRawString:
    256 		return it.val, p.typeOfPrimitive(it)
    257 	case itemRawMultilineString:
    258 		return stripFirstNewline(it.val), p.typeOfPrimitive(it)
    259 	case itemInteger:
    260 		return p.valueInteger(it)
    261 	case itemFloat:
    262 		return p.valueFloat(it)
    263 	case itemBool:
    264 		switch it.val {
    265 		case "true":
    266 			return true, p.typeOfPrimitive(it)
    267 		case "false":
    268 			return false, p.typeOfPrimitive(it)
    269 		default:
    270 			p.bug("Expected boolean value, but got '%s'.", it.val)
    271 		}
    272 	case itemDatetime:
    273 		return p.valueDatetime(it)
    274 	case itemArray:
    275 		return p.valueArray(it)
    276 	case itemInlineTableStart:
    277 		return p.valueInlineTable(it, parentIsArray)
    278 	default:
    279 		p.bug("Unexpected value type: %s", it.typ)
    280 	}
    281 	panic("unreachable")
    282 }
    283 
    284 func (p *parser) valueInteger(it item) (any, tomlType) {
    285 	if !numUnderscoresOK(it.val) {
    286 		p.panicItemf(it, "Invalid integer %q: underscores must be surrounded by digits", it.val)
    287 	}
    288 	if numHasLeadingZero(it.val) {
    289 		p.panicItemf(it, "Invalid integer %q: cannot have leading zeroes", it.val)
    290 	}
    291 
    292 	num, err := strconv.ParseInt(it.val, 0, 64)
    293 	if err != nil {
    294 		// Distinguish integer values. Normally, it'd be a bug if the lexer
    295 		// provides an invalid integer, but it's possible that the number is
    296 		// out of range of valid values (which the lexer cannot determine).
    297 		// So mark the former as a bug but the latter as a legitimate user
    298 		// error.
    299 		if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
    300 			p.panicErr(it, errParseRange{i: it.val, size: "int64"})
    301 		} else {
    302 			p.bug("Expected integer value, but got '%s'.", it.val)
    303 		}
    304 	}
    305 	return num, p.typeOfPrimitive(it)
    306 }
    307 
    308 func (p *parser) valueFloat(it item) (any, tomlType) {
    309 	parts := strings.FieldsFunc(it.val, func(r rune) bool {
    310 		switch r {
    311 		case '.', 'e', 'E':
    312 			return true
    313 		}
    314 		return false
    315 	})
    316 	for _, part := range parts {
    317 		if !numUnderscoresOK(part) {
    318 			p.panicItemf(it, "Invalid float %q: underscores must be surrounded by digits", it.val)
    319 		}
    320 	}
    321 	if len(parts) > 0 && numHasLeadingZero(parts[0]) {
    322 		p.panicItemf(it, "Invalid float %q: cannot have leading zeroes", it.val)
    323 	}
    324 	if !numPeriodsOK(it.val) {
    325 		// As a special case, numbers like '123.' or '1.e2',
    326 		// which are valid as far as Go/strconv are concerned,
    327 		// must be rejected because TOML says that a fractional
    328 		// part consists of '.' followed by 1+ digits.
    329 		p.panicItemf(it, "Invalid float %q: '.' must be followed by one or more digits", it.val)
    330 	}
    331 	val := strings.Replace(it.val, "_", "", -1)
    332 	signbit := false
    333 	if val == "+nan" || val == "-nan" {
    334 		signbit = val == "-nan"
    335 		val = "nan"
    336 	}
    337 	num, err := strconv.ParseFloat(val, 64)
    338 	if err != nil {
    339 		if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
    340 			p.panicErr(it, errParseRange{i: it.val, size: "float64"})
    341 		} else {
    342 			p.panicItemf(it, "Invalid float value: %q", it.val)
    343 		}
    344 	}
    345 	if signbit {
    346 		num = math.Copysign(num, -1)
    347 	}
    348 	return num, p.typeOfPrimitive(it)
    349 }
    350 
    351 var dtTypes = []struct {
    352 	fmt  string
    353 	zone *time.Location
    354 	next bool
    355 }{
    356 	{time.RFC3339Nano, time.Local, false},
    357 	{"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
    358 	{"2006-01-02", internal.LocalDate, false},
    359 	{"15:04:05.999999999", internal.LocalTime, false},
    360 
    361 	// tomlNext
    362 	{"2006-01-02T15:04Z07:00", time.Local, true},
    363 	{"2006-01-02T15:04", internal.LocalDatetime, true},
    364 	{"15:04", internal.LocalTime, true},
    365 }
    366 
    367 func (p *parser) valueDatetime(it item) (any, tomlType) {
    368 	it.val = datetimeRepl.Replace(it.val)
    369 	var (
    370 		t   time.Time
    371 		ok  bool
    372 		err error
    373 	)
    374 	for _, dt := range dtTypes {
    375 		if dt.next && !p.tomlNext {
    376 			continue
    377 		}
    378 		t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
    379 		if err == nil {
    380 			if missingLeadingZero(it.val, dt.fmt) {
    381 				p.panicErr(it, errParseDate{it.val})
    382 			}
    383 			ok = true
    384 			break
    385 		}
    386 	}
    387 	if !ok {
    388 		p.panicErr(it, errParseDate{it.val})
    389 	}
    390 	return t, p.typeOfPrimitive(it)
    391 }
    392 
    393 // Go's time.Parse() will accept numbers without a leading zero; there isn't any
    394 // way to require it. https://github.com/golang/go/issues/29911
    395 //
    396 // Depend on the fact that the separators (- and :) should always be at the same
    397 // location.
    398 func missingLeadingZero(d, l string) bool {
    399 	for i, c := range []byte(l) {
    400 		if c == '.' || c == 'Z' {
    401 			return false
    402 		}
    403 		if (c < '0' || c > '9') && d[i] != c {
    404 			return true
    405 		}
    406 	}
    407 	return false
    408 }
    409 
    410 func (p *parser) valueArray(it item) (any, tomlType) {
    411 	p.setType(p.currentKey, tomlArray, it.pos)
    412 
    413 	var (
    414 		// Initialize to a non-nil slice to make it consistent with how S = []
    415 		// decodes into a non-nil slice inside something like struct { S
    416 		// []string }. See #338
    417 		array = make([]any, 0, 2)
    418 	)
    419 	for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
    420 		if it.typ == itemCommentStart {
    421 			p.expect(itemText)
    422 			continue
    423 		}
    424 
    425 		val, typ := p.value(it, true)
    426 		array = append(array, val)
    427 
    428 		// XXX: type isn't used here, we need it to record the accurate type
    429 		// information.
    430 		//
    431 		// Not entirely sure how to best store this; could use "key[0]",
    432 		// "key[1]" notation, or maybe store it on the Array type?
    433 		_ = typ
    434 	}
    435 	return array, tomlArray
    436 }
    437 
    438 func (p *parser) valueInlineTable(it item, parentIsArray bool) (any, tomlType) {
    439 	var (
    440 		topHash      = make(map[string]any)
    441 		outerContext = p.context
    442 		outerKey     = p.currentKey
    443 	)
    444 
    445 	p.context = append(p.context, p.currentKey)
    446 	prevContext := p.context
    447 	p.currentKey = ""
    448 
    449 	p.addImplicit(p.context)
    450 	p.addContext(p.context, parentIsArray)
    451 
    452 	/// Loop over all table key/value pairs.
    453 	for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
    454 		if it.typ == itemCommentStart {
    455 			p.expect(itemText)
    456 			continue
    457 		}
    458 
    459 		/// Read all key parts.
    460 		k := p.nextPos()
    461 		var key Key
    462 		for ; k.typ != itemKeyEnd && k.typ != itemEOF; k = p.next() {
    463 			key = append(key, p.keyString(k))
    464 		}
    465 		p.assertEqual(itemKeyEnd, k.typ)
    466 
    467 		/// The current key is the last part.
    468 		p.currentKey = key.last()
    469 
    470 		/// All the other parts (if any) are the context; need to set each part
    471 		/// as implicit.
    472 		context := key.parent()
    473 		for i := range context {
    474 			p.addImplicitContext(append(p.context, context[i:i+1]...))
    475 		}
    476 		p.ordered = append(p.ordered, p.context.add(p.currentKey))
    477 
    478 		/// Set the value.
    479 		val, typ := p.value(p.next(), false)
    480 		p.setValue(p.currentKey, val)
    481 		p.setType(p.currentKey, typ, it.pos)
    482 
    483 		hash := topHash
    484 		for _, c := range context {
    485 			h, ok := hash[c]
    486 			if !ok {
    487 				h = make(map[string]any)
    488 				hash[c] = h
    489 			}
    490 			hash, ok = h.(map[string]any)
    491 			if !ok {
    492 				p.panicf("%q is not a table", p.context)
    493 			}
    494 		}
    495 		hash[p.currentKey] = val
    496 
    497 		/// Restore context.
    498 		p.context = prevContext
    499 	}
    500 	p.context = outerContext
    501 	p.currentKey = outerKey
    502 	return topHash, tomlHash
    503 }
    504 
    505 // numHasLeadingZero checks if this number has leading zeroes, allowing for '0',
    506 // +/- signs, and base prefixes.
    507 func numHasLeadingZero(s string) bool {
    508 	if len(s) > 1 && s[0] == '0' && !(s[1] == 'b' || s[1] == 'o' || s[1] == 'x') { // Allow 0b, 0o, 0x
    509 		return true
    510 	}
    511 	if len(s) > 2 && (s[0] == '-' || s[0] == '+') && s[1] == '0' {
    512 		return true
    513 	}
    514 	return false
    515 }
    516 
    517 // numUnderscoresOK checks whether each underscore in s is surrounded by
    518 // characters that are not underscores.
    519 func numUnderscoresOK(s string) bool {
    520 	switch s {
    521 	case "nan", "+nan", "-nan", "inf", "-inf", "+inf":
    522 		return true
    523 	}
    524 	accept := false
    525 	for _, r := range s {
    526 		if r == '_' {
    527 			if !accept {
    528 				return false
    529 			}
    530 		}
    531 
    532 		// isHexis a superset of all the permissable characters surrounding an
    533 		// underscore.
    534 		accept = isHex(r)
    535 	}
    536 	return accept
    537 }
    538 
    539 // numPeriodsOK checks whether every period in s is followed by a digit.
    540 func numPeriodsOK(s string) bool {
    541 	period := false
    542 	for _, r := range s {
    543 		if period && !isDigit(r) {
    544 			return false
    545 		}
    546 		period = r == '.'
    547 	}
    548 	return !period
    549 }
    550 
    551 // Set the current context of the parser, where the context is either a hash or
    552 // an array of hashes, depending on the value of the `array` parameter.
    553 //
    554 // Establishing the context also makes sure that the key isn't a duplicate, and
    555 // will create implicit hashes automatically.
    556 func (p *parser) addContext(key Key, array bool) {
    557 	/// Always start at the top level and drill down for our context.
    558 	hashContext := p.mapping
    559 	keyContext := make(Key, 0, len(key)-1)
    560 
    561 	/// We only need implicit hashes for the parents.
    562 	for _, k := range key.parent() {
    563 		_, ok := hashContext[k]
    564 		keyContext = append(keyContext, k)
    565 
    566 		// No key? Make an implicit hash and move on.
    567 		if !ok {
    568 			p.addImplicit(keyContext)
    569 			hashContext[k] = make(map[string]any)
    570 		}
    571 
    572 		// If the hash context is actually an array of tables, then set
    573 		// the hash context to the last element in that array.
    574 		//
    575 		// Otherwise, it better be a table, since this MUST be a key group (by
    576 		// virtue of it not being the last element in a key).
    577 		switch t := hashContext[k].(type) {
    578 		case []map[string]any:
    579 			hashContext = t[len(t)-1]
    580 		case map[string]any:
    581 			hashContext = t
    582 		default:
    583 			p.panicf("Key '%s' was already created as a hash.", keyContext)
    584 		}
    585 	}
    586 
    587 	p.context = keyContext
    588 	if array {
    589 		// If this is the first element for this array, then allocate a new
    590 		// list of tables for it.
    591 		k := key.last()
    592 		if _, ok := hashContext[k]; !ok {
    593 			hashContext[k] = make([]map[string]any, 0, 4)
    594 		}
    595 
    596 		// Add a new table. But make sure the key hasn't already been used
    597 		// for something else.
    598 		if hash, ok := hashContext[k].([]map[string]any); ok {
    599 			hashContext[k] = append(hash, make(map[string]any))
    600 		} else {
    601 			p.panicf("Key '%s' was already created and cannot be used as an array.", key)
    602 		}
    603 	} else {
    604 		p.setValue(key.last(), make(map[string]any))
    605 	}
    606 	p.context = append(p.context, key.last())
    607 }
    608 
    609 // setValue sets the given key to the given value in the current context.
    610 // It will make sure that the key hasn't already been defined, account for
    611 // implicit key groups.
    612 func (p *parser) setValue(key string, value any) {
    613 	var (
    614 		tmpHash    any
    615 		ok         bool
    616 		hash       = p.mapping
    617 		keyContext = make(Key, 0, len(p.context)+1)
    618 	)
    619 	for _, k := range p.context {
    620 		keyContext = append(keyContext, k)
    621 		if tmpHash, ok = hash[k]; !ok {
    622 			p.bug("Context for key '%s' has not been established.", keyContext)
    623 		}
    624 		switch t := tmpHash.(type) {
    625 		case []map[string]any:
    626 			// The context is a table of hashes. Pick the most recent table
    627 			// defined as the current hash.
    628 			hash = t[len(t)-1]
    629 		case map[string]any:
    630 			hash = t
    631 		default:
    632 			p.panicf("Key '%s' has already been defined.", keyContext)
    633 		}
    634 	}
    635 	keyContext = append(keyContext, key)
    636 
    637 	if _, ok := hash[key]; ok {
    638 		// Normally redefining keys isn't allowed, but the key could have been
    639 		// defined implicitly and it's allowed to be redefined concretely. (See
    640 		// the `valid/implicit-and-explicit-after.toml` in toml-test)
    641 		//
    642 		// But we have to make sure to stop marking it as an implicit. (So that
    643 		// another redefinition provokes an error.)
    644 		//
    645 		// Note that since it has already been defined (as a hash), we don't
    646 		// want to overwrite it. So our business is done.
    647 		if p.isArray(keyContext) {
    648 			p.removeImplicit(keyContext)
    649 			hash[key] = value
    650 			return
    651 		}
    652 		if p.isImplicit(keyContext) {
    653 			p.removeImplicit(keyContext)
    654 			return
    655 		}
    656 		// Otherwise, we have a concrete key trying to override a previous key,
    657 		// which is *always* wrong.
    658 		p.panicf("Key '%s' has already been defined.", keyContext)
    659 	}
    660 
    661 	hash[key] = value
    662 }
    663 
    664 // setType sets the type of a particular value at a given key. It should be
    665 // called immediately AFTER setValue.
    666 //
    667 // Note that if `key` is empty, then the type given will be applied to the
    668 // current context (which is either a table or an array of tables).
    669 func (p *parser) setType(key string, typ tomlType, pos Position) {
    670 	keyContext := make(Key, 0, len(p.context)+1)
    671 	keyContext = append(keyContext, p.context...)
    672 	if len(key) > 0 { // allow type setting for hashes
    673 		keyContext = append(keyContext, key)
    674 	}
    675 	// Special case to make empty keys ("" = 1) work.
    676 	// Without it it will set "" rather than `""`.
    677 	// TODO: why is this needed? And why is this only needed here?
    678 	if len(keyContext) == 0 {
    679 		keyContext = Key{""}
    680 	}
    681 	p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos}
    682 }
    683 
    684 // Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and
    685 // "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly).
    686 func (p *parser) addImplicit(key Key)        { p.implicits[key.String()] = struct{}{} }
    687 func (p *parser) removeImplicit(key Key)     { delete(p.implicits, key.String()) }
    688 func (p *parser) isImplicit(key Key) bool    { _, ok := p.implicits[key.String()]; return ok }
    689 func (p *parser) isArray(key Key) bool       { return p.keyInfo[key.String()].tomlType == tomlArray }
    690 func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) }
    691 
    692 // current returns the full key name of the current context.
    693 func (p *parser) current() string {
    694 	if len(p.currentKey) == 0 {
    695 		return p.context.String()
    696 	}
    697 	if len(p.context) == 0 {
    698 		return p.currentKey
    699 	}
    700 	return fmt.Sprintf("%s.%s", p.context, p.currentKey)
    701 }
    702 
    703 func stripFirstNewline(s string) string {
    704 	if len(s) > 0 && s[0] == '\n' {
    705 		return s[1:]
    706 	}
    707 	if len(s) > 1 && s[0] == '\r' && s[1] == '\n' {
    708 		return s[2:]
    709 	}
    710 	return s
    711 }
    712 
    713 // stripEscapedNewlines removes whitespace after line-ending backslashes in
    714 // multiline strings.
    715 //
    716 // A line-ending backslash is an unescaped \ followed only by whitespace until
    717 // the next newline. After a line-ending backslash, all whitespace is removed
    718 // until the next non-whitespace character.
    719 func (p *parser) stripEscapedNewlines(s string) string {
    720 	var (
    721 		b strings.Builder
    722 		i int
    723 	)
    724 	b.Grow(len(s))
    725 	for {
    726 		ix := strings.Index(s[i:], `\`)
    727 		if ix < 0 {
    728 			b.WriteString(s)
    729 			return b.String()
    730 		}
    731 		i += ix
    732 
    733 		if len(s) > i+1 && s[i+1] == '\\' {
    734 			// Escaped backslash.
    735 			i += 2
    736 			continue
    737 		}
    738 		// Scan until the next non-whitespace.
    739 		j := i + 1
    740 	whitespaceLoop:
    741 		for ; j < len(s); j++ {
    742 			switch s[j] {
    743 			case ' ', '\t', '\r', '\n':
    744 			default:
    745 				break whitespaceLoop
    746 			}
    747 		}
    748 		if j == i+1 {
    749 			// Not a whitespace escape.
    750 			i++
    751 			continue
    752 		}
    753 		if !strings.Contains(s[i:j], "\n") {
    754 			// This is not a line-ending backslash. (It's a bad escape sequence,
    755 			// but we can let replaceEscapes catch it.)
    756 			i++
    757 			continue
    758 		}
    759 		b.WriteString(s[:i])
    760 		s = s[j:]
    761 		i = 0
    762 	}
    763 }
    764 
    765 func (p *parser) replaceEscapes(it item, str string) string {
    766 	var (
    767 		b    strings.Builder
    768 		skip = 0
    769 	)
    770 	b.Grow(len(str))
    771 	for i, c := range str {
    772 		if skip > 0 {
    773 			skip--
    774 			continue
    775 		}
    776 		if c != '\\' {
    777 			b.WriteRune(c)
    778 			continue
    779 		}
    780 
    781 		if i >= len(str) {
    782 			p.bug("Escape sequence at end of string.")
    783 			return ""
    784 		}
    785 		switch str[i+1] {
    786 		default:
    787 			p.bug("Expected valid escape code after \\, but got %q.", str[i+1])
    788 		case ' ', '\t':
    789 			p.panicItemf(it, "invalid escape: '\\%c'", str[i+1])
    790 		case 'b':
    791 			b.WriteByte(0x08)
    792 			skip = 1
    793 		case 't':
    794 			b.WriteByte(0x09)
    795 			skip = 1
    796 		case 'n':
    797 			b.WriteByte(0x0a)
    798 			skip = 1
    799 		case 'f':
    800 			b.WriteByte(0x0c)
    801 			skip = 1
    802 		case 'r':
    803 			b.WriteByte(0x0d)
    804 			skip = 1
    805 		case 'e':
    806 			if p.tomlNext {
    807 				b.WriteByte(0x1b)
    808 				skip = 1
    809 			}
    810 		case '"':
    811 			b.WriteByte(0x22)
    812 			skip = 1
    813 		case '\\':
    814 			b.WriteByte(0x5c)
    815 			skip = 1
    816 		// The lexer guarantees the correct number of characters are present;
    817 		// don't need to check here.
    818 		case 'x':
    819 			if p.tomlNext {
    820 				escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
    821 				b.WriteRune(escaped)
    822 				skip = 3
    823 			}
    824 		case 'u':
    825 			escaped := p.asciiEscapeToUnicode(it, str[i+2:i+6])
    826 			b.WriteRune(escaped)
    827 			skip = 5
    828 		case 'U':
    829 			escaped := p.asciiEscapeToUnicode(it, str[i+2:i+10])
    830 			b.WriteRune(escaped)
    831 			skip = 9
    832 		}
    833 	}
    834 	return b.String()
    835 }
    836 
    837 func (p *parser) asciiEscapeToUnicode(it item, s string) rune {
    838 	hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
    839 	if err != nil {
    840 		p.bug("Could not parse '%s' as a hexadecimal number, but the lexer claims it's OK: %s", s, err)
    841 	}
    842 	if !utf8.ValidRune(rune(hex)) {
    843 		p.panicItemf(it, "Escaped character '\\u%s' is not valid UTF-8.", s)
    844 	}
    845 	return rune(hex)
    846 }