treeOf<Tok, Syn> function
- Syn kind,
- List<
Parser< partsParseError, GreenNode< >Tok, Syn> >
Compose child green-producing parsers into a GreenTree of kind kind.
The child parsers run in sequence; all must succeed (or recover to a
Result.Partial) for the composed parser to produce a tree. Their green
results become the tree's children, in the order given.
This is the combinator form of Rowan's GreenNodeBuilder.start_node /
finish_node pair — a grammar author writes
final array = treeOf(JsonSyn.array, [
lbracket, // Parser<ParseError, JsonGreen>
element.sepBy(comma), // ... flattened into the children
rbracket,
]);
rather than threading ~/.map tuple plumbing. Each part yields exactly
one green; to splice a variable number of children (e.g. a separated
list) into the same parent, have that part yield a wrapper green, or
compose the list at the green level before calling treeOf.
Implementation: a left fold over parts via flatMap, accumulating each
child green into a fresh list per run (the defer gives every parse its
own accumulator), terminated by a map that wraps the collected children.
No new Parser ADT case — composes the existing combinators. The fold
chain length is the static child count at one tree level, not input
depth, so the existing trampoline handles runtime nesting.
Implementation
Parser<ParseError, GreenNode<Tok, Syn>> treeOf<Tok, Syn>(
Syn kind,
List<Parser<ParseError, GreenNode<Tok, Syn>>> parts,
) => defer(() {
final collected = <GreenNode<Tok, Syn>>[];
Parser<ParseError, void> chain = succeed<ParseError, void>(null);
for (final part in parts) {
// Each part runs for its side effect of appending to `collected`;
// its value is discarded by the void cast and thenSkip.
chain = chain.thenSkip(part.map<void>(collected.add));
}
return chain.map(
(_) => GreenTree<Tok, Syn>(kind, List<GreenNode<Tok, Syn>>.of(collected)),
);
});