Skip to content

Commit c8f2371

Browse files
committed
NFC: Move switch parsing to Expressions.swift
1 parent 102f1ac commit c8f2371

File tree

2 files changed

+288
-288
lines changed

2 files changed

+288
-288
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

25762864
extension Parser.Lookahead {

0 commit comments

Comments
 (0)