UNPKG

mathjslab

Version:

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

152 lines (151 loc) 6.1 kB
import type { FunctionTable, NameEntry, NameTable, NodeBuiltInFunction, NodeExpr, NodeFunctionDefinition, NodeInput, UndefinedReferenceTable } from './AST'; /** * 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; 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. */ 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. */ 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. */ assignName(name: string, node: NodeInput, undefinedReference?: string): NameEntry; /** * Define several local variables at once. */ defineNameTable(table: Record<string, NodeInput>): void; /** * Resolve a variable through this scope and, when allowed, its parents. */ resolveName(name: string): NameEntry | undefined; /** * Check whether a name is defined directly in this scope. */ hasLocalName(name: string): boolean; /** * Remove a local variable binding. */ removeName(name: string): void; /** * Remove a variable binding from this scope and all parents. */ clearName(name: string): void; /** * Define or replace a function in the current scope only. */ defineFunction(name: string, func: NodeFunctionDefinition): NodeFunctionDefinition; /** * Merge a function table into this scope. */ 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. */ resolveFunction(name: string): NodeFunctionDefinition | NodeBuiltInFunction | undefined; /** * Check whether a function is defined directly in this scope. */ hasLocalFunction(name: string): boolean; /** * Remove a local function binding. */ removeFunction(name: string): void; /** * Remove a function binding from this scope and all parents. */ clearFunction(name: string): void; /** * Bind formal parameter names to already evaluated argument nodes. */ bindParameters(names: string[], args: NodeExpr[]): 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. */ bindParametersChecked(names: string[], args: NodeExpr[]): 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. */ defineUndefinedReference(name: string, undefinedReference: string): string[]; /** * Resolve unresolved-reference metadata through the parent chain. */ resolveUndefinedReference(name: string): string[] | undefined; /** * Remove a local unresolved-reference entry. */ removeUndefinedReference(name: string): void; /** * Remove unresolved-reference metadata from this scope and all parents. */ clearUndefinedReference(name: string): void; /** * 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. */ 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. */ capture(copyNode: (node: NodeInput) => NodeInput, resolveParentNames?: boolean): Scope; } export { Scope }; export default Scope;