1313@_spi ( RawSyntax) import SwiftSyntax
1414
1515extension Parser {
16- private enum IfConfigContinuationClauseStartKeyword : TokenSpecSet {
16+ private enum PoundIfDirectiveKeywords : TokenSpecSet {
17+ case poundIfKeyword
1718 case poundElseifKeyword
1819 case poundElseKeyword
20+ case pound
1921
2022 var spec : TokenSpec {
2123 switch self {
24+ case . poundIfKeyword: return . poundIfKeyword
2225 case . poundElseifKeyword: return . poundElseifKeyword
2326 case . poundElseKeyword: return . poundElseKeyword
27+ case . pound: return TokenSpec ( . pound, recoveryPrecedence: . openingPoundIf)
2428 }
2529 }
2630
2731 init ? ( lexeme: Lexer . Lexeme ) {
2832 switch PrepareForKeywordMatch ( lexeme) {
33+ case TokenSpec ( . poundIfKeyword) : self = . poundIfKeyword
2934 case TokenSpec ( . poundElseifKeyword) : self = . poundElseifKeyword
3035 case TokenSpec ( . poundElseKeyword) : self = . poundElseKeyword
36+ case TokenSpec ( . pound) : self = . pound
3137 default : return nil
3238 }
3339 }
@@ -103,32 +109,62 @@ extension Parser {
103109 do {
104110 var firstIteration = true
105111 var loopProgress = LoopProgressCondition ( )
106- while let poundIfHandle = firstIteration ? self . canRecoverTo ( . poundIfKeyword) : self . canRecoverTo ( anyIn: IfConfigContinuationClauseStartKeyword . self) ? . handle,
107- loopProgress. evaluate ( self . currentToken)
108- {
109- var ( unexpectedBeforePoundIf, poundIf) = self . eat ( poundIfHandle)
110- firstIteration = false
111- // Parse the condition.
112+ LOOP: while let ( match, handle) = self . canRecoverTo ( anyIn: PoundIfDirectiveKeywords . self) , loopProgress. evaluate ( self . currentToken) {
113+ var unexpectedBeforePound : RawUnexpectedNodesSyntax ?
114+ var pound : RawTokenSyntax
112115 let condition : RawExprSyntax ?
113- switch poundIf. tokenKind {
114- case . poundIfKeyword, . poundElseifKeyword:
116+ var atElifTypo : Bool {
117+ guard self . at ( TokenSpec ( . pound) ) , self . currentToken. trailingTriviaText. isEmpty else {
118+ return false
119+ }
120+ let identifierSpec = TokenSpec ( . identifier, allowAtStartOfLine: false )
121+ var lookahead = self . lookahead ( )
122+ lookahead. consumeAnyToken ( )
123+ guard lookahead. at ( identifierSpec) , lookahead. currentToken. tokenText == " elif " , lookahead. currentToken. leadingTriviaText. isEmpty else {
124+ return false
125+ }
126+ lookahead. consumeAnyToken ( )
127+ return lookahead. at ( identifierSpec)
128+ }
129+
130+ switch match {
131+ case . poundIfKeyword:
132+ if !firstIteration {
133+ break LOOP
134+ }
135+ firstIteration = false
136+ ( unexpectedBeforePound, pound) = self . eat ( handle)
137+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
138+ case . poundElseifKeyword:
139+ ( unexpectedBeforePound, pound) = self . eat ( handle)
115140 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
116141 case . poundElseKeyword:
142+ ( unexpectedBeforePound, pound) = self . eat ( handle)
117143 if let ifToken = self . consume ( if: . init( . if, allowAtStartOfLine: false ) ) {
118- unexpectedBeforePoundIf = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePoundIf , poundIf , ifToken, arena: self . arena)
119- poundIf = self . missingToken ( . poundElseifKeyword)
144+ unexpectedBeforePound = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePound , pound , ifToken, arena: self . arena)
145+ pound = self . missingToken ( . poundElseifKeyword)
120146 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
121147 } else {
122148 condition = nil
123149 }
124- default :
125- preconditionFailure ( " The loop condition should guarantee that we are at one of these tokens " )
150+ case . pound:
151+ if atElifTypo {
152+ ( unexpectedBeforePound, pound) = self . eat ( handle)
153+ guard let identifier = self . consume ( if: TokenSpec ( . identifier, allowAtStartOfLine: false ) ) else {
154+ preconditionFailure ( " The current token should be an identifier, guaranteed by the `atElifTypo` check. " )
155+ }
156+ unexpectedBeforePound = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePound, pound, identifier, arena: self . arena)
157+ pound = self . missingToken ( . poundElseifKeyword)
158+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
159+ } else {
160+ break LOOP
161+ }
126162 }
127163
128164 var elements = [ Element] ( )
129165 do {
130166 var elementsProgress = LoopProgressCondition ( )
131- while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && elementsProgress. evaluate ( currentToken) {
167+ while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && !atElifTypo && elementsProgress. evaluate ( currentToken) {
132168 let newItemAtStartOfLine = self . currentToken. isAtStartOfLine
133169 guard let element = parseElement ( & self , elements. isEmpty) , !element. isEmpty else {
134170 break
@@ -142,8 +178,8 @@ extension Parser {
142178
143179 clauses. append (
144180 RawIfConfigClauseSyntax (
145- unexpectedBeforePoundIf ,
146- poundKeyword: poundIf ,
181+ unexpectedBeforePound ,
182+ poundKeyword: pound ,
147183 condition: condition,
148184 elements: syntax ( & self , elements) ,
149185 arena: self . arena
0 commit comments