spell_zine.js
· 2.3 KiB · JavaScript
Исходник
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 |