Skip to content
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
12 changes: 9 additions & 3 deletions unified/extractor/ast_types.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,7 @@ supertypes:
- tuple_pattern
- constructor_pattern
- or_pattern
- conditional_pattern
- ignore_pattern
- expr_equality_pattern
- bulk_importing_pattern
Expand DownExpand Up@@ -369,7 +370,6 @@ named:
catch_clause:
modifier*: modifier
pattern?: pattern
guard?: expr
body: block

# `switch value { case pattern: body case ...: default: body }`
Expand All@@ -381,11 +381,9 @@ named:
# A single `case ...:` (or `default:`) entry in a switch.
# An entry with multiple `case p1, p2:` patterns uses an `or_pattern`.
# A `default:` entry has no pattern.
# An optional `guard` corresponds to a `where`-clause on the case.
switch_case:
modifier*: modifier
pattern?: pattern
guard?: expr
body: block

# Evaluate 'expr' and match its result against 'pattern', and return true if it matches.
Expand DownExpand Up@@ -446,6 +444,14 @@ named:
modifier*: modifier
pattern*: pattern

# A pattern that matches against a nested pattern, and subsequently checks a condition.
# The match is rejected if the condition does not hold.
# Variables bound in the nested pattern are in scope within the condition.
conditional_pattern:
modifier*: modifier
condition: expr
pattern: pattern

# A pattern with an optional associated name.
pattern_element:
modifier*: modifier
Expand Down
47 changes: 31 additions & 16 deletions unified/extractor/src/languages/swift/swift.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -94,6 +94,19 @@ fn and_chain(
.expect("control-flow statement must have at least one condition")
}

/// Return the only pattern unchanged when there is exactly one, otherwise
/// wrap the list in an `or_pattern`.
fn make_or_pattern(
ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>,
items: Vec<yeast::Id>,
) -> yeast::Id {
if items.len() == 1 {
items[0]
} else {
tree!((or_pattern pattern: {items}))
}
}

/// Translate a multi-part identifier (for example `Foo.Bar.Baz`) into a
/// `member_access_expr` chain rooted at a `name_expr` over the first
/// part. Panics on an empty input because the grammar's `_+` quantifier
Expand DownExpand Up@@ -746,22 +759,17 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
rule!(
(switchCase label: (switchCaseLabel caseItems: _* @items) statements: _* @body)
=>
switch_case {
let pattern = if items.len() == 1 {
items[0]
} else {
tree!((or_pattern pattern: {items}))
};
tree!((switch_case pattern: {pattern} body: (block stmt: {body})))
}
(switch_case
pattern: {make_or_pattern(&mut ctx, items)}
body: (block stmt: {body}))
),
rule!(
(switchCase label: (switchDefaultLabel) statements: _* @body)
=>
(switch_case body: (block stmt: {body}))
),
// A single case item unwraps to its pattern (used as an `or_pattern`
// element).
// A single case item unwraps to its pattern, possibly boxed in conditional_pattern
rule!((switchCaseItem pattern: @p whereClause: (whereClause condition: @cond)) => (conditional_pattern pattern: { p } condition: {cond})),
rule!((switchCaseItem pattern: @p) => pattern { p }),
// A pattern-matching condition (`if case let x = e`, `if case .foo(let x)
// = e`) becomes a `pattern_guard_expr`: the matched pattern and the
Expand DownExpand Up@@ -877,17 +885,24 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
body: {body}
catch_clause: {catches})
),
// Catch block with bound identifier; optional where-clause guard.
rule!(
(catchItem pattern: @pattern whereClause: (whereClause condition: @guard))
=>
(conditional_pattern pattern: {pattern} condition: {guard})
),
rule!(
(catchItem pattern: @pattern)
=>
pattern {pattern}
),
// Catch block with one or more patterns (which have been translated by the catchItem rules)
rule!(
(catchClause
catchItems: (catchItem
pattern: @pattern
whereClause: (whereClause condition: @guard)?)
catchItems: _+ @patterns
body: @body)
=>
(catch_clause
pattern: {pattern}
guard: {guard}
pattern: {make_or_pattern(&mut ctx, patterns)}
body: {body})
),
// Catch block without error binding
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
switch n {
case let x where x > 0:
print("positive")
case let y where y < 0, 0:
print("non-positive")
default:
print("other")
}

