Skip to content

Commit bc66595

Browse files
committed
NFC: Move switch parsing to Expressions.swift
1 parent be452ed commit bc66595

File tree

2 files changed

+280
-280
lines changed

2 files changed

+280
-280
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2146,6 +2146,286 @@ extension Parser {
21462146
}
21472147
}
21482148

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+
21492429
// MARK: Lookahead
21502430

21512431
extension Parser.Lookahead {

0 commit comments

Comments
 (0)