@@ -79,7 +79,7 @@ extension Lexer.Cursor {
79
79
80
80
/// The lexer has finished lexing the contents of a string literal and is now
81
81
/// looking for the closing quote.
82
- case afterStringLiteral( isRawString: Bool )
82
+ case afterStringLiteral( kind : StringLiteralKind , isRawString: Bool )
83
83
84
84
/// The lexer has lexed the closing quote of a string literal that had raw
85
85
/// string delimiters and is now looking for the closing raw string delimiters.
@@ -453,8 +453,8 @@ extension Lexer.Cursor {
453
453
result = lexAfterRawStringDelimiter ( delimiterLength: delimiterLength)
454
454
case . inStringLiteral( kind: let stringLiteralKind, delimiterLength: let delimiterLength) :
455
455
result = lexInStringLiteral ( stringLiteralKind: stringLiteralKind, delimiterLength: delimiterLength)
456
- case . afterStringLiteral( isRawString: _) :
457
- result = lexAfterStringLiteral ( )
456
+ case . afterStringLiteral( kind : let stringLiteralKind , isRawString: _) :
457
+ result = lexAfterStringLiteral ( stringLiteralKind : stringLiteralKind )
458
458
case . afterClosingStringQuote:
459
459
result = lexAfterClosingStringQuote ( )
460
460
case . inStringInterpolationStart( stringLiteralKind: let stringLiteralKind) :
@@ -998,7 +998,7 @@ extension Lexer.Cursor {
998
998
case " 0 " , " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " 6 " , " 7 " , " 8 " , " 9 " :
999
999
return self . lexNumber ( )
1000
1000
case #"'"# , #"""# :
1001
- return self . lexStringQuote ( isOpening : true , leadingDelimiterLength: 0 )
1001
+ return self . lexStringQuote ( matchingOpening : nil , leadingDelimiterLength: 0 )
1002
1002
1003
1003
case " ` " :
1004
1004
return self . lexEscapedIdentifier ( )
@@ -1029,18 +1029,18 @@ extension Lexer.Cursor {
1029
1029
private mutating func lexAfterRawStringDelimiter( delimiterLength: Int ) -> Lexer . Result {
1030
1030
switch self . peek ( ) {
1031
1031
case #"'"# , #"""# :
1032
- return self . lexStringQuote ( isOpening : true , leadingDelimiterLength: delimiterLength)
1032
+ return self . lexStringQuote ( matchingOpening : nil , leadingDelimiterLength: delimiterLength)
1033
1033
case nil :
1034
1034
return Lexer . Result ( . endOfFile)
1035
1035
default :
1036
1036
preconditionFailure ( " state 'afterRawStringDelimiter' expects to be positioned at a quote " )
1037
1037
}
1038
1038
}
1039
1039
1040
- private mutating func lexAfterStringLiteral( ) -> Lexer . Result {
1040
+ private mutating func lexAfterStringLiteral( stringLiteralKind : StringLiteralKind ) -> Lexer . Result {
1041
1041
switch self . peek ( ) {
1042
1042
case #"'"# , #"""# :
1043
- return self . lexStringQuote ( isOpening : false , leadingDelimiterLength: 0 )
1043
+ return self . lexStringQuote ( matchingOpening : stringLiteralKind , leadingDelimiterLength: 0 )
1044
1044
case nil :
1045
1045
return Lexer . Result ( . endOfFile)
1046
1046
default :
@@ -1796,9 +1796,9 @@ extension Lexer.Cursor {
1796
1796
extension Lexer . Cursor {
1797
1797
private func stateTransitionAfterLexingStringQuote( kind: StringLiteralKind ) -> Lexer . StateTransition {
1798
1798
switch currentState {
1799
- case . afterStringLiteral( isRawString: true ) :
1799
+ case . afterStringLiteral( kind : _ , isRawString: true ) :
1800
1800
return . replace( newState: . afterClosingStringQuote)
1801
- case . afterStringLiteral( isRawString: false ) :
1801
+ case . afterStringLiteral( kind : _ , isRawString: false ) :
1802
1802
return . pop
1803
1803
case . afterRawStringDelimiter( delimiterLength: let delimiterLength) :
1804
1804
return . replace( newState: . inStringLiteral( kind: kind, delimiterLength: delimiterLength) )
@@ -1809,62 +1809,70 @@ extension Lexer.Cursor {
1809
1809
}
1810
1810
}
1811
1811
1812
- /// `isOpening` is `true` if this string quote is the opening quote of a string
1813
- /// literal and `false` if we are lexing the closing quote of a string literal.
1814
- mutating func lexStringQuote( isOpening: Bool , leadingDelimiterLength: Int ) -> Lexer . Result {
1812
+ /// `matchingOpening` is the opening literal kind if this string quote is the
1813
+ /// closing quote of a string literal, `nil` if it's the opening quote.
1814
+ mutating func lexStringQuote(
1815
+ matchingOpening: StringLiteralKind ? ,
1816
+ leadingDelimiterLength: Int
1817
+ ) -> Lexer . Result {
1815
1818
if self . advance ( matching: " ' " ) {
1816
1819
return Lexer . Result ( . singleQuote, stateTransition: stateTransitionAfterLexingStringQuote ( kind: . singleQuote) )
1817
1820
}
1818
1821
1819
1822
let firstQuoteConsumed = self . advance ( matching: #"""# )
1820
1823
precondition ( firstQuoteConsumed)
1821
1824
1825
+ // Check to see if we have a multi-line delimiter. If we're matching an
1826
+ // opening '"' then we want to bail since e.g `"a"""` shouldn't try to eat
1827
+ // the '"""' as its closing delimiter.
1822
1828
var lookingForMultilineString = self
1823
- if lookingForMultilineString. advance ( matching: #"""# ) , lookingForMultilineString. advance ( matching: #"""# ) {
1824
- if leadingDelimiterLength > 0 {
1825
- // If this is a string literal, check if we have the closing delimiter on the same line to correctly parse things like `#"""#` as a single line string containing a quote.
1826
- var isSingleLineString = lookingForMultilineString
1827
-
1828
- if isSingleLineString. advanceIfStringDelimiter ( delimiterLength: leadingDelimiterLength) {
1829
- // If we have the correct number of delimiters now, we have something like `#"""#`.
1830
- // This is a single-line string.
1831
- return Lexer . Result ( . stringQuote, stateTransition: stateTransitionAfterLexingStringQuote ( kind: . singleLine) )
1832
- }
1829
+ if matchingOpening == . singleLine
1830
+ || !( lookingForMultilineString. advance ( matching: #"""# ) && lookingForMultilineString. advance ( matching: #"""# ) )
1831
+ {
1832
+ return Lexer . Result ( . stringQuote, stateTransition: stateTransitionAfterLexingStringQuote ( kind: . singleLine) )
1833
+ }
1833
1834
1834
- // Scan ahead until the end of the line. Every time we see a closing
1835
- // quote, check if it is followed by the correct number of closing delimiters.
1836
- while isSingleLineString. is ( notAt: " \r " , " \n " ) {
1837
- if isSingleLineString. advance ( if: { $0 == #"""# } ) {
1838
- if isSingleLineString. advanceIfStringDelimiter ( delimiterLength: leadingDelimiterLength) {
1839
- return Lexer . Result (
1840
- . stringQuote,
1841
- stateTransition: stateTransitionAfterLexingStringQuote ( kind: . singleLine)
1842
- )
1843
- }
1844
- continue
1845
- }
1846
- _ = isSingleLineString. advance ( )
1847
- }
1835
+ if leadingDelimiterLength > 0 {
1836
+ // If this is a string literal, check if we have the closing delimiter on the same line to correctly parse things like `#"""#` as a single line string containing a quote.
1837
+ var isSingleLineString = lookingForMultilineString
1838
+
1839
+ if isSingleLineString. advanceIfStringDelimiter ( delimiterLength: leadingDelimiterLength) {
1840
+ // If we have the correct number of delimiters now, we have something like `#"""#`.
1841
+ // This is a single-line string.
1842
+ return Lexer . Result ( . stringQuote, stateTransition: stateTransitionAfterLexingStringQuote ( kind: . singleLine) )
1848
1843
}
1849
1844
1850
- self = lookingForMultilineString
1851
- let trailingTriviaLexingMode : TriviaLexingMode ?
1852
- if isOpening && self . is ( at: " \n " , " \r " ) {
1853
- // The opening quote of a multi-line string literal must be followed by
1854
- // a newline that's not part of the represented string.
1855
- trailingTriviaLexingMode = . escapedNewlineInMultiLineStringLiteral
1856
- } else {
1857
- trailingTriviaLexingMode = nil
1845
+ // Scan ahead until the end of the line. Every time we see a closing
1846
+ // quote, check if it is followed by the correct number of closing delimiters.
1847
+ while isSingleLineString. is ( notAt: " \r " , " \n " ) {
1848
+ if isSingleLineString. advance ( if: { $0 == #"""# } ) {
1849
+ if isSingleLineString. advanceIfStringDelimiter ( delimiterLength: leadingDelimiterLength) {
1850
+ return Lexer . Result (
1851
+ . stringQuote,
1852
+ stateTransition: stateTransitionAfterLexingStringQuote ( kind: . singleLine)
1853
+ )
1854
+ }
1855
+ continue
1856
+ }
1857
+ _ = isSingleLineString. advance ( )
1858
1858
}
1859
+ }
1859
1860
1860
- return Lexer . Result (
1861
- . multilineStringQuote,
1862
- stateTransition: stateTransitionAfterLexingStringQuote ( kind: . multiLine) ,
1863
- trailingTriviaLexingMode: trailingTriviaLexingMode
1864
- )
1861
+ self = lookingForMultilineString
1862
+ let trailingTriviaLexingMode : TriviaLexingMode ?
1863
+ if matchingOpening == nil && self . is ( at: " \n " , " \r " ) {
1864
+ // The opening quote of a multi-line string literal must be followed by
1865
+ // a newline that's not part of the represented string.
1866
+ trailingTriviaLexingMode = . escapedNewlineInMultiLineStringLiteral
1865
1867
} else {
1866
- return Lexer . Result ( . stringQuote , stateTransition : stateTransitionAfterLexingStringQuote ( kind : . singleLine ) )
1868
+ trailingTriviaLexingMode = nil
1867
1869
}
1870
+
1871
+ return Lexer . Result (
1872
+ . multilineStringQuote,
1873
+ stateTransition: stateTransitionAfterLexingStringQuote ( kind: . multiLine) ,
1874
+ trailingTriviaLexingMode: trailingTriviaLexingMode
1875
+ )
1868
1876
}
1869
1877
1870
1878
/// Returns `true` if the cursor is positioned at `\##(` with `delimiterLength`
@@ -1935,7 +1943,9 @@ extension Lexer.Cursor {
1935
1943
return Lexer . Result (
1936
1944
. stringSegment,
1937
1945
error: error,
1938
- stateTransition: . replace( newState: . afterStringLiteral( isRawString: delimiterLength > 0 ) )
1946
+ stateTransition: . replace(
1947
+ newState: . afterStringLiteral( kind: stringLiteralKind, isRawString: delimiterLength > 0 )
1948
+ )
1939
1949
)
1940
1950
default :
1941
1951
break
@@ -1967,7 +1977,9 @@ extension Lexer.Cursor {
1967
1977
return Lexer . Result (
1968
1978
. stringSegment,
1969
1979
error: error,
1970
- stateTransition: . replace( newState: . afterStringLiteral( isRawString: delimiterLength > 0 ) )
1980
+ stateTransition: . replace(
1981
+ newState: . afterStringLiteral( kind: stringLiteralKind, isRawString: delimiterLength > 0 )
1982
+ )
1971
1983
)
1972
1984
}
1973
1985
}
0 commit comments