@@ -2571,6 +2571,294 @@ extension Parser {
2571
2571
}
2572
2572
}
2573
2573
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
+
2574
2862
// MARK: Lookahead
2575
2863
2576
2864
extension Parser . Lookahead {
0 commit comments