Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class Env {
- constructor(p, e = {}) {
- this.p = p;
- this.e = e;
- }
- get(name) {
- let val = this.e[name];
- if (typeof val !== 'undefined')
- return val;
- if (this.p)
- return this.p.get(name);
- throw `Identifier ${name} is not defined`;
- }
- set(name, val) {
- let exists = !!this.e[name];
- if (!exists)
- this.e[name] = val;
- else
- throw `Identifier ${name} is already defined`;
- }
- }
- function Func(params, body, _env) {
- return (x) => {
- let env = new Env(_env, params.reduce((acc, p, i) => {
- acc[p.v] = x[i];
- return acc;
- }, {}));
- return eval(body, env);
- };
- }
- let proc = process,
- con = console,
- cmpop = (x, f) => x.slice(1).every(f),
- globals = {
- '+': (x) => x.reduce((acc, val) => acc + val),
- '-': (x) => x.reduce((acc, val) => acc - val),
- '/': (x) => x.reduce((acc, val) => { if (val === 0) throw "Cannot divide by 0"; return acc / val; }),
- '*': (x) => x.reduce((acc, val) => acc * val),
- '**': (x) => x.reduce((acc, val) => Math.pow(acc, val)),
- '>': (x) => cmpop(x, (v) => x[0] > v),
- '>=': (x) => cmpop(x, (v) => x[0] >= v),
- '<': (x) => cmpop(x, (v) => x[0] < v),
- '<=': (x) => cmpop(x, (v) => x[0] <= v),
- '==': (x) => cmpop(x, (v) => x[0] === v),
- 'car': (x) => x[0],
- 'cdr': (x) => x.slice(1),
- 'cons': (x) => [x[0]].concat(x.slice(1))
- },
- genv = new Env(null, globals),
- rl = require('readline').createInterface({
- input: proc.stdin,
- output: proc.stdout,
- prompt: '\u039b> '
- });
- function lex(source) {
- return source.replace(/\(|\)/g, ' $& ').match(/".*?"|\S+/g);
- }
- function parse(tokens) {
- if (tokens.length === 0) throw 'Unexpected EOF';
- let token = tokens.shift();
- switch (token) {
- case '(':
- let list = [];
- while (tokens[0] !== ')') list.push(parse(tokens));
- tokens.shift();
- return { t: 'expression', args: list };
- case ')':
- throw 'Unmatched delimiter )';
- default:
- if (token[0] === '"') return { t: 'string', v: token.substr(1, token.length-2) };
- let numberVal = Number(token);
- return isNaN(numberVal) ? { t: 'identifier', v: token } : { t: 'number', v: numberVal };
- }
- }
- function eval(exp, env) {
- switch (exp.t) {
- case 'expression':
- let args = exp.args,
- v = args[0].v;
- if (v === 'if') {
- return eval(args[1], env) ? eval(args[2], env) : eval(args[3], env);
- } else if (v === 'define') {
- env.set(args[1].v, eval(args[2], env));
- } else if (v === 'lambda') {
- return Func(args[1].args, args[2], env);
- } else if (v === 'quote') {
- return args[1];
- }
- else {
- let f = eval(args[0], env),
- vals = args.slice(1).map((e) => eval(e, env));
- return f(vals);
- }
- case 'string':
- case 'number':
- return exp.v;
- case 'identifier':
- return env.get(exp.v);
- default:
- return exp.v;
- }
- }
- rl.prompt();
- rl.on('line', (l) => {
- if (l) {
- try {
- let r = eval(parse(lex(l.trim())), genv);
- if (typeof r !== 'undefined') con.log(r);
- } catch (e) {
- con.error(`Error: ${e}`);
- }
- }
- rl.prompt();
- }).on('close', () => proc.exit(0));
Add Comment
Please, Sign In to add comment