UNPKG

lisp-js-compiler

Version:

JavaScript compiler / interpreter for Lisp language

272 lines 9.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tokenizer_1 = require("./tokenizer"); const utils_1 = require("./utils"); // evaluate a primitive expression exports.exp = api => async (exp) => { if (exp instanceof Array) { return await api.evalAst(exp); } else if (!isNaN(exp)) { return parseFloat(exp); } else if (typeof exp === 'object' || typeof exp === 'function') { return exp; } else if (tokenizer_1.stringDelimiters.indexOf(exp[0]) !== -1 && tokenizer_1.stringDelimiters.indexOf(exp[exp.length - 1]) !== -1) { return exp.slice(1, exp.length - 1); } else { let value = api.getValue(exp); if (value !== undefined) { return value; } else { return await api.evalAst(['throw', `"There are no name '${exp}' in the scope"`]); } } }; // evaluate a list (AST) exports.evalAst = api => async (ast) => { // () = null if (ast.length === 0) { return null; } let op = ast[0]; let args = ast.slice(1, ast.length); if (!op) { return await api.evalAst(['throw', `'Invalid operation in: ${ast}'`]); } if (op instanceof Array) { op = await api.exp(op); // lambda evaluation if (typeof op === 'function') { return await op.apply(api, args); } } let fn = api.getValue(op); if (fn !== undefined) { if (typeof fn === 'function') { return await fn.apply(api, args); } else { return await api.evalAst(['throw', `'The operation is not valid: ${op}'`]); } } else { return await api.evalAst(['throw', `'The operation is not defined: ${op}'`]); } }; async function evaluateFn(api, name, [params, body], args) { let _api = Object.assign({}, api); // Clone stack for clousure _api._stack = _api._stack.map(l => (Object.assign({}, l))); _api._pushScope(); let argsVal = await utils_1.mapAsync(args, api.exp); if (params instanceof Array) { for (let i = 0, len = params.length; i < len; i++) { if (argsVal[i] !== undefined) { api.setValue(params[i], argsVal[i]); } else { return await api.evalAst(['throw', `"Missing argument '${params[i]}' in function '${name}'"`]); } } } else { if (argsVal[0] !== undefined) { api.setValue(params, argsVal[0]); } else { return await api.evalAst(['throw', `"Missing argument '${params[0]}' in function '${name}'"`]); } } let result = await api.exp(body); api._popScope(); return result; } exports.evaluateFn = evaluateFn; exports.setValue = (api, stack) => (name, value) => { let scope = stack[stack.length - 1]; scope[name] = value; }; exports.getValue = (api, stack) => name => { let scope, i = stack.length - 1; while (scope = stack[i]) { if (scope.hasOwnProperty(name)) { return scope[name]; } i--; } if (api.env.hasOwnProperty(name)) { return api.env[name]; } return undefined; }; exports.makeAPI = env => { let api = { env: Object.assign({}, exports.constants, exports.atoms, env), _stack: [], _pushScope: () => api._stack.push({}), _popScope: () => api._stack.pop(), }; api.evalAst = exports.evalAst(api); api.exp = exports.exp(api); api.getValue = exports.getValue(api, api._stack); api.setValue = exports.setValue(api, api._stack); return api; }; exports.constants = { 'true': true, 'false': false, }; exports.atoms = { // Built in // ---- Special Forms process: async function (...args) { let result; this._pushScope(); for (let i = 0, len = args.length, arg; arg = args[i]; i++) { if (arg instanceof Array) { result = await this.evalAst(arg); } else if (i === len - 1) { result = await this.exp(arg); } else { return await this.evalAst(['throw', `'process only can have lists as arguments'`]); } } this._popScope(); return result; }, // Constant definition def: async function (name, exp) { if (name instanceof Array) { let fn = async (...args) => await evaluateFn(this, 'lambda', [name.slice(1), exp], args); this.setValue(name[0], fn); } else { this.setValue(name, await this.exp(exp)); } }, // Lambda definition '->': function (params, body) { return async (...args) => await evaluateFn(this, 'lambda', [params, body], args); }, // Function composition operator '.': function (...fns) { return async (...args) => await utils_1.reduceRightAsync(fns, async (a, fn, i) => await (await this.exp(fn)).apply(this, i === (fns.length - 1) ? a : [a]), args); }, // Inverse function composition operator 'pipe': function (...fns) { return async (...args) => await utils_1.reduceAsync(fns, async (a, fn, i) => await (await this.exp(fn)).apply(this, i === 0 ? a : [a]), args); }, // JS Math (TODO: Doc) Math: async function (...args) { let name = args[0]; name = name[0] === '.' ? name.slice(1) : await this.exp(name); let subj = Math[name]; let fnArgs = await utils_1.expArgs(this, args.slice(1)); return typeof subj === 'function' ? subj.apply(null, fnArgs) : subj; }, cond: async function (...args) { for (let i = 0, arg; arg = args[i]; i++) { if (arg[0] === 'else') { return await this.exp(arg[1]); } else if (await this.exp(arg[0])) { return await this.exp(arg[1]); } } }, if: async function (pred, con, alter) { return await this.exp(pred) ? await this.exp(con) : await this.exp(alter); }, // ---- // Error handling throw: async function (...args) { throw (await utils_1.mapAsync(args, async (a) => await this.exp(a))).join(' '); }, // Mathematical '+': async function (...args) { return await utils_1.reduceAsync(args, async (a, n) => a + await this.exp(n), 0); }, '-': async function (...args) { return await utils_1.reduceAsync(args.slice(1), async (a, n) => a - await this.exp(n), await this.exp(args[0])); }, '*': async function (...args) { return await utils_1.reduceAsync(args, async (a, n) => a * (await this.exp(n)), 1); }, '/': async function (...args) { return await this.exp(args[0]) / await utils_1.reduceAsync(args.slice(1), async (a, n) => a * await this.exp(n), 1); }, // Logical '>': async function (...args) { return await this.exp(args[0]) > await this.exp(args[1]); }, '>=': async function (...args) { return await this.exp(args[0]) >= await this.exp(args[1]); }, '=': async function (...args) { return await this.exp(args[0]) == await this.exp(args[1]); }, '<': async function (...args) { return await this.exp(args[0]) < await this.exp(args[1]); }, '<=': async function (...args) { return await this.exp(args[0]) <= await this.exp(args[1]); }, xor: async function (...args) { return await this.exp(args[0]) ^ await this.exp(args[1]); }, not: async function (...args) { return !await this.exp(args[0]); }, and: async function (...args) { return await utils_1.reduceAsync(args, async (a, n) => a && await this.exp(n), true); }, or: async function (...args) { return await utils_1.reduceAsync(args, async (a, n) => a || await this.exp(n), false); }, // Strings cat: async function (...args) { return await utils_1.reduceAsync(args, async (a, n) => a + await this.exp(n), ''); }, // Key-Value structure kv: async function (...args) { let obj = {}; let evArgs = await utils_1.mapAsync(args, async (a, idx) => (idx % 2 === 1 || a instanceof Array) ? await this.exp(a) : a); for (let i = 0, len = evArgs.length; i < len; i += 2) { obj[evArgs[i]] = evArgs[i + 1]; } return obj; }, // List structure ls: async function (...args) { return await utils_1.mapAsync(args, async (a) => await this.exp(a)); }, get: async function (...args) { let a = await utils_1.mapAsync(args, async (a) => await this.exp(a)); let res = a[0]; for (let i = 1, len = a.length; i < len; i++) { res = res[a[i]]; } return res; }, set: async function (...args) { let a = await utils_1.mapAsync(args, async (a) => await this.exp(a)); let subj = a[0]; for (let i = 1, len = a.length - 2; i < len; i++) { subj = subj[a[i]]; } let lastKey = a[a.length - 2]; let value = a[a.length - 1]; subj[lastKey] = value; return value; }, }; //# sourceMappingURL=interpreter.js.map