Skip to content

Commit 680942e

Browse files
authored
Merge pull request #1910 from ahoppen/ahoppen/prefix-consumption
Simplify consumption of prefixes in a token
2 parents 9cab487 + 4527338 commit 680942e

File tree

8 files changed

+70
-92
lines changed

8 files changed

+70
-92
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -407,22 +407,6 @@ extension Parser {
407407
}
408408

409409
extension Parser {
410-
/// Attempt to consume an ellipsis prefix, splitting the current token if
411-
/// necessary.
412-
mutating func tryConsumeEllipsisPrefix() -> RawTokenSyntax? {
413-
// It is not sufficient to check currentToken.isEllipsis here, as we may
414-
// have something like '...>'.
415-
// TODO: Recovery for different numbers of dots (which also needs to be
416-
// done for regular variadics).
417-
guard self.at(anyIn: Operator.self) != nil else { return nil }
418-
let text = self.currentToken.tokenText
419-
guard text.hasPrefix("...") else { return nil }
420-
return self.consumePrefix(
421-
SyntaxText(rebasing: text.prefix(3)),
422-
as: .ellipsis
423-
)
424-
}
425-
426410
mutating func parseGenericParameters() -> RawGenericParameterClauseSyntax {
427411
if let remainingTokens = remainingTokensIfMaximumNestingLevelReached() {
428412
return RawGenericParameterClauseSyntax(
@@ -435,12 +419,7 @@ extension Parser {
435419
)
436420
}
437421

438-
let langle: RawTokenSyntax
439-
if self.currentToken.starts(with: "<") {
440-
langle = self.consumePrefix("<", as: .leftAngle)
441-
} else {
442-
langle = missingToken(.leftAngle)
443-
}
422+
let langle = self.expectWithoutRecovery(prefix: "<", as: .leftAngle)
444423
var elements = [RawGenericParameterSyntax]()
445424
do {
446425
var keepGoing: RawTokenSyntax? = nil
@@ -452,14 +431,13 @@ extension Parser {
452431
var each = self.consume(if: .keyword(.each))
453432

454433
let (unexpectedBetweenEachAndName, name) = self.expectIdentifier(allowSelfOrCapitalSelfAsIdentifier: true)
455-
if attributes == nil && each == nil && unexpectedBetweenEachAndName == nil && name.isMissing && elements.isEmpty && !self.currentToken.starts(with: ">")
456-
{
434+
if attributes == nil && each == nil && unexpectedBetweenEachAndName == nil && name.isMissing && elements.isEmpty && !self.at(prefix: ">") {
457435
break
458436
}
459437

460438
// Parse the unsupported ellipsis for a type parameter pack 'T...'.
461439
let unexpectedBetweenNameAndColon: RawUnexpectedNodesSyntax?
462-
if let ellipsis = tryConsumeEllipsisPrefix() {
440+
if let ellipsis = self.consume(ifPrefix: "...", as: .ellipsis) {
463441
unexpectedBetweenNameAndColon = RawUnexpectedNodesSyntax([ellipsis], arena: self.arena)
464442
if each == nil {
465443
each = missingToken(.each)
@@ -518,12 +496,7 @@ extension Parser {
518496
whereClause = nil
519497
}
520498

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-
}
499+
let rangle = expectWithoutRecovery(prefix: ">", as: .rightAngle)
527500

528501
let parameters: RawGenericParameterListSyntax
529502
if elements.isEmpty && rangle.isMissing {
@@ -934,7 +907,7 @@ extension Parser {
934907
}
935908

936909
// Detect an attempt to use (early syntax) type parameter pack.
937-
let ellipsis = tryConsumeEllipsisPrefix()
910+
let ellipsis = self.consume(ifPrefix: "...", as: .ellipsis)
938911

939912
// Parse optional inheritance clause.
940913
let inheritance: RawTypeInheritanceClauseSyntax?
@@ -1011,7 +984,7 @@ extension Parser {
1011984
}
1012985

1013986
let generics: RawGenericParameterClauseSyntax?
1014-
if self.currentToken.starts(with: "<") {
987+
if self.at(prefix: "<") {
1015988
generics = self.parseGenericParameters()
1016989
} else {
1017990
generics = nil
@@ -1145,7 +1118,7 @@ extension Parser {
11451118
}
11461119

11471120
let genericParams: RawGenericParameterClauseSyntax?
1148-
if self.currentToken.starts(with: "<") {
1121+
if self.at(prefix: "<") {
11491122
genericParams = self.parseGenericParameters()
11501123
} else {
11511124
genericParams = nil
@@ -1229,14 +1202,14 @@ extension Parser {
12291202
let (unexpectedBeforeSubscriptKeyword, subscriptKeyword) = self.eat(handle)
12301203

12311204
let unexpectedName: RawTokenSyntax?
1232-
if self.at(.identifier) && self.peek().starts(with: "<") || self.peek().rawTokenKind == .leftParen {
1205+
if self.at(.identifier) && self.peek().tokenText.hasPrefix("<") || self.peek().rawTokenKind == .leftParen {
12331206
unexpectedName = self.consumeAnyToken()
12341207
} else {
12351208
unexpectedName = nil
12361209
}
12371210

12381211
let genericParameterClause: RawGenericParameterClauseSyntax?
1239-
if self.currentToken.starts(with: "<") {
1212+
if self.at(prefix: "<") {
12401213
genericParameterClause = self.parseGenericParameters()
12411214
} else {
12421215
genericParameterClause = nil
@@ -1617,7 +1590,7 @@ extension Parser {
16171590

16181591
// Parse a generic parameter list if it is present.
16191592
let generics: RawGenericParameterClauseSyntax?
1620-
if self.currentToken.starts(with: "<") {
1593+
if self.at(prefix: "<") {
16211594
generics = self.parseGenericParameters()
16221595
} else {
16231596
generics = nil
@@ -2012,7 +1985,7 @@ extension Parser {
20121985

20131986
// Optional generic parameters.
20141987
let genericParams: RawGenericParameterClauseSyntax?
2015-
if self.currentToken.starts(with: "<") {
1988+
if self.at(prefix: "<") {
20161989
genericParams = self.parseGenericParameters()
20171990
} else {
20181991
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/Lookahead.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ extension Parser.Lookahead {
117117
///
118118
/// <TOKEN> ... -> consumePrefix(<TOK>) -> [ <TOK> ] <EN> ...
119119
mutating func consumePrefix(_ prefix: SyntaxText, as tokenKind: RawTokenKind) {
120+
precondition(
121+
tokenKind.defaultText == nil || prefix == tokenKind.defaultText!,
122+
"If tokenKind has a defaultText, the prefix needs to match it"
123+
)
120124
let tokenText = self.currentToken.tokenText
121125

122126
if tokenText == prefix {

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: 15 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
@@ -594,6 +605,10 @@ extension Parser {
594605
_ prefix: SyntaxText,
595606
as tokenKind: RawTokenKind
596607
) -> RawTokenSyntax {
608+
precondition(
609+
tokenKind.defaultText == nil || prefix == tokenKind.defaultText!,
610+
"If tokenKind has a defaultText, the prefix needs to match it"
611+
)
597612
let current = self.currentToken
598613
// Current token can be either one-character token we want to consume...
599614
let tokenText = current.tokenText

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: 8 additions & 21 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 {
@@ -656,7 +650,7 @@ extension Parser.Lookahead {
656650
return false
657651
}
658652

659-
if self.currentToken.isEllipsis {
653+
if self.atContextualPunctuator("...") {
660654
self.consumeAnyToken()
661655
}
662656

@@ -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(
@@ -1076,10 +1067,6 @@ extension Lexer.Lexeme {
10761067
|| self.rawTokenKind == .prefixOperator
10771068
}
10781069

1079-
var isEllipsis: Bool {
1080-
return self.isAnyOperator && self.tokenText == "..."
1081-
}
1082-
10831070
var isGenericTypeDisambiguatingToken: Bool {
10841071
switch self.rawTokenKind {
10851072
case .rightParen,

0 commit comments

Comments
 (0)