Skip to content

Commit 70cdb8e

Browse files
authored
Merge pull request #1911 from ahoppen/ahoppen/isargumentlabel
Simplify checking if a token can be an argument label
2 parents 95b2677 + 011f790 commit 70cdb8e

File tree

10 files changed

+81
-52
lines changed

10 files changed

+81
-52
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -541,21 +541,18 @@ extension Parser {
541541
// First check to see if we have the start of a regex literal `/.../`.
542542
// tryLexRegexLiteral(/*forUnappliedOperator*/ false)
543543

544-
switch self.currentToken {
545544
// Try parse an 'if' or 'switch' as an expression. Note we do this here in
546545
// parseUnaryExpression as we don't allow postfix syntax to hang off such
547546
// expressions to avoid ambiguities such as postfix '.member', which can
548547
// currently be parsed as a static dot member for a result builder.
549-
case TokenSpec(.switch):
548+
if self.at(.keyword(.switch)) {
550549
return RawExprSyntax(
551550
parseSwitchExpression(switchHandle: .constant(.keyword(.switch)))
552551
)
553-
case TokenSpec(.if):
552+
} else if self.at(.keyword(.if)) {
554553
return RawExprSyntax(
555554
parseIfExpression(ifHandle: .constant(.keyword(.if)))
556555
)
557-
default:
558-
break
559556
}
560557

561558
switch self.at(anyIn: ExpressionPrefixOperator.self) {
@@ -878,7 +875,7 @@ extension Parser {
878875
// Check if the first '#if' body starts with '.' <identifier>, and parse
879876
// it as a "postfix ifconfig expression".
880877
do {
881-
var backtrack = self.lookahead()
878+
var lookahead = self.lookahead()
882879
// Skip to the first body. We may need to skip multiple '#if' directives
883880
// since we support nested '#if's. e.g.
884881
// baseExpr
@@ -887,13 +884,13 @@ extension Parser {
887884
// .someMember
888885
var loopProgress = LoopProgressCondition()
889886
repeat {
890-
backtrack.eat(.poundIf)
891-
while !backtrack.at(.endOfFile) && !backtrack.currentToken.isAtStartOfLine {
892-
backtrack.skipSingle()
887+
lookahead.eat(.poundIf)
888+
while !lookahead.at(.endOfFile) && !lookahead.currentToken.isAtStartOfLine {
889+
lookahead.skipSingle()
893890
}
894-
} while backtrack.at(.poundIf) && backtrack.hasProgressed(&loopProgress)
891+
} while lookahead.at(.poundIf) && lookahead.hasProgressed(&loopProgress)
895892

896-
guard backtrack.isAtStartOfPostfixExprSuffix() else {
893+
guard lookahead.isAtStartOfPostfixExprSuffix() else {
897894
break
898895
}
899896
}
@@ -1904,7 +1901,7 @@ extension Parser {
19041901
let unexpectedBeforeLabel: RawUnexpectedNodesSyntax?
19051902
let label: RawTokenSyntax?
19061903
let colon: RawTokenSyntax?
1907-
if currentToken.canBeArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
1904+
if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
19081905
(unexpectedBeforeLabel, label) = parseArgumentLabel()
19091906
colon = consumeAnyToken()
19101907
} else {
@@ -1982,7 +1979,7 @@ extension Parser.Lookahead {
19821979
// Fast path: the next two tokens must be a label and a colon.
19831980
// But 'default:' is ambiguous with switch cases and we disallow it
19841981
// (unless escaped) even outside of switches.
1985-
if !self.currentToken.canBeArgumentLabel()
1982+
if !self.atArgumentLabel()
19861983
|| self.at(.keyword(.default))
19871984
|| self.peek().rawTokenKind != .colon
19881985
{

Sources/SwiftParser/Names.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension Parser {
2424
}
2525

2626
mutating func parseArgumentLabel() -> (RawUnexpectedNodesSyntax?, RawTokenSyntax) {
27-
guard self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) else {
27+
guard self.atArgumentLabel(allowDollarIdentifier: true) else {
2828
return (nil, missingToken(.identifier))
2929
}
3030
if let dollarIdent = self.consume(if: .dollarIdentifier) {
@@ -98,7 +98,7 @@ extension Parser {
9898
// A close parenthesis, if empty lists are allowed.
9999
let nextIsRParen = flags.contains(.zeroArgCompoundNames) && next.rawTokenKind == .rightParen
100100
// An argument label.
101-
let nextIsArgLabel = next.canBeArgumentLabel() || next.rawTokenKind == .colon
101+
let nextIsArgLabel = next.isArgumentLabel() || next.rawTokenKind == .colon
102102

103103
guard nextIsRParen || nextIsArgLabel else {
104104
return nil
@@ -117,7 +117,7 @@ extension Parser {
117117
var loopProgress = LoopProgressCondition()
118118
while !self.at(.endOfFile, .rightParen) && self.hasProgressed(&loopProgress) {
119119
// Check to see if there is an argument label.
120-
precondition(self.currentToken.canBeArgumentLabel() && self.peek().rawTokenKind == .colon)
120+
precondition(self.atArgumentLabel() && self.peek().rawTokenKind == .colon)
121121
let name = self.consumeAnyToken()
122122
let (unexpectedBeforeColon, colon) = self.expect(.colon)
123123
elements.append(
@@ -242,7 +242,7 @@ extension Parser.Lookahead {
242242
var loopProgress = LoopProgressCondition()
243243
while !lookahead.at(.endOfFile, .rightParen) && lookahead.hasProgressed(&loopProgress) {
244244
// Check to see if there is an argument label.
245-
guard lookahead.currentToken.canBeArgumentLabel() && lookahead.peek().rawTokenKind == .colon else {
245+
guard lookahead.atArgumentLabel() && lookahead.peek().rawTokenKind == .colon else {
246246
return false
247247
}
248248

@@ -261,18 +261,16 @@ extension Parser.Lookahead {
261261
}
262262

263263
extension Lexer.Lexeme {
264-
func canBeArgumentLabel(allowDollarIdentifier: Bool = false) -> Bool {
265-
// `inout` is reserved as an argument label for historical reasons.
266-
if TypeSpecifier(lexeme: self) == .inout {
267-
return false
268-
}
269-
270-
switch self.rawTokenKind {
264+
func isArgumentLabel(allowDollarIdentifier: Bool = false) -> Bool {
265+
switch self {
271266
case .identifier, .wildcard:
272267
// Identifiers, escaped identifiers, and '_' can be argument labels.
273268
return true
274269
case .dollarIdentifier:
275270
return allowDollarIdentifier
271+
case .keyword(.inout):
272+
// `inout` cannot be an argument label for historical reasons.
273+
return false
276274
default:
277275
// All other keywords can be argument labels.
278276
return self.isLexerClassifiedKeyword

Sources/SwiftParser/Nominals.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,9 @@ extension Parser {
223223
inheritanceClause: nil,
224224
genericWhereClause: nil,
225225
memberBlock: RawMemberDeclBlockSyntax(
226-
leftBrace: RawTokenSyntax(missing: .leftBrace, arena: self.arena),
226+
leftBrace: missingToken(.leftBrace),
227227
members: RawMemberDeclListSyntax(elements: [], arena: self.arena),
228-
rightBrace: RawTokenSyntax(missing: .rightBrace, arena: self.arena),
228+
rightBrace: missingToken(.rightBrace),
229229
arena: self.arena
230230
),
231231
arena: self.arena

Sources/SwiftParser/Parameters.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ extension Parser {
6464
fileprivate mutating func parseParameterNames() -> ParameterNames {
6565
let unexpectedBeforeFirstName: RawUnexpectedNodesSyntax?
6666
let firstName: RawTokenSyntax?
67-
if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) {
67+
if self.atArgumentLabel(allowDollarIdentifier: true) {
6868
(unexpectedBeforeFirstName, firstName) = self.parseArgumentLabel()
6969
} else {
7070
(unexpectedBeforeFirstName, firstName) = (nil, nil)
7171
}
7272

7373
let unexpectedBeforeSecondName: RawUnexpectedNodesSyntax?
7474
let secondName: RawTokenSyntax?
75-
if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) {
75+
if self.atArgumentLabel(allowDollarIdentifier: true) {
7676
(unexpectedBeforeSecondName, secondName) = self.parseArgumentLabel()
7777
} else {
7878
(unexpectedBeforeSecondName, secondName) = (nil, nil)
@@ -280,7 +280,7 @@ extension Parser {
280280
// to be an argument label, don't parse any parameters.
281281
let shouldSkipParameterParsing =
282282
lparen.isMissing
283-
&& (!currentToken.canBeArgumentLabel(allowDollarIdentifier: true) || currentToken.isLexerClassifiedKeyword)
283+
&& (!self.atArgumentLabel(allowDollarIdentifier: true) || currentToken.isLexerClassifiedKeyword)
284284
if !shouldSkipParameterParsing {
285285
var keepGoing = true
286286
var loopProgress = LoopProgressCondition()

Sources/SwiftParser/Parser.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ public struct Parser {
9090
var arena: ParsingSyntaxArena
9191
/// A view of the sequence of lexemes in the input.
9292
var lexemes: Lexer.LexemeSequence
93-
/// The current token. If there was no input, this token will have a kind of `.endOfFile`.
93+
/// The current token that should be consumed next.
94+
///
95+
/// If the end of the source file is reached, this is `.endOfFile`.
96+
///
97+
/// - Important: You should almost never need to access this token directly
98+
/// in the parser. Instead, prefer using the `at` methods.
9499
var currentToken: Lexer.Lexeme
95100

96101
/// The current nesting level, i.e. the number of tokens that

Sources/SwiftParser/Patterns.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ extension Parser.Lookahead {
345345
}
346346

347347
// To have a parameter name here, we need a name.
348-
guard self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) else {
348+
guard self.atArgumentLabel(allowDollarIdentifier: true) else {
349349
return false
350350
}
351351

@@ -356,7 +356,7 @@ extension Parser.Lookahead {
356356
}
357357

358358
// If the next token can be an argument label, we might have a name.
359-
if nextTok.canBeArgumentLabel(allowDollarIdentifier: true) {
359+
if nextTok.isArgumentLabel(allowDollarIdentifier: true) {
360360
// If the first name wasn't a contextual keyword, we're done.
361361
if !self.at(.keyword(.isolated))
362362
&& !self.at(.keyword(.some))
@@ -379,7 +379,7 @@ extension Parser.Lookahead {
379379
return true // isolated :
380380
}
381381
self.consumeAnyToken()
382-
return self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon
382+
return self.atArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon
383383
}
384384
}
385385

Sources/SwiftParser/Statements.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ extension Parser {
786786
}
787787

788788
mutating func parseOptionalControlTransferTarget() -> RawTokenSyntax? {
789-
guard !self.currentToken.isAtStartOfLine else {
789+
guard !self.atStartOfLine else {
790790
return nil
791791
}
792792

Sources/SwiftParser/TokenConsumer.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ extension TokenConsumer {
128128
/// If this is the case, return the `Subset` case that the parser is positioned in
129129
/// as well as a handle to consume that token.
130130
@inline(__always)
131-
mutating func at<SpecSet: TokenSpecSet>(anyIn specSet: SpecSet.Type) -> (SpecSet, TokenConsumptionHandle)? {
131+
mutating func at<SpecSet: TokenSpecSet>(anyIn specSet: SpecSet.Type) -> (spec: SpecSet, handle: TokenConsumptionHandle)? {
132132
#if SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION
133133
if shouldRecordAlternativeTokenChoices {
134134
recordAlternativeTokenChoice(for: self.currentToken, choices: specSet.allCases.map(\.spec))
@@ -308,4 +308,9 @@ extension TokenConsumer {
308308
return false
309309
}
310310
}
311+
312+
/// Whether the current token can be a function argument label.
313+
func atArgumentLabel(allowDollarIdentifier: Bool = false) -> Bool {
314+
return self.currentToken.isArgumentLabel(allowDollarIdentifier: allowDollarIdentifier)
315+
}
311316
}

Sources/SwiftParser/TopLevel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension Parser {
2626
/// as unexpected nodes that have the `isMaximumNestingLevelOverflow` bit set.
2727
/// Check this in places that are likely to cause deep recursion and if this returns non-nil, abort parsing.
2828
mutating func remainingTokensIfMaximumNestingLevelReached() -> RawUnexpectedNodesSyntax? {
29-
if nestingLevel > self.maximumNestingLevel && self.currentToken.rawTokenKind != .endOfFile {
29+
if nestingLevel > self.maximumNestingLevel && !self.at(.endOfFile) {
3030
let remainingTokens = self.consumeRemainingTokens()
3131
return RawUnexpectedNodesSyntax(elements: remainingTokens, isMaximumNestingLevelOverflow: true, arena: self.arena)
3232
} else {

Sources/SwiftParser/Types.swift

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -196,19 +196,49 @@ extension Parser {
196196
mutating func parseSimpleType(
197197
stopAtFirstPeriod: Bool = false
198198
) -> RawTypeSyntax {
199+
enum TypeBaseStart: TokenSpecSet {
200+
case `Self`
201+
case `Any`
202+
case identifier
203+
case leftParen
204+
case leftSquare
205+
case wildcard
206+
207+
init?(lexeme: Lexer.Lexeme) {
208+
switch PrepareForKeywordMatch(lexeme) {
209+
case .keyword(.Self): self = .Self
210+
case .keyword(.Any): self = .Any
211+
case .identifier: self = .identifier
212+
case .leftParen: self = .leftParen
213+
case .leftSquare: self = .leftSquare
214+
case .wildcard: self = .wildcard
215+
default: return nil
216+
}
217+
}
218+
219+
var spec: TokenSpec {
220+
switch self {
221+
case .Self: return .keyword(.Self)
222+
case .Any: return .keyword(.Any)
223+
case .identifier: return .identifier
224+
case .leftParen: return .leftParen
225+
case .leftSquare: return .leftSquare
226+
case .wildcard: return .wildcard
227+
}
228+
}
229+
}
230+
199231
var base: RawTypeSyntax
200-
switch self.currentToken {
201-
case TokenSpec(.Self),
202-
TokenSpec(.Any),
203-
TokenSpec(.identifier):
232+
switch self.at(anyIn: TypeBaseStart.self)?.spec {
233+
case .Self, .Any, .identifier:
204234
base = self.parseTypeIdentifier()
205-
case TokenSpec(.leftParen):
235+
case .leftParen:
206236
base = RawTypeSyntax(self.parseTupleTypeBody())
207-
case TokenSpec(.leftSquare):
237+
case .leftSquare:
208238
base = RawTypeSyntax(self.parseCollectionType())
209-
case TokenSpec(.wildcard):
239+
case .wildcard:
210240
base = RawTypeSyntax(self.parsePlaceholderType())
211-
default:
241+
case nil:
212242
return RawTypeSyntax(RawMissingTypeSyntax(arena: self.arena))
213243
}
214244

@@ -427,7 +457,7 @@ extension Parser {
427457
second = nil
428458
unexpectedBeforeColon = nil
429459
colon = parsedColon
430-
} else if self.currentToken.canBeArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
460+
} else if self.atArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
431461
(unexpectedBeforeSecond, second) = self.parseArgumentLabel()
432462
(unexpectedBeforeColon, colon) = self.expect(.colon)
433463
} else {
@@ -714,7 +744,7 @@ extension Parser.Lookahead {
714744
// by a type annotation.
715745
if self.startsParameterName(isClosure: false, allowMisplacedSpecifierRecovery: false) {
716746
self.consumeAnyToken()
717-
if self.currentToken.canBeArgumentLabel() {
747+
if self.atArgumentLabel() {
718748
self.consumeAnyToken()
719749
guard self.at(.colon) else {
720750
return false
@@ -973,12 +1003,6 @@ extension Parser {
9731003
}
9741004

9751005
extension Lexer.Lexeme {
976-
var isAnyOperator: Bool {
977-
return self.rawTokenKind == .binaryOperator
978-
|| self.rawTokenKind == .postfixOperator
979-
|| self.rawTokenKind == .prefixOperator
980-
}
981-
9821006
var isGenericTypeDisambiguatingToken: Bool {
9831007
switch self.rawTokenKind {
9841008
case .rightParen,

0 commit comments

Comments
 (0)