Skip to content

Instantly share code, notes, and snippets.

@aungmyatmoethegreat
Created August 20, 2022 18:38
Show Gist options
  • Save aungmyatmoethegreat/1c5db2c86573c7cdcaa37c518ca668f7 to your computer and use it in GitHub Desktop.
Save aungmyatmoethegreat/1c5db2c86573c7cdcaa37c518ca668f7 to your computer and use it in GitHub Desktop.
Wuttyi Lang LL parsing
export default class Environment {
// create a variable environment (table of environment)
constructor(record = {}) {
this.record = record;
}
// set the variable to the env with the given name and value
define(name, value) {
this.record[name] = value;
return value;
}
// get the variable value from record
lookup(name) {
if (!this.record.hasOwnProperty(name)) {
throw new ReferenceError(`ReferenceError: ${name} is not defined`)
}
return this.record[name];
}
}
import assert from 'assert';
import Environment from "./Environment.js";
/* It evaluates a JavaScript expression and returns the result */
class Wuttyi {
// Create the Wuttyi instance with the global environment
constructor(env = new Environment()) {
this.global = env;
}
// -------------------- Self evaluation -------------
// Evaluate an expression in the given environment
eval(exp, env = this.global) {
if (isNumber(exp)) {
return exp;
}
if (isString(exp)) {
return exp.slice(1, -1)
}
// -------------- Math 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, this.eval(value));
}
// ---------------- Variable Access ---------------------------
if (isVariableName(exp[0])) {
return env.lookup(exp);
}
throw `Unimplemented: ${exp.stringify()}`;
}
}
function isNumber(exp) {
return typeof exp === 'number';
}
function isString(exp) {
return typeof exp === 'string' && exp.startsWith('"') && exp.endsWith('"');
}
function isVariableName(exp) {
return typeof exp === 'string' && /^[a-zA-Z][a-zA-Z0-9_]*$/.test(exp);
}
// -------------------- Tests ----------------
// set the predefined variable
const wuttyi = new Wuttyi(new Environment({
true: true,
false: false,
null: null,
version: '1.0.0',
}));
// ----------- Math Tests ----------------------
// exp ::= number | string | [+ number, number]
assert.strictEqual(wuttyi.eval(1), 1);
assert.strictEqual(wuttyi.eval('"hello"'), 'hello');
assert.strictEqual(wuttyi.eval(['+', 1, 2]), 3);
assert.strictEqual(wuttyi.eval(['+', ['+', 6, 2], 2]), 10);
assert.strictEqual(wuttyi.eval(['+', ['*', 6, 2], 2]), 14);
assert.strictEqual(wuttyi.eval(['*', ['+', 2, 3], 5]), 25);
// ------------- Variable -----------------------
assert.strictEqual(wuttyi.eval(['var', 'x', 10]), 10);
assert.strictEqual(wuttyi.eval('x'), 10);
// var isUser = true;
assert.strictEqual(wuttyi.eval(['var', 'isUser', 'true']), true);
assert.strictEqual(wuttyi.eval('isUser'), true);
assert.strictEqual(wuttyi.eval(['var', 'y', 100]), 100);
assert.strictEqual(wuttyi.eval('y'), 100);
console.log('Asserting was passed')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment