Skip to content

Commit 18c08e5

Browse files
committed
yeast: Parse query choices
A choice is a collection of separate queries. These will be attempted in turn, and the first one that succeeds is picked. (This differs from the choice operator in tree-sitter, which can match multiple times.) If a capture is present in all alternatives of a choice, then it is directly available in the rule body. If it's absent in some branches, it becomes an Option type. Likewise if repetition is present, the capture can end up with a Vec type (even if it is guaranteed to be present in some alternatives). It is an error to mix raw and translated captures across alternatives. In this case, then only sensible thing to do is to use a raw capture and manually translate things later. Finally, the check that root captures are raw (because the alternative would lead to infinite recursion) is extended to support choice operators as well.
1 parent d3c4a3b commit 18c08e5

2 files changed

Lines changed: 93 additions & 16 deletions

File tree

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ pub(crate) enum Pattern {
7878
pattern: Box<Pattern>,
7979
cardinality: Cardinality,
8080
},
81+
/// A choice that tries its alternatives from left to right.
82+
Choice { alternatives: Vec<Pattern> },
8183
/// An ordered sequence of patterns within a field.
8284
Sequence(Vec<Pattern>),
8385
/// A repeated or optional pattern.
@@ -92,6 +94,9 @@ impl Pattern {
9294
match self {
9395
Pattern::Any { .. } | Pattern::Node { .. } | Pattern::Unnamed(_) => true,
9496
Pattern::Capture { cardinality, .. } => *cardinality == Cardinality::SINGLE,
97+
Pattern::Choice { alternatives } => {
98+
alternatives.iter().all(Pattern::matches_single_node)
99+
}
95100
Pattern::Sequence(_) | Pattern::Repeated { .. } => false,
96101
}
97102
}

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

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
2-
use std::collections::BTreeSet;
2+
use std::collections::BTreeMap;
33
use std::iter::Peekable;
44

55
use crate::ast::{Cardinality, Pattern, Replacement, ReturnAnnotation, Rule};
66

77
type Tokens = Peekable<proc_macro2::token_stream::IntoIter>;
88
type Result<T> = std::result::Result<T, syn::Error>;
9+
/// Maps each capture name to whether it uses `@@` and its identifier for diagnostics.
10+
type CaptureNames = BTreeMap<String, (bool, Ident)>;
911

1012
pub(crate) fn parse_pattern(input: TokenStream) -> Result<Pattern> {
1113
let mut tokens = input.into_iter().peekable();
@@ -16,13 +18,13 @@ pub(crate) fn parse_pattern(input: TokenStream) -> Result<Pattern> {
1618
"unexpected token after query",
1719
));
1820
}
19-
validate_unique_capture_names(&pattern, &mut BTreeSet::new())?;
21+
validate_unique_capture_names(&pattern, &mut CaptureNames::new())?;
2022
Ok(pattern)
2123
}
2224

