Skip to content

Commit 3b7a53f

Browse files
committed
yeast-macros: merge repeated field declarations and support repetition in field patterns
Two changes to parse_query_fields: - Allow `field: (kind)* @cap` (repetition + optional capture) in field position, mirroring how it works for bare children. - When the same field name is declared multiple times in a query (e.g. `condition: (foo) condition: (bar)`), merge them into a single ordered list of children rather than emitting duplicate field entries (which at runtime restart the iterator for the field and cause the second declaration to re-match from the first child).
1 parent ccc1dd5 commit 3b7a53f

1 file changed

Lines changed: 29 additions & 7 deletions

File tree

shared/yeast-macros/src/parse.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,24 @@ fn parse_query_node_inner(tokens: &mut Tokens) -> Result<TokenStream> {
113113
/// appear in any order; bare patterns are accumulated and emitted as a
114114
/// single `("child", ...)` entry.
115115
fn parse_query_fields(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
116-
let mut fields = Vec::new();
116+
// Accumulate per-field elems in declaration order; multiple uses of the
117+
// same field name extend the same list (so e.g. `cond: (foo) cond: (bar)`
118+
// matches a `cond` field whose first child is `foo` and second is `bar`).
119+
let mut field_order: Vec<String> = Vec::new();
120+
let mut field_elems: std::collections::HashMap<String, Vec<TokenStream>> =
121+
std::collections::HashMap::new();
117122
let mut bare_children: Vec<TokenStream> = Vec::new();
123+
let mut push_field_elem = |order: &mut Vec<String>,
124+
map: &mut std::collections::HashMap<String, Vec<TokenStream>>,
125+
name: String,
126+
elem: TokenStream| {
127+
if !map.contains_key(&name) {
128+
order.push(name.clone());
129+
map.insert(name, vec![elem]);
130+
} else {
131+
map.get_mut(&name).unwrap().push(elem);
132+
}
133+
};
118134
while tokens.peek().is_some() {
119135
if peek_is_field(tokens) {
120136
let field_name = expect_ident(tokens, "expected field name")?;
@@ -135,9 +151,7 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
135151
}
136152
};
137153
let elem = maybe_wrap_list_capture(tokens, elem)?;
138-
fields.push(quote! {
139-
(#field_str, vec![#elem])
140-
});
154+
push_field_elem(&mut field_order, &mut field_elems, field_str, elem);
141155
} else {
142156
let child = if peek_is_at(tokens) {
143157
tokens.next();
@@ -153,9 +167,10 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
153167
} else {
154168
atom
155169
};
156-
fields.push(quote! {
157-
(#field_str, vec![yeast::query::QueryListElem::SingleNode(#child)])
158-
});
170+
let elem = quote! {
171+
yeast::query::QueryListElem::SingleNode(#child)
172+
};
173+
push_field_elem(&mut field_order, &mut field_elems, field_str, elem);
159174
}
160175
} else {
161176
// Bare patterns — accumulate into the implicit `child` field.
@@ -168,6 +183,13 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result<Vec<TokenStream>> {
168183
bare_children.extend(elems);
169184
}
170185
}
186+
let mut fields: Vec<TokenStream> = Vec::new();
187+
for name in field_order {
188+
let elems = field_elems.remove(&name).unwrap();
189+
fields.push(quote! {
190+
(#name, vec![#(#elems),*])
191+
});
192+
}
171193
if !bare_children.is_empty() {
172194
fields.push(quote! {
173195
("child", vec![#(#bare_children),*])

0 commit comments

Comments
 (0)