Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103import { type AstNode, type Binding, InterpreterRuntimeError } from "./model.js"
export class ScopeStack { private readonly scopes: Array<Map<string, Binding>>
constructor(scopes: Array<Map<string, Binding>>) { this.scopes = scopes }
reserve(name: string, mutable: boolean, node: AstNode): void { const scope = this.current() if (scope.has(name)) { throw new InterpreterRuntimeError(`Identifier '${name}' has already been declared.`, node) } scope.set(name, { mutable, value: undefined, initialized: false }) }
initialize(name: string, value: unknown, node: AstNode): void { const binding = this.current().get(name) if (!binding || binding.initialized !== false) { throw new InterpreterRuntimeError(`Identifier '${name}' has not been reserved for initialization.`, node) } binding.value = value binding.initialized = true }
declare(name: string, value: unknown, mutable: boolean, node: AstNode): void { const scope = this.current() if (scope.has(name)) { throw new InterpreterRuntimeError(`Identifier '${name}' has already been declared.`, node) } scope.set(name, { mutable, value, initialized: true }) }
get(name: string, node: AstNode): unknown { const binding = this.resolve(name)
if (!binding) { throw new InterpreterRuntimeError(`Unknown identifier '${name}'.`, node).as("ReferenceError") }
if (binding.initialized === false) { throw new InterpreterRuntimeError(`Cannot access '${name}' before initialization.`, node).as("ReferenceError") }
return binding.value }
set(name: string, value: unknown, node: AstNode): unknown { const binding = this.resolve(name)
if (!binding) { throw new InterpreterRuntimeError(`Unknown identifier '${name}'.`, node).as("ReferenceError") }
if (binding.initialized === false) { throw new InterpreterRuntimeError(`Cannot access '${name}' before initialization.`, node).as("ReferenceError") }
if (!binding.mutable) { throw new InterpreterRuntimeError(`Cannot assign to constant '${name}'.`, node).as("TypeError") }
binding.value = value return value }
resolve(name: string): Binding | undefined { for (let index = this.scopes.length - 1; index >= 0; index -= 1) { const scope = this.scopes[index] const binding = scope?.get(name)
if (binding) { return binding } }
return undefined }
current(): Map<string, Binding> { const scope = this.scopes[this.scopes.length - 1]
if (!scope) { throw new InterpreterRuntimeError("Interpreter scope stack is empty.") }
return scope }
push(scope: Map<string, Binding> = new Map()): void { this.scopes.push(scope) }
pop(): void { this.scopes.pop() }
capture(): Array<Map<string, Binding>> { return this.scopes.slice() }}