import { UnknownSymbolError, InvalidType, type TracingLogger } from '@/utils'; import { matchSignature, type Denotable } from '.'; export class Environment { private scope: Map<string, Denotable>; private parent: Environment | null; private logger: TracingLogger; constructor(logger: TracingLogger, parent: Environment | null = null) { this.logger = logger; this.parent = parent; this.scope = new Map(); } public set(name: string, value: Denotable) { this.scope.set(name, value); } public get(name: string): Denotable { if (this.scope.has(name)) { this.logger.debug(`Found Name=(${name}) in current scope`); return this.scope.get(name)!; } if (this.parent) { this.logger.debug(`Looking for Name=(${name}) in parent scope`); return this.parent.get(name); } throw new UnknownSymbolError(`Undefined variable: ${name}`); } public has(name: string): boolean { if (this.scope.has(name)) { this.logger.debug(`Found Name=(${name}) in current scope`); return true; } if (this.parent) { this.logger.debug(`Found Name=(${name}) in current scope`); return this.parent.has(name); } this.logger.debug(`Name=(${name}) not found in any scope`); return false; } public createChild(): Environment { return new Environment(this.logger.createChild('Env'), this); } public apply(name: string, args: Denotable[]): Denotable { const fn = this.get(name); if (typeof fn.value !== 'object' || !fn.value || !('body' in fn.value)) { throw new InvalidType(name + ' is not a valid function'); } const argTypes = args.map(arg => { const { type, value } = arg; const isFunction = type === 'function' && typeof value === 'object' && value && 'signatures' in value; if (isFunction) { return value.signatures; } return type; }); const appliedSignature = matchSignature(argTypes, fn.value.signatures); if (!appliedSignature) { throw new InvalidType(`No matching signature for ${name}`); } this.logger.debug( `Applying Function=(${name}) with Args=(${JSON.stringify(args)}) with Signature=(${JSON.stringify(appliedSignature)})`, ); const value = fn.value.body.apply(this, args); return { type: appliedSignature.return, value }; } }