export interface Locals { reserveAmount(id: number): void; allocSym(ident: string): void; symId(ident: string): number; currentLocalIdCounter(): number; } export class LocalsFnRoot implements Locals { private localsAmount = 0; private symLocalMap: { [key: string]: number } = {}; private localIdCounter: number; constructor(private parent?: Locals) { this.localIdCounter = parent?.currentLocalIdCounter() ?? 0; } public reserveAmount(amount: number): void { this.localsAmount = Math.max(amount, this.localsAmount); } public allocSym(ident: string) { this.symLocalMap[ident] = this.localIdCounter; this.localIdCounter++; this.reserveAmount(this.localIdCounter); } public symId(ident: string): number { if (ident in this.symLocalMap) { return this.symLocalMap[ident]; } if (this.parent) { return this.parent.symId(ident); } throw new Error(`undefined symbol '${ident}'`); } public stackReserved(): number { return this.localsAmount; } public currentLocalIdCounter(): number { return this.localIdCounter; } } export class LocalLeaf implements Locals { private symLocalMap: { [key: string]: number } = {}; private localIdCounter: number; constructor(private parent: Locals) { this.localIdCounter = parent.currentLocalIdCounter(); } public reserveAmount(amount: number): void { this.parent.reserveAmount(amount); } public allocSym(ident: string) { this.symLocalMap[ident] = this.localIdCounter; this.localIdCounter++; this.reserveAmount(this.localIdCounter); } public symId(ident: string): number { if (ident in this.symLocalMap) { return this.symLocalMap[ident]; } return this.parent.symId(ident); } public currentLocalIdCounter(): number { return this.localIdCounter; } }