petcarescript
Version:
PetCareScript - A modern, expressive programming language designed for humans
112 lines (91 loc) • 2.84 kB
JavaScript
/**
* PetCareScript Environment
* Manages variable scope
*/
class Environment {
constructor(enclosing = null) {
this.enclosing = enclosing;
this.values = new Map();
}
define(name, value) {
const varName = this.extractName(name);
this.values.set(varName, value);
}
ancestor(distance) {
let environment = this;
for (let i = 0; i < distance; i++) {
if (!environment.enclosing) {
throw new Error(`Cannot find ancestor at distance ${distance}`);
}
environment = environment.enclosing;
}
return environment;
}
getAt(distance, name) {
const varName = this.extractName(name);
return this.ancestor(distance).values.get(varName);
}
assignAt(distance, name, value) {
const varName = this.extractName(name);
this.ancestor(distance).values.set(varName, value);
}
get(name) {
const varName = this.extractName(name);
if (this.values.has(varName)) {
return this.values.get(varName);
}
if (this.enclosing !== null) {
return this.enclosing.get(varName);
}
throw new Error(`Undefined variable '${varName}'.`);
}
assign(name, value) {
const varName = this.extractName(name);
if (this.values.has(varName)) {
this.values.set(varName, value);
return;
}
if (this.enclosing !== null) {
this.enclosing.assign(varName, value);
return;
}
throw new Error(`Undefined variable '${varName}'.`);
}
// Helper method to extract name from various token/node formats
extractName(name) {
if (typeof name === 'string') {
return name;
}
if (name && name.lexeme) {
return name.lexeme;
}
if (name && name.name && name.name.lexeme) {
return name.name.lexeme;
}
if (name && name.name) {
return name.name;
}
throw new Error(`Cannot extract name from: ${JSON.stringify(name)}`);
}
// Debug method to see all variables in current scope
getVariables() {
return Array.from(this.values.keys());
}
// Check if variable exists in current scope (not ancestors)
hasLocal(name) {
const varName = this.extractName(name);
return this.values.has(varName);
}
// Check if variable exists in any scope
has(name) {
const varName = this.extractName(name);
if (this.values.has(varName)) {
return true;
}
if (this.enclosing !== null) {
return this.enclosing.has(varName);
}
return false;
}
}
module.exports = Environment;