Skip to content

Commit 6fee953

Browse files
authored
Merge pull request #22842 from github/tausbn/yeast-add-nicer-location-syntax
yeast: Add nicer location syntax
2 parents feb95ba + f48bd79 commit 6fee953

9 files changed

Lines changed: 324 additions & 230 deletions

File tree

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub(crate) struct Node {
108108
/// Optional leaf content such as `"foo"` or `#{name}` in
109109
/// `(identifier "foo")` or `(identifier #{name})`.
110110
pub(crate) content: Option<Content>,
111-
/// Named fields such as `method: {name}` in `(call method: {name})`.
111+
/// Named fields and source-location metadata.
112112
pub(crate) fields: Vec<Field>,
113113
}
114114

@@ -131,4 +131,9 @@ pub(crate) enum Field {
131131
/// Whether the field value has a trailing `?`.
132132
optional: bool,
133133
},
134+
/// Source-location metadata such as `location: {source}`.
135+
Location {
136+
/// A Rust block yielding the node or nodes whose locations should be combined.
137+
block: syn::Block,
138+
},
134139
}

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

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub fn query(input: TokenStream) -> TokenStream {
6565
/// field: {expr} - extend a named field with `{expr}`'s ids
6666
/// field: (kind ...)? - set the field only if every `#{expr}`
6767
/// beneath it has a value (see below)
68+
/// location: {expr} - override this node's source range with the
69+
/// span of one or more nodes; may be repeated
6870
/// ```
6971
///
7072
/// # Optional fields
@@ -125,38 +127,6 @@ pub fn trees(input: TokenStream) -> TokenStream {
125127
}
126128
}
127129

128-
/// Build one AST node whose root uses another node's source range.
129-
///
130-
/// Uses the `BuildCtx` binding named `ctx` from the surrounding scope:
131-
///
132-
/// ```text
133-
/// tree_at!(source, (kind ...))
134-
/// ```
135-
#[proc_macro]
136-
pub fn tree_at(input: TokenStream) -> TokenStream {
137-
let input2: TokenStream2 = input.into();
138-
match parse::parse_tree_at_top(input2) {
139-
Ok(output) => output.into(),
140-
Err(err) => err.to_compile_error().into(),
141-
}
142-
}
143-
144-
/// Build one AST node whose root spans a collection of nodes.
145-
///
146-
/// Uses the `BuildCtx` binding named `ctx` from the surrounding scope:
147-
///
148-
/// ```text
149-
/// tree_spanning!(sources, (kind ...))
150-
/// ```
151-
#[proc_macro]
152-
pub fn tree_spanning(input: TokenStream) -> TokenStream {
153-
let input2: TokenStream2 = input.into();
154-
match parse::parse_tree_spanning_top(input2) {
155-
Ok(output) => output.into(),
156-
Err(err) => err.to_compile_error().into(),
157-
}
158-
}
159-
160130
/// Define a desugaring rule with query and transform in one declaration.
161131
///
162132
/// ```text

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

