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 }