@@ -245,7 +245,7 @@ func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) {
245
245
case itemString :
246
246
return p .replaceEscapes (it , it .val ), p .typeOfPrimitive (it )
247
247
case itemMultilineString :
248
- return p .replaceEscapes (it , stripFirstNewline ( p .stripEscapedNewlines (it .val ))), p .typeOfPrimitive (it )
248
+ return p .replaceEscapes (it , p .stripEscapedNewlines ( stripFirstNewline (it .val ))), p .typeOfPrimitive (it )
249
249
case itemRawString :
250
250
return it .val , p .typeOfPrimitive (it )
251
251
case itemRawMultilineString :
@@ -681,49 +681,54 @@ func stripFirstNewline(s string) string {
681
681
return s
682
682
}
683
683
684
- // Remove newlines inside triple-quoted strings if a line ends with "\".
684
+ // stripEscapedNewlines removes whitespace after line-ending backslashes in
685
+ // multiline strings.
686
+ //
687
+ // A line-ending backslash is an unescaped \ followed only by whitespace until
688
+ // the next newline. After a line-ending backslash, all whitespace is removed
689
+ // until the next non-whitespace character.
685
690
func (p * parser ) stripEscapedNewlines (s string ) string {
686
- split := strings .Split (s , "\n " )
687
- if len (split ) < 1 {
688
- return s
689
- }
690
-
691
- escNL := false // Keep track of the last non-blank line was escaped.
692
- for i , line := range split {
693
- line = strings .TrimRight (line , " \t \r " )
694
-
695
- if len (line ) == 0 || line [len (line )- 1 ] != '\\' {
696
- split [i ] = strings .TrimRight (split [i ], "\r " )
697
- if ! escNL && i != len (split )- 1 {
698
- split [i ] += "\n "
699
- }
700
- continue
691
+ var b strings.Builder
692
+ var i int
693
+ for {
694
+ ix := strings .Index (s [i :], `\` )
695
+ if ix < 0 {
696
+ b .WriteString (s )
697
+ return b .String ()
701
698
}
699
+ i += ix
702
700
703
- escBS := true
704
- for j := len (line ) - 1 ; j >= 0 && line [j ] == '\\' ; j -- {
705
- escBS = ! escBS
701
+ if len (s ) > i + 1 && s [i + 1 ] == '\\' {
702
+ // Escaped backslash.
703
+ i += 2
704
+ continue
706
705
}
707
- if escNL {
708
- line = strings .TrimLeft (line , " \t \r " )
706
+ // Scan until the next non-whitespace.
707
+ j := i + 1
708
+ whitespaceLoop:
709
+ for ; j < len (s ); j ++ {
710
+ switch s [j ] {
711
+ case ' ' , '\t' , '\r' , '\n' :
712
+ default :
713
+ break whitespaceLoop
714
+ }
709
715
}
710
- escNL = ! escBS
711
-
712
- if escBS {
713
- split [i ] += "\n "
716
+ if j == i + 1 {
717
+ // Not a whitespace escape.
718
+ i ++
714
719
continue
715
720
}
716
-
717
- if i == len (split )- 1 {
718
- p .panicf ("invalid escape: '\\ '" )
719
- }
720
-
721
- split [i ] = line [:len (line )- 1 ] // Remove \
722
- if len (split )- 1 > i {
723
- split [i + 1 ] = strings .TrimLeft (split [i + 1 ], " \t \r " )
721
+ if ! strings .Contains (s [i :j ], "\n " ) {
722
+ // This is not a line-ending backslash.
723
+ // (It's a bad escape sequence, but we can let
724
+ // replaceEscapes catch it.)
725
+ i ++
726
+ continue
724
727
}
728
+ b .WriteString (s [:i ])
729
+ s = s [j :]
730
+ i = 0
725
731
}
726
- return strings .Join (split , "" )
727
732
}
728
733
729
734
func (p * parser ) replaceEscapes (it item , str string ) string {
0 commit comments