@@ -2571,6 +2571,294 @@ extension Parser {
25712571 }
25722572}
25732573
2574+ // MARK: Switch Statements
2575+
2576+ extension Parser {
2577+ /// Parse a switch statement.
2578+ ///
2579+ /// Grammar
2580+ /// =======
2581+ ///
2582+ /// switch-statement → 'switch' expression '{' switch-cases? '}'
2583+ /// switch-cases → switch-case switch-cases?
2584+ @_spi ( RawSyntax)
2585+ public mutating func parseSwitchStatement( switchHandle: RecoveryConsumptionHandle ) -> RawSwitchStmtSyntax {
2586+ let ( unexpectedBeforeSwitchKeyword, switchKeyword) = self . eat ( switchHandle)
2587+
2588+ let subject = self . parseExpression ( . basic)
2589+ let ( unexpectedBeforeLBrace, lbrace) = self . expect ( . leftBrace)
2590+
2591+ let cases = self . parseSwitchCases ( allowStandaloneStmtRecovery: !lbrace. isMissing)
2592+
2593+ let ( unexpectedBeforeRBrace, rbrace) = self . expectRightBrace ( leftBrace: lbrace, introducer: switchKeyword)
2594+ return RawSwitchStmtSyntax (
2595+ unexpectedBeforeSwitchKeyword,
2596+ switchKeyword: switchKeyword,
2597+ expression: subject,
2598+ unexpectedBeforeLBrace,
2599+ leftBrace: lbrace,
2600+ cases: cases,
2601+ unexpectedBeforeRBrace,
2602+ rightBrace: rbrace,
2603+ arena: self . arena
2604+ )
2605+ }
2606+
2607+ /// Parse a list of switch case clauses.
2608+ ///
2609+ /// Grammar
2610+ /// =======
2611+ ///
2612+ /// switch-cases → switch-case switch-cases?
2613+ ///
2614+ /// If `allowStandaloneStmtRecovery` is `true` and we discover a statement that
2615+ /// isn't covered by a case, we assume that the developer forgot to wrote the
2616+ /// `case` and synthesize it. If `allowStandaloneStmtOrDeclRecovery` is `false`,
2617+ /// this recovery is disabled.
2618+ @_spi ( RawSyntax)
2619+ public mutating func parseSwitchCases( allowStandaloneStmtRecovery: Bool ) -> RawSwitchCaseListSyntax {
2620+ var elements = [ RawSwitchCaseListSyntax . Element] ( )
2621+ var elementsProgress = LoopProgressCondition ( )
2622+ while !self . at ( any: [ . eof, . rightBrace, . poundEndifKeyword, . poundElseifKeyword, . poundElseKeyword] )
2623+ && elementsProgress. evaluate ( currentToken)
2624+ {
2625+ if self . lookahead ( ) . isAtStartOfSwitchCase ( allowRecovery: false ) {
2626+ elements. append ( . switchCase( self . parseSwitchCase ( ) ) )
2627+ } else if self . canRecoverTo ( . poundIfKeyword) != nil {
2628+ // '#if' in 'case' position can enclose zero or more 'case' or 'default'
2629+ // clauses.
2630+ elements. append (
2631+ . ifConfigDecl(
2632+ self . parsePoundIfDirective (
2633+ { $0. parseSwitchCases ( allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) } ,
2634+ syntax: { parser, cases in
2635+ guard cases. count == 1 , let firstCase = cases. first else {
2636+ assert ( cases. isEmpty)
2637+ return . switchCases( RawSwitchCaseListSyntax ( elements: [ ] , arena: parser. arena) )
2638+ }
2639+ return . switchCases( firstCase)
2640+ }
2641+ )
2642+ )
2643+ )
2644+ } else if allowStandaloneStmtRecovery && ( self . atStartOfExpression ( ) || self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) ) {
2645+ // Synthesize a label for the stamenent or declaration that isn't coverd by a case right now.
2646+ let statements = parseSwitchCaseBody ( )
2647+ elements. append (
2648+ . switchCase(
2649+ RawSwitchCaseSyntax (
2650+ unknownAttr: nil ,
2651+ label: . case(
2652+ RawSwitchCaseLabelSyntax (
2653+ caseKeyword: missingToken ( . caseKeyword, text: nil ) ,
2654+ caseItems: RawCaseItemListSyntax (
2655+ elements: [
2656+ RawCaseItemSyntax (
2657+ pattern: RawPatternSyntax (
2658+ RawIdentifierPatternSyntax (
2659+ identifier: missingToken ( . identifier, text: nil ) ,
2660+ arena: self . arena
2661+ )
2662+ ) ,
2663+ whereClause: nil ,
2664+ trailingComma: nil ,
2665+ arena: self . arena
2666+ )
2667+ ] ,
2668+ arena: self . arena
2669+ ) ,
2670+ colon: missingToken ( . colon, text: nil ) ,
2671+ arena: self . arena
2672+ )
2673+ ) ,
2674+ statements: statements,
2675+ arena: self . arena
2676+ )
2677+ )
2678+ )
2679+ } else if self . lookahead ( ) . isAtStartOfSwitchCase ( allowRecovery: true ) {
2680+ elements. append ( . switchCase( self . parseSwitchCase ( ) ) )
2681+ } else {
2682+ break
2683+ }
2684+ }
2685+ return RawSwitchCaseListSyntax ( elements: elements, arena: self . arena)
2686+ }
2687+
2688+ mutating func parseSwitchCaseBody( ) -> RawCodeBlockItemListSyntax {
2689+ var items = [ RawCodeBlockItemSyntax] ( )
2690+ var loopProgress = LoopProgressCondition ( )
2691+ while !self . at ( any: [ . rightBrace, . poundEndifKeyword, . poundElseifKeyword, . poundElseKeyword] )
2692+ && !self . lookahead ( ) . isStartOfConditionalSwitchCases ( ) ,
2693+ let newItem = self . parseCodeBlockItem ( ) ,
2694+ loopProgress. evaluate ( currentToken)
2695+ {
2696+ items. append ( newItem)
2697+ }
2698+ return RawCodeBlockItemListSyntax ( elements: items, arena: self . arena)
2699+ }
2700+
2701+ /// Parse a single switch case clause.
2702+ ///
2703+ /// Grammar
2704+ /// =======
2705+ ///
2706+ /// switch-case → case-label statements
2707+ /// switch-case → default-label statements
2708+ /// switch-case → conditional-switch-case
2709+ @_spi ( RawSyntax)
2710+ public mutating func parseSwitchCase( ) -> RawSwitchCaseSyntax {
2711+ var unknownAttr : RawAttributeSyntax ?
2712+ if let at = self . consume ( if: . atSign) {
2713+ let ( unexpectedBeforeIdent, ident) = self . expectIdentifier ( )
2714+
2715+ var tokenList = [ RawTokenSyntax] ( )
2716+ var loopProgress = LoopProgressCondition ( )
2717+ while let atSign = self . consume ( if: . atSign) , loopProgress. evaluate ( currentToken) {
2718+ tokenList. append ( atSign)
2719+ tokenList. append ( self . expectIdentifierWithoutRecovery ( ) )
2720+ }
2721+
2722+ unknownAttr = RawAttributeSyntax (
2723+ atSignToken: at,
2724+ unexpectedBeforeIdent,
2725+ attributeName: ident,
2726+ leftParen: nil ,
2727+ argument: nil ,
2728+ rightParen: nil ,
2729+ tokenList: tokenList. isEmpty ? nil : RawTokenListSyntax ( elements: tokenList, arena: self . arena) ,
2730+ arena: self . arena
2731+ )
2732+ } else {
2733+ unknownAttr = nil
2734+ }
2735+
2736+ let label : RawSwitchCaseSyntax . Label
2737+ switch self . canRecoverTo ( anyIn: SwitchCaseStart . self) {
2738+ case ( . caseKeyword, let handle) ? :
2739+ label = . case( self . parseSwitchCaseLabel ( handle) )
2740+ case ( . defaultKeyword, let handle) ? :
2741+ label = . default( self . parseSwitchDefaultLabel ( handle) )
2742+ case nil :
2743+ label = . case(
2744+ RawSwitchCaseLabelSyntax (
2745+ caseKeyword: missingToken ( . caseKeyword) ,
2746+ caseItems: RawCaseItemListSyntax (
2747+ elements: [
2748+ RawCaseItemSyntax (
2749+ pattern: RawPatternSyntax ( RawIdentifierPatternSyntax ( identifier: missingToken ( . identifier) , arena: self . arena) ) ,
2750+ whereClause: nil ,
2751+ trailingComma: nil ,
2752+ arena: self . arena
2753+ )
2754+ ] ,
2755+ arena: self . arena
2756+ ) ,
2757+ colon: missingToken ( . colon) ,
2758+ arena: self . arena
2759+ )
2760+ )
2761+ }
2762+
2763+ // Parse the body.
2764+ let statements = parseSwitchCaseBody ( )
2765+
2766+ return RawSwitchCaseSyntax (
2767+ unknownAttr: unknownAttr,
2768+ label: label,
2769+ statements: statements,
2770+ arena: self . arena
2771+ )
2772+ }
2773+
2774+ /// Parse a switch case with a 'case' label.
2775+ ///
2776+ /// Grammar
2777+ /// =======
2778+ ///
2779+ /// case-label → attributes? case case-item-list ':'
2780+ /// case-item-list → pattern where-clause? | pattern where-clause? ',' case-item-list
2781+ @_spi ( RawSyntax)
2782+ public mutating func parseSwitchCaseLabel(
2783+ _ handle: RecoveryConsumptionHandle
2784+ ) -> RawSwitchCaseLabelSyntax {
2785+ let ( unexpectedBeforeCaseKeyword, caseKeyword) = self . eat ( handle)
2786+ var caseItems = [ RawCaseItemSyntax] ( )
2787+ do {
2788+ var keepGoing : RawTokenSyntax ? = nil
2789+ var loopProgress = LoopProgressCondition ( )
2790+ repeat {
2791+ let ( pattern, whereClause) = self . parseGuardedCasePattern ( )
2792+ keepGoing = self . consume ( if: . comma)
2793+ caseItems. append (
2794+ RawCaseItemSyntax (
2795+ pattern: pattern,
2796+ whereClause: whereClause,
2797+ trailingComma: keepGoing,
2798+ arena: self . arena
2799+ )
2800+ )
2801+ } while keepGoing != nil && loopProgress. evaluate ( currentToken)
2802+ }
2803+ let ( unexpectedBeforeColon, colon) = self . expect ( . colon)
2804+ return RawSwitchCaseLabelSyntax (
2805+ unexpectedBeforeCaseKeyword,
2806+ caseKeyword: caseKeyword,
2807+ caseItems: RawCaseItemListSyntax ( elements: caseItems, arena: self . arena) ,
2808+ unexpectedBeforeColon,
2809+ colon: colon,
2810+ arena: self . arena
2811+ )
2812+ }
2813+
2814+ /// Parse a switch case with a 'default' label.
2815+ ///
2816+ /// Grammar
2817+ /// =======
2818+ ///
2819+ /// default-label → attributes? 'default' ':'
2820+ @_spi ( RawSyntax)
2821+ public mutating func parseSwitchDefaultLabel(
2822+ _ handle: RecoveryConsumptionHandle
2823+ ) -> RawSwitchDefaultLabelSyntax {
2824+ let ( unexpectedBeforeDefaultKeyword, defaultKeyword) = self . eat ( handle)
2825+ let ( unexpectedBeforeColon, colon) = self . expect ( . colon)
2826+ return RawSwitchDefaultLabelSyntax (
2827+ unexpectedBeforeDefaultKeyword,
2828+ defaultKeyword: defaultKeyword,
2829+ unexpectedBeforeColon,
2830+ colon: colon,
2831+ arena: self . arena
2832+ )
2833+ }
2834+
2835+ /// Parse a pattern-matching clause for a case statement,
2836+ /// including the guard expression.
2837+ ///
2838+ /// Grammar
2839+ /// =======
2840+ ///
2841+ /// case-item → pattern where-clause?
2842+ mutating func parseGuardedCasePattern( ) -> ( RawPatternSyntax , RawWhereClauseSyntax ? ) {
2843+ let pattern = self . parseMatchingPattern ( context: . matching)
2844+
2845+ // Parse the optional 'where' guard, with this particular pattern's bound
2846+ // vars in scope.
2847+ let whereClause : RawWhereClauseSyntax ?
2848+ if let whereKeyword = self . consume ( if: . whereKeyword) {
2849+ let guardExpr = self . parseExpression ( . trailingClosure)
2850+ whereClause = RawWhereClauseSyntax (
2851+ whereKeyword: whereKeyword,
2852+ guardResult: guardExpr,
2853+ arena: self . arena
2854+ )
2855+ } else {
2856+ whereClause = nil
2857+ }
2858+ return ( pattern, whereClause)
2859+ }
2860+ }
2861+
25742862// MARK: Lookahead
25752863
25762864extension Parser . Lookahead {
0 commit comments