@rhost/testkit 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@ursamu/mushcode/.github/workflows/publish.yml +36 -0
- package/node_modules/@ursamu/mushcode/LICENSE +21 -0
- package/node_modules/@ursamu/mushcode/README.md +110 -0
- package/node_modules/@ursamu/mushcode/_dist/mod.d.ts +36 -0
- package/node_modules/@ursamu/mushcode/_dist/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/parser/mod.d.ts +41 -0
- package/node_modules/@ursamu/mushcode/_dist/parser/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/commands.d.ts +15 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/commands.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/deps.d.ts +18 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/deps.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/mod.d.ts +20 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/tags.d.ts +6 -0
- package/node_modules/@ursamu/mushcode/_dist/src/analyze/tags.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/context.d.ts +85 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/context.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/engine.d.ts +48 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/engine.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/mod.d.ts +26 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/stdlib/mod.d.ts +3 -0
- package/node_modules/@ursamu/mushcode/_dist/src/eval/stdlib/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/lint/mod.d.ts +38 -0
- package/node_modules/@ursamu/mushcode/_dist/src/lint/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/print/mod.d.ts +18 -0
- package/node_modules/@ursamu/mushcode/_dist/src/print/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/print/printer.d.ts +15 -0
- package/node_modules/@ursamu/mushcode/_dist/src/print/printer.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/traverse/mod.d.ts +19 -0
- package/node_modules/@ursamu/mushcode/_dist/src/traverse/mod.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/traverse/transform.d.ts +27 -0
- package/node_modules/@ursamu/mushcode/_dist/src/traverse/transform.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/_dist/src/traverse/walk.d.ts +27 -0
- package/node_modules/@ursamu/mushcode/_dist/src/traverse/walk.d.ts.map +1 -0
- package/node_modules/@ursamu/mushcode/deno.json +26 -0
- package/node_modules/@ursamu/mushcode/deno.lock +42 -0
- package/node_modules/@ursamu/mushcode/docs/analyze.md +145 -0
- package/node_modules/@ursamu/mushcode/docs/eval.md +312 -0
- package/node_modules/@ursamu/mushcode/docs/lint.md +152 -0
- package/node_modules/@ursamu/mushcode/docs/parser.md +196 -0
- package/node_modules/@ursamu/mushcode/docs/print.md +84 -0
- package/node_modules/@ursamu/mushcode/docs/stdlib.md +418 -0
- package/node_modules/@ursamu/mushcode/docs/traverse.md +167 -0
- package/node_modules/@ursamu/mushcode/grammar/mux-softcode.pegjs +781 -0
- package/node_modules/@ursamu/mushcode/mod.js +44 -0
- package/node_modules/@ursamu/mushcode/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/mod.ts +63 -0
- package/node_modules/@ursamu/mushcode/package.json +38 -0
- package/node_modules/@ursamu/mushcode/parser/mod.js +47 -0
- package/node_modules/@ursamu/mushcode/parser/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/parser/mod.ts +99 -0
- package/node_modules/@ursamu/mushcode/parser/mux-softcode.js +3833 -0
- package/node_modules/@ursamu/mushcode/parser/mux-softcode.mjs +3837 -0
- package/node_modules/@ursamu/mushcode/src/analyze/commands.js +29 -0
- package/node_modules/@ursamu/mushcode/src/analyze/commands.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/analyze/commands.ts +46 -0
- package/node_modules/@ursamu/mushcode/src/analyze/deps.js +45 -0
- package/node_modules/@ursamu/mushcode/src/analyze/deps.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/analyze/deps.ts +51 -0
- package/node_modules/@ursamu/mushcode/src/analyze/mod.js +18 -0
- package/node_modules/@ursamu/mushcode/src/analyze/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/analyze/mod.ts +20 -0
- package/node_modules/@ursamu/mushcode/src/analyze/tags.js +11 -0
- package/node_modules/@ursamu/mushcode/src/analyze/tags.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/analyze/tags.ts +11 -0
- package/node_modules/@ursamu/mushcode/src/eval/context.js +22 -0
- package/node_modules/@ursamu/mushcode/src/eval/context.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/context.ts +177 -0
- package/node_modules/@ursamu/mushcode/src/eval/engine.js +238 -0
- package/node_modules/@ursamu/mushcode/src/eval/engine.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/engine.ts +276 -0
- package/node_modules/@ursamu/mushcode/src/eval/mod.js +25 -0
- package/node_modules/@ursamu/mushcode/src/eval/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/mod.ts +31 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/compare.js +56 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/compare.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/compare.ts +16 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/db.js +91 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/db.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/db.ts +104 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/iter.js +91 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/iter.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/iter.ts +98 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/logic.js +79 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/logic.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/logic.ts +84 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/math.js +120 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/math.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/math.ts +115 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/mod.js +17 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/mod.ts +19 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/register.js +28 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/register.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/register.ts +31 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/string.js +153 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/string.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/eval/stdlib/string.ts +154 -0
- package/node_modules/@ursamu/mushcode/src/lint/builtin_arities.js +212 -0
- package/node_modules/@ursamu/mushcode/src/lint/builtin_arities.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/lint/builtin_arities.ts +68 -0
- package/node_modules/@ursamu/mushcode/src/lint/mod.js +60 -0
- package/node_modules/@ursamu/mushcode/src/lint/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/lint/mod.ts +96 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/arg_count.js +37 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/arg_count.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/arg_count.ts +44 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/iter_var_outside_iter.js +55 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/iter_var_outside_iter.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/iter_var_outside_iter.ts +60 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/missing_wildcard.js +31 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/missing_wildcard.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/missing_wildcard.ts +40 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/register_before_set.js +59 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/register_before_set.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/lint/rules/register_before_set.ts +64 -0
- package/node_modules/@ursamu/mushcode/src/print/lock_printer.js +43 -0
- package/node_modules/@ursamu/mushcode/src/print/lock_printer.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/print/lock_printer.ts +41 -0
- package/node_modules/@ursamu/mushcode/src/print/mod.js +17 -0
- package/node_modules/@ursamu/mushcode/src/print/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/print/mod.ts +18 -0
- package/node_modules/@ursamu/mushcode/src/print/printer.js +91 -0
- package/node_modules/@ursamu/mushcode/src/print/printer.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/print/printer.ts +132 -0
- package/node_modules/@ursamu/mushcode/src/traverse/child_slots.js +129 -0
- package/node_modules/@ursamu/mushcode/src/traverse/child_slots.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/traverse/child_slots.ts +51 -0
- package/node_modules/@ursamu/mushcode/src/traverse/mod.js +17 -0
- package/node_modules/@ursamu/mushcode/src/traverse/mod.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/traverse/mod.ts +19 -0
- package/node_modules/@ursamu/mushcode/src/traverse/transform.js +70 -0
- package/node_modules/@ursamu/mushcode/src/traverse/transform.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/traverse/transform.ts +84 -0
- package/node_modules/@ursamu/mushcode/src/traverse/walk.js +55 -0
- package/node_modules/@ursamu/mushcode/src/traverse/walk.js.map +1 -0
- package/node_modules/@ursamu/mushcode/src/traverse/walk.ts +82 -0
- package/node_modules/@ursamu/mushcode/tests/01-literals.test.ts +105 -0
- package/node_modules/@ursamu/mushcode/tests/02-substitutions.test.ts +145 -0
- package/node_modules/@ursamu/mushcode/tests/03-function-calls.test.ts +184 -0
- package/node_modules/@ursamu/mushcode/tests/04-eval-blocks.test.ts +110 -0
- package/node_modules/@ursamu/mushcode/tests/05-braced-strings.test.ts +119 -0
- package/node_modules/@ursamu/mushcode/tests/06-commands.test.ts +222 -0
- package/node_modules/@ursamu/mushcode/tests/07-dollar-patterns.test.ts +156 -0
- package/node_modules/@ursamu/mushcode/tests/08-lock-expressions.test.ts +159 -0
- package/node_modules/@ursamu/mushcode/tests/09-edge-cases.test.ts +162 -0
- package/node_modules/@ursamu/mushcode/tests/10-regression.test.ts +211 -0
- package/node_modules/@ursamu/mushcode/tests/11-tags.test.ts +357 -0
- package/node_modules/@ursamu/mushcode/tests/12-locations.test.ts +162 -0
- package/node_modules/@ursamu/mushcode/tests/13-eval.test.ts +389 -0
- package/node_modules/@ursamu/mushcode/tests/analyze.test.ts +194 -0
- package/node_modules/@ursamu/mushcode/tests/helpers.ts +69 -0
- package/node_modules/@ursamu/mushcode/tests/lint.test.ts +232 -0
- package/node_modules/@ursamu/mushcode/tests/print.test.ts +204 -0
- package/node_modules/@ursamu/mushcode/tests/traverse.test.ts +211 -0
- package/package.json +4 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
2
|
+
/** Split "obj/attr" → [objExpr, attrName]. Returns null if no "/" found. */ function splitObjAttr(s) {
|
|
3
|
+
const slash = s.indexOf("/");
|
|
4
|
+
return slash === -1 ? null : [
|
|
5
|
+
s.slice(0, slash),
|
|
6
|
+
s.slice(slash + 1)
|
|
7
|
+
];
|
|
8
|
+
}
|
|
9
|
+
// ── DB-backed functions ───────────────────────────────────────────────────────
|
|
10
|
+
export const dbFunctions = {
|
|
11
|
+
/**
|
|
12
|
+
* get(obj/attr) — read the named attribute from a target object.
|
|
13
|
+
* Returns "" if the attribute is unset.
|
|
14
|
+
*/ get: {
|
|
15
|
+
minArgs: 1,
|
|
16
|
+
maxArgs: 1,
|
|
17
|
+
async exec (args, ctx, engine) {
|
|
18
|
+
const pair = splitObjAttr(args[0]);
|
|
19
|
+
if (!pair) return "#-1 BAD ARGUMENT FORMAT";
|
|
20
|
+
const [objExpr, attrName] = pair;
|
|
21
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, objExpr);
|
|
22
|
+
if (!objId) return "#-1 NO MATCH";
|
|
23
|
+
return await engine.accessor.getAttr(objId, attrName.toUpperCase()) ?? "";
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
/** name(obj) — return the display name of a target object. */ name: {
|
|
27
|
+
minArgs: 1,
|
|
28
|
+
maxArgs: 1,
|
|
29
|
+
async exec (args, ctx, engine) {
|
|
30
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, args[0]);
|
|
31
|
+
if (!objId) return "#-1 NO MATCH";
|
|
32
|
+
return engine.accessor.getName(objId);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
/** hasattr(obj, attr) — "1" if the attribute exists on obj, "0" if not. */ hasattr: {
|
|
36
|
+
minArgs: 2,
|
|
37
|
+
maxArgs: 2,
|
|
38
|
+
async exec (args, ctx, engine) {
|
|
39
|
+
const [objExpr, attrName] = args;
|
|
40
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, objExpr);
|
|
41
|
+
if (!objId) return "0";
|
|
42
|
+
const val = await engine.accessor.getAttr(objId, attrName.toUpperCase());
|
|
43
|
+
return val !== null ? "1" : "0";
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
/** hasflag(obj, flag) — "1" if obj has the flag, "0" if not. */ hasflag: {
|
|
47
|
+
minArgs: 2,
|
|
48
|
+
maxArgs: 2,
|
|
49
|
+
async exec (args, ctx, engine) {
|
|
50
|
+
const [objExpr, flag] = args;
|
|
51
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, objExpr);
|
|
52
|
+
if (!objId) return "0";
|
|
53
|
+
return await engine.accessor.hasFlag(objId, flag) ? "1" : "0";
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* u(obj/attr[, arg0, arg1, …]) — evaluate the named attribute as a function.
|
|
58
|
+
*
|
|
59
|
+
* The attribute value is evaluated as softcode in a child context where:
|
|
60
|
+
* %0–%9 ← the extra arguments passed to u()
|
|
61
|
+
* %! ← the object that owns the attribute (new executor)
|
|
62
|
+
* %@ ← the previous executor (caller)
|
|
63
|
+
* %# ← unchanged (original enactor)
|
|
64
|
+
* depth ← incremented by 1 (returns #-1 if maxDepth exceeded)
|
|
65
|
+
*
|
|
66
|
+
* When "obj/" is omitted, the executor is used as the target object.
|
|
67
|
+
*/ u: {
|
|
68
|
+
minArgs: 1,
|
|
69
|
+
maxArgs: Infinity,
|
|
70
|
+
async exec (args, ctx, engine) {
|
|
71
|
+
const [target, ...argVals] = args;
|
|
72
|
+
// Resolve object and attribute name
|
|
73
|
+
const pair = splitObjAttr(target);
|
|
74
|
+
const objId = pair ? await engine.accessor.resolveTarget(ctx.enactor, pair[0]) : ctx.executor;
|
|
75
|
+
const attrName = pair ? pair[1] : target;
|
|
76
|
+
if (!objId) return "#-1 NO MATCH";
|
|
77
|
+
const attrVal = await engine.accessor.getAttr(objId, attrName.toUpperCase());
|
|
78
|
+
if (attrVal === null) return "#-1 NO SUCH ATTRIBUTE";
|
|
79
|
+
const subCtx = {
|
|
80
|
+
...ctx,
|
|
81
|
+
executor: objId,
|
|
82
|
+
caller: ctx.executor,
|
|
83
|
+
args: argVals,
|
|
84
|
+
registers: new Map(),
|
|
85
|
+
depth: ctx.depth + 1
|
|
86
|
+
};
|
|
87
|
+
return engine.evalString(attrVal, subCtx);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sources":["./db.ts"],"names":[],"mappings":"AAEA,iFAAiF;AAEjF,2EAA2E,GAC3E,SAAS,aAAa,CAAS;EAC7B,MAAM,QAAQ,EAAE,OAAO,CAAC;EACxB,OAAO,UAAU,CAAC,IAAI,OAAO;IAAC,EAAE,KAAK,CAAC,GAAG;IAAQ,EAAE,KAAK,CAAC,QAAQ;GAAG;AACtE;AAEA,iFAAiF;AAEjF,OAAO,MAAM,cAA4C;EACvD;;;GAGC,GACD,KAAK;IACH,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI,EAAE,GAAG,EAAE,MAAM;MAC1B,MAAM,OAAO,aAAa,AAAC,IAAiB,CAAC,EAAE;MAC/C,IAAI,CAAC,MAAM,OAAO;MAClB,MAAM,CAAC,SAAS,SAAS,GAAG;MAC5B,MAAM,QAAQ,MAAM,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,EAAE;MAC/D,IAAI,CAAC,OAAO,OAAO;MACnB,OAAO,AAAC,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,SAAS,WAAW,OAAQ;IAC3E;EACF;EAEA,4DAA4D,GAC5D,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI,EAAE,GAAG,EAAE,MAAM;MAC1B,MAAM,QAAQ,MAAM,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,EAAE,AAAC,IAAiB,CAAC,EAAE;MACpF,IAAI,CAAC,OAAO,OAAO;MACnB,OAAO,OAAO,QAAQ,CAAC,OAAO,CAAC;IACjC;EACF;EAEA,yEAAyE,GACzE,SAAS;IACP,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI,EAAE,GAAG,EAAE,MAAM;MAC1B,MAAM,CAAC,SAAS,SAAS,GAAG;MAC5B,MAAM,QAAQ,MAAM,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,EAAE;MAC/D,IAAI,CAAC,OAAO,OAAO;MACnB,MAAM,MAAM,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,SAAS,WAAW;MACrE,OAAO,QAAQ,OAAO,MAAM;IAC9B;EACF;EAEA,8DAA8D,GAC9D,SAAS;IACP,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI,EAAE,GAAG,EAAE,MAAM;MAC1B,MAAM,CAAC,SAAS,KAAK,GAAG;MACxB,MAAM,QAAQ,MAAM,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,EAAE;MAC/D,IAAI,CAAC,OAAO,OAAO;MACnB,OAAO,AAAC,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,QAAS,MAAM;IAC9D;EACF;EAEA;;;;;;;;;;;GAWC,GACD,GAAG;IACD,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI,EAAE,GAAG,EAAE,MAAM;MAC1B,MAAM,CAAC,QAAQ,GAAG,QAAQ,GAAG;MAE7B,oCAAoC;MACpC,MAAM,OAAO,aAAa;MAC1B,MAAM,QAAU,OACZ,MAAM,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,EAAE,IAAI,CAAC,EAAE,IACxD,IAAI,QAAQ;MAChB,MAAM,WAAW,OAAO,IAAI,CAAC,EAAE,GAAG;MAElC,IAAI,CAAC,OAAO,OAAO;MAEnB,MAAM,UAAU,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,SAAS,WAAW;MACzE,IAAI,YAAY,MAAM,OAAO;MAE7B,MAAM,SAAsB;QAC1B,GAAG,GAAG;QACN,UAAW;QACX,QAAW,IAAI,QAAQ;QACvB,MAAW;QACX,WAAW,IAAI;QACf,OAAW,IAAI,KAAK,GAAG;MACzB;MAEA,OAAO,OAAO,UAAU,CAAC,SAAS;IACpC;EACF;AACF,EAAE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { EvalContext, FunctionImpl } from "../context.ts";
|
|
2
|
+
|
|
3
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/** Split "obj/attr" → [objExpr, attrName]. Returns null if no "/" found. */
|
|
6
|
+
function splitObjAttr(s: string): [string, string] | null {
|
|
7
|
+
const slash = s.indexOf("/");
|
|
8
|
+
return slash === -1 ? null : [s.slice(0, slash), s.slice(slash + 1)];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ── DB-backed functions ───────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export const dbFunctions: Record<string, FunctionImpl> = {
|
|
14
|
+
/**
|
|
15
|
+
* get(obj/attr) — read the named attribute from a target object.
|
|
16
|
+
* Returns "" if the attribute is unset.
|
|
17
|
+
*/
|
|
18
|
+
get: {
|
|
19
|
+
minArgs: 1, maxArgs: 1,
|
|
20
|
+
async exec(args, ctx, engine) {
|
|
21
|
+
const pair = splitObjAttr((args as string[])[0]);
|
|
22
|
+
if (!pair) return "#-1 BAD ARGUMENT FORMAT";
|
|
23
|
+
const [objExpr, attrName] = pair;
|
|
24
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, objExpr);
|
|
25
|
+
if (!objId) return "#-1 NO MATCH";
|
|
26
|
+
return (await engine.accessor.getAttr(objId, attrName.toUpperCase())) ?? "";
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/** name(obj) — return the display name of a target object. */
|
|
31
|
+
name: {
|
|
32
|
+
minArgs: 1, maxArgs: 1,
|
|
33
|
+
async exec(args, ctx, engine) {
|
|
34
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, (args as string[])[0]);
|
|
35
|
+
if (!objId) return "#-1 NO MATCH";
|
|
36
|
+
return engine.accessor.getName(objId);
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/** hasattr(obj, attr) — "1" if the attribute exists on obj, "0" if not. */
|
|
41
|
+
hasattr: {
|
|
42
|
+
minArgs: 2, maxArgs: 2,
|
|
43
|
+
async exec(args, ctx, engine) {
|
|
44
|
+
const [objExpr, attrName] = args as string[];
|
|
45
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, objExpr);
|
|
46
|
+
if (!objId) return "0";
|
|
47
|
+
const val = await engine.accessor.getAttr(objId, attrName.toUpperCase());
|
|
48
|
+
return val !== null ? "1" : "0";
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/** hasflag(obj, flag) — "1" if obj has the flag, "0" if not. */
|
|
53
|
+
hasflag: {
|
|
54
|
+
minArgs: 2, maxArgs: 2,
|
|
55
|
+
async exec(args, ctx, engine) {
|
|
56
|
+
const [objExpr, flag] = args as string[];
|
|
57
|
+
const objId = await engine.accessor.resolveTarget(ctx.enactor, objExpr);
|
|
58
|
+
if (!objId) return "0";
|
|
59
|
+
return (await engine.accessor.hasFlag(objId, flag)) ? "1" : "0";
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* u(obj/attr[, arg0, arg1, …]) — evaluate the named attribute as a function.
|
|
65
|
+
*
|
|
66
|
+
* The attribute value is evaluated as softcode in a child context where:
|
|
67
|
+
* %0–%9 ← the extra arguments passed to u()
|
|
68
|
+
* %! ← the object that owns the attribute (new executor)
|
|
69
|
+
* %@ ← the previous executor (caller)
|
|
70
|
+
* %# ← unchanged (original enactor)
|
|
71
|
+
* depth ← incremented by 1 (returns #-1 if maxDepth exceeded)
|
|
72
|
+
*
|
|
73
|
+
* When "obj/" is omitted, the executor is used as the target object.
|
|
74
|
+
*/
|
|
75
|
+
u: {
|
|
76
|
+
minArgs: 1, maxArgs: Infinity,
|
|
77
|
+
async exec(args, ctx, engine) {
|
|
78
|
+
const [target, ...argVals] = args as string[];
|
|
79
|
+
|
|
80
|
+
// Resolve object and attribute name
|
|
81
|
+
const pair = splitObjAttr(target);
|
|
82
|
+
const objId = pair
|
|
83
|
+
? await engine.accessor.resolveTarget(ctx.enactor, pair[0])
|
|
84
|
+
: ctx.executor;
|
|
85
|
+
const attrName = pair ? pair[1] : target;
|
|
86
|
+
|
|
87
|
+
if (!objId) return "#-1 NO MATCH";
|
|
88
|
+
|
|
89
|
+
const attrVal = await engine.accessor.getAttr(objId, attrName.toUpperCase());
|
|
90
|
+
if (attrVal === null) return "#-1 NO SUCH ATTRIBUTE";
|
|
91
|
+
|
|
92
|
+
const subCtx: EvalContext = {
|
|
93
|
+
...ctx,
|
|
94
|
+
executor: objId,
|
|
95
|
+
caller: ctx.executor,
|
|
96
|
+
args: argVals,
|
|
97
|
+
registers: new Map(),
|
|
98
|
+
depth: ctx.depth + 1,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return engine.evalString(attrVal, subCtx);
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Split a string by delimiter.
|
|
4
|
+
* Space delimiter collapses consecutive whitespace (MUX convention).
|
|
5
|
+
*/ function splitDelim(str, delim) {
|
|
6
|
+
if (!str) return [];
|
|
7
|
+
return delim === " " ? str.trim().split(/\s+/).filter(Boolean) : str.split(delim);
|
|
8
|
+
}
|
|
9
|
+
function joinDelim(items, delim) {
|
|
10
|
+
return items.join(delim);
|
|
11
|
+
}
|
|
12
|
+
// ── Iter / list functions ─────────────────────────────────────────────────────
|
|
13
|
+
export const iterFunctions = {
|
|
14
|
+
/**
|
|
15
|
+
* iter(list, body[, idelim[, odelim]])
|
|
16
|
+
* Evaluates body for each item, binding ## to the item and #@ to its 1-based index.
|
|
17
|
+
*/ iter: {
|
|
18
|
+
eval: "lazy",
|
|
19
|
+
minArgs: 2,
|
|
20
|
+
maxArgs: 4,
|
|
21
|
+
async exec (args, ctx) {
|
|
22
|
+
const thunks = args;
|
|
23
|
+
const list = await thunks[0]();
|
|
24
|
+
const body = thunks[1];
|
|
25
|
+
const inDelim = thunks[2] ? await thunks[2]() || " " : " ";
|
|
26
|
+
const outDelim = thunks[3] ? await thunks[3]() : " ";
|
|
27
|
+
const items = splitDelim(list, inDelim);
|
|
28
|
+
if (items.length === 0) return "";
|
|
29
|
+
const results = [];
|
|
30
|
+
for(let i = 0; i < items.length; i++){
|
|
31
|
+
const frame = {
|
|
32
|
+
item: items[i],
|
|
33
|
+
index: i + 1
|
|
34
|
+
};
|
|
35
|
+
results.push(await body({
|
|
36
|
+
iterStack: [
|
|
37
|
+
frame,
|
|
38
|
+
...ctx.iterStack
|
|
39
|
+
]
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
return joinDelim(results, outDelim);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
/** words(str[, delim]) — count words in a space- (or delim-) separated list. */ words: {
|
|
46
|
+
minArgs: 1,
|
|
47
|
+
maxArgs: 2,
|
|
48
|
+
exec (args) {
|
|
49
|
+
const [str, delim = " "] = args;
|
|
50
|
+
return String(splitDelim(str, delim).length);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
/** word(str, n[, delim]) — return the nth word (1-based). */ word: {
|
|
54
|
+
minArgs: 2,
|
|
55
|
+
maxArgs: 3,
|
|
56
|
+
exec (args) {
|
|
57
|
+
const [str, nStr, delim = " "] = args;
|
|
58
|
+
const n = parseInt(nStr, 10);
|
|
59
|
+
if (isNaN(n) || n < 1) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
60
|
+
return splitDelim(str, delim)[n - 1] ?? "";
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
/** first(str[, delim]) — return the first word. */ first: {
|
|
64
|
+
minArgs: 1,
|
|
65
|
+
maxArgs: 2,
|
|
66
|
+
exec (args) {
|
|
67
|
+
const [str, delim = " "] = args;
|
|
68
|
+
return splitDelim(str, delim)[0] ?? "";
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
/** last(str[, delim]) — return the last word. */ last: {
|
|
72
|
+
minArgs: 1,
|
|
73
|
+
maxArgs: 2,
|
|
74
|
+
exec (args) {
|
|
75
|
+
const [str, delim = " "] = args;
|
|
76
|
+
const items = splitDelim(str, delim);
|
|
77
|
+
return items[items.length - 1] ?? "";
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
/** rest(str[, delim]) — return all words after the first. */ rest: {
|
|
81
|
+
minArgs: 1,
|
|
82
|
+
maxArgs: 2,
|
|
83
|
+
exec (args) {
|
|
84
|
+
const [str, delim = " "] = args;
|
|
85
|
+
const items = splitDelim(str, delim);
|
|
86
|
+
if (items.length <= 1) return "";
|
|
87
|
+
return joinDelim(items.slice(1), delim === " " ? " " : delim);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=iter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iter.js","sources":["./iter.ts"],"names":[],"mappings":"AAEA,iFAAiF;AAEjF;;;CAGC,GACD,SAAS,WAAW,GAAW,EAAE,KAAa;EAC5C,IAAI,CAAC,KAAK,OAAO,EAAE;EACnB,OAAO,UAAU,MACb,IAAI,IAAI,GAAG,KAAK,CAAC,OAAO,MAAM,CAAC,WAC/B,IAAI,KAAK,CAAC;AAChB;AAEA,SAAS,UAAU,KAAe,EAAE,KAAa;EAC/C,OAAO,MAAM,IAAI,CAAC;AACpB;AAEA,iFAAiF;AAEjF,OAAO,MAAM,gBAA8C;EACzD;;;GAGC,GACD,MAAM;IACJ,MAAM;IACN,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI,EAAE,GAAG;MAClB,MAAM,SAAW;MACjB,MAAM,OAAW,MAAM,MAAM,CAAC,EAAE;MAChC,MAAM,OAAW,MAAM,CAAC,EAAE;MAC1B,MAAM,UAAW,MAAM,CAAC,EAAE,GAAI,AAAC,MAAM,MAAM,CAAC,EAAE,MAAO,MAAO;MAC5D,MAAM,WAAW,MAAM,CAAC,EAAE,GAAI,MAAM,MAAM,CAAC,EAAE,KAAe;MAE5D,MAAM,QAAQ,WAAW,MAAM;MAC/B,IAAI,MAAM,MAAM,KAAK,GAAG,OAAO;MAE/B,MAAM,UAAoB,EAAE;MAC5B,IAAK,IAAI,IAAI,GAAG,IAAI,MAAM,MAAM,EAAE,IAAK;QACrC,MAAM,QAAmB;UAAE,MAAM,KAAK,CAAC,EAAE;UAAE,OAAO,IAAI;QAAE;QACxD,QAAQ,IAAI,CAAC,MAAM,KAAK;UAAE,WAAW;YAAC;eAAU,IAAI,SAAS;WAAC;QAAC;MACjE;MACA,OAAO,UAAU,SAAS;IAC5B;EACF;EAEA,8EAA8E,GAC9E,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,QAAQ,GAAG,CAAC,GAAG;MAC3B,OAAO,OAAO,WAAW,KAAK,OAAO,MAAM;IAC7C;EACF;EAEA,2DAA2D,GAC3D,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,MAAM,QAAQ,GAAG,CAAC,GAAG;MACjC,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM;MACvC,OAAO,WAAW,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI;IAC1C;EACF;EAEA,iDAAiD,GACjD,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,QAAQ,GAAG,CAAC,GAAG;MAC3B,OAAO,WAAW,KAAK,MAAM,CAAC,EAAE,IAAI;IACtC;EACF;EAEA,+CAA+C,GAC/C,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,QAAQ,GAAG,CAAC,GAAG;MAC3B,MAAM,QAAQ,WAAW,KAAK;MAC9B,OAAO,KAAK,CAAC,MAAM,MAAM,GAAG,EAAE,IAAI;IACpC;EACF;EAEA,2DAA2D,GAC3D,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,QAAQ,GAAG,CAAC,GAAG;MAC3B,MAAM,QAAQ,WAAW,KAAK;MAC9B,IAAI,MAAM,MAAM,IAAI,GAAG,OAAO;MAC9B,OAAO,UAAU,MAAM,KAAK,CAAC,IAAI,UAAU,MAAM,MAAM;IACzD;EACF;AACF,EAAE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { EvalThunk, FunctionImpl, IterFrame } from "../context.ts";
|
|
2
|
+
|
|
3
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Split a string by delimiter.
|
|
7
|
+
* Space delimiter collapses consecutive whitespace (MUX convention).
|
|
8
|
+
*/
|
|
9
|
+
function splitDelim(str: string, delim: string): string[] {
|
|
10
|
+
if (!str) return [];
|
|
11
|
+
return delim === " "
|
|
12
|
+
? str.trim().split(/\s+/).filter(Boolean)
|
|
13
|
+
: str.split(delim);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function joinDelim(items: string[], delim: string): string {
|
|
17
|
+
return items.join(delim);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Iter / list functions ─────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export const iterFunctions: Record<string, FunctionImpl> = {
|
|
23
|
+
/**
|
|
24
|
+
* iter(list, body[, idelim[, odelim]])
|
|
25
|
+
* Evaluates body for each item, binding ## to the item and #@ to its 1-based index.
|
|
26
|
+
*/
|
|
27
|
+
iter: {
|
|
28
|
+
eval: "lazy",
|
|
29
|
+
minArgs: 2, maxArgs: 4,
|
|
30
|
+
async exec(args, ctx) {
|
|
31
|
+
const thunks = args as EvalThunk[];
|
|
32
|
+
const list = await thunks[0]();
|
|
33
|
+
const body = thunks[1];
|
|
34
|
+
const inDelim = thunks[2] ? ((await thunks[2]()) || " ") : " ";
|
|
35
|
+
const outDelim = thunks[3] ? (await thunks[3]()) : " ";
|
|
36
|
+
|
|
37
|
+
const items = splitDelim(list, inDelim);
|
|
38
|
+
if (items.length === 0) return "";
|
|
39
|
+
|
|
40
|
+
const results: string[] = [];
|
|
41
|
+
for (let i = 0; i < items.length; i++) {
|
|
42
|
+
const frame: IterFrame = { item: items[i], index: i + 1 };
|
|
43
|
+
results.push(await body({ iterStack: [frame, ...ctx.iterStack] }));
|
|
44
|
+
}
|
|
45
|
+
return joinDelim(results, outDelim);
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/** words(str[, delim]) — count words in a space- (or delim-) separated list. */
|
|
50
|
+
words: {
|
|
51
|
+
minArgs: 1, maxArgs: 2,
|
|
52
|
+
exec(args) {
|
|
53
|
+
const [str, delim = " "] = args as string[];
|
|
54
|
+
return String(splitDelim(str, delim).length);
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/** word(str, n[, delim]) — return the nth word (1-based). */
|
|
59
|
+
word: {
|
|
60
|
+
minArgs: 2, maxArgs: 3,
|
|
61
|
+
exec(args) {
|
|
62
|
+
const [str, nStr, delim = " "] = args as string[];
|
|
63
|
+
const n = parseInt(nStr, 10);
|
|
64
|
+
if (isNaN(n) || n < 1) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
65
|
+
return splitDelim(str, delim)[n - 1] ?? "";
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/** first(str[, delim]) — return the first word. */
|
|
70
|
+
first: {
|
|
71
|
+
minArgs: 1, maxArgs: 2,
|
|
72
|
+
exec(args) {
|
|
73
|
+
const [str, delim = " "] = args as string[];
|
|
74
|
+
return splitDelim(str, delim)[0] ?? "";
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/** last(str[, delim]) — return the last word. */
|
|
79
|
+
last: {
|
|
80
|
+
minArgs: 1, maxArgs: 2,
|
|
81
|
+
exec(args) {
|
|
82
|
+
const [str, delim = " "] = args as string[];
|
|
83
|
+
const items = splitDelim(str, delim);
|
|
84
|
+
return items[items.length - 1] ?? "";
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/** rest(str[, delim]) — return all words after the first. */
|
|
89
|
+
rest: {
|
|
90
|
+
minArgs: 1, maxArgs: 2,
|
|
91
|
+
exec(args) {
|
|
92
|
+
const [str, delim = " "] = args as string[];
|
|
93
|
+
const items = splitDelim(str, delim);
|
|
94
|
+
if (items.length <= 1) return "";
|
|
95
|
+
return joinDelim(items.slice(1), delim === " " ? " " : delim);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** MUX truthiness: empty string and "0" are false; everything else is true. */ function truthy(s) {
|
|
2
|
+
return s !== "" && s !== "0";
|
|
3
|
+
}
|
|
4
|
+
export const logicFunctions = {
|
|
5
|
+
/** if(cond, iftrue[, iffalse]) */ "if": {
|
|
6
|
+
eval: "lazy",
|
|
7
|
+
minArgs: 2,
|
|
8
|
+
maxArgs: 3,
|
|
9
|
+
async exec (args) {
|
|
10
|
+
const [cond, yes, no] = args;
|
|
11
|
+
return truthy(await cond()) ? await yes() : no ? await no() : "";
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
/** ifelse(cond, iftrue, iffalse) */ ifelse: {
|
|
15
|
+
eval: "lazy",
|
|
16
|
+
minArgs: 3,
|
|
17
|
+
maxArgs: 3,
|
|
18
|
+
async exec (args) {
|
|
19
|
+
const [cond, yes, no] = args;
|
|
20
|
+
return truthy(await cond()) ? await yes() : await no();
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* switch(value, pattern1, result1[, …[, default]])
|
|
25
|
+
* Exact-match comparison; returns first matching result or default.
|
|
26
|
+
*/ "switch": {
|
|
27
|
+
eval: "lazy",
|
|
28
|
+
minArgs: 3,
|
|
29
|
+
maxArgs: Infinity,
|
|
30
|
+
async exec (args) {
|
|
31
|
+
const thunks = args;
|
|
32
|
+
const value = await thunks[0]();
|
|
33
|
+
const rest = thunks.slice(1);
|
|
34
|
+
const hasDefault = rest.length % 2 === 1;
|
|
35
|
+
const pairs = hasDefault ? rest.slice(0, -1) : rest;
|
|
36
|
+
for(let i = 0; i < pairs.length; i += 2){
|
|
37
|
+
if (value === await pairs[i]()) return await pairs[i + 1]();
|
|
38
|
+
}
|
|
39
|
+
return hasDefault ? await rest[rest.length - 1]() : "";
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
/** and(v1, v2, …) — short-circuit, returns "1" or "0". */ "and": {
|
|
43
|
+
eval: "lazy",
|
|
44
|
+
minArgs: 2,
|
|
45
|
+
maxArgs: Infinity,
|
|
46
|
+
async exec (args) {
|
|
47
|
+
for (const thunk of args){
|
|
48
|
+
if (!truthy(await thunk())) return "0";
|
|
49
|
+
}
|
|
50
|
+
return "1";
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
/** or(v1, v2, …) — short-circuit, returns "1" or "0". */ "or": {
|
|
54
|
+
eval: "lazy",
|
|
55
|
+
minArgs: 2,
|
|
56
|
+
maxArgs: Infinity,
|
|
57
|
+
async exec (args) {
|
|
58
|
+
for (const thunk of args){
|
|
59
|
+
if (truthy(await thunk())) return "1";
|
|
60
|
+
}
|
|
61
|
+
return "0";
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
not: {
|
|
65
|
+
minArgs: 1,
|
|
66
|
+
maxArgs: 1,
|
|
67
|
+
exec (args) {
|
|
68
|
+
return truthy(args[0]) ? "0" : "1";
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
/** t(val) — returns "1" if val is truthy, "0" otherwise. */ t: {
|
|
72
|
+
minArgs: 1,
|
|
73
|
+
maxArgs: 1,
|
|
74
|
+
exec (args) {
|
|
75
|
+
return truthy(args[0]) ? "1" : "0";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic.js","sources":["./logic.ts"],"names":[],"mappings":"AAEA,6EAA6E,GAC7E,SAAS,OAAO,CAAS;EACvB,OAAO,MAAM,MAAM,MAAM;AAC3B;AAEA,OAAO,MAAM,iBAA+C;EAC1D,gCAAgC,GAChC,MAAM;IACJ,MAAM;IACN,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI;MACb,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG;MACxB,OAAO,OAAO,MAAM,UAAU,MAAM,QAAS,KAAK,MAAM,OAAO;IACjE;EACF;EAEA,kCAAkC,GAClC,QAAQ;IACN,MAAM;IACN,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI;MACb,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG;MACxB,OAAO,OAAO,MAAM,UAAU,MAAM,QAAQ,MAAM;IACpD;EACF;EAEA;;;GAGC,GACD,UAAU;IACR,MAAM;IACN,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI;MACb,MAAM,SAAW;MACjB,MAAM,QAAW,MAAM,MAAM,CAAC,EAAE;MAChC,MAAM,OAAW,OAAO,KAAK,CAAC;MAC9B,MAAM,aAAa,KAAK,MAAM,GAAG,MAAM;MACvC,MAAM,QAAW,aAAa,KAAK,KAAK,CAAC,GAAG,CAAC,KAAK;MAElD,IAAK,IAAI,IAAI,GAAG,IAAI,MAAM,MAAM,EAAE,KAAK,EAAG;QACxC,IAAI,UAAU,MAAM,KAAK,CAAC,EAAE,IAAI,OAAO,MAAM,KAAK,CAAC,IAAI,EAAE;MAC3D;MACA,OAAO,aAAa,MAAM,IAAI,CAAC,KAAK,MAAM,GAAG,EAAE,KAAK;IACtD;EACF;EAEA,wDAAwD,GACxD,OAAO;IACL,MAAM;IACN,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI;MACb,KAAK,MAAM,SAAS,KAAqB;QACvC,IAAI,CAAC,OAAO,MAAM,UAAU,OAAO;MACrC;MACA,OAAO;IACT;EACF;EAEA,uDAAuD,GACvD,MAAM;IACJ,MAAM;IACN,SAAS;IAAG,SAAS;IACrB,MAAM,MAAK,IAAI;MACb,KAAK,MAAM,SAAS,KAAqB;QACvC,IAAI,OAAO,MAAM,UAAU,OAAO;MACpC;MACA,OAAO;IACT;EACF;EAEA,KAAK;IACH,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MAAI,OAAO,OAAO,AAAC,IAAiB,CAAC,EAAE,IAAI,MAAM;IAAK;EACjE;EAEA,0DAA0D,GAC1D,GAAG;IACD,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MAAI,OAAO,OAAO,AAAC,IAAiB,CAAC,EAAE,IAAI,MAAM;IAAK;EACjE;AACF,EAAE"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { EvalThunk, FunctionImpl } from "../context.ts";
|
|
2
|
+
|
|
3
|
+
/** MUX truthiness: empty string and "0" are false; everything else is true. */
|
|
4
|
+
function truthy(s: string): boolean {
|
|
5
|
+
return s !== "" && s !== "0";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const logicFunctions: Record<string, FunctionImpl> = {
|
|
9
|
+
/** if(cond, iftrue[, iffalse]) */
|
|
10
|
+
"if": {
|
|
11
|
+
eval: "lazy",
|
|
12
|
+
minArgs: 2, maxArgs: 3,
|
|
13
|
+
async exec(args) {
|
|
14
|
+
const [cond, yes, no] = args as EvalThunk[];
|
|
15
|
+
return truthy(await cond()) ? await yes() : (no ? await no() : "");
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/** ifelse(cond, iftrue, iffalse) */
|
|
20
|
+
ifelse: {
|
|
21
|
+
eval: "lazy",
|
|
22
|
+
minArgs: 3, maxArgs: 3,
|
|
23
|
+
async exec(args) {
|
|
24
|
+
const [cond, yes, no] = args as EvalThunk[];
|
|
25
|
+
return truthy(await cond()) ? await yes() : await no();
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* switch(value, pattern1, result1[, …[, default]])
|
|
31
|
+
* Exact-match comparison; returns first matching result or default.
|
|
32
|
+
*/
|
|
33
|
+
"switch": {
|
|
34
|
+
eval: "lazy",
|
|
35
|
+
minArgs: 3, maxArgs: Infinity,
|
|
36
|
+
async exec(args) {
|
|
37
|
+
const thunks = args as EvalThunk[];
|
|
38
|
+
const value = await thunks[0]();
|
|
39
|
+
const rest = thunks.slice(1);
|
|
40
|
+
const hasDefault = rest.length % 2 === 1;
|
|
41
|
+
const pairs = hasDefault ? rest.slice(0, -1) : rest;
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < pairs.length; i += 2) {
|
|
44
|
+
if (value === await pairs[i]()) return await pairs[i + 1]();
|
|
45
|
+
}
|
|
46
|
+
return hasDefault ? await rest[rest.length - 1]() : "";
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/** and(v1, v2, …) — short-circuit, returns "1" or "0". */
|
|
51
|
+
"and": {
|
|
52
|
+
eval: "lazy",
|
|
53
|
+
minArgs: 2, maxArgs: Infinity,
|
|
54
|
+
async exec(args) {
|
|
55
|
+
for (const thunk of args as EvalThunk[]) {
|
|
56
|
+
if (!truthy(await thunk())) return "0";
|
|
57
|
+
}
|
|
58
|
+
return "1";
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/** or(v1, v2, …) — short-circuit, returns "1" or "0". */
|
|
63
|
+
"or": {
|
|
64
|
+
eval: "lazy",
|
|
65
|
+
minArgs: 2, maxArgs: Infinity,
|
|
66
|
+
async exec(args) {
|
|
67
|
+
for (const thunk of args as EvalThunk[]) {
|
|
68
|
+
if (truthy(await thunk())) return "1";
|
|
69
|
+
}
|
|
70
|
+
return "0";
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
not: {
|
|
75
|
+
minArgs: 1, maxArgs: 1,
|
|
76
|
+
exec(args) { return truthy((args as string[])[0]) ? "0" : "1"; },
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/** t(val) — returns "1" if val is truthy, "0" otherwise. */
|
|
80
|
+
t: {
|
|
81
|
+
minArgs: 1, maxArgs: 1,
|
|
82
|
+
exec(args) { return truthy((args as string[])[0]) ? "1" : "0"; },
|
|
83
|
+
},
|
|
84
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
2
|
+
function toNum(s) {
|
|
3
|
+
const n = Number(s);
|
|
4
|
+
if (!isFinite(n)) throw new Error(`ARGUMENT (${s}) IS NOT A NUMBER`);
|
|
5
|
+
return n;
|
|
6
|
+
}
|
|
7
|
+
/** Format a result: integers stay integers; floats get at most 6 sig digits. */ function fmt(n) {
|
|
8
|
+
if (!isFinite(n)) throw new Error("RESULT IS NOT A NUMBER");
|
|
9
|
+
if (Number.isInteger(n)) return String(n);
|
|
10
|
+
return String(parseFloat(n.toPrecision(6)));
|
|
11
|
+
}
|
|
12
|
+
// ── Math functions ────────────────────────────────────────────────────────────
|
|
13
|
+
export const mathFunctions = {
|
|
14
|
+
add: {
|
|
15
|
+
minArgs: 2,
|
|
16
|
+
maxArgs: Infinity,
|
|
17
|
+
exec (args) {
|
|
18
|
+
return fmt(args.map(toNum).reduce((a, b)=>a + b, 0));
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
sub: {
|
|
22
|
+
minArgs: 2,
|
|
23
|
+
maxArgs: 2,
|
|
24
|
+
exec (args) {
|
|
25
|
+
const [a, b] = args;
|
|
26
|
+
return fmt(toNum(a) - toNum(b));
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
mul: {
|
|
30
|
+
minArgs: 2,
|
|
31
|
+
maxArgs: Infinity,
|
|
32
|
+
exec (args) {
|
|
33
|
+
return fmt(args.map(toNum).reduce((a, b)=>a * b, 1));
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
div: {
|
|
37
|
+
minArgs: 2,
|
|
38
|
+
maxArgs: 2,
|
|
39
|
+
exec (args) {
|
|
40
|
+
const [a, b] = args;
|
|
41
|
+
const na = toNum(a), nb = toNum(b);
|
|
42
|
+
if (nb === 0) throw new Error("DIVIDE BY ZERO");
|
|
43
|
+
if (Number.isInteger(na) && Number.isInteger(nb)) return String(Math.trunc(na / nb));
|
|
44
|
+
return fmt(na / nb);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
mod: {
|
|
48
|
+
minArgs: 2,
|
|
49
|
+
maxArgs: 2,
|
|
50
|
+
exec (args) {
|
|
51
|
+
const [a, b] = args;
|
|
52
|
+
const na = toNum(a), nb = toNum(b);
|
|
53
|
+
if (nb === 0) throw new Error("DIVIDE BY ZERO");
|
|
54
|
+
return fmt(na % nb);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
abs: {
|
|
58
|
+
minArgs: 1,
|
|
59
|
+
maxArgs: 1,
|
|
60
|
+
exec (args) {
|
|
61
|
+
return fmt(Math.abs(toNum(args[0])));
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
round: {
|
|
65
|
+
minArgs: 2,
|
|
66
|
+
maxArgs: 2,
|
|
67
|
+
exec (args) {
|
|
68
|
+
const [a, p] = args;
|
|
69
|
+
const prec = Math.max(0, Math.trunc(toNum(p)));
|
|
70
|
+
const factor = Math.pow(10, prec);
|
|
71
|
+
return fmt(Math.round(toNum(a) * factor) / factor);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
floor: {
|
|
75
|
+
minArgs: 1,
|
|
76
|
+
maxArgs: 1,
|
|
77
|
+
exec (args) {
|
|
78
|
+
return String(Math.floor(toNum(args[0])));
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
ceil: {
|
|
82
|
+
minArgs: 1,
|
|
83
|
+
maxArgs: 1,
|
|
84
|
+
exec (args) {
|
|
85
|
+
return String(Math.ceil(toNum(args[0])));
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
max: {
|
|
89
|
+
minArgs: 2,
|
|
90
|
+
maxArgs: Infinity,
|
|
91
|
+
exec (args) {
|
|
92
|
+
return fmt(Math.max(...args.map(toNum)));
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
min: {
|
|
96
|
+
minArgs: 2,
|
|
97
|
+
maxArgs: Infinity,
|
|
98
|
+
exec (args) {
|
|
99
|
+
return fmt(Math.min(...args.map(toNum)));
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
power: {
|
|
103
|
+
minArgs: 2,
|
|
104
|
+
maxArgs: 2,
|
|
105
|
+
exec (args) {
|
|
106
|
+
const [a, b] = args;
|
|
107
|
+
return fmt(Math.pow(toNum(a), toNum(b)));
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
sqrt: {
|
|
111
|
+
minArgs: 1,
|
|
112
|
+
maxArgs: 1,
|
|
113
|
+
exec (args) {
|
|
114
|
+
const n = toNum(args[0]);
|
|
115
|
+
if (n < 0) throw new Error("ARGUMENT OUT OF RANGE");
|
|
116
|
+
return fmt(Math.sqrt(n));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
//# sourceMappingURL=math.js.map
|