Skip to content

[Parser] Accept 'self' after 'each' #1895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 42 additions & 26 deletions Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,46 @@ extension Parser {
arena: self.arena
)
)

case (.repeat, let handle)?:
// 'repeat' is the start of a pack expansion expression.
return RawExprSyntax(parsePackExpansionExpr(repeatHandle: handle, flavor, pattern: pattern))
Comment on lines +529 to +531
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that we already never parse repeat as a function call in the following (not a regression introduced by this PR, this just made me realize it…

func repeat() {}
repeat()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need backticks. Before repeat expression for SE-0393 repeat was only for repeat {} while <condition> statement. Now repeat is a statement only if it's followed by {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for reminding me. Now that you mention it, I think I left the same comment somewhere else already 🤦🏽


case (.each, let handle)?:
// `each` is only contextually a keyword, if it's followed by an
// identifier or 'self' on the same line. We do this to ensure that we do
// not break any 'each' functions defined by users. This is following with
// what we have done for the consume keyword.
switch self.peek() {
case TokenSpec(.identifier, allowAtStartOfLine: false),
TokenSpec(.dollarIdentifier, allowAtStartOfLine: false),
TokenSpec(.self, allowAtStartOfLine: false):
break
default:
// Break out of `outer switch` on failure.
break EXPR_PREFIX
}

let each = self.eat(handle)
let packRef = self.parseSequenceExpressionElement(flavor, pattern: pattern)
return RawExprSyntax(
RawPackElementExprSyntax(
eachKeyword: each,
packRefExpr: packRef,
arena: self.arena
)
)

case (.any, _)?:
// `any` is only contextually a keyword if it's followed by an identifier
// on the same line.
guard case TokenSpec(.identifier, allowAtStartOfLine: false) = self.peek() else {
break EXPR_PREFIX
}
// 'any' is parsed as a part of 'type'.
let type = self.parseType()
return RawExprSyntax(RawTypeExprSyntax(type: type, arena: self.arena))

case nil:
break
}
Expand All @@ -549,10 +589,6 @@ extension Parser {
// tryLexRegexLiteral(/*forUnappliedOperator*/ false)

switch self.currentToken {
case TokenSpec(.repeat):
// 'repeat' is the start of a pack expansion expression.
return RawExprSyntax(parsePackExpansionExpr(flavor, pattern: pattern))

// Try parse an 'if' or 'switch' as an expression. Note we do this here in
// parseUnaryExpression as we don't allow postfix syntax to hang off such
// expressions to avoid ambiguities such as postfix '.member', which can
Expand Down Expand Up @@ -1234,27 +1270,6 @@ extension Parser {
return RawExprSyntax(RawUnresolvedPatternExprSyntax(pattern: pattern, arena: self.arena))
}

// We might have a contextual keyword followed by an identifier.
// 'each <identifier>' is a pack element expr, and 'any <identifier>'
// is an existential type expr.
if self.peek().rawTokenKind == .identifier, !self.peek().isAtStartOfLine {
if self.at(.keyword(.any)) {
let ty = self.parseType()
return RawExprSyntax(RawTypeExprSyntax(type: ty, arena: self.arena))
}

if let each = self.consume(if: .keyword(.each)) {
let packRef = self.parseSequenceExpressionElement(flavor, pattern: pattern)
return RawExprSyntax(
RawPackElementExprSyntax(
eachKeyword: each,
packRefExpr: packRef,
arena: self.arena
)
)
}
}

return RawExprSyntax(self.parseIdentifierExpression())
case (.Self, _)?: // Self
return RawExprSyntax(self.parseIdentifierExpression())
Expand Down Expand Up @@ -1493,10 +1508,11 @@ extension Parser {
/// pack-expansion-expression → 'repeat' pattern-expression
/// pattern-expression → expression
mutating func parsePackExpansionExpr(
repeatHandle: TokenConsumptionHandle,
_ flavor: ExprFlavor,
pattern: PatternContext
) -> RawPackExpansionExprSyntax {
let repeatKeyword = self.consumeAnyToken()
let repeatKeyword = self.eat(repeatHandle)
let patternExpr = self.parseExpression(flavor, pattern: pattern)

return RawPackExpansionExprSyntax(
Expand Down
9 changes: 9 additions & 0 deletions Sources/SwiftParser/TokenSpecSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,9 @@ enum ExpressionModifierKeyword: TokenSpecSet {
case `try`
case consume
case copy
case `repeat`
case each
case any

init?(lexeme: Lexer.Lexeme) {
switch PrepareForKeywordMatch(lexeme) {
Expand All @@ -625,6 +628,9 @@ enum ExpressionModifierKeyword: TokenSpecSet {
case TokenSpec(.try): self = .try
case TokenSpec(.consume): self = .consume
case TokenSpec(.copy): self = .copy
case TokenSpec(.repeat): self = .repeat
case TokenSpec(.each): self = .each
case TokenSpec(.any): self = .any
default: return nil
}
}
Expand All @@ -637,6 +643,9 @@ enum ExpressionModifierKeyword: TokenSpecSet {
case .consume: return .keyword(.consume)
case .copy: return .keyword(.copy)
case .try: return .keyword(.try)
case .repeat: return .keyword(.repeat)
case .each: return .keyword(.each)
case .any: return .keyword(.any)
}
}
}
Expand Down