From 8a3bb915e41ef993550a47426b47eb49781f3fd7 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 11:58:54 +0000 Subject: [PATCH] unified/swift: Use `tree!` instead of ctx.node Cleans up a few places where we were constructing trees piece by piece rather than using the `tree!` macro. In the process, Copilot noticed an issue that should probably be addressed: the labeled_statement rule can never fire, since there are no such nodes in the input. This is possibly a simple as making _labeled_statement (which _does_ exist) named, but I haven't attempted this. Finally, a small change to yeast makes it so that the contents of a {} interpolation can be a Rust block (previously it could only be a single expression). This avoids the need to double-wrap instances where you want to interpolate a single node produced as the final value of some block. --- shared/yeast-macros/src/parse.rs | 14 ++++++------- shared/yeast/doc/yeast.md | 16 +++++++++++++- .../extractor/src/languages/swift/swift.rs | 21 +++++++------------ 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 96cfa808775..3c1d4d44bec 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -359,7 +359,7 @@ fn parse_direct_node(tokens: &mut Tokens, ctx: &Ident) -> Result { Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => { let group = expect_group(tokens, Delimiter::Brace)?; let expr = group.stream(); - Ok(quote! { ::std::convert::Into::::into(#expr) }) + Ok(quote! { ::std::convert::Into::::into({ #expr }) }) } Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => { let group = expect_group(tokens, Delimiter::Parenthesis)?; @@ -396,7 +396,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result Result::into) + { #expr }.into_iter().map(::std::convert::Into::::into) } } else { let expr = group.stream(); - quote! { (#expr).into_iter() } + quote! { { #expr }.into_iter() } }; let chained = parse_chain_suffix(tokens, ctx, base)?; stmts.push(quote! { @@ -617,11 +617,11 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) + { #expr }.into_iter().map(::std::convert::Into::::into) } } else { let expr = group.stream(); - quote! { (#expr).into_iter() } + quote! { { #expr }.into_iter() } }; let chained = parse_chain_suffix(tokens, ctx, base)?; items.push(quote! { @@ -630,7 +630,7 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into(#expr)); + __nodes.push(::std::convert::Into::::into({ #expr })); }); } continue; diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 823bf1c1942..1700029b43c 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -265,7 +265,21 @@ occurrences of the same `$name` within one `BuildCtx` share the same value: ) ``` -`{..expr}` splices a `Vec` (or any iterable of `Id`): +The contents of `{…}` are treated as a Rust block, so multi-statement +expressions (with `let` bindings) work too: + +```rust +(assignment + left: {tmp} + right: { + let lit = ctx.literal("integer", "0"); + tree!((binary_expr op: (operator "+") left: {tmp} right: {lit})) + }) +``` + +`{..expr}` splices a `Vec` (or any iterable of `Id`); the contents +are likewise a Rust block, so the splice can be the result of arbitrary +computation: ```rust yeast::trees!(ctx, diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index e2ae10bcbd1..7820d81e29f 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -725,11 +725,11 @@ fn translation_rules() -> Vec> { body: (block stmt: {..body})) ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. - rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{ + rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => { let text = ctx.ast.source_text(lbl.into()); - let name = ctx.literal("identifier", &text[..text.len() - 1]); - vec![ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] - }}), + let name = &text[..text.len() - 1]; + tree!((labeled_stmt label: (identifier #{name}) stmt: {stmt})) + }), // ---- Collections ---- // Array literal rule!((array_literal element: _* @elems) => (array_literal element: {..elems})), @@ -739,16 +739,9 @@ fn translation_rules() -> Vec> { rule!( (dictionary_literal key: _* @keys value: _* @vals) => - (map_literal element: {..{ - keys.iter().zip(vals.iter()).map(|(&k, &v)| { - let k_id: usize = k.into(); - let v_id: usize = v.into(); - ctx.node("key_value_pair", vec![ - ("key", vec![k_id]), - ("value", vec![v_id]), - ]) - }).collect::>() - }}) + (map_literal element: {..keys.into_iter().zip(vals).map(|(k, v)| + tree!((key_value_pair key: {k} value: {v})) + )}) ), rule!((dictionary_literal element: _* @elems) => (map_literal element: {..elems})), rule!((dictionary_literal_item key: @k value: @v) => (key_value_pair key: {k} value: {v})),