@@ -2146,6 +2146,286 @@ extension Parser {
2146
2146
}
2147
2147
}
2148
2148
2149
+ // MARK: Switch Statements
2150
+
2151
+ extension Parser {
2152
+ /// Parse a switch statement.
2153
+ ///
2154
+ /// Grammar
2155
+ /// =======
2156
+ ///
2157
+ /// switch-statement → 'switch' expression '{' switch-cases? '}'
2158
+ /// switch-cases → switch-case switch-cases?
2159
+ @_spi ( RawSyntax)
2160
+ public mutating func parseSwitchStatement( switchHandle: RecoveryConsumptionHandle ) -> RawSwitchStmtSyntax {
2161
+ let ( unexpectedBeforeSwitchKeyword, switchKeyword) = self . eat ( switchHandle)
2162
+
2163
+ let subject = self . parseExpression ( . basic)
2164
+ let ( unexpectedBeforeLBrace, lbrace) = self . expect ( . leftBrace)
2165
+
2166
+ let cases = self . parseSwitchCases ( allowStandaloneStmtRecovery: !lbrace. isMissing)
2167
+
2168
+ let ( unexpectedBeforeRBrace, rbrace) = self . expectRightBrace ( leftBrace: lbrace, introducer: switchKeyword)
2169
+ return RawSwitchStmtSyntax (
2170
+ unexpectedBeforeSwitchKeyword,
2171
+ switchKeyword: switchKeyword,
2172
+ expression: subject,
2173
+ unexpectedBeforeLBrace,
2174
+ leftBrace: lbrace,
2175
+ cases: cases,
2176
+ unexpectedBeforeRBrace,
2177
+ rightBrace: rbrace,
2178
+ arena: self . arena
2179
+ )
2180
+ }
2181
+
2182
+ /// Parse a list of switch case clauses.
2183
+ ///
2184
+ /// Grammar
2185
+ /// =======
2186
+ ///
2187
+ /// switch-cases → switch-case switch-cases?
2188
+ ///
2189
+ /// If `allowStandaloneStmtRecovery` is `true` and we discover a statement that
2190
+ /// isn't covered by a case, we assume that the developer forgot to wrote the
2191
+ /// `case` and synthesize it. If `allowStandaloneStmtOrDeclRecovery` is `false`,
2192
+ /// this recovery is disabled.
2193
+ @_spi ( RawSyntax)
2194
+ public mutating func parseSwitchCases( allowStandaloneStmtRecovery: Bool ) -> RawSwitchCaseListSyntax {
2195
+ var elements = [ RawSwitchCaseListSyntax . Element] ( )
2196
+ var elementsProgress = LoopProgressCondition ( )
2197
+ while !self . at ( any: [ . eof, . rightBrace, . poundEndifKeyword, . poundElseifKeyword, . poundElseKeyword] )
2198
+ && elementsProgress. evaluate ( currentToken)
2199
+ {
2200
+ if self . withLookahead ( { $0. isAtStartOfSwitchCase ( allowRecovery: false ) } ) {
2201
+ elements. append ( . switchCase( self . parseSwitchCase ( ) ) )
2202
+ } else if self . canRecoverTo ( . poundIfKeyword) != nil {
2203
+ // '#if' in 'case' position can enclose zero or more 'case' or 'default'
2204
+ // clauses.
2205
+ elements. append (
2206
+ . ifConfigDecl(
2207
+ self . parsePoundIfDirective (
2208
+ { $0. parseSwitchCases ( allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) } ,
2209
+ syntax: { parser, cases in
2210
+ guard cases. count == 1 , let firstCase = cases. first else {
2211
+ assert ( cases. isEmpty)
2212
+ return . switchCases( RawSwitchCaseListSyntax ( elements: [ ] , arena: parser. arena) )
2213
+ }
2214
+ return . switchCases( firstCase)
2215
+ }
2216
+ )
2217
+ )
2218
+ )
2219
+ } else if allowStandaloneStmtRecovery && ( self . atStartOfExpression ( ) || self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) ) {
2220
+ // Synthesize a label for the stamenent or declaration that isn't coverd by a case right now.
2221
+ let statements = parseSwitchCaseBody ( )
2222
+ elements. append (
2223
+ . switchCase(
2224
+ RawSwitchCaseSyntax (
2225
+ unknownAttr: nil ,
2226
+ label: . case(
2227
+ RawSwitchCaseLabelSyntax (
2228
+ caseKeyword: missingToken ( . keyword( . case) , text: nil ) ,
2229
+ caseItems: RawCaseItemListSyntax (
2230
+ elements: [
2231
+ RawCaseItemSyntax (
2232
+ pattern: RawPatternSyntax (
2233
+ RawIdentifierPatternSyntax (
2234
+ identifier: missingToken ( . identifier, text: nil ) ,
2235
+ arena: self . arena
2236
+ )
2237
+ ) ,
2238
+ whereClause: nil ,
2239
+ trailingComma: nil ,
2240
+ arena: self . arena
2241
+ )
2242
+ ] ,
2243
+ arena: self . arena
2244
+ ) ,
2245
+ colon: missingToken ( . colon, text: nil ) ,
2246
+ arena: self . arena
2247
+ )
2248
+ ) ,
2249
+ statements: statements,
2250
+ arena: self . arena
2251
+ )
2252
+ )
2253
+ )
2254
+ } else if self . withLookahead ( { $0. isAtStartOfSwitchCase ( allowRecovery: true ) } ) {
2255
+ elements. append ( . switchCase( self . parseSwitchCase ( ) ) )
2256
+ } else {
2257
+ break
2258
+ }
2259
+ }
2260
+ return RawSwitchCaseListSyntax ( elements: elements, arena: self . arena)
2261
+ }
2262
+
2263
+ mutating func parseSwitchCaseBody( ) -> RawCodeBlockItemListSyntax {
2264
+ var items = [ RawCodeBlockItemSyntax] ( )
2265
+ var loopProgress = LoopProgressCondition ( )
2266
+ while !self . at ( any: [ . rightBrace, . poundEndifKeyword, . poundElseifKeyword, . poundElseKeyword] )
2267
+ && !self . withLookahead ( { $0. isStartOfConditionalSwitchCases ( ) } ) ,
2268
+ let newItem = self . parseCodeBlockItem ( ) ,
2269
+ loopProgress. evaluate ( currentToken)
2270
+ {
2271
+ items. append ( newItem)
2272
+ }
2273
+ return RawCodeBlockItemListSyntax ( elements: items, arena: self . arena)
2274
+ }
2275
+
2276
+ /// Parse a single switch case clause.
2277
+ ///
2278
+ /// Grammar
2279
+ /// =======
2280
+ ///
2281
+ /// switch-case → case-label statements
2282
+ /// switch-case → default-label statements
2283
+ /// switch-case → conditional-switch-case
2284
+ @_spi ( RawSyntax)
2285
+ public mutating func parseSwitchCase( ) -> RawSwitchCaseSyntax {
2286
+ var unknownAttr : RawAttributeSyntax ?
2287
+ if let at = self . consume ( if: . atSign) {
2288
+ let ( unexpectedBeforeIdent, ident) = self . expectIdentifier ( )
2289
+
2290
+ unknownAttr = RawAttributeSyntax (
2291
+ atSignToken: at,
2292
+ unexpectedBeforeIdent,
2293
+ attributeName: RawTypeSyntax ( RawSimpleTypeIdentifierSyntax ( name: ident, genericArgumentClause: nil , arena: self . arena) ) ,
2294
+ leftParen: nil ,
2295
+ argument: nil ,
2296
+ rightParen: nil ,
2297
+ arena: self . arena
2298
+ )
2299
+ } else {
2300
+ unknownAttr = nil
2301
+ }
2302
+
2303
+ let label : RawSwitchCaseSyntax . Label
2304
+ switch self . canRecoverTo ( anyIn: SwitchCaseStart . self) {
2305
+ case ( . caseKeyword, let handle) ? :
2306
+ label = . case( self . parseSwitchCaseLabel ( handle) )
2307
+ case ( . defaultKeyword, let handle) ? :
2308
+ label = . default( self . parseSwitchDefaultLabel ( handle) )
2309
+ case nil :
2310
+ label = . case(
2311
+ RawSwitchCaseLabelSyntax (
2312
+ caseKeyword: missingToken ( . keyword( . case) ) ,
2313
+ caseItems: RawCaseItemListSyntax (
2314
+ elements: [
2315
+ RawCaseItemSyntax (
2316
+ pattern: RawPatternSyntax ( RawIdentifierPatternSyntax ( identifier: missingToken ( . identifier) , arena: self . arena) ) ,
2317
+ whereClause: nil ,
2318
+ trailingComma: nil ,
2319
+ arena: self . arena
2320
+ )
2321
+ ] ,
2322
+ arena: self . arena
2323
+ ) ,
2324
+ colon: missingToken ( . colon) ,
2325
+ arena: self . arena
2326
+ )
2327
+ )
2328
+ }
2329
+
2330
+ // Parse the body.
2331
+ let statements = parseSwitchCaseBody ( )
2332
+
2333
+ return RawSwitchCaseSyntax (
2334
+ unknownAttr: unknownAttr,
2335
+ label: label,
2336
+ statements: statements,
2337
+ arena: self . arena
2338
+ )
2339
+ }
2340
+
2341
+ /// Parse a switch case with a 'case' label.
2342
+ ///
2343
+ /// Grammar
2344
+ /// =======
2345
+ ///
2346
+ /// case-label → attributes? case case-item-list ':'
2347
+ /// case-item-list → pattern where-clause? | pattern where-clause? ',' case-item-list
2348
+ @_spi ( RawSyntax)
2349
+ public mutating func parseSwitchCaseLabel(
2350
+ _ handle: RecoveryConsumptionHandle
2351
+ ) -> RawSwitchCaseLabelSyntax {
2352
+ let ( unexpectedBeforeCaseKeyword, caseKeyword) = self . eat ( handle)
2353
+ var caseItems = [ RawCaseItemSyntax] ( )
2354
+ do {
2355
+ var keepGoing : RawTokenSyntax ? = nil
2356
+ var loopProgress = LoopProgressCondition ( )
2357
+ repeat {
2358
+ let ( pattern, whereClause) = self . parseGuardedCasePattern ( )
2359
+ keepGoing = self . consume ( if: . comma)
2360
+ caseItems. append (
2361
+ RawCaseItemSyntax (
2362
+ pattern: pattern,
2363
+ whereClause: whereClause,
2364
+ trailingComma: keepGoing,
2365
+ arena: self . arena
2366
+ )
2367
+ )
2368
+ } while keepGoing != nil && loopProgress. evaluate ( currentToken)
2369
+ }
2370
+ let ( unexpectedBeforeColon, colon) = self . expect ( . colon)
2371
+ return RawSwitchCaseLabelSyntax (
2372
+ unexpectedBeforeCaseKeyword,
2373
+ caseKeyword: caseKeyword,
2374
+ caseItems: RawCaseItemListSyntax ( elements: caseItems, arena: self . arena) ,
2375
+ unexpectedBeforeColon,
2376
+ colon: colon,
2377
+ arena: self . arena
2378
+ )
2379
+ }
2380
+
2381
+ /// Parse a switch case with a 'default' label.
2382
+ ///
2383
+ /// Grammar
2384
+ /// =======
2385
+ ///
2386
+ /// default-label → attributes? 'default' ':'
2387
+ @_spi ( RawSyntax)
2388
+ public mutating func parseSwitchDefaultLabel(
2389
+ _ handle: RecoveryConsumptionHandle
2390
+ ) -> RawSwitchDefaultLabelSyntax {
2391
+ let ( unexpectedBeforeDefaultKeyword, defaultKeyword) = self . eat ( handle)
2392
+ let ( unexpectedBeforeColon, colon) = self . expect ( . colon)
2393
+ return RawSwitchDefaultLabelSyntax (
2394
+ unexpectedBeforeDefaultKeyword,
2395
+ defaultKeyword: defaultKeyword,
2396
+ unexpectedBeforeColon,
2397
+ colon: colon,
2398
+ arena: self . arena
2399
+ )
2400
+ }
2401
+
2402
+ /// Parse a pattern-matching clause for a case statement,
2403
+ /// including the guard expression.
2404
+ ///
2405
+ /// Grammar
2406
+ /// =======
2407
+ ///
2408
+ /// case-item → pattern where-clause?
2409
+ mutating func parseGuardedCasePattern( ) -> ( RawPatternSyntax , RawWhereClauseSyntax ? ) {
2410
+ let pattern = self . parseMatchingPattern ( context: . matching)
2411
+
2412
+ // Parse the optional 'where' guard, with this particular pattern's bound
2413
+ // vars in scope.
2414
+ let whereClause : RawWhereClauseSyntax ?
2415
+ if let whereKeyword = self . consume ( if: . keyword( . where) ) {
2416
+ let guardExpr = self . parseExpression ( . trailingClosure)
2417
+ whereClause = RawWhereClauseSyntax (
2418
+ whereKeyword: whereKeyword,
2419
+ guardResult: guardExpr,
2420
+ arena: self . arena
2421
+ )
2422
+ } else {
2423
+ whereClause = nil
2424
+ }
2425
+ return ( pattern, whereClause)
2426
+ }
2427
+ }
2428
+
2149
2429
// MARK: Lookahead
2150
2430
2151
2431
extension Parser . Lookahead {
0 commit comments