2325
fn validate_unique_capture_names(
2426
pattern: &Pattern,
25-
capture_names: &mut BTreeSet<String>,
27+
capture_names: &mut CaptureNames,
2628
) -> Result<()> {
2729
match pattern {
2830
Pattern::Any { .. } | Pattern::Unnamed(_) => {}
@@ -31,10 +33,12 @@ fn validate_unique_capture_names(
3133
validate_unique_capture_names(pattern, capture_names)?;
3234
}
3335
}
34-
Pattern::Capture { name, pattern, .. } => {
36+
Pattern::Capture {
37+
name, raw, pattern, ..
38+
} => {
3539
validate_unique_capture_names(pattern, capture_names)?;
3640
let name_string = name.to_string();
37-
if !capture_names.insert(name_string.clone()) {
41+
if capture_names.contains_key(&name_string) {
3842
return Err(syn::Error::new_spanned(
3943
name,
4044
format!(
@@ -43,6 +47,30 @@ fn validate_unique_capture_names(
4347
),
4448
));
4549
}
50+
capture_names.insert(name_string, (*raw, name.clone()));
51+
}
52+
Pattern::Choice { alternatives } => {
53+
let mut unified_names = capture_names.clone();
54+
for alternative in alternatives {
55+
let mut alternative_names = capture_names.clone();
56+
validate_unique_capture_names(alternative, &mut alternative_names)?;
57+
for (name, (raw, identifier)) in alternative_names {
58+
if let Some((unified_raw, _)) = unified_names.get(&name) {
59+
if *unified_raw != raw {
60+
return Err(syn::Error::new_spanned(
61+
identifier,
62+
format!(
63+
"capture `{name}` must consistently use `@` or `@@` \
64+
across query choice alternatives"
65+
),
66+
));
67+
}
68+
} else {
69+
unified_names.insert(name, (raw, identifier));
70+
}
71+
}
72+
}
73+
*capture_names = unified_names;
4674
}
4775
Pattern::Sequence(patterns) => {
4876
for pattern in patterns {
@@ -60,22 +88,33 @@ pub(crate) fn parse_rule(input: TokenStream) -> Result<Rule> {
6088
let (pattern_and_guard, replacement) = split_arrow(input)?;
6189
let (pattern, guard) = split_guard(pattern_and_guard)?;
6290
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-
}
91+
validate_rule_root_captures(&pattern)?;
7292
Ok(Rule {
7393
pattern,
7494
guard,
7595
replacement: parse_replacement(replacement)?,
7696
})
7797
}
7898

99+
fn validate_rule_root_captures(pattern: &Pattern) -> Result<()> {
100+
match pattern {
101+
Pattern::Capture {
102+
name, raw: false, ..
103+
} => Err(syn::Error::new_spanned(
104+
name,
105+
"a capture of the rule root must use `@@`; the matched root cannot be eagerly translated",
106+
)),
107+
Pattern::Capture { pattern, .. } => validate_rule_root_captures(pattern),
108+
Pattern::Choice { alternatives } => {
109+
for alternative in alternatives {
110+
validate_rule_root_captures(alternative)?;
111+
}
112+
Ok(())
113+
}
114+
_ => Ok(()),
115+
}
116+
}
117+
79118
fn parse_pattern_with_capture(tokens: &mut Tokens) -> Result<Pattern> {
80119
let pattern = parse_pattern_atom(tokens)?;
81120
maybe_capture(tokens, pattern, Cardinality::SINGLE)
@@ -92,6 +131,10 @@ fn parse_pattern_atom(tokens: &mut Tokens) -> Result<Pattern> {
92131
let mut inner = group.stream().into_iter().peekable();
93132
parse_parenthesized_pattern(&mut inner, group.span())
94133
}
134+
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Bracket => {
135+
let group = expect_group(tokens, Delimiter::Bracket)?;
136+
parse_choice(group)
137+
}
95138
Some(TokenTree::Ident(identifier)) if *identifier == "_" => {
96139
tokens.next();
97140
Ok(Pattern::Any {
@@ -101,11 +144,29 @@ fn parse_pattern_atom(tokens: &mut Tokens) -> Result<Pattern> {
101144
Some(TokenTree::Literal(_)) => Ok(Pattern::Unnamed(expect_string_literal(tokens)?)),
102145
Some(token) => Err(syn::Error::new_spanned(
103146
token.clone(),
104-
"expected `(`, `_`, or string literal in query",
147+
"expected `(`, `[`, `_`, or string literal in query",
105148
)),
106149
}
107150
}
108151

152+
fn parse_choice(group: proc_macro2::Group) -> Result<Pattern> {
153+
let span = group.span();
154+
let mut tokens = group.stream().into_iter().peekable();
155+
let mut alternatives = Vec::new();
156+
while tokens.peek().is_some() {
157+
let pattern = parse_pattern_atom(&mut tokens)?;
158+
let pattern = maybe_repeat(&mut tokens, pattern)?;
159+
alternatives.push(pattern);
160+
}
161+
if alternatives.is_empty() {
162+
return Err(syn::Error::new(
163+
span,
164+
"choice must contain at least one query",
165+
));
166+
}
167+
Ok(Pattern::Choice { alternatives })
168+
}
169+
109170
fn parse_parenthesized_pattern(tokens: &mut Tokens, span: Span) -> Result<Pattern> {
110171
match tokens.peek() {
111172
None => Err(syn::Error::new(span, "empty query groups are not allowed")),
@@ -148,7 +209,12 @@ fn parse_parenthesized_pattern(tokens: &mut Tokens, span: Span) -> Result<Patter
148209
Ok(pattern)
149210
}
150211
}
151-
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Parenthesis => {
212+
Some(TokenTree::Group(group))
213+
if matches!(
214+
group.delimiter(),
215+
Delimiter::Parenthesis | Delimiter::Bracket
216+
) =>
217+
{
152218
let patterns = parse_pattern_list(tokens)?;
153219
finish_sequence(tokens, patterns, span)
154220
}
@@ -222,6 +288,12 @@ fn push_field_pattern(fields: &mut Vec<(String, Pattern)>, name: String, pattern
222288
fn parse_pattern_list(tokens: &mut Tokens) -> Result<Vec<Pattern>> {
223289
let mut patterns = Vec::new();
224290
while tokens.peek().is_some() {
291+
if peek_is_group(tokens, Delimiter::Bracket) {
292+
let pattern = parse_pattern_atom(tokens)?;
293+
patterns.push(maybe_repeat(tokens, pattern)?);
294+
continue;
295+
}
296+
225297
if peek_is_group(tokens, Delimiter::Parenthesis) {
226298
let group = expect_group(tokens, Delimiter::Parenthesis)?;
227299
let mut inner = group.stream().into_iter().peekable();

0 commit comments

Comments
 (0)