UNPKG

@thi.ng/pointfree

Version:

Pointfree functional composition / Forth style stack execution engine

170 lines (169 loc) 3.86 kB
import { isArray } from "@thi.ng/checks/is-array"; import { isPlainObject } from "@thi.ng/checks/is-plain-object"; import { illegalArgs } from "@thi.ng/errors/illegal-arguments"; import { illegalState } from "@thi.ng/errors/illegal-state"; import { defOp1, defOp2, defOp2v } from "./ops.js"; import { $, $n } from "./safe.js"; import { invrot, swap } from "./stack.js"; import { $stackFn, defWord } from "./word.js"; const list = (ctx) => (ctx[0].push([]), ctx); const obj = (ctx) => (ctx[0].push({}), ctx); const pushl = (ctx) => { $(ctx[0], 2); const stack = ctx[0]; const a = stack.pop(); a.unshift(stack.pop()); stack.push(a); return ctx; }; const pushr = (ctx) => { const stack = ctx[0]; const n = stack.length - 2; $n(n, 0); stack[n].push(stack[n + 1]); stack.length--; return ctx; }; const popr = (ctx) => { const stack = ctx[0]; const n = stack.length - 1; $n(n, 0); const a = stack[n]; !a.length && illegalState("can't pop empty array"); stack.push(a.pop()); return ctx; }; const pull = defWord([popr, swap]); const pull2 = defWord([pull, pull]); const pull3 = defWord([pull2, pull]); const pull4 = defWord([pull2, pull2]); const vadd = defOp2v((b, a) => a + b); const vsub = defOp2v((b, a) => a - b); const vmul = defOp2v((b, a) => a * b); const vdiv = defOp2v((b, a) => a / b); const split = (ctx) => { const stack = ctx[0]; const n = stack.length - 2; $n(n, 0); const a = stack[n]; const b = stack[n + 1]; stack[n + 1] = a.splice(b, a.length - b); return ctx; }; const cat = (ctx) => { const stack = ctx[0]; const n = stack.length - 2; $n(n, 0); stack[n] = stack[n].concat(stack.pop()); return ctx; }; const catr = (ctx) => { const stack = ctx[0]; const n = stack.length - 2; $n(n, 0); stack[n] = stack.pop().concat(stack[n]); return ctx; }; const mapl = (ctx) => { $(ctx[0], 2); const stack = ctx[0]; const w = $stackFn(stack.pop()); const list2 = stack.pop(); const n = list2.length; for (let i = 0; i < n; i++) { ctx[0].push(list2[i]); ctx = w(ctx); } return ctx; }; const mapll = (ctx) => { $(ctx[0], 2); let stack = ctx[0]; const w = $stackFn(stack.pop()); const list2 = stack.pop(); const n = list2.length; let r = 0; for (let i = 0; i < n; i++) { let m = stack.length; stack.push(list2[i]); ctx = w(ctx); stack = ctx[0]; r += stack.length - m; } stack.push(stack.splice(stack.length - r, r)); return ctx; }; const foldl = defWord([invrot, mapl]); const collect = (ctx) => { const stack = ctx[0]; let n = stack.length - 1, m; $n(n, 0); $n(n -= m = stack.pop(), 0); stack.push(stack.splice(n, m)); return ctx; }; const defTuple = (n) => defWord([n, collect]); const vec2 = defTuple(2); const vec3 = defTuple(3); const vec4 = defTuple(4); const defJoin = (sep = "") => defOp1((x) => x.join(sep)); const join = defOp2((sep, buf) => buf.join(sep)); const length = defOp1((x) => x.length); const copy = defOp1( (x) => isArray(x) ? x.slice() : isPlainObject(x) ? { ...x } : illegalArgs(`can't copy type ${typeof x}`) ); const at = defOp2((b, a) => a[b]); const setat = (ctx) => { const stack = ctx[0]; const n = stack.length - 3; $n(n, 0); stack[n + 1][stack[n + 2]] = stack[n]; stack[n] = stack[n + 1]; stack.length -= 2; return ctx; }; const bindkeys = (ctx) => { const stack = ctx[0]; $(stack, 2); const obj2 = stack.pop(); const keys = stack.pop(); $(stack, keys.length); for (let i = keys.length - 1; i >= 0; i--) { obj2[keys[i]] = stack.pop(); } stack.push(obj2); return ctx; }; export { at, bindkeys, cat, catr, collect, copy, defJoin, defTuple, foldl, join, length, list, mapl, mapll, obj, popr, pull, pull2, pull3, pull4, pushl, pushr, setat, split, vadd, vdiv, vec2, vec3, vec4, vmul, vsub };