Skip to content

Commit f1cc1e5

Browse files
authored
Merge pull request #22084 from github/tausbn/yeast-miscellaneous-cleanup
yeast: Miscellaneous cleanup
2 parents 3983e4d + 041a8e6 commit f1cc1e5

12 files changed

Lines changed: 364 additions & 565 deletions

File tree

shared/tree-sitter-extractor/src/extractor/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<'a> AstNode for Node<'a> {
6666

6767
impl AstNode for yeast::Node {
6868
fn kind(&self) -> &str {
69-
yeast::Node::kind(self)
69+
yeast::Node::kind_name(self)
7070
}
7171
fn is_named(&self) -> bool {
7272
yeast::Node::is_named(self)
@@ -882,7 +882,6 @@ fn emit_extras_in(visitor: &mut Visitor, node: Node<'_>) {
882882
}
883883

884884
fn traverse_yeast(tree: &yeast::Ast, visitor: &mut Visitor) {
885-
use yeast::Cursor;
886885
let mut cursor = tree.walk();
887886
visitor.enter_node(cursor.node());
888887
let mut recurse = true;

shared/yeast-macros/src/lib.rs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,14 @@ pub fn query(input: TokenStream) -> TokenStream {
4141
/// (kind "literal") - leaf with static content
4242
/// (kind #{expr}) - leaf with computed content (expr.to_string())
4343
/// (kind $fresh) - leaf with auto-generated unique name
44-
/// {expr} - embed a Rust expression returning Id
45-
/// {..expr} - splice an iterable of Id (in child/field position)
46-
/// field: {..expr} - splice into a named field
47-
/// {expr}.map(p -> tpl) - apply tpl to each element; splice result
48-
/// {expr}.reduce_left(f -> init, acc, e -> fold)
49-
/// - fold with per-element init; splice 0 or 1 result
44+
/// {expr} - embed a Rust expression, dispatched via
45+
/// the `IntoFieldIds` trait: `Id` pushes a
46+
/// single id; iterables (`Vec<Id>`,
47+
/// `Option<Id>`, iterator chains) splice
48+
/// their elements
49+
/// field: {expr} - extend a named field with `{expr}`'s ids
5050
/// ```
5151
///
52-
/// Chain syntax after `{expr}` or `{..expr}`:
53-
/// - `.map(param -> template)` — one output node per input element.
54-
/// - `.reduce_left(first -> init, acc, elem -> fold)` — fold left; the first
55-
/// element is converted by `init`, subsequent elements are folded by `fold`
56-
/// with the accumulator bound to `acc`. An empty iterable yields nothing.
57-
/// - Chains always splice (the result is iterable).
58-
/// - Multiple chains can be chained, e.g. `.map(...).reduce_left(...)`.
59-
///
6052
/// Can be called with an explicit context or using the implicit context
6153
/// from an enclosing `rule!`:
6254
///
@@ -100,7 +92,7 @@ pub fn trees(input: TokenStream) -> TokenStream {
10092
/// rule!(
10193
/// (query_pattern field: (_) @name (kind)* @repeated (_)? @optional)
10294
/// =>
103-
/// (output_template field: {name} {..repeated})
95+
/// (output_template field: {name} {repeated})
10496
/// )
10597
///
10698
/// // Shorthand: captures become fields on the output node

shared/yeast-macros/src/parse.rs

Lines changed: 34 additions & 179 deletions
Large diffs are not rendered by default.

shared/yeast/doc/yeast.md

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ yeast::tree!(ctx,
214214
```rust
215215
yeast::trees!(ctx,
216216
(assignment left: {tmp} right: {right})
217-
{..body}
217+
{body}
218218
)
219219
```
220220

@@ -256,12 +256,26 @@ occurrences of the same `$name` within one `BuildCtx` share the same value:
256256

257257
### Embedded Rust expressions
258258

259-
`{expr}` embeds a Rust expression that returns a single node `Id`:
259+
`{expr}` embeds a Rust expression whose value is appended to the
260+
enclosing field (or to the rule body's id list). Dispatch happens via
261+
the [`IntoFieldIds`] trait, which is implemented for:
262+
263+
- `Id` — pushes the single id.
264+
- Any `IntoIterator<Item: Into<Id>>` — extends with all yielded ids
265+
(covers `Vec<Id>`, `Option<Id>`, iterator chains, etc.).
266+
267+
So the same `{expr}` syntax handles single ids, splices, and zero-or-many
268+
options uniformly:
260269

261270
```rust
262271
(assignment
263-
left: {some_node_id} // insert a pre-built node
264-
right: {rhs} // insert a captured value (inside rule!)
272+
left: {some_node_id} // a single Id
273+
right: {rhs} // a captured value (inside rule!)
274+
)
275+
276+
yeast::trees!(ctx,
277+
(assignment left: {tmp} right: {right})
278+
{extra_nodes} // splices a Vec<Id>
265279
)
266280
```
267281

@@ -277,21 +291,17 @@ expressions (with `let` bindings) work too:
277291
})
278292
```
279293

280-
`{..expr}` splices a `Vec<Id>` (or any iterable of `Id`); the contents
281-
are likewise a Rust block, so the splice can be the result of arbitrary
282-
computation:
294+
Inside `rule!`, captures are Rust variables — `{name}` works for
295+
single, optional, and repeated captures alike:
283296

284297
```rust
285-
yeast::trees!(ctx,
286-
(assignment left: {tmp} right: {right})
287-
{..extra_nodes} // splice a Vec<Id>
298+
rule!(
299+
(assignment left: @lhs right: _* @parts)
300+
=>
301+
(assignment left: {lhs} right: (block stmt: {parts}))
288302
)
289303
```
290304

291-
Inside `rule!`, captures are Rust variables, so `{name}` inserts a
292-
single capture (`Id`) and `{..name}` splices a repeated capture
293-
(`Vec<Id>`).
294-
295305
### Raw captures (`@@name`)
296306

297307
The default `@name` capture marker is *auto-translated*: in OneShot
@@ -302,15 +312,15 @@ already conforms to the output schema.
302312
For rules that need the raw (input-schema) capture — typically to read
303313
its source text or to translate it explicitly with mutable context
304314
state between calls — use `@@name` instead. The body sees the original
305-
input-schema `NodeRef`:
315+
input-schema `Id`:
306316

307317
```rust
308318
yeast::rule!(
309319
(assignment left: (_) @@raw_lhs right: (_) @rhs)
310320
=>
311321
{
312322
// raw_lhs is untranslated: read its original source text.
313-
let text = ctx.ast.source_text(raw_lhs.into());
323+
let text = ctx.ast.source_text(raw_lhs);
314324
// rhs is already translated by the auto-translate prefix.
315325
tree!((call
316326
method: (identifier #{text.as_str()})

shared/yeast/src/build.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,6 @@ impl<'a, C> BuildCtx<'a, C> {
158158
self.ast
159159
.create_named_token_with_range(kind, generated, self.source_range)
160160
}
161-
162-
/// Prepend a value to a field of an existing node.
163-
pub fn prepend_field(&mut self, node_id: Id, field_name: &str, value_id: Id) {
164-
let field_id = self
165-
.ast
166-
.field_id_for_name(field_name)
167-
.unwrap_or_else(|| panic!("build: field '{field_name}' not found"));
168-
self.ast.prepend_field_child(node_id, field_id, value_id);
169-
}
170161
}
171162

172163
impl<C: Clone> BuildCtx<'_, C> {
@@ -176,9 +167,6 @@ impl<C: Clone> BuildCtx<'_, C> {
176167
/// (translation is not meaningful when input and output share a
177168
/// schema).
178169
///
179-
/// Accepts any value convertible to [`Id`] (including [`crate::NodeRef`]),
180-
/// so manual rules can pass capture bindings directly without unwrapping.
181-
///
182170
/// Errors if this `BuildCtx` was constructed by hand (without a
183171
/// translator handle) — for example, in unit tests that don't go
184172
/// through the rule driver.
@@ -189,20 +177,6 @@ impl<C: Clone> BuildCtx<'_, C> {
189177
None => Err("translate() called on a BuildCtx without a translator handle".into()),
190178
}
191179
}
192-
193-
/// Translate an optional capture, returning the first translated id or
194-
/// `None`. Convenience for `?`-quantifier captures (`Option<NodeRef>`).
195-
///
196-
/// If the underlying translation produces multiple ids for a single
197-
/// input, only the first is returned. For most use cases (e.g.
198-
/// translating a single type annotation) this is what you want; if
199-
/// you need all ids, use [`translate`] directly.
200-
pub fn translate_opt<I: Into<Id>>(&mut self, id: Option<I>) -> Result<Option<Id>, String> {
201-
match id {
202-
Some(id) => Ok(self.translate(id)?.into_iter().next()),
203-
None => Ok(None),
204-
}
205-
}
206180
}
207181

208182
impl<C> std::ops::Deref for BuildCtx<'_, C> {

shared/yeast/src/captures.rs

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -54,37 +54,15 @@ impl Captures {
5454
self.captures.entry(key).or_default().push(id);
5555
}
5656

57-
pub fn map_captures(&mut self, kind: &str, f: &mut impl FnMut(Id) -> Id) {
58-
if let Some(ids) = self.captures.get_mut(kind) {
59-
for id in ids {
60-
*id = f(*id);
61-
}
62-
}
63-
}
64-
65-
/// Apply a fallible function to every captured id (across all keys),
66-
/// replacing each id with the results. A function returning an empty
67-
/// vector removes the capture; returning multiple ids splices them
68-
/// into the capture's value list (suitable for `*`/`+` captures).
69-
/// Stops and returns the error on the first failure.
70-
pub fn try_map_all_captures<E>(
71-
&mut self,
72-
mut f: impl FnMut(Id) -> Result<Vec<Id>, E>,
73-
) -> Result<(), E> {
74-
for ids in self.captures.values_mut() {
75-
let mut new_ids = Vec::with_capacity(ids.len());
76-
for &id in ids.iter() {
77-
new_ids.extend(f(id)?);
78-
}
79-
*ids = new_ids;
80-
}
81-
Ok(())
82-
}
83-
84-
/// Like [`try_map_all_captures`] but leaves captures whose name appears
85-
/// in `skip` untouched. Used by the `rule!` macro to support `@@name`
86-
/// (raw) captures alongside the default auto-translated `@name`
87-
/// captures.
57+
/// Apply a fallible function to every captured id, replacing each id
58+
/// with the results. A function returning an empty vector removes
59+
/// the capture; returning multiple ids splices them into the
60+
/// capture's value list (suitable for `*`/`+` captures). Captures
61+
/// whose name appears in `skip` are left untouched. Stops and
62+
/// returns the error on the first failure.
63+
///
64+
/// Used by the `rule!` macro's auto-translate prefix to translate
65+
/// every capture except those marked `@@name` (raw).
8866
pub fn try_map_captures_except<E>(
8967
&mut self,
9068
skip: &[&str],
@@ -102,12 +80,6 @@ impl Captures {
10280
}
10381
Ok(())
10482
}
105-
pub fn map_captures_to(&mut self, from: &str, to: &'static str, f: &mut impl FnMut(Id) -> Id) {
106-
if let Some(from_ids) = self.captures.get(from) {
107-
let new_values = from_ids.iter().copied().map(f).collect();
108-
self.captures.insert(to, new_values);
109-
}
110-
}
11183

11284
pub fn merge(&mut self, other: &Captures) {
11385
for (key, ids) in &other.captures {

shared/yeast/src/cursor.rs

Lines changed: 0 additions & 8 deletions
This file was deleted.

shared/yeast/src/dump.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::Write;
22

3-
use crate::{schema::Schema, Ast, Node, NodeContent, CHILD_FIELD};
3+
use crate::{schema::Schema, Ast, Id, Node, NodeContent, CHILD_FIELD};
44

55
/// Options for controlling AST dump output.
66
pub struct DumpOptions {
@@ -34,16 +34,11 @@ impl Default for DumpOptions {
3434
/// method:
3535
/// identifier "foo"
3636
/// ```
37-
pub fn dump_ast(ast: &Ast, root: usize, source: &str) -> String {
37+
pub fn dump_ast(ast: &Ast, root: Id, source: &str) -> String {
3838
dump_ast_with_options(ast, root, source, &DumpOptions::default())
3939
}
4040

41-
pub fn dump_ast_with_options(
42-
ast: &Ast,
43-
root: usize,
44-
source: &str,
45-
options: &DumpOptions,
46-
) -> String {
41+
pub fn dump_ast_with_options(ast: &Ast, root: Id, source: &str, options: &DumpOptions) -> String {
4742
let mut out = String::new();
4843
dump_node(ast, root, source, options, 0, None, &mut out);
4944
out
@@ -53,7 +48,7 @@ pub fn dump_ast_with_options(
5348
///
5449
/// Any node that does not match the expected type set for its parent field is
5550
/// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line.
56-
pub fn dump_ast_with_type_errors(ast: &Ast, root: usize, source: &str, schema: &Schema) -> String {
51+
pub fn dump_ast_with_type_errors(ast: &Ast, root: Id, source: &str, schema: &Schema) -> String {
5752
dump_ast_with_type_errors_and_options(ast, root, source, schema, &DumpOptions::default())
5853
}
5954

@@ -63,7 +58,7 @@ pub fn dump_ast_with_type_errors(ast: &Ast, root: usize, source: &str, schema: &
6358
/// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line.
6459
pub fn dump_ast_with_type_errors_and_options(
6560
ast: &Ast,
66-
root: usize,
61+
root: Id,
6762
source: &str,
6863
schema: &Schema,
6964
options: &DumpOptions,
@@ -176,7 +171,7 @@ fn expected_for_field<'a>(
176171

177172
fn dump_node(
178173
ast: &Ast,
179-
id: usize,
174+
id: Id,
180175
source: &str,
181176
options: &DumpOptions,
182177
indent: usize,
@@ -315,7 +310,7 @@ fn dump_node(
315310
/// Dump a leaf node inline (no newline prefix, caller provides context).
316311
fn dump_node_inline(
317312
ast: &Ast,
318-
id: usize,
313+
id: Id,
319314
source: &str,
320315
options: &DumpOptions,
321316
type_check: Option<(

0 commit comments

Comments
 (0)