Skip to content

Commit 15e4e8f

Browse files
committed
Simplify prefix consumption in the parser
1 parent b46348a commit 15e4e8f

File tree

7 files changed

+59
-69
lines changed

7 files changed

+59
-69
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -435,12 +435,7 @@ extension Parser {
435435
)
436436
}
437437

438-
let langle: RawTokenSyntax
439-
if self.currentToken.starts(with: "<") {
440-
langle = self.consumePrefix("<", as: .leftAngle)
441-
} else {
442-
langle = missingToken(.leftAngle)
443-
}
438+
let langle = self.expectWithoutRecovery(prefix: "<", as: .leftAngle)
444439
var elements = [RawGenericParameterSyntax]()
445440
do {
446441
var keepGoing: RawTokenSyntax? = nil
@@ -452,8 +447,7 @@ extension Parser {
452447
var each = self.consume(if: .keyword(.each))
453448

454449
let (unexpectedBetweenEachAndName, name) = self.expectIdentifier(allowSelfOrCapitalSelfAsIdentifier: true)
455-
if attributes == nil && each == nil && unexpectedBetweenEachAndName == nil && name.isMissing && elements.isEmpty && !self.currentToken.starts(with: ">")
456-
{
450+
if attributes == nil && each == nil && unexpectedBetweenEachAndName == nil && name.isMissing && elements.isEmpty && !self.at(prefix: ">") {
457451
break
458452
}
459453

@@ -518,12 +512,7 @@ extension Parser {
518512
whereClause = nil
519513
}
520514

521-
let rangle: RawTokenSyntax
522-
if self.currentToken.starts(with: ">") {
523-
rangle = self.consumePrefix(">", as: .rightAngle)
524-
} else {
525-
rangle = RawTokenSyntax(missing: .rightAngle, arena: self.arena)
526-
}
515+
let rangle = expectWithoutRecovery(prefix: ">", as: .rightAngle)
527516

528517
let parameters: RawGenericParameterListSyntax
529518
if elements.isEmpty && rangle.isMissing {
@@ -1011,7 +1000,7 @@ extension Parser {
10111000
}
10121001

10131002
let generics: RawGenericParameterClauseSyntax?
1014-
if self.currentToken.starts(with: "<") {
1003+
if self.at(prefix: "<") {
10151004
generics = self.parseGenericParameters()
10161005
} else {
10171006
generics = nil
@@ -1145,7 +1134,7 @@ extension Parser {
11451134
}
11461135

11471136
let genericParams: RawGenericParameterClauseSyntax?
1148-
if self.currentToken.starts(with: "<") {
1137+
if self.at(prefix: "<") {
11491138
genericParams = self.parseGenericParameters()
11501139
} else {
11511140
genericParams = nil
@@ -1229,14 +1218,14 @@ extension Parser {
12291218
let (unexpectedBeforeSubscriptKeyword, subscriptKeyword) = self.eat(handle)
12301219

12311220
let unexpectedName: RawTokenSyntax?
1232-
if self.at(.identifier) && self.peek().starts(with: "<") || self.peek().rawTokenKind == .leftParen {
1221+
if self.at(.identifier) && self.peek().tokenText.hasPrefix("<") || self.peek().rawTokenKind == .leftParen {
12331222
unexpectedName = self.consumeAnyToken()
12341223
} else {
12351224
unexpectedName = nil
12361225
}
12371226

12381227
let genericParameterClause: RawGenericParameterClauseSyntax?
1239-
if self.currentToken.starts(with: "<") {
1228+
if self.at(prefix: "<") {
12401229
genericParameterClause = self.parseGenericParameters()
12411230
} else {
12421231
genericParameterClause = nil
@@ -1617,7 +1606,7 @@ extension Parser {
16171606

16181607
// Parse a generic parameter list if it is present.
16191608
let generics: RawGenericParameterClauseSyntax?
1620-
if self.currentToken.starts(with: "<") {
1609+
if self.at(prefix: "<") {
16211610
generics = self.parseGenericParameters()
16221611
} else {
16231612
generics = nil
@@ -2012,7 +2001,7 @@ extension Parser {
20122001

20132002
// Optional generic parameters.
20142003
let genericParams: RawGenericParameterClauseSyntax?
2015-
if self.currentToken.starts(with: "<") {
2004+
if self.at(prefix: "<") {
20162005
genericParams = self.parseGenericParameters()
20172006
} else {
20182007
genericParams = nil

Sources/SwiftParser/Expressions.swift

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,21 +1023,12 @@ extension Parser {
10231023

10241024
for _ in 0..<numComponents {
10251025
// Consume a period, if there is one.
1026-
let period: RawTokenSyntax?
1027-
if self.currentToken.starts(with: ".") {
1028-
period = self.consumePrefix(".", as: .period)
1029-
} else {
1030-
period = nil
1031-
}
1026+
let period = self.consume(ifPrefix: ".", as: .period)
10321027

10331028
// Consume the '!' or '?'.
1034-
let questionOrExclaim: RawTokenSyntax
1035-
if self.currentToken.starts(with: "!") {
1036-
questionOrExclaim = self.consumePrefix("!", as: .exclamationMark)
1037-
} else {
1038-
precondition(self.currentToken.starts(with: "?"))
1039-
questionOrExclaim = self.consumePrefix("?", as: .postfixQuestionMark)
1040-
}
1029+
let questionOrExclaim =
1030+
self.consume(ifPrefix: "!", as: .exclamationMark)
1031+
?? self.expectWithoutRecovery(prefix: "?", as: .postfixQuestionMark)
10411032

10421033
components.append(
10431034
RawKeyPathComponentSyntax(
@@ -1078,7 +1069,7 @@ extension Parser {
10781069
// operator token. Since keypath allows '.!' '.?' and '.[', consume '.'
10791070
// the token is an operator starts with '.', or the following token is '['.
10801071
let rootType: RawTypeSyntax?
1081-
if !self.currentToken.starts(with: ".") {
1072+
if !self.at(prefix: ".") {
10821073
rootType = self.parseSimpleType(stopAtFirstPeriod: true)
10831074
} else {
10841075
rootType = nil

Sources/SwiftParser/Names.swift

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ extension Parser.Lookahead {
228228
guard lookahead.canParseTypeIdentifier() else {
229229
return false
230230
}
231-
return lookahead.currentToken.starts(with: ".")
231+
return lookahead.at(prefix: ".")
232232
}
233233
}
234234

@@ -289,12 +289,4 @@ extension Lexer.Lexeme {
289289
// constructed from them.
290290
return self.rawTokenKind == .keyword
291291
}
292-
293-
func starts(with symbol: SyntaxText) -> Bool {
294-
guard Operator(lexeme: self) != nil || self.rawTokenKind.isPunctuation else {
295-
return false
296-
}
297-
298-
return self.tokenText.hasPrefix(symbol)
299-
}
300292
}

Sources/SwiftParser/Nominals.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ extension Parser {
233233
}
234234

235235
let primaryOrGenerics: T.PrimaryOrGenerics?
236-
if self.currentToken.starts(with: "<") {
236+
if self.at(prefix: "<") {
237237
primaryOrGenerics = T.parsePrimaryOrGenerics(&self)
238238
} else {
239239
primaryOrGenerics = nil
@@ -352,18 +352,10 @@ extension Parser {
352352
)
353353
} while keepGoing != nil && loopProgress.evaluate(currentToken)
354354
}
355-
let unexpectedBeforeRangle: RawUnexpectedNodesSyntax?
356-
let rangle: RawTokenSyntax
357-
if self.currentToken.starts(with: ">") {
358-
unexpectedBeforeRangle = nil
359-
rangle = self.consumePrefix(">", as: .rightAngle)
360-
} else {
361-
(unexpectedBeforeRangle, rangle) = self.expect(.rightAngle)
362-
}
355+
let rangle = self.expectWithoutRecovery(prefix: ">", as: .rightAngle)
363356
return RawPrimaryAssociatedTypeClauseSyntax(
364357
leftAngle: langle,
365358
primaryAssociatedTypeList: RawPrimaryAssociatedTypeListSyntax(elements: associatedTypes, arena: self.arena),
366-
unexpectedBeforeRangle,
367359
rightAngle: rangle,
368360
arena: self.arena
369361
)

Sources/SwiftParser/Parser.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,17 @@ extension Parser {
471471
)
472472
}
473473

474+
/// If the current token starts with the given prefix, consume the prefis as the given token kind.
475+
///
476+
/// Otherwise, synthesize a missing token of the given kind.
477+
mutating func expectWithoutRecovery(prefix: SyntaxText, as tokenKind: RawTokenKind) -> Token {
478+
if self.at(prefix: prefix) {
479+
return consumePrefix(prefix, as: tokenKind)
480+
} else {
481+
return missingToken(tokenKind)
482+
}
483+
}
484+
474485
/// - Parameters:
475486
/// - keywordRecovery: If set to `true` and the parser is currently
476487
/// positioned at a keyword instead of an identifier, this method recovers

Sources/SwiftParser/TokenConsumer.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ protocol TokenConsumer {
2323
/// Consume the current token and change its token kind to `remappedTokenKind`.
2424
mutating func consumeAnyToken(remapping remappedTokenKind: RawTokenKind) -> Token
2525

26+
/// Consumes a given token, or splits the current token into a leading token
27+
/// matching the given token and a trailing token and consumes the leading
28+
/// token.
29+
///
30+
/// <TOKEN> ... -> consumePrefix(<TOK>) -> [ <TOK> ] <EN> ...
31+
mutating func consumePrefix(_ prefix: SyntaxText, as tokenKind: RawTokenKind) -> Token
32+
2633
/// Synthesize a missing token with `kind`.
2734
/// If `text` is not `nil`, use it for the token's text, otherwise use the token's default text.
2835
mutating func missingToken(_ kind: RawTokenKind, text: SyntaxText?) -> Token
@@ -137,6 +144,12 @@ extension TokenConsumer {
137144
return nil
138145
}
139146

147+
/// Whether the current token’s text starts with the given prefix.
148+
@inline(__always)
149+
mutating func at(prefix: SyntaxText) -> Bool {
150+
return self.currentToken.tokenText.hasPrefix(prefix)
151+
}
152+
140153
/// Eat a token that we know we are currently positioned at, based on `at(anyIn:)`.
141154
@inline(__always)
142155
mutating func eat(_ handle: TokenConsumptionHandle) -> Token {
@@ -248,6 +261,17 @@ extension TokenConsumer {
248261
return nil
249262
}
250263
}
264+
265+
/// If the current token starts with the given prefix, consume the prefis as the given token kind.
266+
///
267+
/// Otherwise, return `nil`.
268+
mutating func consume(ifPrefix prefix: SyntaxText, as tokenKind: RawTokenKind) -> Token? {
269+
if self.at(prefix: prefix) {
270+
return consumePrefix(prefix, as: tokenKind)
271+
} else {
272+
return nil
273+
}
274+
}
251275
}
252276

253277
// MARK: Convenience functions

Sources/SwiftParser/Types.swift

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,7 @@ extension Parser {
422422
/// generic-argument-list → generic-argument | generic-argument ',' generic-argument-list
423423
/// generic-argument → type
424424
mutating func parseGenericArguments() -> RawGenericArgumentClauseSyntax {
425-
precondition(self.currentToken.starts(with: "<"))
426-
let langle = self.consumePrefix("<", as: .leftAngle)
425+
let langle = self.expectWithoutRecovery(prefix: "<", as: .leftAngle)
427426
var arguments = [RawGenericArgumentSyntax]()
428427
do {
429428
var keepGoing: RawTokenSyntax? = nil
@@ -444,12 +443,7 @@ extension Parser {
444443
} while keepGoing != nil && loopProgress.evaluate(currentToken)
445444
}
446445

447-
let rangle: RawTokenSyntax
448-
if self.currentToken.starts(with: ">") {
449-
rangle = self.consumePrefix(">", as: .rightAngle)
450-
} else {
451-
rangle = RawTokenSyntax(missing: .rightAngle, arena: self.arena)
452-
}
446+
let rangle = self.expectWithoutRecovery(prefix: ">", as: .rightAngle)
453447

454448
let args: RawGenericArgumentListSyntax
455449
if arguments.isEmpty && rangle.isMissing {
@@ -884,7 +878,7 @@ extension Parser.Lookahead {
884878
self.consumeAnyToken()
885879

886880
// Parse an optional generic argument list.
887-
if self.currentToken.starts(with: "<") && !self.consumeGenericArguments() {
881+
if self.at(prefix: "<") && !self.consumeGenericArguments() {
888882
return false
889883
}
890884

@@ -905,13 +899,11 @@ extension Parser.Lookahead {
905899

906900
mutating func consumeGenericArguments() -> Bool {
907901
// Parse the opening '<'.
908-
guard self.currentToken.starts(with: "<") else {
902+
guard self.consume(ifPrefix: "<", as: .leftAngle) != nil else {
909903
return false
910904
}
911905

912-
self.consumePrefix("<", as: .leftAngle)
913-
914-
if !self.currentToken.starts(with: ">") {
906+
if !self.at(prefix: ">") {
915907
var loopProgress = LoopProgressCondition()
916908
repeat {
917909
guard self.canParseType() else {
@@ -921,11 +913,10 @@ extension Parser.Lookahead {
921913
} while self.consume(if: .comma) != nil && loopProgress.evaluate(currentToken)
922914
}
923915

924-
guard self.currentToken.starts(with: ">") else {
916+
guard self.consume(ifPrefix: ">", as: .rightAngle) != nil else {
925917
return false
926918
}
927919

928-
self.consumePrefix(">", as: .rightAngle)
929920
return true
930921
}
931922
}
@@ -1001,7 +992,7 @@ extension Parser {
1001992

1002993
extension Parser {
1003994
mutating func parseResultType() -> RawTypeSyntax {
1004-
if self.currentToken.starts(with: "<") {
995+
if self.at(prefix: "<") {
1005996
let generics = self.parseGenericParameters()
1006997
let baseType = self.parseType()
1007998
return RawTypeSyntax(

0 commit comments

Comments
 (0)