/** * AST visitor and position-lookup utilities. * * Import boundary note: * - This file lives at src/ level (not in runtime/), and depends only on * the shared AST/type files (./ast-nodes.js, ./source-location.js). * - No imports from lexer/*, parser/*, or runtime/* — keeps this module * usable by language-service style tooling that never touches the * runtime evaluator. */ import type { ASTNode } from './ast-nodes.js'; /** * Pre-order depth-first traversal of the AST reachable from `root`. * Calls `visit` once for every ASTNode, including RecoveryErrorNode and * PartialExpressionNode. Span-less segments (FieldAccess/BracketAccess * variants, DictEntryNode.key variants) are descended through to reach * their ASTNode children but are never themselves passed to `visit`. * * Implemented as an explicit-stack iterative walk (rather than recursion) * so that deeply nested ASTs do not risk a `RangeError: Maximum call stack * size exceeded`. Children are pushed onto the stack in reverse order so * they pop, and are visited, in source (left-to-right) order — preserving * the same parent-before-children, left-to-right visit order as the * recursive form. */ export declare function walkAst(root: ASTNode, visit: (node: ASTNode) => void): void; /** * Returns the deepest ASTNode reachable from `root` whose span contains * `offset` (0-based absolute character offset, matching * `SourceLocation.offset`), or `null` if no such node exists. Descends * through span-less FieldAccess/DictEntryNode.key segments to reach child * ASTNodes but never returns a segment. * * Children are tried before `root` itself (rather than gating descent on * `root`'s own span first): some node shapes carry a span narrower than * their true source extent (see `ownsOffset`), so a matching descendant * can be reachable even when `root`'s own span does not contain `offset`. * For ordinary well-nested spans this yields the same result as gating on * `root` first. * * Implemented as an explicit-stack iterative depth-first walk (rather than * recursion) to avoid stack overflow on deeply nested ASTs, while * preserving the exact child-first (deepest-node) return semantics of the * recursive form: a matching descendant is always returned before its * ancestor is even checked. * * Span-based pruning: before descending into a child, `spanContains` is * checked against the child's own span, so subtrees whose span cannot * possibly contain `offset` are skipped entirely — this keeps lookup * latency proportional to cursor nesting depth rather than total node * count. The one exception is `VariableNode`: per `ownsOffset`, its own * `span` covers only the leading `$name` token while its access-chain * children (`FieldAccessComputed`/`FieldAccessBlock` inner expressions) * can extend further, so a `VariableNode` child is always descended into * unconditionally, without a span pre-check. */ export declare function nodeAtPosition(root: ASTNode, offset: number): ASTNode | null;