Skip to content

Commit cb3425e

Browse files
committed
Refactor canRecoverTo to return a RecoveryConsumptionHandle
1 parent 92e751c commit cb3425e

File tree

3 files changed

+40
-23
lines changed

3 files changed

+40
-23
lines changed

Sources/SwiftParser/Lookahead.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ extension Parser.Lookahead {
8383
return
8484
}
8585
var lookahead = self.lookahead()
86-
if lookahead.canRecoverTo([kind]) {
86+
if lookahead.canRecoverTo([kind]) != nil {
8787
for _ in 0..<lookahead.tokensConsumed {
8888
self.consumeAnyToken()
8989
}

Sources/SwiftParser/Parser.swift

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,12 @@ extension Parser {
209209
/// Checks if it can reach a token of the given `kind` by skipping unexpected
210210
/// tokens that have lower ``TokenPrecedence`` than expected token.
211211
@_spi(RawSyntax)
212-
public func canRecoverTo(_ kind: RawTokenKind) -> Bool {
212+
public func canRecoverTo(_ kind: RawTokenKind) -> RecoveryConsumptionHandle? {
213213
if self.at(kind) {
214-
return true
214+
return RecoveryConsumptionHandle(
215+
unexpectedTokens: 0,
216+
tokenConsumptionHandle: TokenConsumptionHandle(tokenKind: kind)
217+
)
215218
}
216219
var lookahead = self.lookahead()
217220
return lookahead.canRecoverTo([kind])
@@ -224,9 +227,15 @@ extension Parser {
224227
public func canRecoverToContextualKeyword(
225228
_ name: SyntaxText,
226229
precedence: TokenPrecedence = TokenPrecedence(.contextualKeyword)
227-
) -> Bool {
230+
) -> RecoveryConsumptionHandle? {
228231
if self.atContextualKeyword(name) {
229-
return true
232+
return RecoveryConsumptionHandle(
233+
unexpectedTokens: 0,
234+
tokenConsumptionHandle: TokenConsumptionHandle(
235+
tokenKind: self.currentToken.tokenKind,
236+
remappedKind: .contextualKeyword
237+
)
238+
)
230239
}
231240
var lookahead = self.lookahead()
232241
return lookahead.canRecoverTo([], contextualKeywords: [name])
@@ -238,9 +247,12 @@ extension Parser {
238247
@_spi(RawSyntax)
239248
public func canRecoverTo(
240249
any kinds: [RawTokenKind]
241-
) -> Bool {
250+
) -> RecoveryConsumptionHandle? {
242251
if self.at(any: kinds) {
243-
return true
252+
return RecoveryConsumptionHandle(
253+
unexpectedTokens: 0,
254+
tokenConsumptionHandle: TokenConsumptionHandle(tokenKind: self.currentToken.tokenKind)
255+
)
244256
}
245257
var lookahead = self.lookahead()
246258
return lookahead.canRecoverTo(kinds)
@@ -279,20 +291,16 @@ extension Parser {
279291
/// Implements the paradigm shared across all `expect` methods.
280292
private mutating func expectImpl(
281293
consume: (inout Parser) -> RawTokenSyntax?,
282-
canRecoverTo: (inout Lookahead) -> Bool,
294+
canRecoverTo: (inout Lookahead) -> RecoveryConsumptionHandle?,
283295
makeMissing: (inout Parser) -> RawTokenSyntax
284296
) -> (unexpected: RawUnexpectedNodesSyntax?, token: RawTokenSyntax) {
285297
if let tok = consume(&self) {
286298
return (nil, tok)
287299
}
288300
var lookahead = self.lookahead()
289-
if canRecoverTo(&lookahead) {
290-
var unexpectedTokens = [RawSyntax]()
291-
for _ in 0..<lookahead.tokensConsumed {
292-
unexpectedTokens.append(RawSyntax(self.consumeAnyToken()))
293-
}
294-
let token = consume(&self)!
295-
return (RawUnexpectedNodesSyntax(elements: unexpectedTokens, arena: self.arena), token)
301+
if let handle = canRecoverTo(&lookahead) {
302+
let (unexpectedTokens, token) = self.eat(handle)
303+
return (unexpectedTokens, token)
296304
}
297305
return (nil, makeMissing(&self))
298306
}

Sources/SwiftParser/Recovery.swift

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
/// After calling `consume(ifAnyFrom:)` we know which token we are positioned
1919
/// at based on that function's return type. This handle allows consuming that
2020
/// token.
21-
struct RecoveryConsumptionHandle {
21+
@_spi(RawSyntax)
22+
public struct RecoveryConsumptionHandle {
2223
var unexpectedTokens: Int
2324
var tokenConsumptionHandle: TokenConsumptionHandle
2425
}
@@ -36,7 +37,9 @@ extension Parser.Lookahead {
3637
_ kinds: [RawTokenKind],
3738
contextualKeywords: [SyntaxText] = [],
3839
recoveryPrecedence: TokenPrecedence? = nil
39-
) -> Bool {
40+
) -> RecoveryConsumptionHandle? {
41+
let initialTokensConsumed = self.tokensConsumed
42+
4043
var precedences = kinds.map(TokenPrecedence.init)
4144
if !contextualKeywords.isEmpty {
4245
precedences += [TokenPrecedence(.identifier), TokenPrecedence(.contextualKeyword)]
@@ -49,22 +52,28 @@ extension Parser.Lookahead {
4952
break
5053
}
5154
if self.at(any: kinds, contextualKeywords: contextualKeywords) {
52-
return true
55+
return RecoveryConsumptionHandle(
56+
unexpectedTokens: self.tokensConsumed - initialTokensConsumed,
57+
tokenConsumptionHandle: TokenConsumptionHandle(
58+
tokenKind: self.currentToken.tokenKind,
59+
remappedKind: self.at(any: [], contextualKeywords: contextualKeywords) ? .contextualKeyword : nil
60+
)
61+
)
5362
}
5463
let currentTokenPrecedence = TokenPrecedence(self.currentToken.tokenKind)
5564
if currentTokenPrecedence >= recoveryPrecedence {
5665
break
5766
}
5867
self.consumeAnyToken()
5968
if let closingDelimiter = currentTokenPrecedence.closingTokenKind {
60-
guard self.canRecoverTo([closingDelimiter]) else {
69+
guard self.canRecoverTo([closingDelimiter]) != nil else {
6170
break
6271
}
6372
self.eat(closingDelimiter)
6473
}
6574
}
6675

67-
return false
76+
return nil
6877
}
6978

7079
/// Checks if we can reach a token in `subset` by skipping tokens that have
@@ -76,6 +85,8 @@ extension Parser.Lookahead {
7685
anyIn subset: Subset.Type,
7786
recoveryPrecedence: TokenPrecedence? = nil
7887
) -> (Subset, RecoveryConsumptionHandle)? {
88+
let initialTokensConsumed = self.tokensConsumed
89+
7990
assert(!subset.allCases.isEmpty, "Subset must have at least one case")
8091
let recoveryPrecedence = recoveryPrecedence ?? subset.allCases.map({
8192
if let precedence = $0.precedence {
@@ -84,8 +95,6 @@ extension Parser.Lookahead {
8495
return TokenPrecedence($0.rawTokenKind)
8596
}
8697
}).min()!
87-
let initialTokensConsumed = self.tokensConsumed
88-
assert(!subset.allCases.isEmpty)
8998
while !self.at(.eof) {
9099
if !recoveryPrecedence.shouldSkipOverNewlines,
91100
self.currentToken.isAtStartOfLine {
@@ -103,7 +112,7 @@ extension Parser.Lookahead {
103112
}
104113
self.consumeAnyToken()
105114
if let closingDelimiter = currentTokenPrecedence.closingTokenKind {
106-
guard self.canRecoverTo([closingDelimiter]) else {
115+
guard self.canRecoverTo([closingDelimiter]) != nil else {
107116
break
108117
}
109118
self.eat(closingDelimiter)

0 commit comments

Comments
 (0)