Skip to content

Commit 20ce5b8

Browse files
committed
yeast: Split up IDs during lowering
Instead of a single, mutable, AST shared by both the input and the output, we now distinguish between `ast` (the input AST, now immutable), and `output` (the output AST, mutable). Additionally, input AST nodes now automatically get translated if they are returned from a Rust block. This does present one small awkwardness. Previously, tree templates were infallible, but now -- because we potentially call `ctx.translate` from inside -- they might fail (e.g. because recursive translation fails in some way). Rather than make `tree!` fallible (which would require a fair number of changes), I opted to just record the first error. This error is then returned by the surrounding `Rule` (which _is_ fallible).
1 parent bb6aaf6 commit 20ce5b8

5 files changed

Lines changed: 353 additions & 169 deletions

File tree

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

Lines changed: 11 additions & 9 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

‎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
}

0 commit comments

Comments
 (0)