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
+8
View File
@@ -75,6 +75,13 @@ pub struct WalkContext<'a> {
/// skipped from the value list because the dispatch path
/// auto-fills them).
pub user_listed_columns: Option<Vec<String>>,
/// Count of active `Node::Subgrammar` frames on the walk
/// stack (ADR-0026 §2). The walker increments on entry to a
/// `Subgrammar`, restores the saved value on exit, and
/// refuses past `driver::MAX_SUBGRAMMAR_DEPTH` so a
/// pathologically nested expression fails with a friendly
/// error instead of overflowing the process stack.
pub subgrammar_depth: usize,
}
impl<'a> WalkContext<'a> {
@@ -100,6 +107,7 @@ impl<'a> WalkContext<'a> {
pending_value_column: None,
pending_hint_mode: None,
user_listed_columns: None,
subgrammar_depth: 0,
}
}
}