perf(generate): build Seq and Repeat child ranges without temporaries

`try_seq` reserves a `Seq`'s child range up front and fills slots as
recursion produces each member. The `Repeat` arm builds
`Choice(repeat(x), blank)` directly, skipping the `choice` helper.

Slightly faster and less memory used.
This commit is contained in:
Will Lillis 2026-08-21 17:46:01 -04:00
parent 91e875ad7b
commit 1e070eb28e
2 changed files with 29 additions and 9 deletions

View file

@ -402,13 +402,9 @@ impl RulePool {
.collect::<ParseGrammarResult<Vec<_>>>()?;
Ok(self.choice(&members))
}
RuleJSON::SEQ { members } => {
let members = members
.into_iter()
.map(|m| self.parse_rule(m, is_token, diagnostics))
.collect::<ParseGrammarResult<Vec<_>>>()?;
Ok(self.seq(&members))
}
RuleJSON::SEQ { members } => self.try_seq(members.into_iter(), |pool, m| {
pool.parse_rule(m, is_token, diagnostics)
}),
RuleJSON::FIELD { name, content } => {
let content = self.parse_rule(*content, is_token, diagnostics)?;
let name = self.intern(&name);
@ -418,7 +414,10 @@ impl RulePool {
let content = self.parse_rule(*content, is_token, diagnostics)?;
let repeat = self.repeat(content);
let blank = self.blank();
Ok(self.choice(&[repeat, blank]))
// Neither element is a `Choice` or equal to one another, so we
// can skip `self.choice`'s flattening loop.
let range = self.push_children(&[repeat, blank]);
Ok(self.push_node(Rule::Choice(range)))
}
RuleJSON::REPEAT1 { content } => {
let content = self.parse_rule(*content, is_token, diagnostics)?;

View file

@ -304,13 +304,34 @@ impl RulePool {
self.push_node(Rule::Eof)
}
#[cfg_attr(not(test), expect(dead_code))]
pub fn seq(&mut self, ids: &[RuleId]) -> RuleId {
let range = self.push_children(ids);
self.push_node(Rule::Seq(range))
}
pub fn try_seq<T, E>(
&mut self,
items: impl ExactSizeIterator<Item = T>,
mut f: impl FnMut(&mut Self, T) -> Result<RuleId, E>,
) -> Result<RuleId, E> {
let len = items.len();
let start = self.children.len();
self.children.resize(start + len, RuleId(0));
for (i, item) in items.enumerate() {
let id = f(self, item)?;
self.children[start + i] = id;
}
let range = RuleIdRange {
start: start as u32,
len: len as u32,
};
Ok(self.push_node(Rule::Seq(range)))
}
/// Flatten nested choices and de-dup structurally, keeping a `Choice` node
/// event for a single element
/// even for a single element
pub fn choice(&mut self, ids: &[RuleId]) -> RuleId {
let mut elements: Vec<RuleId> = Vec::with_capacity(ids.len());
let mut stack: Vec<RuleId> = Vec::with_capacity(ids.len());