mathjslab
Version:
MathJSLab - An interpreter with language syntax like MATLAB®/Octave, ISBN 978-65-00-82338-7.
279 lines (278 loc) • 10.8 kB
TypeScript
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;