---

sourceFile
endOfFileToken: endOfFile
statements:
codeBlockItem
item:
expressionStmt
expression:
switchExpr
leftBrace: {
rightBrace: }
cases:
switchCase
label:
switchCaseLabel
colon: :
caseKeyword: case
caseItems:
switchCaseItem
pattern:
valueBindingPattern
pattern:
identifierPattern
identifier: identifier "x"
bindingSpecifier: let
whereClause:
whereClause
condition:
infixOperatorExpr
operator:
binaryOperatorExpr
operator: binaryOperator ">"
leftOperand:
declReferenceExpr
baseName: identifier "x"
rightOperand:
integerLiteralExpr
literal: integerLiteral "0"
whereKeyword: where
statements:
codeBlockItem
item:
functionCallExpr
leftParen: (
rightParen: )
arguments:
labeledExpr
expression:
stringLiteralExpr
closingQuote: "
openingQuote: "
segments:
stringSegment
content: stringSegment "positive"
additionalTrailingClosures:
calledExpression:
declReferenceExpr
baseName: identifier "print"
switchCase
label:
switchCaseLabel
colon: :
caseKeyword: case
caseItems:
switchCaseItem
trailingComma: ,
pattern:
valueBindingPattern
pattern:
identifierPattern
identifier: identifier "y"
bindingSpecifier: let
whereClause:
whereClause
condition:
infixOperatorExpr
operator:
binaryOperatorExpr
operator: binaryOperator "<"
leftOperand:
declReferenceExpr
baseName: identifier "y"
rightOperand:
integerLiteralExpr
literal: integerLiteral "0"
whereKeyword: where
switchCaseItem
pattern:
expressionPattern
expression:
integerLiteralExpr
literal: integerLiteral "0"
statements:
codeBlockItem
item:
functionCallExpr
leftParen: (
rightParen: )
arguments:
labeledExpr
expression:
stringLiteralExpr
closingQuote: "
openingQuote: "
segments:
stringSegment
content: stringSegment "non-positive"
additionalTrailingClosures:
calledExpression:
declReferenceExpr
baseName: identifier "print"
switchCase
label:
switchDefaultLabel
colon: :
defaultKeyword: default
statements:
codeBlockItem
item:
functionCallExpr
leftParen: (
rightParen: )
arguments:
labeledExpr
expression:
stringLiteralExpr
closingQuote: "
openingQuote: "
segments:
stringSegment
content: stringSegment "other"
additionalTrailingClosures:
calledExpression:
declReferenceExpr
baseName: identifier "print"
subject:
declReferenceExpr
baseName: identifier "n"
switchKeyword: switch

---

top_level
body:
block
stmt:
switch_expr
value:
name_expr
identifier: identifier "n"
case:
switch_case
pattern:
conditional_pattern
condition:
binary_expr
left:
name_expr
identifier: identifier "x"
operator: infix_operator ">"
right: int_literal "0"
pattern:
name_pattern
identifier: identifier "x"
body:
block
stmt:
call_expr
callee:
name_expr
identifier: identifier "print"
argument:
argument
value: string_literal "\"positive\""
switch_case
pattern:
or_pattern
pattern:
conditional_pattern
condition:
binary_expr
left:
name_expr
identifier: identifier "y"
operator: infix_operator "<"
right: int_literal "0"
pattern:
name_pattern
identifier: identifier "y"
expr_equality_pattern
expr: int_literal "0"
body:
block
stmt:
call_expr
callee:
name_expr
identifier: identifier "print"
argument:
argument
value: string_literal "\"non-positive\""
switch_case
body:
block
stmt:
call_expr
callee:
name_expr
identifier: identifier "print"
argument:
argument
value: string_literal "\"other\""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
switch n {
case let x where x > 0:
print("positive")
case let y where y < 0, 0:
print("non-positive")
default:
print("other")
}
Loading
Loading