@@ -2621,6 +2621,288 @@ extension Parser {
26212621 }
26222622}
26232623
2624+ // MARK: Switch Expressions
2625+
2626+ extension Parser {
2627+ /// Parse a switch expression.
2628+ ///
2629+ /// Grammar
2630+ /// =======
2631+ ///
2632+ /// switch-expression → 'switch' expression '{' switch-cases? '}'
2633+ /// switch-cases → switch-case switch-cases?
2634+ @_spi ( RawSyntax)
2635+ public mutating func parseSwitchExpression(
2636+ switchHandle: RecoveryConsumptionHandle
2637+ ) -> RawSwitchExprSyntax {
2638+ let ( unexpectedBeforeSwitchKeyword, switchKeyword) = self . eat ( switchHandle)
2639+
2640+ let subject = self . parseExpression ( . basic)
2641+ let ( unexpectedBeforeLBrace, lbrace) = self . expect ( . leftBrace)
2642+
2643+ let cases = self . parseSwitchCases ( allowStandaloneStmtRecovery: !lbrace. isMissing)
2644+
2645+ let ( unexpectedBeforeRBrace, rbrace) = self . expectRightBrace ( leftBrace: lbrace, introducer: switchKeyword)
2646+ return RawSwitchExprSyntax (
2647+ unexpectedBeforeSwitchKeyword,
2648+ switchKeyword: switchKeyword,
2649+ expression: subject,
2650+ unexpectedBeforeLBrace,
2651+ leftBrace: lbrace,
2652+ cases: cases,
2653+ unexpectedBeforeRBrace,
2654+ rightBrace: rbrace,
2655+ arena: self . arena
2656+ )
2657+ }
2658+
2659+ /// Parse a list of switch case clauses.
2660+ ///
2661+ /// Grammar
2662+ /// =======
2663+ ///
2664+ /// switch-cases → switch-case switch-cases?
2665+ ///
2666+ /// If `allowStandaloneStmtRecovery` is `true` and we discover a statement that
2667+ /// isn't covered by a case, we assume that the developer forgot to wrote the
2668+ /// `case` and synthesize it. If `allowStandaloneStmtOrDeclRecovery` is `false`,
2669+ /// this recovery is disabled.
2670+ @_spi ( RawSyntax)
2671+ public mutating func parseSwitchCases( allowStandaloneStmtRecovery: Bool ) -> RawSwitchCaseListSyntax {
2672+ var elements = [ RawSwitchCaseListSyntax . Element] ( )
2673+ var elementsProgress = LoopProgressCondition ( )
2674+ while !self . at ( any: [ . eof, . rightBrace, . poundEndifKeyword, . poundElseifKeyword, . poundElseKeyword] )
2675+ && elementsProgress. evaluate ( currentToken)
2676+ {
2677+ if self . lookahead ( ) . isAtStartOfSwitchCase ( allowRecovery: false ) {
2678+ elements. append ( . switchCase( self . parseSwitchCase ( ) ) )
2679+ } else if self . canRecoverTo ( . poundIfKeyword) != nil {
2680+ // '#if' in 'case' position can enclose zero or more 'case' or 'default'
2681+ // clauses.
2682+ elements. append (
2683+ . ifConfigDecl(
2684+ self . parsePoundIfDirective (
2685+ { $0. parseSwitchCases ( allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) } ,
2686+ syntax: { parser, cases in
2687+ guard cases. count == 1 , let firstCase = cases. first else {
2688+ assert ( cases. isEmpty)
2689+ return . switchCases( RawSwitchCaseListSyntax ( elements: [ ] , arena: parser. arena) )
2690+ }
2691+ return . switchCases( firstCase)
2692+ }
2693+ )
2694+ )
2695+ )
2696+ } else if allowStandaloneStmtRecovery && ( self . atStartOfExpression ( ) || self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) ) {
2697+ // Synthesize a label for the stamenent or declaration that isn't coverd by a case right now.
2698+ let statements = parseSwitchCaseBody ( )
2699+ elements. append (
2700+ . switchCase(
2701+ RawSwitchCaseSyntax (
2702+ unknownAttr: nil ,
2703+ label: . case(
2704+ RawSwitchCaseLabelSyntax (
2705+ caseKeyword: missingToken ( . keyword( . case) , text: nil ) ,
2706+ caseItems: RawCaseItemListSyntax (
2707+ elements: [
2708+ RawCaseItemSyntax (
2709+ pattern: RawPatternSyntax (
2710+ RawIdentifierPatternSyntax (
2711+ identifier: missingToken ( . identifier, text: nil ) ,
2712+ arena: self . arena
2713+ )
2714+ ) ,
2715+ whereClause: nil ,
2716+ trailingComma: nil ,
2717+ arena: self . arena
2718+ )
2719+ ] ,
2720+ arena: self . arena
2721+ ) ,
2722+ colon: missingToken ( . colon, text: nil ) ,
2723+ arena: self . arena
2724+ )
2725+ ) ,
2726+ statements: statements,
2727+ arena: self . arena
2728+ )
2729+ )
2730+ )
2731+ } else if self . lookahead ( ) . isAtStartOfSwitchCase ( allowRecovery: true ) {
2732+ elements. append ( . switchCase( self . parseSwitchCase ( ) ) )
2733+ } else {
2734+ break
2735+ }
2736+ }
2737+ return RawSwitchCaseListSyntax ( elements: elements, arena: self . arena)
2738+ }
2739+
2740+ mutating func parseSwitchCaseBody( ) -> RawCodeBlockItemListSyntax {
2741+ var items = [ RawCodeBlockItemSyntax] ( )
2742+ var loopProgress = LoopProgressCondition ( )
2743+ while !self . at ( any: [ . rightBrace, . poundEndifKeyword, . poundElseifKeyword, . poundElseKeyword] )
2744+ && !self . lookahead ( ) . isStartOfConditionalSwitchCases ( ) ,
2745+ let newItem = self . parseCodeBlockItem ( ) ,
2746+ loopProgress. evaluate ( currentToken)
2747+ {
2748+ items. append ( newItem)
2749+ }
2750+ return RawCodeBlockItemListSyntax ( elements: items, arena: self . arena)
2751+ }
2752+
2753+ /// Parse a single switch case clause.
2754+ ///
2755+ /// Grammar
2756+ /// =======
2757+ ///
2758+ /// switch-case → case-label statements
2759+ /// switch-case → default-label statements
2760+ /// switch-case → conditional-switch-case
2761+ @_spi ( RawSyntax)
2762+ public mutating func parseSwitchCase( ) -> RawSwitchCaseSyntax {
2763+ var unknownAttr : RawAttributeSyntax ?
2764+ if let at = self . consume ( if: . atSign) {
2765+ let ( unexpectedBeforeIdent, ident) = self . expectIdentifier ( )
2766+
2767+ unknownAttr = RawAttributeSyntax (
2768+ atSignToken: at,
2769+ unexpectedBeforeIdent,
2770+ attributeName: RawTypeSyntax ( RawSimpleTypeIdentifierSyntax ( name: ident, genericArgumentClause: nil , arena: self . arena) ) ,
2771+ leftParen: nil ,
2772+ argument: nil ,
2773+ rightParen: nil ,
2774+ arena: self . arena
2775+ )
2776+ } else {
2777+ unknownAttr = nil
2778+ }
2779+
2780+ let label : RawSwitchCaseSyntax . Label
2781+ switch self . canRecoverTo ( anyIn: SwitchCaseStart . self) {
2782+ case ( . caseKeyword, let handle) ? :
2783+ label = . case( self . parseSwitchCaseLabel ( handle) )
2784+ case ( . defaultKeyword, let handle) ? :
2785+ label = . default( self . parseSwitchDefaultLabel ( handle) )
2786+ case nil :
2787+ label = . case(
2788+ RawSwitchCaseLabelSyntax (
2789+ caseKeyword: missingToken ( . keyword( . case) ) ,
2790+ caseItems: RawCaseItemListSyntax (
2791+ elements: [
2792+ RawCaseItemSyntax (
2793+ pattern: RawPatternSyntax ( RawIdentifierPatternSyntax ( identifier: missingToken ( . identifier) , arena: self . arena) ) ,
2794+ whereClause: nil ,
2795+ trailingComma: nil ,
2796+ arena: self . arena
2797+ )
2798+ ] ,
2799+ arena: self . arena
2800+ ) ,
2801+ colon: missingToken ( . colon) ,
2802+ arena: self . arena
2803+ )
2804+ )
2805+ }
2806+
2807+ // Parse the body.
2808+ let statements = parseSwitchCaseBody ( )
2809+
2810+ return RawSwitchCaseSyntax (
2811+ unknownAttr: unknownAttr,
2812+ label: label,
2813+ statements: statements,
2814+ arena: self . arena
2815+ )
2816+ }
2817+
2818+ /// Parse a switch case with a 'case' label.
2819+ ///
2820+ /// Grammar
2821+ /// =======
2822+ ///
2823+ /// case-label → attributes? case case-item-list ':'
2824+ /// case-item-list → pattern where-clause? | pattern where-clause? ',' case-item-list
2825+ @_spi ( RawSyntax)
2826+ public mutating func parseSwitchCaseLabel(
2827+ _ handle: RecoveryConsumptionHandle
2828+ ) -> RawSwitchCaseLabelSyntax {
2829+ let ( unexpectedBeforeCaseKeyword, caseKeyword) = self . eat ( handle)
2830+ var caseItems = [ RawCaseItemSyntax] ( )
2831+ do {
2832+ var keepGoing : RawTokenSyntax ? = nil
2833+ var loopProgress = LoopProgressCondition ( )
2834+ repeat {
2835+ let ( pattern, whereClause) = self . parseGuardedCasePattern ( )
2836+ keepGoing = self . consume ( if: . comma)
2837+ caseItems. append (
2838+ RawCaseItemSyntax (
2839+ pattern: pattern,
2840+ whereClause: whereClause,
2841+ trailingComma: keepGoing,
2842+ arena: self . arena
2843+ )
2844+ )
2845+ } while keepGoing != nil && loopProgress. evaluate ( currentToken)
2846+ }
2847+ let ( unexpectedBeforeColon, colon) = self . expect ( . colon)
2848+ return RawSwitchCaseLabelSyntax (
2849+ unexpectedBeforeCaseKeyword,
2850+ caseKeyword: caseKeyword,
2851+ caseItems: RawCaseItemListSyntax ( elements: caseItems, arena: self . arena) ,
2852+ unexpectedBeforeColon,
2853+ colon: colon,
2854+ arena: self . arena
2855+ )
2856+ }
2857+
2858+ /// Parse a switch case with a 'default' label.
2859+ ///
2860+ /// Grammar
2861+ /// =======
2862+ ///
2863+ /// default-label → attributes? 'default' ':'
2864+ @_spi ( RawSyntax)
2865+ public mutating func parseSwitchDefaultLabel(
2866+ _ handle: RecoveryConsumptionHandle
2867+ ) -> RawSwitchDefaultLabelSyntax {
2868+ let ( unexpectedBeforeDefaultKeyword, defaultKeyword) = self . eat ( handle)
2869+ let ( unexpectedBeforeColon, colon) = self . expect ( . colon)
2870+ return RawSwitchDefaultLabelSyntax (
2871+ unexpectedBeforeDefaultKeyword,
2872+ defaultKeyword: defaultKeyword,
2873+ unexpectedBeforeColon,
2874+ colon: colon,
2875+ arena: self . arena
2876+ )
2877+ }
2878+
2879+ /// Parse a pattern-matching clause for a case statement,
2880+ /// including the guard expression.
2881+ ///
2882+ /// Grammar
2883+ /// =======
2884+ ///
2885+ /// case-item → pattern where-clause?
2886+ mutating func parseGuardedCasePattern( ) -> ( RawPatternSyntax , RawWhereClauseSyntax ? ) {
2887+ let pattern = self . parseMatchingPattern ( context: . matching)
2888+
2889+ // Parse the optional 'where' guard, with this particular pattern's bound
2890+ // vars in scope.
2891+ let whereClause : RawWhereClauseSyntax ?
2892+ if let whereKeyword = self . consume ( if: . keyword( . where) ) {
2893+ let guardExpr = self . parseExpression ( . trailingClosure)
2894+ whereClause = RawWhereClauseSyntax (
2895+ whereKeyword: whereKeyword,
2896+ guardResult: guardExpr,
2897+ arena: self . arena
2898+ )
2899+ } else {
2900+ whereClause = nil
2901+ }
2902+ return ( pattern, whereClause)
2903+ }
2904+ }
2905+
26242906// MARK: Lookahead
26252907
26262908extension Parser . Lookahead {
0 commit comments