最終更新 14 hours ago

修正履歴 b19f58b54f7157298fb035a7b2147ca0e05c26a3

chalk.js Raw 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