@@ -595,10 +595,118 @@ extension Parser {
595595 )
596596 }
597597 }
598+
599+ mutating func parseSimpleString( ) -> RawSimpleStringLiteralExprSyntax {
600+ let openDelimiter = self . consume ( if: . rawStringDelimiter)
601+ let ( unexpectedBeforeOpenQuote, openQuote) = self . expect ( anyIn: SimpleStringLiteralExprSyntax . OpenQuoteOptions. self, default: . stringQuote)
602+
603+ /// Parse segments.
604+ var segments : [ RawStringSegmentSyntax ] = [ ]
605+ var loopProgress = LoopProgressCondition ( )
606+ while loopProgress. evaluate ( self . currentToken) {
607+ // If we encounter a token with leading trivia, we're no longer in the
608+ // string literal.
609+ guard currentToken. leadingTriviaText. isEmpty else { break }
610+
611+ if let stringSegment = self . consume ( if: . stringSegment, TokenSpec ( . identifier, remapping: . stringSegment) ) {
612+ var unexpectedAfterContent : RawUnexpectedNodesSyntax ?
613+
614+ if let backslash = self . consume ( if: . backslash) {
615+ let ( unexpectedBeforeDelimiter, delimiter) = self . parsePoundDelimiter ( . rawStringDelimiter, matching: openDelimiter)
616+ let leftParen = self . expectWithoutRecoveryOrLeadingTrivia ( . leftParen)
617+ let expressions = RawTupleExprElementListSyntax ( elements: self . parseArgumentListElements ( pattern: . none) , arena: self . arena)
618+
619+ // For recovery, eat anything up to the next token that either starts a new string segment or terminates the string.
620+ // This allows us to skip over extraneous identifiers etc. in an unterminated string interpolation.
621+ var unexpectedBeforeRightParen : [ RawTokenSyntax ] = [ ]
622+ var unexpectedProgress = LoopProgressCondition ( )
623+ while !self . at ( . rightParen, . stringSegment, . backslash) && !self . at ( TokenSpec ( openQuote. tokenKind) , . endOfFile)
624+ && unexpectedProgress. evaluate ( self . currentToken)
625+ {
626+ unexpectedBeforeRightParen. append ( self . consumeAnyToken ( ) )
627+ }
628+ // Consume the right paren if present, ensuring that it's on the same
629+ // line if this is a single-line literal. Leading trivia is fine as
630+ // we allow e.g "\(foo )".
631+ let rightParen : Token
632+ if self . at ( . rightParen) && self . currentToken. isAtStartOfLine && openQuote. tokenKind != . multilineStringQuote {
633+ rightParen = missingToken ( . rightParen)
634+ } else {
635+ rightParen = self . expectWithoutRecovery ( . rightParen)
636+ }
637+ if case . inStringInterpolation = self . currentToken. cursor. currentState {
638+ // The parser has more knowledge that we have reached the end of the
639+ // string interpolation now, even if we haven't seen the closing ')'.
640+ // For example, consider the following code
641+ // "\(abc "
642+ // Since the lexer doesn't know anything about the expression structure,
643+ // it assumes that the `"` starts a new string literal. But since we
644+ // know in the parser that an identifier cannot be followed by a string
645+ // literal without a connecting binary operator and can thus consider
646+ // it as the surrounding string literal end, which thus also terminates
647+ // the string interpolation.
648+ self . lexemes. perform ( stateTransition: . pop, currentToken: & self . currentToken)
649+ }
650+ unexpectedAfterContent = RawUnexpectedNodesSyntax ( [
651+ backslash,
652+ delimiter,
653+ leftParen,
654+ // expressions,
655+ rightParen
656+ ] , arena: self . arena)
657+ // interpolatedSegments.append(
658+ // RawExpressionSegmentSyntax(
659+ // backslash: backslash,
660+ // unexpectedBeforeDelimiter,
661+ // rawStringDelimiter: delimiter,
662+ // leftParen: leftParen,
663+ // expressions: expressions,
664+ // RawUnexpectedNodesSyntax(unexpectedBeforeRightParen, arena: self.arena),
665+ // rightParen: rightParen,
666+ // arena: self.arena
667+ // )
668+ // )
669+ }
670+
671+ segments. append ( RawStringSegmentSyntax ( content: stringSegment, unexpectedAfterContent, arena: self . arena) )
672+ } else {
673+ break
674+ }
675+ }
676+
677+ let ( unexpectedBetweenSegmentAndCloseQuote, closeQuote) = self . expect (
678+ anyIn: SimpleStringLiteralExprSyntax . CloseQuoteOptions. self,
679+ default: openQuote. closeTokenKind
680+ )
681+ let closeDelimiter = self . consume ( if: . rawStringDelimiter)
682+
683+ return RawSimpleStringLiteralExprSyntax (
684+ RawUnexpectedNodesSyntax ( combining: unexpectedBeforeOpenQuote, openDelimiter, arena: self . arena) ,
685+ openQuote: openQuote,
686+ segments: RawSimpleStringLiteralSegmentsSyntax ( elements: segments, arena: self . arena) ,
687+ unexpectedBetweenSegmentAndCloseQuote,
688+ closeQuote: closeQuote,
689+ RawUnexpectedNodesSyntax ( [ closeDelimiter] , arena: self . arena) ,
690+ arena: self . arena
691+ )
692+ }
598693}
599694
600695// MARK: - Utilities
601696
697+ fileprivate extension RawTokenSyntax {
698+ var closeTokenKind : SimpleStringLiteralExprSyntax . CloseQuoteOptions {
699+ switch self {
700+ case . multilineStringQuote:
701+ return . multilineStringQuote
702+ case . stringQuote:
703+ return . stringQuote
704+ default :
705+ fatalError ( " Unsupported type " )
706+ }
707+ }
708+ }
709+
602710fileprivate extension SyntaxText {
603711 private func hasSuffix( _ other: String ) -> Bool {
604712 var other = other
0 commit comments