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:
@@ -292,6 +292,28 @@ pub enum Node {
|
||||
separator: Option<&'static Self>,
|
||||
min: usize,
|
||||
},
|
||||
/// Walks the referenced `&'static Node` once, mandatory
|
||||
/// (ADR-0026 §2). The reference indirection is what lets a
|
||||
/// named `static` grammar fragment appear inside its own
|
||||
/// subtree: a `Seq` / `Choice` embeds its children by value
|
||||
/// and so cannot close a cycle, but a `&'static Node`
|
||||
/// reference can point back at an enclosing fragment. This
|
||||
/// is the mechanism the stratified WHERE-expression grammar
|
||||
/// recurses through — the `( or_expr )` branch and the
|
||||
/// `not_expr` self-reference.
|
||||
///
|
||||
/// The walker counts active `Subgrammar` frames in
|
||||
/// `WalkContext::subgrammar_depth` and refuses past
|
||||
/// `walker::driver::MAX_SUBGRAMMAR_DEPTH`, so pathologically
|
||||
/// nested input (`((((…))))`) fails with a friendly error
|
||||
/// rather than overflowing the parser stack.
|
||||
///
|
||||
/// The static counterpart of `DynamicSubgrammar`: that one
|
||||
/// builds a fresh node from the `WalkContext` at walk time;
|
||||
/// this one references a fixed fragment already in the
|
||||
/// grammar tree.
|
||||
#[allow(dead_code)]
|
||||
Subgrammar(&'static Self),
|
||||
/// Resolves at walk time using the active `WalkContext`.
|
||||
/// Phase D+ uses this for `column_value_list`. The factory
|
||||
/// is pure in `ctx`, so the walker memoizes the resolution
|
||||
|
||||
Reference in New Issue
Block a user