Lines changed: 84 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ impl Node {
319319
let ctx = Ident::new("ctx", Span::call_site());
320320
let kind_str = self.kind.to_string();
321321

322-
if let Some(content) = &self.content {
323-
return match content {
322+
let node_init = if let Some(content) = &self.content {
323+
let node = match content {
324324
Content::Static(lit) => quote! { #ctx.literal(#kind_str, #lit) },
325325
Content::Computed(block) => {
326326
let block = rust_block(block);
@@ -362,74 +362,105 @@ impl Node {
362362
}
363363
}
364364
};
365-
}
365+
quote! { let __node: yeast::Id = #node; }
366+
} else {
367+
TokenStream::new()
368+
};
366369

367370
let mut stmts = Vec::new();
368371
let mut field_args = Vec::new();
369372

370373
for (field_counter, field) in self.fields.iter().enumerate() {
371-
let Field::Named {
372-
name,
373-
value,
374-
optional,
375-
} = field;
376-
let field_name = name.to_string();
377-
let field_str = field_name
378-
.strip_prefix("r#")
379-
.unwrap_or(&field_name)
380-
.to_string();
381-
let temp = Ident::new(
382-
&format!("__field_{field_str}_{field_counter}"),
383-
Span::call_site(),
384-
);
385-
386-
match value {
387-
Template::Splice(block) => {
388-
debug_assert!(!optional);
374+
match field {
375+
Field::Location { block } => {
389376
let block = rust_block(block);
390377
stmts.push(quote! {
391-
let mut #temp: Vec<yeast::Id> = Vec::new();
392-
yeast::IntoFieldIds::extend_into(#block, &mut #temp);
393-
});
394-
field_args.push(quote! {
395-
if !#temp.is_empty() {
396-
__fields.push((#field_str, #temp));
397-
}
378+
yeast::IntoFieldIds::extend_into(#block, &mut __location_ids);
398379
});
399380
}
400-
Template::Node(child) if *optional => {
401-
let label = fresh_fallible_label();
402-
let value = child.lower(&LoweringContext {
403-
fallible_scope: Some(&label),
404-
});
405-
stmts.push(quote! {
406-
#[allow(unused_labels)]
407-
let #temp: ::std::option::Option<yeast::Id> = #label: {
408-
::std::option::Option::Some(#value)
409-
};
410-
});
411-
field_args.push(quote! {
412-
if let ::std::option::Option::Some(__id) = #temp {
413-
__fields.push((#field_str, vec![__id]));
381+
Field::Named {
382+
name,
383+
value,
384+
optional,
385+
} => {
386+
let field_name = name.to_string();
387+
let field_str = field_name
388+
.strip_prefix("r#")
389+
.unwrap_or(&field_name)
390+
.to_string();
391+
let temp = Ident::new(
392+
&format!("__field_{field_str}_{field_counter}"),
393+
Span::call_site(),
394+
);
395+
396+
match value {
397+
Template::Splice(block) => {
398+
debug_assert!(!optional);
399+
let block = rust_block(block);
400+
stmts.push(quote! {
401+
let mut #temp: Vec<yeast::Id> = Vec::new();
402+
yeast::IntoFieldIds::extend_into(#block, &mut #temp);
403+
});
404+
field_args.push(quote! {
405+
if !#temp.is_empty() {
406+
__fields.push((#field_str, #temp));
407+
}
408+
});
414409
}
415-
});
416-
}
417-
Template::Node(child) => {
418-
let value = child.lower(context);
419-
stmts.push(quote! { let #temp: yeast::Id = #value; });
420-
field_args.push(quote! {
421-
__fields.push((#field_str, vec![#temp]));
422-
});
410+
Template::Node(child) if *optional => {
411+
let label = fresh_fallible_label();
412+
let value = child.lower(&LoweringContext {
413+
fallible_scope: Some(&label),
414+
});
415+
stmts.push(quote! {
416+
#[allow(unused_labels)]
417+
let #temp: ::std::option::Option<yeast::Id> = #label: {
418+
::std::option::Option::Some(#value)
419+
};
420+
});
421+
field_args.push(quote! {
422+
if let ::std::option::Option::Some(__id) = #temp {
423+
__fields.push((#field_str, vec![__id]));
424+
}
425+
});
426+
}
427+
Template::Node(child) => {
428+
let value = child.lower(context);
429+
stmts.push(quote! { let #temp: yeast::Id = #value; });
430+
field_args.push(quote! {
431+
__fields.push((#field_str, vec![#temp]));
432+
});
433+
}
434+
}
423435
}
424436
}
425437
}
426438

439+
let build_node = if self.content.is_none() {
440+
quote! {
441+
let mut __fields: Vec<(&str, Vec<yeast::Id>)> = Vec::new();
442+
#(#field_args)*
443+
let __node: yeast::Id = #ctx.node(#kind_str, __fields);
444+
}
445+
} else {
446+
TokenStream::new()
447+
};
448+
427449
quote! {
428450
{
451+
let mut __location_ids: Vec<yeast::Id> = Vec::new();
452+
#node_init
429453
#(#stmts)*
430-
let mut __fields: Vec<(&str, Vec<yeast::Id>)> = Vec::new();
431-
#(#field_args)*
432-
#ctx.node(#kind_str, __fields)
454+
#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);
463+
#ctx.set_node_source_range(__node, __source_range)
433464
}
434465
}
435466
}

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

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
use proc_macro2::{Span, TokenStream, TokenTree};
22
use quote::quote;
3-
use syn::{
4-
parse::{Parse, ParseStream},
5-
Expr, Token,
6-
};
73

84
type Result<T> = std::result::Result<T, syn::Error>;
95

@@ -28,47 +24,6 @@ pub fn parse_trees_top(input: TokenStream) -> Result<TokenStream> {
2824
Ok(crate::ast::Template::lower_list(&templates))
2925
}
3026

31-
pub fn parse_tree_at_top(input: TokenStream) -> Result<TokenStream> {
32-
let LocatedTreeInput { source, template } = syn::parse2(input)?;
33-
let template = crate::template_parse::parse_template(template)?;
34-
let node = template.lower_root();
35-
let ctx = syn::Ident::new("ctx", Span::call_site());
36-
37-
Ok(quote! {
38-
{
39-
let __yeast_source: yeast::Id = { #source };
40-
let __yeast_source_range = #ctx
41-
.ast
42-
.get_node(__yeast_source)
43-
.and_then(|node| node.source_range());
44-
let __yeast_node: yeast::Id = #node;
45-
#ctx.set_node_source_range(__yeast_node, __yeast_source_range)
46-
}
47-
})
48-
}
49-
50-
pub fn parse_tree_spanning_top(input: TokenStream) -> Result<TokenStream> {
51-
let LocatedTreeInput {
52-
source: sources,
53-
template,
54-
} = syn::parse2(input)?;
55-
let template = crate::template_parse::parse_template(template)?;
56-
let node = template.lower_root();
57-
let ctx = syn::Ident::new("ctx", Span::call_site());
58-
59-
Ok(quote! {
60-
{
61-
let __yeast_source_range = ::std::iter::IntoIterator::into_iter({ #sources })
62-
.filter_map(|source: yeast::Id| {
63-
#ctx.ast.get_node(source).and_then(|node| node.source_range())
64-
})
65-
.reduce(yeast::Range::union);
66-
let __yeast_node: yeast::Id = #node;
67-
#ctx.set_node_source_range(__yeast_node, __yeast_source_range)
68-
}
69-
})
70-
}
71-
7227
pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
7328
Ok(crate::rule_parse::parse_rule(input)?.lower())
7429
}
@@ -127,20 +82,6 @@ pub fn parse_rules_top(input: TokenStream) -> Result<TokenStream> {
12782
})
12883
}
12984

130-
struct LocatedTreeInput {
131-
source: Expr,
132-
template: TokenStream,
133-
}
134-
135-
impl Parse for LocatedTreeInput {
136-
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
137-
let source = input.parse()?;
138-
input.parse::<Token![,]>()?;
139-
let template = input.parse()?;
140-
Ok(Self { source, template })
141-
}
142-
}
143-
14485
struct NamedString {
14586
value: String,
14687
}

0 commit comments

Comments
 (0)