walker: Node::ScopedSubgrammar variant + scope-frame stack (ADR-0032 §10.2)
Sub-phase 2b checkpoint 1 — adds the foundation for SQL SELECT lexical-scope discipline without changing existing walker semantics. New types in `dsl::walker::context`: - `TableBinding` — one FROM-source binding with table name, optional alias, and schema-resolved columns (§10.1). - `CteBinding` + `CteColumn` — a CTE definition visible from inside its body (WITH RECURSIVE self-reference) and from the outer scope after harvest (§10.3). - `ScopeFrame` — `from_scope`, `cte_bindings`, and `projection_aliases` for one lexical scope. Default-empty; the fields will be populated by later 2b checkpoints. `WalkContext` gains `from_scope_stack: Vec<ScopeFrame>`, initialised with one bottom frame in both `new()` and `with_schema()`. The bottom frame is the implicit top-level scope DSL paths and top-level SQL statements operate in; `Node::ScopedSubgrammar` entries push and pop additional frames on top. `current_table` / `current_table_columns` remain as direct fields for this checkpoint — converting them to derived helpers is a later 2b step. New grammar-tree variant: - `Node::ScopedSubgrammar(&'static Self)` — like `Subgrammar`, but pushes a fresh `ScopeFrame` on entry and pops it on exit (ADR-0032 §10.2). Shares `subgrammar_depth` with the plain Subgrammar variant so the MAX_SUBGRAMMAR_DEPTH = 64 cap fires uniformly across both — §9's "no new walker capability for grammar recursion" claim holds. DSL Expr (ADR-0026) and sql_expr.rs ladder (ADR-0031) recursion continue to use the plain Subgrammar variant and never push a scope. Driver gains a parallel `walk_scoped_subgrammar` arm; the push/pop is unconditional so a speculatively-walked branch a later Choice rolls back leaves the stack clean. Test coverage in `driver.rs`: - A recursive ScopedSubgrammar test grammar walks correctly through depths 0-3. - The depth cap fires the same `expression_too_deep` friendly validation error as for plain Subgrammar. - The bottom frame invariant: `WalkContext::new` seeds exactly one frame, and after a walk the stack is restored. No grammar tree references the new variant yet — the rewire of sql_select.rs CTE bodies and the sql_expr.rs additive extensions for §5/§6 are the next 2b checkpoint. Test totals: 1330 baseline + 3 = 1333 passing, 0 failed, 1 ignored. Clippy clean.
This commit is contained in:
@@ -316,6 +316,24 @@ pub enum Node {
|
||||
/// this one references a fixed fragment already in the
|
||||
/// grammar tree.
|
||||
Subgrammar(&'static Self),
|
||||
/// Like `Subgrammar`, but the walker additionally **pushes a
|
||||
/// new `ScopeFrame`** onto `WalkContext::from_scope_stack` on
|
||||
/// entry and pops it on exit (ADR-0032 §10.2). The
|
||||
/// `subgrammar_depth` counter increments uniformly across
|
||||
/// both variants — the depth cap applies the same way — so
|
||||
/// this variant introduces no new walker capability for
|
||||
/// grammar recursion; it only layers lexical-scope discipline
|
||||
/// on top.
|
||||
///
|
||||
/// Used at every SQL `SELECT` recursion point: subqueries
|
||||
/// in `sql_expr.rs` (scalar `(SELECT …)`, `IN (SELECT …)`,
|
||||
/// `[NOT] EXISTS (SELECT …)`) and CTE bodies in
|
||||
/// `sql_select.rs` reference the compound-SELECT through
|
||||
/// `Node::ScopedSubgrammar(&SQL_SELECT_COMPOUND)`. DSL `Expr`
|
||||
/// recursion (ADR-0026) and the `sql_expr.rs` precedence-
|
||||
/// ladder recursion (ADR-0031) keep using the plain
|
||||
/// `Subgrammar` variant and never push a scope.
|
||||
ScopedSubgrammar(&'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