@@ -16,18 +16,21 @@ extension Parser {
16
16
private enum IfConfigContinuationClauseStartKeyword : TokenSpecSet {
17
17
case poundElseifKeyword
18
18
case poundElseKeyword
19
+ case pound
19
20
20
21
var spec : TokenSpec {
21
22
switch self {
22
23
case . poundElseifKeyword: return . poundElseifKeyword
23
24
case . poundElseKeyword: return . poundElseKeyword
25
+ case . pound: return TokenSpec ( . pound, recoveryPrecedence: . openingPoundIf)
24
26
}
25
27
}
26
28
27
29
init ? ( lexeme: Lexer . Lexeme ) {
28
30
switch PrepareForKeywordMatch ( lexeme) {
29
31
case TokenSpec ( . poundElseifKeyword) : self = . poundElseifKeyword
30
32
case TokenSpec ( . poundElseKeyword) : self = . poundElseKeyword
33
+ case TokenSpec ( . pound) : self = . pound
31
34
default : return nil
32
35
}
33
36
}
@@ -100,56 +103,64 @@ extension Parser {
100
103
}
101
104
102
105
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)
115
137
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
126
140
}
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. " )
140
146
}
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
141
152
}
153
+ }
142
154
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
151
162
)
152
- }
163
+ )
153
164
}
154
165
155
166
let ( unexpectedBeforePoundEndIf, poundEndIf) = self . expect ( . poundEndifKeyword)
@@ -160,6 +171,40 @@ extension Parser {
160
171
arena: self . arena
161
172
)
162
173
}
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
+ }
163
208
}
164
209
165
210
extension Parser {
0 commit comments