Skip to content

Commit 052d686

Browse files
authored
Merge pull request #22875 from github/tausbn/yeast-split-input-output-arenas
yeast: Split input/output ASTs
2 parents 2864ac8 + 6974644 commit 052d686

14 files changed

Lines changed: 876 additions & 576 deletions

File tree

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

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

69-
impl AstNode for yeast::Node {
69+
impl<I: yeast::TreeId> AstNode for yeast::Node<I> {
7070
fn kind(&self) -> &str {
7171
yeast::Node::kind_name(self)
7272
}
@@ -1043,7 +1043,7 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) {
10431043
}
10441044
}
10451045

1046-
fn traverse_yeast(tree: &yeast::Ast, visitor: &mut Visitor) {
1046+
fn traverse_yeast(tree: &yeast::OutputAst, visitor: &mut Visitor) {
10471047
let mut cursor = tree.walk();
10481048
visitor.enter_node(cursor.node());
10491049
let mut recurse = true;

‎shared/yeast-macros/src/lib.rs‎

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@ pub fn query(input: TokenStream) -> TokenStream {
5050
}
5151
}
5252

53-
/// Build a single AST node from a template, returning its `Id`.
53+
/// Build a single AST node from a template, returning its `OutputId`.
5454
///
5555
/// # Template syntax
5656
///
5757
/// ```text
5858
/// (kind "literal") - leaf with static content
5959
/// (kind #{expr}) - leaf with computed content (expr.to_string())
6060
/// {expr} - embed a Rust expression, dispatched via
61-
/// the `IntoFieldIds` trait: `Id` pushes a
62-
/// single id; iterables (`Vec<Id>`,
63-
/// `Option<Id>`, iterator chains) splice
61+
/// the `IntoFieldIds` trait: `OutputId` pushes a
62+
/// single id; iterables (`Vec<OutputId>`,
63+
/// `Option<OutputId>`, iterator chains) splice
6464
/// their elements
6565
/// field: {expr} - extend a named field with `{expr}`'s ids
6666
/// field: (kind ...)? - set the field only if every `#{expr}`
@@ -108,9 +108,9 @@ pub fn tree(input: TokenStream) -> TokenStream {
108108
}
109109
}
110110

