/** * Read-only scope analysis over a parsed `Program`. Designed for editor * integrations (variable hover, go-to-definition, find-references, * rename-symbol, scope-visible name completion). * * Mirrors the scope rules used by `analyzeProgram` in * `./semantic-diagnostics` but exposes them as a queryable surface keyed * by source offset: * * - `visibleAt(offset)` walks the scope chain at the cursor and returns * every visible declaration, innermost first, with outer-scope shadows * excluded. * - `resolve(name, offset)` finds the innermost binding of `name` from * the perspective of `offset`. * - `resolveDecl(offset)` returns the binding whose own `nameId.loc` * contains `offset` — declarator-position lookup, useful for * go-to-definition on the declarator itself and for rename refactors * without a separate AST walk. * - `references(decl)` walks every `VariableRef` and returns the ones * that resolve to `decl` at their own offset. `$`-declarations only. * - `diceReferences(decl)` walks every `StructuredDiceRoll` and returns * the ones whose `name` slot equals `decl.name`. `@`-declarations only. * * `references` and `diceReferences` throw on misuse (passing a * `$`-declaration to `diceReferences` or vice versa) rather than returning * `[]`, so a typoed call surfaces as an error instead of silently empty. */ import type { Assignment, ComprehensionExpr, DiceDeclaration, FoldExpr, Identifier, ParameterDeclaration, Program, StructuredDiceRoll, VariableRef } from './program'; export interface DeclarationInfo { /** Bare name, without sigil. Same as `nameId.name`. */ name: string; kind: 'assignment' | 'parameter' | 'dice-declaration' | 'binding'; /** * The declaring AST node. * * - `assignment` → `Assignment` statement. * - `parameter` → `ParameterDeclaration` statement. * - `dice-declaration` → `DiceDeclaration` statement. * - `binding` → the comprehension (`for $x in …`) or `fold` (acc/elem) * expression that introduces the binder. `RepeatExpr` does NOT bind a * loop variable — its body re-evaluates inner `Assignment` statements * per iteration — so it never appears here. */ node: Assignment | ParameterDeclaration | DiceDeclaration | ComprehensionExpr | FoldExpr; /** * Identifier node for the specific binder that introduced this name. * For `Assignment` / `ParameterDeclaration` / `DiceDeclaration` this is * the statement's `name`. For `ComprehensionExpr` it's `binder`. For * `FoldExpr` it's either `accName` or `elemName` depending on which * binder this info describes. * * Use `nameId.loc` for precise source positions of the binder * identifier alone — useful for rename refactors and binder-targeted * go-to-definition. Always populated by `analyzeScope`. */ nameId: Identifier; } export interface ScopeAnalysis { /** All `$variable` and `@dicename` declarations visible at `offset`, * innermost scope first. Outer-scope declarations shadowed by an inner * same-named binding are excluded. */ visibleAt(offset: number): DeclarationInfo[]; /** The innermost declaration of `name` visible at `offset`, or * `undefined` if the name is unbound there. */ resolve(name: string, offset: number): DeclarationInfo | undefined; /** The declaration whose own `nameId.loc` contains `offset`, or * `undefined` if `offset` isn't inside any binder identifier. * * Binder identifier spans don't overlap, so the result is unambiguous. * Pairs with `resolve` for full positional lookup: at a use-site * `resolve(nameAt(o), o)` finds the binding; at a declarator * `resolveDecl(o)` returns the declaration directly without needing * to know the name. */ resolveDecl(offset: number): DeclarationInfo | undefined; /** All `VariableRef` use-sites that resolve to `decl` at their own * offset. Handles shadowing: a use-site bound by a closer-scope * declaration of the same name does not appear here. * * Throws on a `dice-declaration` info — those are not referenced via * `VariableRef`. Use `diceReferences` instead. */ references(decl: DeclarationInfo): readonly VariableRef[]; /** All `StructuredDiceRoll` use-sites whose `name` equals `decl.name`. * Dice declarations live in a separate namespace and are not subject * to lexical shadowing, so this is a flat name match across the * program. * * Throws on any non-`dice-declaration` info — `$`-references go * through `references`. */ diceReferences(decl: DeclarationInfo): readonly StructuredDiceRoll[]; } export declare function analyzeScope(program: Program): ScopeAnalysis;