Repository navigation
Conversation
This will be superseded by the next commit, which generalised the mechanism and also makes the implementation simpler.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Two critical compilation failures remain: missing OutputId support breaks translated captures, and premature candidate construction causes moved-value errors when alternatives consume the same repeated capture.
2 open issues
What changed in this PR
Adds ordered, guarded choices to Yeast tree templates, replacing postfix ?. The new syntax lets rewrite rules express fallbacks and check interpolation dependencies before constructing template nodes.
Changes:
- Add choice parsing, validation, and lowering with guard and dependency checks.
- Update documentation and tests for fallback selection, nested choices, and orphan-node prevention.
- Migrate Swift translation rules to bracketed choices.
| File | Description |
|---|---|
unified/extractor/src/languages/swift/swift.rs |
Migrates optional nodes and conditional rewrites to choices. |
shared/yeast/tests/test.rs |
Covers guards, fallbacks, nested choices, and construction preflight. |
shared/yeast/src/lib.rs |
Updates interpolation diagnostics and optional-value implementations. |
shared/yeast/doc/yeast.md |
Documents choice syntax, behavior, and examples. |
shared/yeast-macros/src/template_parse.rs |
Parses and validates choices and guards; removes postfix ? parsing. |
shared/yeast-macros/src/lower.rs |
Generates dependency checks and alternative-selection code. |
shared/yeast-macros/src/lib.rs |
Updates public macro documentation for choices and return types. |
shared/yeast-macros/src/ast.rs |
Adds choice alternatives and removes the optional-field flag. |
Review effort: Max
Adds a new syntactic form
```
[tree1 if {cond1}
tree2
tree3 if {cond3}
]
```
to the language, the intended semantics being that we output the first
tree that is usable. Two things can prevent a tree from being used:
- the guard condition (a Rust expression evaluating to a boolean) may
fail;
- a string interpolation (e.g. `#{name}`) may fail to have a value.
If all of the alternatives fail, then the result is `None`. This makes
the syntax a generalisation of the `?` postfix operator that was removed
in the previous commit. `(foo)?` can be replaced with a single-element
alternative `[(foo)]`.
As the changes to swift.rs show, this can clean up a bunch of
constructions where we currently rely on helper functions or somewhat
complicated Rust blocks.
In terms of the implementation, we go through the alternatives in turn,
first evaluating the guard condition. If this succeeds, we evaluate (and
temporarily store) all of the `#{...}` interpolations, and if those all
return `Some(...)` values, we instantiate the template itself. A benefit
of this implementation compared to the one for `?` (which used named
labels and `break`s) is that if either requirement fails, no AST nodes
are constructed. (The `?` implementation would eagerly construct the
tree, and then abort if any of the interpolations failed. This could
leave orphaned nodes in the AST arena.)
A choice cannot contain another choice as an immediate child (as that
choice could simply have its branches inlined in the parent), but
nesting further down is allow. In that case, the inner choice takes care
of handling its internal "failures", and these do not bubble up to the
outer choice.
tausbn
force-pushed
the
tausbn/yeast-add-template-choices
branch
from
October 27, 2026 15:03
bb7eb03 to
7027a76
Compare
tausbn
marked this pull request as ready for review
October 27, 2026 15:04
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

When creating the target tree for some rewrite rule, we quite often want to make the construction conditional on the presence of certain captures, or on the current state of the context.
Previously we added the postfix
?operator for this purpose. This made it possible to write a tree template likewherein the entire
baznode disappears (because of the postfix?) if thenameinterpolation fails (e.g. ifnameisNone).There are a few drawbacks with this approach:
Nonealternative.#{...}interpolations.(This also made the implementation somewhat complicated, requiring the use of labels and
breaks in the lowered Rust code.)Instead of the above, this PR implements a generalisation of this concept. Taking inspiration from the
tree-sitterquery language, we use square brackets to indicate a list of alternatives that should be attempted in turn. The first alternative that succeeds is the tree that is returned from this choice operator. Moreover, individual choices can have abool-returning Rust-block as a guard.Thus, something like
would have the following interpretation
cond1holds, and all#{...}interpolations intree1are valid (e.g. different fromNone), then returntree1#{...}interpolations intree2are valid, return thatcond3holds and all#{...}interpolations intree3are valid, return thatNoneThis fully supersedes the existing
?syntax (which we therefore remove first):(...)?can be rewritten to just[(...)].Before constructing any trees, the implementation first checks that the relevant condition (if present) holds, and that all
#{...}interpolations have usable values. Only if both of these are true is the tree construction attempted. This prevents any orphaned nodes from being constructed.