Son aktivite 14 hours ago

Revizyon e5bfedb88c64cc4679dfc3af4f65e20e26b7ccb3

chalk.js Ham Playground
1(() =>
2 globalThis.chalk = () => {
3 // A naive tuplespace. Mostly for global event coordination
4 let messages = [];
5 let handlers = [];
6 const rm = (from, idx) => {
7 for (let i = idx.length - 1; i >= 0; i--) { from.splice(idx[i], 1) }
8 }
9 const mutate = () => {
10 let matched = false;
11 let toRm = [];
12 let idx = 0;
13 let msgToRm = new Set();
14 for (let h of handlers) {
15 const isMatch = h.doQuery(msgToRm);
16 matched ||= isMatch;
17 if (isMatch && h.isOnce) {
18 toRm.push(idx);
19 }
20 idx++;
21 }
22 let rmIdx = Array.from(msgToRm);
23 rmIdx.sort((a,b) => a-b);
24 rm(handlers, toRm);
25 rm(messages, rmIdx);
26 };
27
28 const query = (vars, predicates, callback, toRm, pIdx = 0) => {
29 let matched = false;
30 let mIdx = 0;
31 for (const m of messages) {
32 if (predicates[pIdx](vars, m)) {
33 let keep = predicates[pIdx].keep;
34 if (pIdx < predicates.length - 1) {
35 let didMatch = query(vars, predicates, callback, toRm, pIdx+1);
36 matched ||= didMatch;
37 if (matched && !keep) { toRm.add(mIdx); }
38 } else {
39 callback(vars);
40 if (!keep) { toRm.add(mIdx); }
41 matched = true;
42 }
43 }
44 mIdx++;
45 }
46 return matched;
47 }
48
49 const doWhen = (patterns, handler, isOnce=false) => {
50 const predicates = [];
51 const vars = new Set();
52 for (let patt of patterns) {
53 let keep = false;
54 if (patt.endsWith("?")) { patt = patt.slice(0,-1); keep = true; }
55 const init = [];
56 const conditions = [];
57 const cleanup = [];
58 let idx = 0;
59 for (const p of patt.split(" ")) {
60 const k = p.slice(1);
61 if (p.startsWith("~$") && vars.has(p)) {
62 if (vars.has(p)) {
63 conditions.push(`t[${idx}] !== v["${k.slice(1)}"]`);
64 } else {
65 throw new Error("Cannot negate a var until after it has been captured");
66 }
67 } else if (p.startsWith("~")) {
68 conditions.push(`t[${idx}] !== "${k}"`);
69 } else if (p.startsWith("$")) {
70 if (vars.has(k)) {
71 conditions.push(`t[${idx}] === v["${k}"]`);
72 } else {
73 init.push(`v["${k}"] = t[${idx}]`)
74 cleanup.push(`delete v["${k}"]`)
75 vars.add(k);
76 }
77 } else {
78 conditions.push(`t[${idx}] === "${p}"`);
79 }
80 idx++;
81 }
82 let fn = new Function('v', 't',
83 `${init.join(';')}; let ret = t.length === ${idx} && ${conditions.join(' && ')}; if (!ret) { ${cleanup.join('; ')}; } return ret;`
84 );
85 fn.keep = keep;
86 predicates.push(fn);
87 }
88 handlers.push({ doQuery(toRm) {
89 return query({}, predicates, handler, toRm);
90 }, isOnce });
91 mutate();
92 }
93 return { _:{ messages, handlers },
94 msg(...data) {
95 messages.push(data);
96 mutate();
97 }, when(...args) { doWhen(args.slice(0,-1), args.at(-1)); },
98 once(patt, handler) { doWhen(patt, handler, true); }
99 }
100})();
101
chalk.tests.js Ham Playground
1import "./test.js";
2import "./chalk.js";
3
4test("chalk", {
5 basicTest() {
6 const board = globalThis.chalk();
7
8 let didFire = false;
9 board.when("test", (_) => didFire = true);
10 board.msg("test");
11 assert(didFire, "Did not fire");
12 },
13 basicTest2() {
14 const board = globalThis.chalk();
15
16 let didFire = false;
17 board.when("test $x", (_) => { didFire = (_.x === "yo"); })
18 board.msg("test", "yo");
19 assert(didFire, "Did not fire");
20 },
21 joinTest(){
22 const board = chalk();
23
24 let didFire = false;
25 board.when("test $a", "taste $a", (_) => { didFire = _.a == "yo"})
26 board.msg("test", "yo");
27 assert(!didFire, "Fired prematurely");
28 board.msg("taste", "ey");
29 assert(!didFire, "Fired prematurely");
30 board.msg("taste", "yo");
31 assert(didFire, "Did not fire when it should have");
32 },
33 keepTest() {
34 const board = chalk();
35
36 let didFire = false;
37 board.when("test $a?", "taste $a", (_) => { didFire = _.a == "yo"})
38 board.msg("test", "yo");
39 assert(!didFire, "Fired prematurely");
40 board.msg("taste", "ey");
41 assert(!didFire, "Fired prematurely");
42 board.msg("taste", "yo");
43 assert(didFire, "Did not fire when it should have");
44 assert(board._.messages.find((x) => x[0] === "test" && x[1] === "yo"), "Message was removed and shouldn't have been")
45 },
46 negationTest(){
47 const board = chalk();
48
49 let didFire = false;
50 board.when("test ~foo", (_) => { didFire = true})
51 board.msg("test", "foo");
52 assert(!didFire, "Fired prematurely");
53 board.msg("test", "yo");
54 assert(didFire, "Did not fire when it should have");
55 },
56 saturationTest() {
57 const board = chalk();
58
59 let $aFired = false;
60 let fooFired = false;
61 board.when("test $a", (_) => { $aFired = true; })
62 board.when("test foo", (_) => { fooFired = true; })
63 board.msg("test", "foo");
64 assert($aFired && fooFired, "Two handlers should have fired");
65 }
66});
67
68
test.js Ham Playground
1globalThis.test = (suite, tests) => {
2 process.stdout.write(suite + ": ");
3 for (const [name, t] of Object.entries(tests)) {
4 try {
5 t();
6 process.stdout.write(".");
7 } catch(ex) {
8 console.error("In " + suite +", test " + name + " failed:", ex)
9 }
10 }
11 console.log();
12}
13
14// For global scope
15globalThis.assert = (cond, msg) => { if (!cond) throw new Error(msg); };
16