const assert = require('assert');
const Environment = require('./Environment')
/**
* Eva interpreter
*/
class Eva {
/**
* Creates an Eva instance with the global environment.
*/
constructor(global = new Environment()) {
this.global = global;
}
eval(exp, env = this.global) {
// ----------------------------------------------
// Self-evaluating expressions:
if (isNumber(exp)) {
return exp;
}
if (isString(exp)) {
return exp.slice(1, -1);
}
// ----------------------------------------------
// Mathematical operations:
if (exp[0] === '+') {
return this.eval(exp[1]) + this.eval(exp[2]);
}
if (exp[0] === '*') {
return this.eval(exp[1]) * this.eval(exp[2]);
}
if (exp[0] === '-') {
return this.eval(exp[1]) - this.eval(exp[2]);
}
if (exp[0] === '/') {
return this.eval(exp[1]) / this.eval(exp[2]);
}
if (exp[0] === '%') {
return this.eval(exp[1]) % this.eval(exp[2]);
}
// ------------------------------------------------
// Variable declaration
if (exp[0] === 'var') {
const [_, name, value] = exp;
return env.define(name, value);
}
throw `Unimplemented: ${JSON.stringify(exp)}`;
}
}
// ------------------------------------------------
// Helper functions
function isNumber(exp){
return typeof exp === 'number';
}
function isString(exp) {
return typeof exp === 'string' && exp[0] === '"' && exp.slice(-1) === '"';
}
// ------------------------------------------------
// Tests:
const eva = new Eva();
assert.strictEqual(eva.eval(1), 1);
assert.strictEqual(eva.eval('"hello"'), 'hello');
// Math:
assert.strictEqual(eva.eval(['+', 1, 5]), 6);
assert.strictEqual(eva.eval(['+', ['+', 3, 2], 5]), 10);
assert.strictEqual(eva.eval(['+', ['*', 3, 2], 5]), 11);
assert.strictEqual(eva.eval(['+', ['-', 3, 2], 5]), 6);
assert.strictEqual(eva.eval(['+', ['/', 3, 2], 5]), 6.5);
assert.strictEqual(eva.eval(['+', ['%', 3, 2], 5]), 6);
// Variables:
assert.strictEqual(eva.eval(["var", "x", 10], 10))
console.log('All assertions passed.');
/**
* Environment: names storage
*/
module.exports = class Environment {
constructor(record = {}, parent = null) {
this.record = record;
}
define(name, value) {
this.record[name] = value;
return value;
}
}
return env.define(name, value);
^
TypeError: env.define is not a function
at Eva.eval (/Users/chuck/ContEd/CompSci/building-an-interpreter-from-scratch/eva/Eva.js:57:18)
at Object.<anonymous> (/Users/chuck/ContEd/CompSci/building-an-interpreter-from-scratch/eva/Eva.js:95:24)
at Module._compile (node:internal/modules/cjs/loader:1092:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1121:10)
at Module.load (node:internal/modules/cjs/loader:972:32)
at Function.Module._load (node:internal/modules/cjs/loader:813:14)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
at node:internal/main/run_main_module:17:47
I have tested the Environment class outside of the Eva class and it works as expected.
insights appreciated.