13
13
@_spi ( RawSyntax) import SwiftSyntax
14
14
15
15
extension Parser {
16
- private enum IfConfigContinuationClauseStartKeyword : TokenSpecSet {
16
+ private enum PoundIfDirectiveKeywords : TokenSpecSet {
17
+ case poundIfKeyword
17
18
case poundElseifKeyword
18
19
case poundElseKeyword
20
+ case pound
19
21
20
22
var spec : TokenSpec {
21
23
switch self {
24
+ case . poundIfKeyword: return . poundIfKeyword
22
25
case . poundElseifKeyword: return . poundElseifKeyword
23
26
case . poundElseKeyword: return . poundElseKeyword
27
+ case . pound: return TokenSpec ( . pound, recoveryPrecedence: . openingPoundIf)
24
28
}
25
29
}
26
30
27
31
init ? ( lexeme: Lexer . Lexeme ) {
28
32
switch PrepareForKeywordMatch ( lexeme) {
33
+ case TokenSpec ( . poundIfKeyword) : self = . poundIfKeyword
29
34
case TokenSpec ( . poundElseifKeyword) : self = . poundElseifKeyword
30
35
case TokenSpec ( . poundElseKeyword) : self = . poundElseKeyword
36
+ case TokenSpec ( . pound) : self = . pound
31
37
default : return nil
32
38
}
33
39
}
@@ -103,32 +109,62 @@ extension Parser {
103
109
do {
104
110
var firstIteration = true
105
111
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
112
115
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)
115
140
condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
116
141
case . poundElseKeyword:
142
+ ( unexpectedBeforePound, pound) = self . eat ( handle)
117
143
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)
120
146
condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
121
147
} else {
122
148
condition = nil
123
149
}
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
+ }
126
162
}
127
163
128
164
var elements = [ Element] ( )
129
165
do {
130
166
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) {
132
168
let newItemAtStartOfLine = self . currentToken. isAtStartOfLine
133
169
guard let element = parseElement ( & self , elements. isEmpty) , !element. isEmpty else {
134
170
break
@@ -142,8 +178,8 @@ extension Parser {
142
178
143
179
clauses. append (
144
180
RawIfConfigClauseSyntax (
145
- unexpectedBeforePoundIf ,
146
- poundKeyword: poundIf ,
181
+ unexpectedBeforePound ,
182
+ poundKeyword: pound ,
147
183
condition: condition,
148
184
elements: syntax ( & self , elements) ,
149
185
arena: self . arena
0 commit comments