111-
/// Build a list of AST nodes from a template, returning `Vec<Id>`.
111+
/// Build a list of AST nodes from a template, returning `Vec<OutputId>`.
112112
///
113-
/// Like `tree!` but returns `Vec<Id>` and supports multiple top-level
113+
/// Like `tree!` but returns `Vec<OutputId>` and supports multiple top-level
114114
/// elements. All syntax from `tree!` is available.
115115
///
116116
/// Uses the `BuildCtx` binding named `ctx` from the surrounding scope:
@@ -148,9 +148,11 @@ pub fn trees(input: TokenStream) -> TokenStream {
148148
/// ```
149149
///
150150
/// Captures become Rust variables automatically:
151-
/// - `@name` (no quantifier) → `name: Id`
152-
/// - `@name` (after `*`/`+`) → `name: Vec<Id>`
153-
/// - `@name` (after `?`) → `name: Option<Id>`
151+
/// - `@name` (no quantifier) → `name: OutputId`
152+
/// - `@name` (after `*`/`+`) → `name: Vec<OutputId>`
153+
/// - `@name` (after `?`) → `name: Option<OutputId>`
154+
///
155+
/// Raw `@@name` captures use the corresponding input `Id` types instead.
154156
///
155157
/// A guard is an optional Rust condition between the query and `=>`. It is
156158
/// evaluated after the query matches and before any `@` captures are
@@ -190,8 +192,8 @@ pub fn rule(input: TokenStream) -> TokenStream {
190192
/// output: "ast_types.yml",
191193
/// [
192194
/// (source_file (_)* @cs) => (top_level body: {..cs}),
193-
/// (simple_identifier) @id => (name_expr identifier: (identifier #{id})),
194-
/// rule!((integer_literal) @lit => (int_literal #{lit})),
195+
/// (simple_identifier) @@id => (name_expr identifier: (identifier #{id})),
196+
/// rule!((integer_literal) @@lit => (int_literal #{lit})),
195197
/// helper_fn(),
196198
/// ]
197199
/// };

‎shared/yeast-macros/src/lower.rs‎

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,20 @@ impl Rule {
3333
.iter()
3434
.map(|capture| capture.name.to_string())
3535
.collect::<Vec<_>>();
36-
let raw_bindings = capture_bindings(raw_captures.into_iter());
37-
let translated_bindings = capture_bindings(translated_captures.into_iter());
36+
let captures_ident = Ident::new("__captures", Span::call_site());
37+
let translated_captures_ident = Ident::new("__translated_captures", Span::call_site());
38+
let guard_bindings =
39+
capture_bindings(captures.iter(), quote! { yeast::Id }, &captures_ident);
40+
let raw_bindings = capture_bindings(
41+
raw_captures.into_iter(),
42+
quote! { yeast::Id },
43+
&captures_ident,
44+
);
45+
let translated_bindings = capture_bindings(
46+
translated_captures.into_iter(),
47+
quote! { yeast::OutputId },
48+
&translated_captures_ident,
49+
);
3850
let replacement = self.replacement.lower();
3951
let guard = self
4052
.guard
@@ -48,20 +60,20 @@ impl Rule {
4860
yeast::Rule::guarded(
4961
__query,
5062
Box::new(|__ast: &yeast::Ast, __captures: &yeast::captures::Captures, __user_ctx: &mut _| {
51-
#(#raw_bindings)*
52-
#(#translated_bindings)*
63+
#(#guard_bindings)*
5364
let ast = __ast;
5465
let #ctx = __user_ctx;
5566
Ok(#guard)
5667
}),
57-
Box::new(|__ast: &mut yeast::Ast, mut __captures: yeast::captures::Captures, __source_range: Option<yeast::Range>, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| {
68+
Box::new(|__ast: &yeast::Ast, __output: &mut yeast::OutputAst, __captures: yeast::captures::Captures, __source_range: Option<yeast::Range>, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| {
5869
let __skip: &[&str] = &[#(#raw_capture_names),*];
59-
__translator.auto_translate_captures(&mut __captures, __ast, __user_ctx, __skip)?;
70+
let __translated_captures =
71+
__translator.translate_captures(&__captures, __ast, __output, __user_ctx, __skip)?;
6072
#(#raw_bindings)*
6173
#(#translated_bindings)*
62-
let mut #ctx = yeast::build::BuildCtx::with_translator(__ast, &__captures, __source_range, __user_ctx, __translator);
63-
let __result: Vec<yeast::Id> = { #replacement };
64-
let __result = #ctx.finish_rule(__result);
74+
let mut #ctx = yeast::build::BuildCtx::with_translator(__ast, __output, __source_range, __user_ctx, __translator);
75+
let __result: Vec<yeast::OutputId> = { #replacement };
76+
let __result = #ctx.finish_rule(__result)?;
6577
Ok(__result)
6678
}),
6779
)
@@ -77,9 +89,10 @@ impl Replacement {
7789
Replacement::Rust { body, .. } => {
7890
let body = rust_block(body);
7991
quote! {
80-
let __value = #body;
81-
let mut __ids: Vec<yeast::Id> = Vec::new();
82-
yeast::IntoFieldIds::extend_into(__value, &mut __ids);
92+
let __value = ::std::iter::IntoIterator::into_iter(#body)
93+
.collect::<Vec<_>>();
94+
let mut __ids: Vec<yeast::OutputId> = Vec::new();
95+
ctx.extend_field_ids(__value, &mut __ids);
8396
__ids
8497
}
8598
}
@@ -241,25 +254,29 @@ fn combine_cardinality(parent: Cardinality, child: Cardinality) -> Cardinality {
241254
}
242255
}
243256

244-
fn capture_bindings<'a>(captures: impl Iterator<Item = &'a CaptureBinding>) -> Vec<TokenStream> {
257+
fn capture_bindings<'a>(
258+
captures: impl Iterator<Item = &'a CaptureBinding>,
259+
id_type: TokenStream,
260+
captures_ident: &Ident,
261+
) -> Vec<TokenStream> {
245262
captures
246263
.map(|capture| {
247264
let name_string = capture.name.to_string();
248265
let name = Ident::new(&name_string, Span::call_site());
249266
match (capture.cardinality.multiple, capture.cardinality.required) {
250267
(true, _) => {
251268
quote! {
252-
let #name: Vec<yeast::Id> = __captures.get_all(#name_string);
269+
let #name: Vec<#id_type> = #captures_ident.get_all(#name_string);
253270
}
254271
}
255272
(false, false) => {
256273
quote! {
257-
let #name: Option<yeast::Id> = __captures.get_opt(#name_string);
274+
let #name: Option<#id_type> = #captures_ident.get_opt(#name_string);
258275
}
259276
}
260277
(false, true) => {
261278
quote! {
262-
let #name: yeast::Id = __captures.get_var(#name_string).unwrap();
279+
let #name: #id_type = #captures_ident.get_var(#name_string).unwrap();
263280
}
264281
}
265282
}
@@ -288,15 +305,17 @@ impl Template {
288305
Template::Splice(block) => {
289306
let block = rust_block(block);
290307
quote! {
291-
yeast::IntoFieldIds::extend_into(#block, &mut __nodes);
308+
let __value = ::std::iter::IntoIterator::into_iter(#block)
309+
.collect::<Vec<_>>();
310+
ctx.extend_field_ids(__value, &mut __nodes);
292311
}
293312
}
294313
})
295314
.collect::<Vec<_>>();
296315

297316
quote! {
298317
{
299-
let mut __nodes: Vec<yeast::Id> = Vec::new();
318+
let mut __nodes: Vec<yeast::OutputId> = Vec::new();
300319
#(#items)*
301320
__nodes
302321
}
@@ -308,7 +327,7 @@ impl Template {
308327
Template::Node(node) => node.lower(context),
309328
Template::Splice(block) => {
310329
let block = rust_block(block);
311-
quote! { ::std::convert::Into::<yeast::Id>::into(#block) }
330+
quote! { ::std::convert::Into::<yeast::OutputId>::into(#block) }
312331
}
313332
}
314333
}
@@ -334,9 +353,9 @@ impl Node {
334353
break #label ::std::option::Option::None;
335354
};
336355
let __value =
337-
yeast::YeastDisplay::yeast_to_string(__value_ref, &*#ctx.ast);
356+
yeast::YeastDisplay::yeast_to_string(__value_ref, &#ctx);
338357
let __source_range =
339-
yeast::YeastSourceRange::yeast_source_range(__value_ref, &*#ctx.ast);
358+
yeast::YeastSourceRange::yeast_source_range(__value_ref, &#ctx);
340359
#ctx.literal_with_source_range(
341360
#kind_str,
342361
&__value,
@@ -349,9 +368,9 @@ impl Node {
349368
{
350369
let __expr = #block;
351370
let __value =
352-
yeast::YeastDisplay::yeast_to_string(&__expr, &*#ctx.ast);
371+
yeast::YeastDisplay::yeast_to_string(&__expr, &#ctx);
353372
let __source_range =
354-
yeast::YeastSourceRange::yeast_source_range(&__expr, &*#ctx.ast);
373+
yeast::YeastSourceRange::yeast_source_range(&__expr, &#ctx);
355374
#ctx.literal_with_source_range(
356375
#kind_str,
357376
&__value,
@@ -362,7 +381,7 @@ impl Node {
362381
}
363382
}
364383
};
365-
quote! { let __node: yeast::Id = #node; }
384+
quote! { let __node: yeast::OutputId = #node; }
366385
} else {
367386
TokenStream::new()
368387
};
@@ -375,7 +394,9 @@ impl Node {
375394
Field::Location { block } => {
376395
let block = rust_block(block);
377396
stmts.push(quote! {
378-
yeast::IntoFieldIds::extend_into(#block, &mut __location_ids);
397+
let __value = ::std::iter::IntoIterator::into_iter(#block)
398+
.collect::<Vec<_>>();
399+
#ctx.extend_location_ranges(__value, &mut __location_ranges);
379400
});
380401
}
381402
Field::Named {
@@ -398,8 +419,10 @@ impl Node {
398419
debug_assert!(!optional);
399420
let block = rust_block(block);
400421
stmts.push(quote! {
401-
let mut #temp: Vec<yeast::Id> = Vec::new();
402-
yeast::IntoFieldIds::extend_into(#block, &mut #temp);
422+
let mut #temp: Vec<yeast::OutputId> = Vec::new();
423+
let __value = ::std::iter::IntoIterator::into_iter(#block)
424+
.collect::<Vec<_>>();
425+
#ctx.extend_field_ids(__value, &mut #temp);
403426
});
404427
field_args.push(quote! {
405428
if !#temp.is_empty() {
@@ -414,7 +437,7 @@ impl Node {
414437
});
415438
stmts.push(quote! {
416439
#[allow(unused_labels)]
417-
let #temp: ::std::option::Option<yeast::Id> = #label: {
440+
let #temp: ::std::option::Option<yeast::OutputId> = #label: {
418441
::std::option::Option::Some(#value)
419442
};
420443
});
@@ -426,7 +449,7 @@ impl Node {
426449
}
427450
Template::Node(child) => {
428451
let value = child.lower(context);
429-
stmts.push(quote! { let #temp: yeast::Id = #value; });
452+
stmts.push(quote! { let #temp: yeast::OutputId = #value; });
430453
field_args.push(quote! {
431454
__fields.push((#field_str, vec![#temp]));
432455
});
@@ -438,28 +461,21 @@ impl Node {
438461

439462
let build_node = if self.content.is_none() {
440463
quote! {
441-
let mut __fields: Vec<(&str, Vec<yeast::Id>)> = Vec::new();
464+
let mut __fields: Vec<(&str, Vec<yeast::OutputId>)> = Vec::new();
442465
#(#field_args)*
443-
let __node: yeast::Id = #ctx.node(#kind_str, __fields);
466+
let __node: yeast::OutputId = #ctx.node(#kind_str, __fields);
444467
}
445468
} else {
446469
TokenStream::new()
447470
};
448471

449472
quote! {
450473
{
451-
let mut __location_ids: Vec<yeast::Id> = Vec::new();
474+
let mut __location_ranges: Vec<yeast::Range> = Vec::new();
452475
#node_init
453476
#(#stmts)*
454477
#build_node
455-
let __source_range = __location_ids
456-
.into_iter()
457-
.filter_map(|__source| {
458-
#ctx.ast
459-
.get_node(__source)
460-
.and_then(|__node| __node.source_range())
461-
})
462-
.reduce(yeast::Range::union);
478+
let __source_range = __location_ranges.into_iter().reduce(yeast::Range::union);
463479
#ctx.set_node_source_range(__node, __source_range)
464480
}
465481
}

‎shared/yeast-macros/src/rule_parse.rs‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,18 @@ fn validate_unique_capture_names(
5959
pub(crate) fn parse_rule(input: TokenStream) -> Result<Rule> {
6060
let (pattern_and_guard, replacement) = split_arrow(input)?;
6161
let (pattern, guard) = split_guard(pattern_and_guard)?;
62+
let pattern = parse_pattern(pattern)?;
63+
if let Pattern::Capture {
64+
name, raw: false, ..
65+
} = &pattern
66+
{
67+
return Err(syn::Error::new_spanned(
68+
name,
69+
"a capture of the rule root must use `@@`; the matched root cannot be eagerly translated",
70+
));
71+
}
6272
Ok(Rule {
63-
pattern: parse_pattern(pattern)?,
73+
pattern,
6474
guard,
6575
replacement: parse_replacement(replacement)?,
6676
})
@@ -548,7 +558,7 @@ mod tests {
548558
#[test]
549559
fn parses_template_replacement() {
550560
let rule = parse_rule(quote! {
551-
(identifier) @name
561+
(identifier) @@name
552562
=>
553563
(identifier #{name})
554564
})
@@ -558,6 +568,22 @@ mod tests {
558568
assert!(matches!(rule.replacement, Replacement::Templates(_)));
559569
}
560570

571+
#[test]
572+
fn rejects_eager_capture_of_rule_root() {
573+
let result = parse_rule(quote! {
574+
(identifier) @name
575+
=>
576+
(identifier #{name})
577+
});
578+
let Err(error) = result else {
579+
panic!("expected eager rule-root capture to be rejected");
580+
};
581+
assert_eq!(
582+
error.to_string(),
583+
"a capture of the rule root must use `@@`; the matched root cannot be eagerly translated"
584+
);
585+
}
586+
561587
#[test]
562588
fn repeated_capture_owns_its_cardinality() {
563589
let pattern = parse_pattern(quote!((array (identifier)* @items))).unwrap();

‎shared/yeast/src/bin/main.rs‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ fn main() {
2020
let args = Cli::parse();
2121
let language = get_language(&args.language);
2222
let source = std::fs::read_to_string(&args.file).unwrap();
23-
let runner: yeast::Runner = yeast::Runner::new(language, &[]);
24-
let ast = runner.run(&source).unwrap();
23+
let mut parser = tree_sitter::Parser::new();
24+
parser.set_language(&language).unwrap();
25+
let tree = parser.parse(&source, None).unwrap();
26+
let ast = yeast::Ast::from_tree_with_schema_and_source(
27+
yeast::schema::from_language(&language),
28+
&tree,
29+
&language,
30+
source.as_bytes().to_vec(),
31+
);
2532
println!("{}", ast.print(&source, ast.get_root()));
2633
}

0 commit comments

Comments
 (0)