export interface Token {
type: "KEYWORD" | "IDENTIFIER" | "NUMBER" | "OPERATOR" | "PUNCTUATION";
value: string;
}
export interface ASTNode {
type: string;
[key: string]: any;
}
export class Environment {
private values: Map<string, any> = new Map();
private parent: Environment | null;
constructor(parent: Environment | null = null) {
this.parent = parent;
}
// Define variable in current scope
public define(name: string, value: any): void {
this.values.set(name, value);
}
// Lookup variable traversing parent chain
public get(name: string): any {
if (this.values.has(name)) {
return this.values.get(name);
}
if (this.parent) {
return this.parent.get(name);
}
throw new Error(`ReferenceError: ${name} is not defined`);
}
// Assign variable in scope chain where declared
public assign(name: string, value: any): void {
if (this.values.has(name)) {
this.values.set(name, value);
return;
}
if (this.parent) {
this.parent.assign(name, value);
return;
}
throw new Error(`ReferenceError: Cannot assign to undefined variable ${name}`);
}
}
export class ASTEvaluator {
/**
* Recursively evaluates AST nodes in the current Environment scope
*/
public evaluate(node: ASTNode, env: Environment): any {
switch (node.type) {
case "Program":
let lastValue = null;
for (const statement of node.body) {
lastValue = this.evaluate(statement, env);
}
return lastValue;
case "VariableDeclaration":
const initVal = node.init ? this.evaluate(node.init, env) : null;
env.define(node.id, initVal);
return initVal;
case "AssignmentExpression":
const rightVal = this.evaluate(node.value, env);
env.assign(node.id, rightVal);
return rightVal;
case "Literal":
return node.value;
case "Identifier":
return env.get(node.name);
case "BinaryExpression":
const leftVal = this.evaluate(node.left, env);
const operand = this.evaluate(node.right, env);
return this.applyOperator(node.operator, leftVal, operand);
case "ForStatement":
// Initialize loop variable in a nested loop scope environment
const loopEnv = new Environment(env);
this.evaluate(node.init, loopEnv);
while (this.evaluate(node.test, loopEnv)) {
this.evaluate(node.body, loopEnv);
this.evaluate(node.update, loopEnv);
}
return null;
default:
throw new Error(`Unknown AST Node type: ${node.type}`);
}
}
private applyOperator(op: string, left: any, right: any): any {
switch (op) {
case "+": return left + right;
case "-": return left - right;
case "*": return left * right;
case "/": return left / right;
case "<=": return left <= right;
case "==": return left == right;
default:
throw new Error(`Unsupported operator: ${op}`);
}
}
}