UNPKG

mathjslab

Version:

MathJSLab - An interpreter with language syntax like MATLAB®/Octave, ISBN 978-65-00-82338-7.

279 lines (278 loc) 10.8 kB
import type { ExpressionBoundaryValue, FunctionTable, NameEntry, NameTable, NodeBuiltInFunction, NodeFunctionDefinition, NodeInput, UndefinedReferenceTable } from './AST'; /** * Package/class import table for one lexical scope. */ type ImportTable = { /** Explicit simple-name aliases, e.g. `Point -> [pkg.Point]`. */ explicit: Record<string, string[]>; /** Wildcard package prefixes, e.g. `pkg` for `import pkg.*`. */ wildcard: string[]; }; /** * Represents a lexical workspace/scope. * * A scope stores variable bindings, function bindings, and forward-reference * bookkeeping. The parent chain models MATLAB/Octave-like lexical lookup for * ordinary functions, nested functions, anonymous-function closures, `eval`, * `evalin`, `assignin`, `global`, and `persistent`. * * Two flags tune lookup/assignment for special function-workspace cases: * * - `resolveParentNames`: when false, variable lookup stops at this scope. * This is used for captured snapshots where later parent changes must not * leak into a closure. * - `assignExistingParentNames`: when true, assignments update an existing * parent binding instead of always creating a local name. Nested functions use * this to emulate MATLAB/Octave shared workspaces. */ declare class Scope { parent?: Scope | undefined; nameTable: NameTable; functionTable: FunctionTable; undefinedReferenceTable: UndefinedReferenceTable; importTable: ImportTable; resolveParentNames: boolean; assignExistingParentNames: boolean; /** * Use `Scope.create` so scope tables are always prototype-less maps. */ private constructor(); /** * Create a fresh scope with empty name/function/reference tables. * * @param parent Optional parent scope for lexical lookup. * @param resolveParentNames Whether variable lookup may continue into the parent chain. * @returns New scope with prototype-less tables. */ static readonly create: (parent?: Scope, resolveParentNames?: boolean) => Scope; /** * Define or replace a variable in the current scope only. * * Existing entries are mutated in place so global aliases, persistent * bindings, and UI references that point at the entry object continue to see * updates. * * @param name Variable name. * @param node Value node to bind. * @param undefinedReference Optional unresolved-reference marker. * @returns The created or updated name-table entry. */ defineName(name: string, node: NodeInput, undefinedReference?: string): NameEntry; /** * Assign a value using the current scope's assignment policy. * * Ordinary scopes define locally. Nested-function scopes can opt into * parent assignment through `assignExistingParentNames`, which preserves * MATLAB/Octave shared-variable behavior. * * @param name Variable name. * @param node Value node to assign. * @param undefinedReference Optional unresolved-reference marker. * @returns The created or updated name-table entry. */ assignName(name: string, node: NodeInput, undefinedReference?: string): NameEntry; /** * Define several local variables at once. * * @param table Name/value table to merge into the local scope. */ defineNameTable(table: Record<string, NodeInput>): void; /** * Resolve a variable through this scope and, when allowed, its parents. * * @param name Variable name. * @returns Matching entry, if found. */ resolveName(name: string): NameEntry | undefined; /** * Check whether a name is defined directly in this scope. * * @param name Variable name. * @returns `true` when the name exists locally. */ hasLocalName(name: string): boolean; /** * Remove a local variable binding. * * @param name Variable name to remove. */ removeName(name: string): void; /** * Remove a variable binding from this scope and all parents. * * @param name Variable name to clear. */ clearName(name: string): void; /** * Define or replace a function in the current scope only. * * @param name Function name. * @param func Function definition node. * @returns Stored function definition. */ defineFunction(name: string, func: NodeFunctionDefinition): NodeFunctionDefinition; /** * Merge a function table into this scope. * * @param table Function table to merge. */ defineFunctionTable(table: FunctionTable): void; /** * Resolve a function through the lexical function table chain. * * Function lookup intentionally remains parent-aware even when * `resolveParentNames` is false; captured scopes still need live fallback * for forward-referenced local/nested functions. * * @param name Function name. * @returns Matching user or built-in function node, if found. */ resolveFunction(name: string): NodeFunctionDefinition | NodeBuiltInFunction | undefined; /** * Check whether a function is defined directly in this scope. * * @param name Function name. * @returns `true` when the function exists locally. */ hasLocalFunction(name: string): boolean; /** * Remove a local function binding. * * @param name Function name to remove. */ removeFunction(name: string): void; /** * Remove a function binding from this scope and all parents. * * @param name Function name to clear. */ clearFunction(name: string): void; /** * Bind formal parameter names to already evaluated argument nodes. * * @param names Formal parameter names. * @param args Evaluated argument nodes. */ bindParameters(names: string[], args: ExpressionBoundaryValue[]): void; /** * Bind formal parameters after checking exact arity. * * This low-level helper predates the richer function-call pipeline and is * kept for focused tests and simple call paths. * * @param names Formal parameter names. * @param args Evaluated argument nodes. * @throws Error when the list lengths differ. */ bindParametersChecked(names: string[], args: ExpressionBoundaryValue[]): void; /** * Record that `name` depends on an unresolved identifier. * * Forward references are stored per scope so assigning a missing name can * later trigger re-resolution without confusing unrelated workspaces. * * @param name Name that depends on an unresolved reference. * @param undefinedReference Missing identifier name. * @returns Mutable set of unresolved references for `name`. */ defineUndefinedReference(name: string, undefinedReference: string): Set<string>; /** * Resolve unresolved-reference metadata through the parent chain. * * @param name Name to inspect. * @returns Set of unresolved references, if any. */ resolveUndefinedReference(name: string): Set<string> | undefined; /** * Remove a local unresolved-reference entry. * * @param name Name to remove from the local unresolved-reference table. */ removeUndefinedReference(name: string): void; /** * Remove unresolved-reference metadata from this scope and all parents. * * @param name Name to clear from all reachable unresolved-reference tables. */ clearUndefinedReference(name: string): void; /** * Register one MATLAB-style package/class import in the current scope. * * @param qualifiedName Fully qualified class name or wildcard package import. */ defineImport(qualifiedName: string): void; /** * Remove all imports declared directly in this scope. * * Parent imports remain visible through the lexical chain, matching the * same local-only behavior used by ordinary name/function tables. */ clearImports(): void; /** * Create a detached copy of imports declared directly in this scope. * * @returns Copy suitable for later restoration. */ importSnapshot(): ImportTable; /** * Replace imports declared directly in this scope. * * Parent imports are not touched, preserving lexical import visibility. * * @param importTable Snapshot produced by `importSnapshot`. */ restoreImports(importTable: ImportTable): void; /** * Return the currently visible import declarations. * * Imports are reported from the innermost scope to outer scopes, with * explicit imports before wildcard package imports in each scope. Duplicate * entries are suppressed while preserving first visibility. * * @returns Fully qualified imports visible from this scope. */ importList(): string[]; /** * Return possible fully qualified names imported for a simple name. * * Local imports take precedence over parent imports. Wildcard imports are * returned from innermost to outermost scope and preserve source order. * * @param name Simple class or function name. * @returns Candidate fully qualified imported names. */ importedNameCandidates(name: string): string[]; /** * Deep-copy the visible variable/function environment into a detached chain. * * Anonymous functions use snapshots so captured variables keep the value * they had when the handle was created. Function tables and unresolved * reference lists are also copied so later forward-reference resolution * remains deterministic for the captured environment. * * @param copyNode Callback used to copy bound AST/runtime values. * @returns Detached scope chain. */ snapshot(copyNode: (node: NodeInput) => NodeInput): Scope; /** * Create a lexical overlay over the current scope. * * Current local entries are copied, while misses fall back to the live * parent scope. Named local/nested function handles use this mode: existing * bindings are stable, but later function definitions can still be resolved * through the parent chain. * * @param copyNode Callback used to copy local AST/runtime values. * @param resolveParentNames Whether variable lookup may continue to the parent. * @returns Captured overlay scope. */ capture(copyNode: (node: NodeInput) => NodeInput, resolveParentNames?: boolean): Scope; /** * Clone an explicit import table while preserving the prototype-less * internal representation used for scope maps. */ private static cloneExplicitImports; } export { Scope }; export type { ImportTable }; export default Scope;