Skip to content

Commit 5136d87

Browse files
committed
unified/swift: Replace reduce_left with Rust helpers
(Both reduce_left and map are still supported, but we could remove them at this point.) I think this way of writing things makes the intent a lot clearer -- it avoids extending the yeast rule language with complicated constructs, pushing the complexity (such as it is) into Rust instead.
1 parent 474bcd4 commit 5136d87

1 file changed

Lines changed: 45 additions & 7 deletions

File tree

  • unified/extractor/src/languages/swift

unified/extractor/src/languages/swift/swift.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,41 @@ fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Optio
5454
}
5555
}
5656

57+
/// Combine a list of boolean sub-conditions into a single expression by
58+
/// left-folding with the infix `&&` operator. Used by control-flow
59+
/// rules (`if`, `guard`, `while`, `repeat-while`) whose tree-sitter
60+
/// nodes carry one or more comma-separated conditions that the target
61+
/// AST represents as a single `condition:` field. Panics on an empty
62+
/// input because every caller's grammar guarantees at least one
63+
/// condition.
64+
fn and_chain(
65+
ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>,
66+
conds: Vec<yeast::NodeRef>,
67+
) -> yeast::Id {
68+
conds.into_iter()
69+
.map(yeast::Id::from)
70+
.reduce(|acc, elem| {
71+
tree!((binary_expr operator: (infix_operator "&&") left: {acc} right: {elem}))
72+
})
73+
.expect("control-flow statement must have at least one condition")
74+
}
75+
76+
/// Translate a multi-part identifier (for example `Foo.Bar.Baz`) into a
77+
/// `member_access_expr` chain rooted at a `name_expr` over the first
78+
/// part. Panics on an empty input because the grammar's `_+` quantifier
79+
/// guarantees at least one part.
80+
fn member_chain(
81+
ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>,
82+
parts: Vec<yeast::NodeRef>,
83+
) -> yeast::Id {
84+
let mut iter = parts.into_iter();
85+
let first = iter.next().expect("identifier with `part:` must have at least one part");
86+
let init = tree!((name_expr identifier: (identifier #{first})));
87+
iter.fold(init, |acc, elem| {
88+
tree!((member_access_expr base: {acc} member: (identifier #{elem})))
89+
})
90+
}
91+
5792
fn translation_rules() -> Vec<Rule<SwiftContext>> {
5893
vec![
5994
// ---- Top-level ----
@@ -585,11 +620,12 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
585620
argument: (argument value: {closure}))
586621
),
587622
// ---- Control flow ----
623+
// If statement
588624
rule!(
589625
(if_statement condition: _* @cond body: @then_body else_branch: _? @else_stmts)
590626
=>
591627
(if_expr
592-
condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem}))
628+
condition: {and_chain(&mut ctx, cond)}
593629
then: {then_body}
594630
else: {..else_stmts})
595631
),
@@ -598,7 +634,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
598634
(guard_statement condition: _* @cond body: (block statement: _* @else_stmts))
599635
=>
600636
(guard_if_stmt
601-
condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem}))
637+
condition: {and_chain(&mut ctx, cond)}
602638
else: (block stmt: {..else_stmts}))
603639
),
604640
// Ternary expression → if_expr
@@ -676,13 +712,17 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
676712
rule!(
677713
(while_statement condition: _* @cond body: (block statement: _* @body))
678714
=>
679-
(while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body}))
715+
(while_stmt
716+
condition: {and_chain(&mut ctx, cond)}
717+
body: (block stmt: {..body}))
680718
),
681719
// Repeat-while loop
682720
rule!(
683721
(repeat_while_statement condition: _* @cond body: (block statement: _* @body))
684722
=>
685-
(do_while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body}))
723+
(do_while_stmt
724+
condition: {and_chain(&mut ctx, cond)}
725+
body: (block stmt: {..body}))
686726
),
687727
// Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token.
688728
rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{
@@ -770,9 +810,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
770810
rule!(
771811
(identifier part: _+ @parts)
772812
=>
773-
{parts}.reduce_left(
774-
first -> (name_expr identifier: (identifier #{first})),
775-
acc, elem -> (member_access_expr base: {acc} member: (identifier #{elem})))
813+
{member_chain(&mut ctx, parts)}
776814
),
777815
// Scoped import declaration (for example `import struct Foo.Bar`):
778816
// flatten the identifier parts into a member_access_expr and bind the

0 commit comments

Comments
 (0)