UNPKG

@dhiraj2105/dlang

Version:

A basic programming language built for learning and experimenting

277 lines (248 loc) 8.04 kB
// ===================================== // evaluator.js // This file executes the AST // from the parser and runs the actual logic of the program // ✅ Input: AST from parser // ✅ Output: Console output, variable storage, etc. // // 🧠 Example: // AST: [{ type: 'VariableDeclaration', name: 'x', value: { type: 'NumberLiteral', value: 5 } }] // Result: Stores variable 'x' with value 5 // ========================================== // ------------------------------ // 📦 Environment Object // This acts like memory to store variables // ------------------------------ const env = {}; // store functions definations const functions = {}; // Control flow signals for break and continue class BreakSignal {} class ContinueSignal {} // return signal class ReturnSignal { constructor(value) { this.value = value; } } // ------------------------------ // Main Evaluate function // Accepts the full AST and runs each node // ------------------------------ function evaluate(ast) { for (const node of ast) { evaluateNode(node); // evaluate each top- level AST node in sequence } } // ------------------------------ // Dispatcher: Handle different node types // Delegates to the correct handler based on node.type // ------------------------------ function evaluateNode(node) { switch (node.type) { case "VariableDeclaration": return handleVariableDeclaration(node); case "PrintStatement": return handlePrintStatement(node); case "IfStatement": return handleIfStatement(node); case "WhileStatement": return handleWhileStatement(node); case "BlockStatement": return handleBlockStatement(node); case "BinaryExpression": return evaluateBinaryExpression(node); case "AssignmentExpression": return handleAssignment(node); case "ContinueStatement": // Handling ContinueStatement return handleContinueStatement(); // Trigger continue logic case "BreakStatement": // Handling BreakStatement return handleBreakStatement(); // Trigger break logic case "NumberLiteral": case "StringLiteral": case "Identifier": return evaluateExpression(node); case "FunctionDeclaration": return handleFunctionDeclaration(node); case "ReturnStatement": return handleReturnStatement(node); case "ExpressionStatement": return evaluateExpression(node.expression); case "CallExpression": return handleCallExpression(node); default: throw new Error("Unknown AST Node Type: " + node.type); } } // ------------------------------ // Handle Variable Declaration // stores variable values in the environment // ------------------------------ function handleVariableDeclaration(node) { const { name, value } = node; // destructure variable name and assigned value const evaluatedValue = evaluateExpression(value); // evaluate expression on right hand side env[name] = evaluatedValue; // store in env return evaluatedValue; } // ------------------------------ // 🖨️ Handle Print Statement // Example: print x or print 5 + 2 // ------------------------------ function handlePrintStatement(node) { const value = evaluateExpression(node.value); console.log(value); // print to output return value; } // ------------------------------ // If Statement // ------------------------------ function handleIfStatement(node) { const testResult = evaluateExpression(node.condition); if (testResult) { return evaluateNode(node.thenBranch); } else if (node.elseBranch) { return evaluateNode(node.elseBranch); } } // ------------------------------ // 🔁 While Statement // ------------------------------ function handleWhileStatement(node) { let result; while (evaluateExpression(node.condition)) { try { result = evaluateNode(node.body); } catch (e) { if (e instanceof BreakSignal) break; if (e instanceof ContinueSignal) continue; throw e; // rethrow other unexpected errors } } return result; } // ------------------------------ // Handle Continue Signal // This will signal the loop to continue to the next iteration // ------------------------------ function handleContinueStatement() { throw new ContinueSignal(); // Trigger a ContinueSignal to skip the current loop iteration } // ------------------------------ // Handle Break Signal // This will signal the loop to break and exit // ------------------------------ function handleBreakStatement() { throw new BreakSignal(); // Trigger a BreakSignal to exit the loop } function handleAssignment(node) { if (!(node.name in env)) { throw new Error(`Cannot assign to undeclared variable: ${node.name}`); } const newValue = evaluateExpression(node.value); env[node.name] = newValue; return newValue; } // ------------------------------ // Block Statement // Executes multiple statements inside a block // ------------------------------ function handleBlockStatement(node) { let result; for (const stmt of node.body) { result = evaluateNode(stmt); } return result; } // ------------------------------ // 🧮 Handle Arithmetic and Binary Expressions // Example: 5 + 3, 10 - x // ------------------------------ function evaluateBinaryExpression(node) { const left = evaluateExpression(node.left); // recursively evaluate left side const right = evaluateExpression(node.right); // recusively evaluate right side switch (node.operator) { case "+": return left + right; case "-": return left - right; case "*": return left * right; case "/": return left / right; case "=": return left === right; case "<": return left < right; case ">": return left > right; case "==": // ✅ ADDED return left === right; case "!=": // ✅ ADDED return left !== right; default: throw new Error("Unsupported operator: " + node.operator); } } // ------------------------------ // 🧠 Evaluate Expressions // Handles literals, variables, binary expressions // ------------------------------ function evaluateExpression(expr) { switch (expr.type) { case "NumberLiteral": return expr.value; // return numeric value directly case "StringLiteral": return expr.value; // return string value directly case "Identifier": // lookup variable name in environment if (expr.name in env) { return env[expr.name]; // return stored value } else { throw new Error(`Undefined variable: ${expr.name}`); } case "BinaryExpression": return evaluateBinaryExpression(expr); // recurse for nested expression case "CallExpression": return handleCallExpression(expr); default: throw new Error("Unknown expression type: " + expr.type); } } function handleFunctionDeclaration(node) { const { name, params, body } = node; functions[name] = { params, body }; } function handleCallExpression(node) { const func = functions[node.name]; if (!func) { throw new Error(`Undefined function: ${node.name}`); } const { params, body } = func; const args = node.arguments.map(evaluateExpression); // Create a local environment for the function const oldEnv = { ...env }; // Assign arguments to parameters for (let i = 0; i < params.length; i++) { env[params[i]] = args[i]; } try { const result = evaluateNode(body); // Evaluate function body return result; // If function ends without return, return undefined } catch (e) { if (e instanceof ReturnSignal) { return e.value; // if a return is encountered, return its value } throw e; // rethrow unexpected errors } finally { // Restore previous environment after function call for (const key of Object.keys(env)) { delete env[key]; } Object.assign(env, oldEnv); } } function handleReturnStatement(node) { const value = evaluateExpression(node.value); throw new ReturnSignal(value); // Immediately return the value } export default evaluate;