Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion shared/yeast-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ pub fn query(input: TokenStream) -> TokenStream {
/// ```text
/// (kind "literal") - leaf with static content
/// (kind #{expr}) - leaf with computed content (expr.to_string())
/// (kind $fresh) - leaf with auto-generated unique name
/// {expr} - embed a Rust expression, dispatched via
/// the `IntoFieldIds` trait: `Id` pushes a
/// single id; iterables (`Vec<Id>`,
Expand Down
18 changes: 3 additions & 15 deletions shared/yeast-macros/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ fn parse_direct_node(
}

/// Parse the inside of a parenthesized node: `kind fields... children...`
/// or `kind "literal"` or `kind $fresh`.
/// or `kind "literal"`.
fn parse_direct_node_inner(
tokens: &mut Tokens,
ctx: &Ident,
Expand Down Expand Up @@ -474,14 +474,6 @@ fn parse_direct_node_inner(
});
}

// Check for (kind $fresh)
if peek_is_dollar(tokens) {
tokens.next();
let name = expect_ident(tokens, "expected fresh variable name after $")?;
let name_str = name.to_string();
return Ok(quote! { #ctx.fresh(#kind_str, #name_str) });
}

// Parse named fields
let mut stmts = Vec::new();
let mut field_args = Vec::new();
Expand Down Expand Up @@ -973,7 +965,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
let #ctx_ident = __user_ctx;
Ok(#guard)
}),
Box::new(|__ast: &mut yeast::Ast, mut __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option<yeast::Range>, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| {
Box::new(|__ast: &mut yeast::Ast, mut __captures: yeast::captures::Captures, __source_range: Option<yeast::Range>, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| {
// Auto-translation prefix: recursively translate every
// captured node before invoking the user's transform body,
// except for `@@name` captures listed in `__skip` which the
Expand All @@ -985,7 +977,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
__translator.auto_translate_captures(&mut __captures, __ast, __user_ctx, __skip)?;
#(#raw_bindings)*
#(#translated_bindings)*
let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator);
let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __source_range, __user_ctx, __translator);
let __result: Vec<yeast::Id> = { #transform_body };
let __result = #ctx_ident.finish_rule(__result);
Ok(__result)
Expand Down Expand Up @@ -1048,10 +1040,6 @@ fn peek_is_literal(tokens: &mut Tokens) -> bool {
matches!(tokens.peek(), Some(TokenTree::Literal(_)))
}

fn peek_is_dollar(tokens: &mut Tokens) -> bool {
matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '$')
}

fn peek_is_hash(tokens: &mut Tokens) -> bool {
matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '#')
}
Expand Down
60 changes: 2 additions & 58 deletions shared/yeast/doc/yeast.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ yeast::rule!(
);

// Standalone — explicit context
let fresh = yeast::tree_builder::FreshScope::new();
let mut ctx = BuildCtx::new(ast, &captures, &fresh);
let mut user_ctx = ();
let mut ctx = BuildCtx::new(ast, &captures, &mut user_ctx);
let id = yeast::tree!(ctx,
(assignment
left: {ctx.capture("lhs")}
Expand Down Expand Up @@ -374,25 +374,6 @@ Outside a `?`, interpolating an `Option` with `#{expr}` remains a compile error.
That is deliberate: it keeps the choice between "leave the field unset" and
"unwrap it" explicit at every interpolation.

### Fresh identifiers

`(kind $name)` creates a leaf node with an auto-generated unique name. All
occurrences of the same `$name` within one `BuildCtx` share the same value:

```rust
(block
parameters: (block_parameters
(identifier $tmp) // generates e.g. "$tmp-0"
)
body: (block_body
(assignment
left: {pat}
right: (identifier $tmp) // same "$tmp-0" value
)
)
)
```

### Embedded Rust expressions

`{expr}` embeds a Rust expression whose value is appended to the
Expand Down Expand Up @@ -479,43 +460,6 @@ Mix `@` and `@@` freely in the same rule. In a Repeating phase both
markers are equivalent (auto-translation is a no-op for repeating
rules).

## Complete example: for-loop desugaring

This rule rewrites Ruby's `for pat in val do body end` into
`val.each { |tmp| pat = tmp; body }`:

```rust
let for_rule = yeast::rule!(
(for
pattern: (_) @pat
value: (in (_) @val)
body: (do (_)* @body)
)
=>
(call
receiver: {val}
method: (identifier "each")
block: (block
parameters: (block_parameters
(identifier $tmp)
)
body: (block_body
(assignment
left: {pat}
right: (identifier $tmp)
)
{..body}
)
)
)
);
```

Captures from the query (`@pat`, `@val`, `@body`) become Rust variables
automatically: single captures bind as `Id`, repeated captures (after
`*` or `+`) as `Vec<Id>`, and optional captures (after `?`) as
`Option<Id>`.

## The `rule!` macro

`rule!` combines a query and a transform into a single declaration.
Expand Down
28 changes: 4 additions & 24 deletions shared/yeast/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use std::collections::{BTreeMap, BTreeSet};

use crate::captures::Captures;
use crate::tree_builder::FreshScope;
use crate::{Ast, FieldId, Id, KindId, NodeContent, Range, TranslatorHandle};

/// Context for building new AST nodes during a transformation.
///
/// Used by the `tree!` and `trees!` macros. Holds a mutable reference to the
/// AST, a reference to the captures from a query match, a `FreshScope` for
/// generating unique identifiers, and a mutable reference to a user-defined
/// context of type `C`.
/// AST, a reference to the captures from a query match, and a mutable reference
/// to a user-defined context of type `C`.
///
/// The user context `C` is shared across rules via the framework's driver:
/// outer rules can write to it before recursive translation, and inner rules
Expand All @@ -32,7 +30,6 @@ use crate::{Ast, FieldId, Id, KindId, NodeContent, Range, TranslatorHandle};
pub struct BuildCtx<'a, C: 'a = ()> {
pub ast: &'a mut Ast,
pub captures: &'a Captures,
pub fresh: &'a FreshScope,
/// Source range of the node matched by the current rule.
///
/// The `rule!` macro applies this range to locally-created result roots
Expand All @@ -54,16 +51,10 @@ pub struct BuildCtx<'a, C: 'a = ()> {
}

impl<'a, C> BuildCtx<'a, C> {
pub fn new(
ast: &'a mut Ast,
captures: &'a Captures,
fresh: &'a FreshScope,
user_ctx: &'a mut C,
) -> Self {
pub fn new(ast: &'a mut Ast, captures: &'a Captures, user_ctx: &'a mut C) -> Self {
Self {
ast,
captures,
fresh,
source_range: None,
user_ctx,
translator: None,
Expand All @@ -75,14 +66,12 @@ impl<'a, C> BuildCtx<'a, C> {
pub fn with_source_range(
ast: &'a mut Ast,
captures: &'a Captures,
fresh: &'a FreshScope,
source_range: Option<Range>,
user_ctx: &'a mut C,
) -> Self {
Self {
ast,
captures,
fresh,
source_range,
user_ctx,
translator: None,
Expand All @@ -95,15 +84,13 @@ impl<'a, C> BuildCtx<'a, C> {
pub fn with_translator(
ast: &'a mut Ast,
captures: &'a Captures,
fresh: &'a FreshScope,
source_range: Option<Range>,
user_ctx: &'a mut C,
translator: TranslatorHandle<'a, C>,
) -> Self {
Self {
ast,
captures,
fresh,
source_range,
user_ctx,
translator: Some(translator),
Expand Down Expand Up @@ -262,12 +249,6 @@ impl<'a, C> BuildCtx<'a, C> {
let source_range = self.source_range_of(source).map(Range::empty_at_start);
self.literal_with_source_range(kind, value, source_range)
}

/// Create a leaf node with an auto-generated unique name.
pub fn fresh(&mut self, kind: &'static str, name: &str) -> Id {
let generated = self.fresh.resolve(name);
self.create_named_token_with_range(kind, generated, None)
}
}

impl<C: Clone> BuildCtx<'_, C> {
Expand Down Expand Up @@ -302,7 +283,7 @@ impl<C: Clone> BuildCtx<'_, C> {

/// Run `f` with a temporary child [`BuildCtx`] whose `user_ctx` is
/// a fresh clone of the current one, sharing everything else
/// (`ast`, `captures`, `fresh`, source ranges, `translator`) by re-borrow.
/// (`ast`, `captures`, source ranges, `translator`) by re-borrow.
/// Nodes constructed through the child remain part of the current rule
/// invocation. Any mutations `f` makes to the child's `user_ctx`
/// are discarded when it returns — no restore needed, because the
Expand Down Expand Up @@ -334,7 +315,6 @@ impl<C: Clone> BuildCtx<'_, C> {
let mut child = BuildCtx {
ast: &mut *self.ast,
captures: self.captures,
fresh: self.fresh,
source_range: self.source_range,
user_ctx: &mut child_user_ctx,
translator: self.translator,
Expand Down
Loading
Loading