@@ -16,18 +16,21 @@ extension Parser {
1616 private enum IfConfigContinuationClauseStartKeyword : TokenSpecSet {
1717 case poundElseifKeyword
1818 case poundElseKeyword
19+ case pound
1920
2021 var spec : TokenSpec {
2122 switch self {
2223 case . poundElseifKeyword: return . poundElseifKeyword
2324 case . poundElseKeyword: return . poundElseKeyword
25+ case . pound: return TokenSpec ( . pound, recoveryPrecedence: . openingPoundIf)
2426 }
2527 }
2628
2729 init ? ( lexeme: Lexer . Lexeme ) {
2830 switch PrepareForKeywordMatch ( lexeme) {
2931 case TokenSpec ( . poundElseifKeyword) : self = . poundElseifKeyword
3032 case TokenSpec ( . poundElseKeyword) : self = . poundElseKeyword
33+ case TokenSpec ( . pound) : self = . pound
3134 default : return nil
3235 }
3336 }
@@ -100,56 +103,64 @@ extension Parser {
100103 }
101104
102105 var clauses = [ RawIfConfigClauseSyntax] ( )
103- do {
104- var firstIteration = true
105- 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- let condition : RawExprSyntax ?
113- switch poundIf. tokenKind {
114- case . poundIfKeyword, . poundElseifKeyword:
106+
107+ // Parse #if
108+ let ( unexpectedBeforePoundIfKeyword, poundIfKeyword) = self . expect ( . poundIfKeyword)
109+ let condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
110+
111+ clauses. append (
112+ RawIfConfigClauseSyntax (
113+ unexpectedBeforePoundIfKeyword,
114+ poundKeyword: poundIfKeyword,
115+ condition: condition,
116+ elements: syntax ( & self , parseIfConfigClauseElements ( parseElement, addSemicolonIfNeeded: addSemicolonIfNeeded) ) ,
117+ arena: self . arena
118+ )
119+ )
120+
121+ // Proceed to parse #if continuation clauses (#elseif, #else, check #elif typo, #endif)
122+ var loopProgress = LoopProgressCondition ( )
123+ LOOP: while let ( match, handle) = self . canRecoverTo ( anyIn: IfConfigContinuationClauseStartKeyword . self) , loopProgress. evaluate ( self . currentToken) {
124+ var unexpectedBeforePoundKeyword : RawUnexpectedNodesSyntax ?
125+ var poundKeyword : RawTokenSyntax
126+ let condition : RawExprSyntax ?
127+
128+ switch match {
129+ case . poundElseifKeyword:
130+ ( unexpectedBeforePoundKeyword, poundKeyword) = self . eat ( handle)
131+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
132+ case . poundElseKeyword:
133+ ( unexpectedBeforePoundKeyword, poundKeyword) = self . eat ( handle)
134+ if let ifToken = self . consume ( if: . init( . if, allowAtStartOfLine: false ) ) {
135+ unexpectedBeforePoundKeyword = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePoundKeyword, poundKeyword, ifToken, arena: self . arena)
136+ poundKeyword = self . missingToken ( . poundElseifKeyword)
115137 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
116- case . poundElseKeyword:
117- 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)
120- condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
121- } else {
122- condition = nil
123- }
124- default :
125- preconditionFailure ( " The loop condition should guarantee that we are at one of these tokens " )
138+ } else {
139+ condition = nil
126140 }
127-
128- var elements = [ Element] ( )
129- do {
130- var elementsProgress = LoopProgressCondition ( )
131- while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && elementsProgress. evaluate ( currentToken) {
132- let newItemAtStartOfLine = self . currentToken. isAtStartOfLine
133- guard let element = parseElement ( & self , elements. isEmpty) , !element. isEmpty else {
134- break
135- }
136- if let lastElement = elements. last, let fixedUpLastItem = addSemicolonIfNeeded ( lastElement, newItemAtStartOfLine, & self ) {
137- elements [ elements. count - 1 ] = fixedUpLastItem
138- }
139- elements. append ( element)
141+ case . pound:
142+ if self . atElifTypo ( ) {
143+ ( unexpectedBeforePoundKeyword, poundKeyword) = self . eat ( handle)
144+ guard let elif = self . consume ( if: TokenSpec ( . identifier, allowAtStartOfLine: false ) ) else {
145+ preconditionFailure ( " The current token should be an identifier, guaranteed by the `atElifTypo` check. " )
140146 }
147+ unexpectedBeforePoundKeyword = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePoundKeyword, poundKeyword, elif, arena: self . arena)
148+ poundKeyword = self . missingToken ( . poundElseifKeyword)
149+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
150+ } else {
151+ break LOOP
141152 }
153+ }
142154
143- clauses. append (
144- RawIfConfigClauseSyntax (
145- unexpectedBeforePoundIf,
146- poundKeyword: poundIf,
147- condition: condition,
148- elements: syntax ( & self , elements) ,
149- arena: self . arena
150- )
155+ clauses. append (
156+ RawIfConfigClauseSyntax (
157+ unexpectedBeforePoundKeyword,
158+ poundKeyword: poundKeyword,
159+ condition: condition,
160+ elements: syntax ( & self , parseIfConfigClauseElements ( parseElement, addSemicolonIfNeeded: addSemicolonIfNeeded) ) ,
161+ arena: self . arena
151162 )
152- }
163+ )
153164 }
154165
155166 let ( unexpectedBeforePoundEndIf, poundEndIf) = self . expect ( . poundEndifKeyword)
@@ -160,6 +171,40 @@ extension Parser {
160171 arena: self . arena
161172 )
162173 }
174+
175+ private mutating func atElifTypo( ) -> Bool {
176+ guard self . at ( TokenSpec ( . pound) ) , self . currentToken. trailingTriviaText. isEmpty else {
177+ return false
178+ }
179+ var lookahead = self . lookahead ( )
180+ lookahead. consumeAnyToken ( ) // consume `#`
181+ guard lookahead. at ( TokenSpec ( . identifier, allowAtStartOfLine: false ) ) , lookahead. currentToken. tokenText == " elif " , lookahead. currentToken. leadingTriviaText. isEmpty else {
182+ return false // `#` and `elif` must not be separated by trivia
183+ }
184+ lookahead. consumeAnyToken ( ) // consume `elif`
185+ // We are only at a `elif` typo if it’s followed by an identifier for the condition.
186+ // `#elif` or `#elif(…)` could be macro invocations.
187+ return lookahead. at ( TokenSpec ( . identifier, allowAtStartOfLine: false ) )
188+ }
189+
190+ private mutating func parseIfConfigClauseElements< Element: RawSyntaxNodeProtocol > (
191+ _ parseElement: ( _ parser: inout Parser , _ isFirstElement: Bool ) -> Element ? ,
192+ addSemicolonIfNeeded: ( _ lastElement: Element , _ newItemAtStartOfLine: Bool , _ parser: inout Parser ) -> Element ?
193+ ) -> [ Element ] {
194+ var elements = [ Element] ( )
195+ var elementsProgress = LoopProgressCondition ( )
196+ while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && !self . atElifTypo ( ) && elementsProgress. evaluate ( currentToken) {
197+ let newItemAtStartOfLine = self . currentToken. isAtStartOfLine
198+ guard let element = parseElement ( & self , elements. isEmpty) , !element. isEmpty else {
199+ break
200+ }
201+ if let lastElement = elements. last, let fixedUpLastItem = addSemicolonIfNeeded ( lastElement, newItemAtStartOfLine, & self ) {
202+ elements [ elements. count - 1 ] = fixedUpLastItem
203+ }
204+ elements. append ( element)
205+ }
206+ return elements
207+ }
163208}
164209
165210extension Parser {
0 commit comments