spell_zine.js
· 2.3 KiB · JavaScript
Bruto
Playground
// Build:
// npx google-closure-compiler -O SIMPLE minmoire.js > minmoire.min.js && du -b minmoire.min.js
// Changes from base grimoire:
// - API names
// - Rules are two-length arrays rather than {needs, makes} objects
// - Some compressed logic. Eg. removed do/while for executing facts, which may change behavior of edge cases
// - parsing based on .split() rather than regex matches
// - Assumes various ES6+ syntax
// - various heresies like var (instead of const), omitting { } from if statements, etc
// Grab the salt
// note: "Tally" renamed to C, standing for Count
var C = (t,k,n) => t[k] = (k in t ? t[k] : 0) + n;
var spl = (d) => (i) => i.split(d).filter(c => !!c)
// Lay the circle, trace the lines
var R = [], I = [] // Recipes (rules) and Inventory (current facts)
// Eval
var E = (i='') => {
P(i);
// following code was formerly the run() function
for (var r of R) { // recipe
for (var n in r[0]) {
if ((I[n] || 0) < r[0][n]) break;
}
// deduct needs
for (var n in r[0]) C(I, n, -r[0][n]);
// add (or execute) makes
for (var m in r[1]) {
var cq = r[1][m] // consequence
try {
cq(I);
}
catch {
// Consequence is assumed to be a number
C(I, m, cq);
}
}
}
}
// Parse into inventory
var P = (i) => {
// Mode characters; order matters for : and ;
// (state variable `m` is initialized here)
var ms = [ ':', '>', m = ';'];
for (let [t, c] of spl(/[^\S]/)(i).map(spl('^'))) { // token, count
if (t == ':') W();
if (ms.includes(t)) m = t;
else C((m == ';') ? I : R.at(-1)[ms.indexOf(m)], t, parseInt(c) || 1);
}
}
// Has
var H = (...n) => n.reduce((h, f) => h && I[f], !0 )
// Register (W stands for Write)
var W = (N={}, M={}) => R.push([ N, M ])
// Basic test
// const program = `
// : a > b ;
// : b^100 > overflow_b ;
// : > a b^99 c
// `;
// E(program);
// console.assert(
// H("overflow_b")
// && !H("a")
// && !H("b")
// && H("c")
// )
// W({overflow_b:1}, {overflow_b:function() {console.log('Too much B!')}})
// E()
// Original readable API
// const grimoire = {
// recipes: R,
// inventory: I,
// eval: E,
// parse: P,
// // note: run() was folded into eval()
// has: H,
// register: W
// }
| 1 | // Build: |
| 2 | // npx google-closure-compiler -O SIMPLE minmoire.js > minmoire.min.js && du -b minmoire.min.js |
| 3 | |
| 4 | // Changes from base grimoire: |
| 5 | // - API names |
| 6 | // - Rules are two-length arrays rather than {needs, makes} objects |
| 7 | // - Some compressed logic. Eg. removed do/while for executing facts, which may change behavior of edge cases |
| 8 | // - parsing based on .split() rather than regex matches |
| 9 | // - Assumes various ES6+ syntax |
| 10 | // - various heresies like var (instead of const), omitting { } from if statements, etc |
| 11 | |
| 12 | // Grab the salt |
| 13 | // note: "Tally" renamed to C, standing for Count |
| 14 | var C = (t,k,n) => t[k] = (k in t ? t[k] : 0) + n; |
| 15 | var spl = (d) => (i) => i.split(d).filter(c => !!c) |
| 16 | |
| 17 | // Lay the circle, trace the lines |
| 18 | var R = [], I = [] // Recipes (rules) and Inventory (current facts) |
| 19 | |
| 20 | // Eval |
| 21 | var E = (i='') => { |
| 22 | P(i); |
| 23 | // following code was formerly the run() function |
| 24 | for (var r of R) { // recipe |
| 25 | for (var n in r[0]) { |
| 26 | if ((I[n] || 0) < r[0][n]) break; |
| 27 | } |
| 28 | // deduct needs |
| 29 | for (var n in r[0]) C(I, n, -r[0][n]); |
| 30 | // add (or execute) makes |
| 31 | for (var m in r[1]) { |
| 32 | var cq = r[1][m] // consequence |
| 33 | try { |
| 34 | cq(I); |
| 35 | } |
| 36 | catch { |
| 37 | // Consequence is assumed to be a number |
| 38 | C(I, m, cq); |
| 39 | } |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // Parse into inventory |
| 45 | var P = (i) => { |
| 46 | // Mode characters; order matters for : and ; |
| 47 | // (state variable `m` is initialized here) |
| 48 | var ms = [ ':', '>', m = ';']; |
| 49 | for (let [t, c] of spl(/[^\S]/)(i).map(spl('^'))) { // token, count |
| 50 | if (t == ':') W(); |
| 51 | if (ms.includes(t)) m = t; |
| 52 | else C((m == ';') ? I : R.at(-1)[ms.indexOf(m)], t, parseInt(c) || 1); |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | // Has |
| 57 | var H = (...n) => n.reduce((h, f) => h && I[f], !0 ) |
| 58 | |
| 59 | // Register (W stands for Write) |
| 60 | var W = (N={}, M={}) => R.push([ N, M ]) |
| 61 | |
| 62 | // Basic test |
| 63 | // const program = ` |
| 64 | // : a > b ; |
| 65 | // : b^100 > overflow_b ; |
| 66 | // : > a b^99 c |
| 67 | // `; |
| 68 | // E(program); |
| 69 | // console.assert( |
| 70 | // H("overflow_b") |
| 71 | // && !H("a") |
| 72 | // && !H("b") |
| 73 | // && H("c") |
| 74 | // ) |
| 75 | // W({overflow_b:1}, {overflow_b:function() {console.log('Too much B!')}}) |
| 76 | // E() |
| 77 | |
| 78 | // Original readable API |
| 79 | // const grimoire = { |
| 80 | // recipes: R, |
| 81 | // inventory: I, |
| 82 | // eval: E, |
| 83 | // parse: P, |
| 84 | // // note: run() was folded into eval() |
| 85 | // has: H, |
| 86 | // register: W |
| 87 | // } |
| 88 |