Skip to content

Commit 5bfe541

Browse files
committed
Introduce peek(isAt:)
This makes it a lot easier to check if the next token is of a specific kind.
1 parent 2e8bff5 commit 5bfe541

File tree

9 files changed

+86
-66
lines changed

9 files changed

+86
-66
lines changed

Sources/SwiftParser/Availability.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ extension Parser {
152152
case (.star, _)?:
153153
entry = self.parseAvailabilitySpec()
154154
case (.identifier, let handle)?:
155-
if self.peek().rawTokenKind == .comma {
155+
if self.peek(isAt: .comma) {
156156
// An argument like `_iOS13Aligned` that isn't followed by a version.
157157
let version = self.eat(handle)
158158
entry = .token(version)

Sources/SwiftParser/Declarations.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ extension Parser {
252252
return RawDeclSyntax(self.parseMacroExpansionDeclaration(attrs, handle))
253253
case nil:
254254
if inMemberDeclList {
255-
let isProbablyVarDecl = self.at(.identifier, .wildcard) && self.peek().rawTokenKind.is(.colon, .equal, .comma)
256-
let isProbablyTupleDecl = self.at(.leftParen) && self.peek().rawTokenKind.is(.identifier, .wildcard)
255+
let isProbablyVarDecl = self.at(.identifier, .wildcard) && self.peek(isAt: .colon, .equal, .comma)
256+
let isProbablyTupleDecl = self.at(.leftParen) && self.peek(isAt: .identifier, .wildcard)
257257

258258
if isProbablyVarDecl || isProbablyTupleDecl {
259259
return RawDeclSyntax(self.parseBindingDeclaration(attrs, .missing(.keyword(.var))))
@@ -1036,7 +1036,7 @@ extension Parser {
10361036
let identifier: RawTokenSyntax
10371037
if self.at(anyIn: Operator.self) != nil || self.at(.exclamationMark, .prefixAmpersand) {
10381038
var name = self.currentToken.tokenText
1039-
if name.count > 1 && name.hasSuffix("<") && self.peek().rawTokenKind == .identifier {
1039+
if name.count > 1 && name.hasSuffix("<") && self.peek(isAt: .identifier) {
10401040
name = SyntaxText(rebasing: name.dropLast())
10411041
}
10421042
unexpectedBeforeIdentifier = nil
@@ -1121,7 +1121,7 @@ extension Parser {
11211121
let (unexpectedBeforeSubscriptKeyword, subscriptKeyword) = self.eat(handle)
11221122

11231123
let unexpectedName: RawTokenSyntax?
1124-
if self.at(.identifier) && self.peek().tokenText.hasPrefix("<") || self.peek().rawTokenKind == .leftParen {
1124+
if self.at(.identifier) && self.peek().tokenText.hasPrefix("<") || self.peek(isAt: .leftParen) {
11251125
unexpectedName = self.consumeAnyToken()
11261126
} else {
11271127
unexpectedName = nil

Sources/SwiftParser/Expressions.swift

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ extension Parser {
320320
return parseUnresolvedAsExpr(handle: handle)
321321

322322
case (.async, _)?:
323-
if self.peek().rawTokenKind == .arrow || TokenSpec(.throws) ~= self.peek() {
323+
if self.peek(isAt: .arrow, .keyword(.throws)) {
324324
fallthrough
325325
} else {
326326
return nil
@@ -348,6 +348,20 @@ extension Parser {
348348
}
349349
}
350350

351+
/// Whether the current token is a valid contextual exprssion modifier like
352+
/// `copy`, `consume`.
353+
///
354+
/// `copy` etc. are only contextually a keyword if they are followed by an
355+
/// identifier or keyword on the same line. We do this to ensure that we do
356+
/// not break any copy functions defined by users.
357+
private mutating func isContextualExpressionModifier() -> Bool {
358+
return self.peek(
359+
isAt: TokenSpec(.identifier, allowAtStartOfLine: false),
360+
TokenSpec(.dollarIdentifier, allowAtStartOfLine: false),
361+
TokenSpec(.self, allowAtStartOfLine: false)
362+
)
363+
}
364+
351365
/// Parse an expression sequence element.
352366
mutating func parseSequenceExpressionElement(
353367
_ flavor: ExprFlavor,
@@ -430,17 +444,7 @@ extension Parser {
430444
)
431445

432446
case (.copy, let handle)?:
433-
// `copy` is only contextually a keyword, if it's followed by an
434-
// identifier or keyword on the same line. We do this to ensure that we do
435-
// not break any copy functions defined by users. This is following with
436-
// what we have done for the consume keyword.
437-
switch self.peek() {
438-
case TokenSpec(.identifier, allowAtStartOfLine: false),
439-
TokenSpec(.dollarIdentifier, allowAtStartOfLine: false),
440-
TokenSpec(.self, allowAtStartOfLine: false):
441-
break
442-
default:
443-
// Break out of `outer switch` on failure.
447+
if !isContextualExpressionModifier() {
444448
break EXPR_PREFIX
445449
}
446450

@@ -459,17 +463,7 @@ extension Parser {
459463
)
460464

461465
case (.consume, let handle)?:
462-
// `consume` is only contextually a keyword, if it's followed by an
463-
// identifier or keyword on the same line. We do this to ensure that we do
464-
// not break any copy functions defined by users. This is following with
465-
// what we have done for the consume keyword.
466-
switch self.peek() {
467-
case TokenSpec(.identifier, allowAtStartOfLine: false),
468-
TokenSpec(.dollarIdentifier, allowAtStartOfLine: false),
469-
TokenSpec(.self, allowAtStartOfLine: false):
470-
break
471-
default:
472-
// Break out of the outer `switch`.
466+
if !isContextualExpressionModifier() {
473467
break EXPR_PREFIX
474468
}
475469

@@ -492,17 +486,7 @@ extension Parser {
492486
return RawExprSyntax(parsePackExpansionExpr(repeatHandle: handle, flavor, pattern: pattern))
493487

494488
case (.each, let handle)?:
495-
// `each` is only contextually a keyword, if it's followed by an
496-
// identifier or 'self' on the same line. We do this to ensure that we do
497-
// not break any 'each' functions defined by users. This is following with
498-
// what we have done for the consume keyword.
499-
switch self.peek() {
500-
case TokenSpec(.identifier, allowAtStartOfLine: false),
501-
TokenSpec(.dollarIdentifier, allowAtStartOfLine: false),
502-
TokenSpec(.self, allowAtStartOfLine: false):
503-
break
504-
default:
505-
// Break out of `outer switch` on failure.
489+
if !isContextualExpressionModifier() {
506490
break EXPR_PREFIX
507491
}
508492

@@ -517,11 +501,10 @@ extension Parser {
517501
)
518502

519503
case (.any, _)?:
520-
// `any` is only contextually a keyword if it's followed by an identifier
521-
// on the same line.
522-
guard case TokenSpec(.identifier, allowAtStartOfLine: false) = self.peek() else {
504+
if !isContextualExpressionModifier() {
523505
break EXPR_PREFIX
524506
}
507+
525508
// 'any' is parsed as a part of 'type'.
526509
let type = self.parseType()
527510
return RawExprSyntax(RawTypeExprSyntax(type: type, arena: self.arena))
@@ -995,7 +978,7 @@ extension Parser {
995978
// Check for a [] or .[] suffix. The latter is only permitted when there
996979
// are no components.
997980
if self.at(TokenSpec(.leftSquare, allowAtStartOfLine: false))
998-
|| (components.isEmpty && self.at(.period) && self.peek().rawTokenKind == .leftSquare)
981+
|| (components.isEmpty && self.at(.period) && self.peek(isAt: .leftSquare))
999982
{
1000983
// Consume the '.', if it's allowed here.
1001984
let period: RawTokenSyntax?
@@ -1722,7 +1705,7 @@ extension Parser {
17221705
let unexpectedBeforeEqual: RawUnexpectedNodesSyntax?
17231706
let equal: RawTokenSyntax?
17241707
let expression: RawExprSyntax
1725-
if self.peek().rawTokenKind == .equal {
1708+
if self.peek(isAt: .equal) {
17261709
// The name is a new declaration.
17271710
(unexpectedBeforeName, name) = self.expect(.identifier, TokenSpec(.self, remapping: .identifier), default: .identifier)
17281711
(unexpectedBeforeEqual, equal) = self.expect(.equal)
@@ -1901,7 +1884,7 @@ extension Parser {
19011884
let unexpectedBeforeLabel: RawUnexpectedNodesSyntax?
19021885
let label: RawTokenSyntax?
19031886
let colon: RawTokenSyntax?
1904-
if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
1887+
if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek(isAt: .colon) {
19051888
(unexpectedBeforeLabel, label) = parseArgumentLabel()
19061889
colon = consumeAnyToken()
19071890
} else {
@@ -1914,9 +1897,7 @@ extension Parser {
19141897
// this case lexes as a binary operator because it neither leads nor
19151898
// follows a proper subexpression.
19161899
let expr: RawExprSyntax
1917-
if self.at(.binaryOperator)
1918-
&& (self.peek().rawTokenKind == .comma || self.peek().rawTokenKind == .rightParen || self.peek().rawTokenKind == .rightSquare)
1919-
{
1900+
if self.at(.binaryOperator) && self.peek(isAt: .comma, .rightParen, .rightSquare) {
19201901
let (ident, args) = self.parseDeclNameRef(.operators)
19211902
expr = RawExprSyntax(
19221903
RawIdentifierExprSyntax(

Sources/SwiftParser/Names.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,10 @@ extension Parser {
9393

9494
// Okay, let's look ahead and see if the next token is something that could
9595
// be in an arg label list...
96-
let next = self.peek()
97-
9896
// A close parenthesis, if empty lists are allowed.
99-
let nextIsRParen = flags.contains(.zeroArgCompoundNames) && next.rawTokenKind == .rightParen
97+
let nextIsRParen = flags.contains(.zeroArgCompoundNames) && peek(isAt: .rightParen)
10098
// An argument label.
101-
let nextIsArgLabel = next.isArgumentLabel() || next.rawTokenKind == .colon
99+
let nextIsArgLabel = peek().isArgumentLabel() || peek(isAt: .colon)
102100

103101
guard nextIsRParen || nextIsArgLabel else {
104102
return nil
@@ -117,7 +115,7 @@ extension Parser {
117115
var loopProgress = LoopProgressCondition()
118116
while !self.at(.endOfFile, .rightParen) && self.hasProgressed(&loopProgress) {
119117
// Check to see if there is an argument label.
120-
precondition(self.atArgumentLabel() && self.peek().rawTokenKind == .colon)
118+
precondition(self.atArgumentLabel() && self.peek(isAt: .colon))
121119
let name = self.consumeAnyToken()
122120
let (unexpectedBeforeColon, colon) = self.expect(.colon)
123121
elements.append(

Sources/SwiftParser/Patterns.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ extension Parser {
181181

182182
/// If we have something like `x SomeType`, use the indication that `SomeType` starts with a capital letter (and is thus probably a type name)
183183
/// as an indication that the user forgot to write the colon instead of forgetting to write the comma to separate two elements.
184-
if label == nil, colon == nil, self.at(.identifier), peek().rawTokenKind == .identifier, peek().tokenText.isStartingWithUppercase {
184+
if label == nil, colon == nil, self.at(.identifier), peek(isAt: .identifier), peek().tokenText.isStartingWithUppercase {
185185
label = consume(if: .identifier)
186186
colon = self.missingToken(.colon)
187187
}

Sources/SwiftParser/Statements.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ extension Parser {
177177
guard
178178
self.at(.keyword(.let), .keyword(.var), .keyword(.case))
179179
|| self.at(.keyword(.inout))
180-
|| (lastBindingKind != nil && self.peek().rawTokenKind == .equal)
180+
|| (lastBindingKind != nil && self.peek(isAt: .equal))
181181
else {
182182
// If we lack it, then this is theoretically a boolean condition.
183183
// However, we also need to handle migrating from Swift 2 syntax, in

Sources/SwiftParser/SyntaxUtils.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,6 @@ extension SyntaxText {
113113
}
114114
}
115115

116-
extension RawTokenKind {
117-
func `is`(_ kind1: RawTokenKind, _ kind2: RawTokenKind) -> Bool {
118-
return self == kind1 || self == kind2
119-
}
120-
121-
func `is`(_ kind1: RawTokenKind, _ kind2: RawTokenKind, _ kind3: RawTokenKind) -> Bool {
122-
return self == kind1 || self == kind2 || self == kind3
123-
}
124-
}
125-
126116
extension RawTriviaPiece {
127117
var isIndentationWhitespace: Bool {
128118
switch self {

Sources/SwiftParser/TokenConsumer.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,57 @@ extension TokenConsumer {
172172
}
173173
}
174174

175+
extension TokenConsumer {
176+
/// Returns whether the next (peeked) token matches `spec`
177+
@inline(__always)
178+
mutating func peek(isAt spec: TokenSpec) -> Bool {
179+
#if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION
180+
if shouldRecordAlternativeTokenChoices {
181+
recordAlternativeTokenChoice(for: self.peek(), choices: [spec])
182+
}
183+
#endif
184+
return spec ~= self.peek()
185+
}
186+
187+
/// Returns whether the next (peeked) token matches one of the two specs.
188+
@inline(__always)
189+
mutating func peek(
190+
isAt spec1: TokenSpec,
191+
_ spec2: TokenSpec
192+
) -> Bool {
193+
#if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION
194+
if shouldRecordAlternativeTokenChoices {
195+
recordAlternativeTokenChoice(for: self.peek(), choices: [spec1, spec2])
196+
}
197+
#endif
198+
switch self.peek() {
199+
case spec1: return true
200+
case spec2: return true
201+
default: return false
202+
}
203+
}
204+
205+
/// Returns whether the next (peeked) token matches one of the three specs.
206+
@inline(__always)
207+
mutating func peek(
208+
isAt spec1: TokenSpec,
209+
_ spec2: TokenSpec,
210+
_ spec3: TokenSpec
211+
) -> Bool {
212+
#if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION
213+
if shouldRecordAlternativeTokenChoices {
214+
recordAlternativeTokenChoice(for: self.peek(), choices: [spec1, spec2, spec3])
215+
}
216+
#endif
217+
switch self.peek() {
218+
case spec1: return true
219+
case spec2: return true
220+
case spec3: return true
221+
default: return false
222+
}
223+
}
224+
}
225+
175226
// MARK: Consuming tokens (`consume`)
176227

177228
extension TokenConsumer {

Sources/SwiftParser/Types.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ extension Parser {
457457
second = nil
458458
unexpectedBeforeColon = nil
459459
colon = parsedColon
460-
} else if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
460+
} else if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek(isAt: .colon) {
461461
(unexpectedBeforeSecond, second) = self.parseArgumentLabel()
462462
(unexpectedBeforeColon, colon) = self.expect(.colon)
463463
} else {

0 commit comments

Comments
 (0)