walker: add Subgrammar node + recursion-depth cap (ADR-0026 step 1)

New `Node::Subgrammar(&'static Node)` variant lets a named
static grammar fragment recurse through a reference — `Seq` /
`Choice` embed children by value and cannot close a cycle, but
a `&'static Node` can point back at an enclosing fragment. This
is the mechanism the stratified WHERE-expression grammar
(ADR-0026 §2) recurses through.

The walker counts active Subgrammar frames in
`WalkContext::subgrammar_depth` and refuses past
`MAX_SUBGRAMMAR_DEPTH` (64), surfacing a friendly
`parse.custom.expression_too_deep` error instead of a stack
overflow. Depth is saved/restored per frame so a
speculatively-walked-then-rolled-back Choice branch leaves no
residue.

No grammar references the node yet; covered by walker unit
tests with a small recursive `( x )` test grammar.
This commit is contained in:
claude@clouddev1
2026-05-18 22:36:19 +00:00
parent ac41938365
commit f0b2043a39
5 changed files with 164 additions and 1 deletions
+5
View File
@@ -372,6 +372,11 @@ parse:
replay_path_expected: "expected a path after `replay`"
create_table_needs_pk: |-
tables need at least one column. Add `with pk` for a default `id INTEGER PRIMARY KEY`, or `with pk <name>(<type>)` to choose. Use a comma-separated list for compound primary keys.
# ADR-0026 §1: the recursion-depth guard on the
# WHERE-expression grammar. Input nested past the cap
# (`((((…))))`) stops here with a friendly error instead
# of overflowing the parser stack.
expression_too_deep: "expression nested too deeply"
on_action_specified_twice: "`on {target}` specified twice"
change_column_flags_exclusive: "`--force-conversion` and `--dont-convert` are mutually exclusive — pick one."
unknown_type: "unknown type '{found}' (expected one of: {expected})"