@rhost/testkit 1.5.1 → 1.5.3
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/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +4 -1
- package/dist/cli/init.js.map +1 -1
- 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 +10 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sources":["./math.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { FunctionImpl } from "../context.ts";
|
|
2
|
+
|
|
3
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
function toNum(s: string): number {
|
|
6
|
+
const n = Number(s);
|
|
7
|
+
if (!isFinite(n)) throw new Error(`ARGUMENT (${s}) IS NOT A NUMBER`);
|
|
8
|
+
return n;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Format a result: integers stay integers; floats get at most 6 sig digits. */
|
|
12
|
+
function fmt(n: number): string {
|
|
13
|
+
if (!isFinite(n)) throw new Error("RESULT IS NOT A NUMBER");
|
|
14
|
+
if (Number.isInteger(n)) return String(n);
|
|
15
|
+
return String(parseFloat(n.toPrecision(6)));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ── Math functions ────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export const mathFunctions: Record<string, FunctionImpl> = {
|
|
21
|
+
add: {
|
|
22
|
+
minArgs: 2, maxArgs: Infinity,
|
|
23
|
+
exec(args) {
|
|
24
|
+
return fmt((args as string[]).map(toNum).reduce((a, b) => a + b, 0));
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
sub: {
|
|
29
|
+
minArgs: 2, maxArgs: 2,
|
|
30
|
+
exec(args) {
|
|
31
|
+
const [a, b] = args as string[];
|
|
32
|
+
return fmt(toNum(a) - toNum(b));
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
mul: {
|
|
37
|
+
minArgs: 2, maxArgs: Infinity,
|
|
38
|
+
exec(args) {
|
|
39
|
+
return fmt((args as string[]).map(toNum).reduce((a, b) => a * b, 1));
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
div: {
|
|
44
|
+
minArgs: 2, maxArgs: 2,
|
|
45
|
+
exec(args) {
|
|
46
|
+
const [a, b] = args as string[];
|
|
47
|
+
const na = toNum(a), nb = toNum(b);
|
|
48
|
+
if (nb === 0) throw new Error("DIVIDE BY ZERO");
|
|
49
|
+
if (Number.isInteger(na) && Number.isInteger(nb)) return String(Math.trunc(na / nb));
|
|
50
|
+
return fmt(na / nb);
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
mod: {
|
|
55
|
+
minArgs: 2, maxArgs: 2,
|
|
56
|
+
exec(args) {
|
|
57
|
+
const [a, b] = args as string[];
|
|
58
|
+
const na = toNum(a), nb = toNum(b);
|
|
59
|
+
if (nb === 0) throw new Error("DIVIDE BY ZERO");
|
|
60
|
+
return fmt(na % nb);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
abs: {
|
|
65
|
+
minArgs: 1, maxArgs: 1,
|
|
66
|
+
exec(args) { return fmt(Math.abs(toNum((args as string[])[0]))); },
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
round: {
|
|
70
|
+
minArgs: 2, maxArgs: 2,
|
|
71
|
+
exec(args) {
|
|
72
|
+
const [a, p] = args as string[];
|
|
73
|
+
const prec = Math.max(0, Math.trunc(toNum(p)));
|
|
74
|
+
const factor = Math.pow(10, prec);
|
|
75
|
+
return fmt(Math.round(toNum(a) * factor) / factor);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
floor: {
|
|
80
|
+
minArgs: 1, maxArgs: 1,
|
|
81
|
+
exec(args) { return String(Math.floor(toNum((args as string[])[0]))); },
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
ceil: {
|
|
85
|
+
minArgs: 1, maxArgs: 1,
|
|
86
|
+
exec(args) { return String(Math.ceil(toNum((args as string[])[0]))); },
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
max: {
|
|
90
|
+
minArgs: 2, maxArgs: Infinity,
|
|
91
|
+
exec(args) { return fmt(Math.max(...(args as string[]).map(toNum))); },
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
min: {
|
|
95
|
+
minArgs: 2, maxArgs: Infinity,
|
|
96
|
+
exec(args) { return fmt(Math.min(...(args as string[]).map(toNum))); },
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
power: {
|
|
100
|
+
minArgs: 2, maxArgs: 2,
|
|
101
|
+
exec(args) {
|
|
102
|
+
const [a, b] = args as string[];
|
|
103
|
+
return fmt(Math.pow(toNum(a), toNum(b)));
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
sqrt: {
|
|
108
|
+
minArgs: 1, maxArgs: 1,
|
|
109
|
+
exec(args) {
|
|
110
|
+
const n = toNum((args as string[])[0]);
|
|
111
|
+
if (n < 0) throw new Error("ARGUMENT OUT OF RANGE");
|
|
112
|
+
return fmt(Math.sqrt(n));
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { mathFunctions } from "./math.js";
|
|
2
|
+
import { stringFunctions } from "./string.js";
|
|
3
|
+
import { compareFunctions } from "./compare.js";
|
|
4
|
+
import { logicFunctions } from "./logic.js";
|
|
5
|
+
import { registerFunctions } from "./register.js";
|
|
6
|
+
import { iterFunctions } from "./iter.js";
|
|
7
|
+
import { dbFunctions } from "./db.js";
|
|
8
|
+
/** Register the full standard softcode function library on an engine. */ export function registerStdlib(engine) {
|
|
9
|
+
for (const [n, impl] of Object.entries(mathFunctions))engine.registerFunction(n, impl);
|
|
10
|
+
for (const [n, impl] of Object.entries(stringFunctions))engine.registerFunction(n, impl);
|
|
11
|
+
for (const [n, impl] of Object.entries(compareFunctions))engine.registerFunction(n, impl);
|
|
12
|
+
for (const [n, impl] of Object.entries(logicFunctions))engine.registerFunction(n, impl);
|
|
13
|
+
for (const [n, impl] of Object.entries(registerFunctions))engine.registerFunction(n, impl);
|
|
14
|
+
for (const [n, impl] of Object.entries(iterFunctions))engine.registerFunction(n, impl);
|
|
15
|
+
for (const [n, impl] of Object.entries(dbFunctions))engine.registerFunction(n, impl);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=mod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.js","sources":["./mod.ts"],"names":[],"mappings":"AACA,SAAS,aAAa,oBAAwB;AAC9C,SAAS,eAAe,sBAAwB;AAChD,SAAS,gBAAgB,uBAAwB;AACjD,SAAS,cAAc,qBAAwB;AAC/C,SAAS,iBAAiB,wBAAwB;AAClD,SAAS,aAAa,oBAAwB;AAC9C,SAAS,WAAW,kBAAwB;AAE5C,uEAAuE,GACvE,OAAO,SAAS,eAAe,MAAmB;EAChD,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,eAAmB,OAAO,gBAAgB,CAAC,GAAG;EACrF,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,iBAAmB,OAAO,gBAAgB,CAAC,GAAG;EACrF,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,kBAAmB,OAAO,gBAAgB,CAAC,GAAG;EACrF,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,gBAAmB,OAAO,gBAAgB,CAAC,GAAG;EACrF,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,mBAAmB,OAAO,gBAAgB,CAAC,GAAG;EACrF,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,eAAmB,OAAO,gBAAgB,CAAC,GAAG;EACrF,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,aAAmB,OAAO,gBAAgB,CAAC,GAAG;AACvF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IEvalEngine } from "../context.ts";
|
|
2
|
+
import { mathFunctions } from "./math.js";
|
|
3
|
+
import { stringFunctions } from "./string.js";
|
|
4
|
+
import { compareFunctions } from "./compare.js";
|
|
5
|
+
import { logicFunctions } from "./logic.js";
|
|
6
|
+
import { registerFunctions } from "./register.js";
|
|
7
|
+
import { iterFunctions } from "./iter.js";
|
|
8
|
+
import { dbFunctions } from "./db.js";
|
|
9
|
+
|
|
10
|
+
/** Register the full standard softcode function library on an engine. */
|
|
11
|
+
export function registerStdlib(engine: IEvalEngine): void {
|
|
12
|
+
for (const [n, impl] of Object.entries(mathFunctions)) engine.registerFunction(n, impl);
|
|
13
|
+
for (const [n, impl] of Object.entries(stringFunctions)) engine.registerFunction(n, impl);
|
|
14
|
+
for (const [n, impl] of Object.entries(compareFunctions)) engine.registerFunction(n, impl);
|
|
15
|
+
for (const [n, impl] of Object.entries(logicFunctions)) engine.registerFunction(n, impl);
|
|
16
|
+
for (const [n, impl] of Object.entries(registerFunctions))engine.registerFunction(n, impl);
|
|
17
|
+
for (const [n, impl] of Object.entries(iterFunctions)) engine.registerFunction(n, impl);
|
|
18
|
+
for (const [n, impl] of Object.entries(dbFunctions)) engine.registerFunction(n, impl);
|
|
19
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const registerFunctions = {
|
|
2
|
+
/** setq(reg, value) — store value in named register; returns "". */ setq: {
|
|
3
|
+
minArgs: 2,
|
|
4
|
+
maxArgs: 2,
|
|
5
|
+
exec (args, ctx) {
|
|
6
|
+
const [reg, val] = args;
|
|
7
|
+
ctx.registers.set(reg, val);
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
/** r(reg) — read named register (empty string if unset). */ r: {
|
|
12
|
+
minArgs: 1,
|
|
13
|
+
maxArgs: 1,
|
|
14
|
+
exec (args, ctx) {
|
|
15
|
+
return ctx.registers.get(args[0]) ?? "";
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
/** setr(reg, value) — like setq but returns value. */ setr: {
|
|
19
|
+
minArgs: 2,
|
|
20
|
+
maxArgs: 2,
|
|
21
|
+
exec (args, ctx) {
|
|
22
|
+
const [reg, val] = args;
|
|
23
|
+
ctx.registers.set(reg, val);
|
|
24
|
+
return val;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sources":["./register.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,oBAAkD;EAC7D,kEAAkE,GAClE,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI,EAAE,GAAG;MACZ,MAAM,CAAC,KAAK,IAAI,GAAG;MACnB,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK;MACvB,OAAO;IACT;EACF;EAEA,0DAA0D,GAC1D,GAAG;IACD,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI,EAAE,GAAG;MACZ,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,AAAC,IAAiB,CAAC,EAAE,KAAK;IACrD;EACF;EAEA,oDAAoD,GACpD,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI,EAAE,GAAG;MACZ,MAAM,CAAC,KAAK,IAAI,GAAG;MACnB,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK;MACvB,OAAO;IACT;EACF;AACF,EAAE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FunctionImpl } from "../context.ts";
|
|
2
|
+
|
|
3
|
+
export const registerFunctions: Record<string, FunctionImpl> = {
|
|
4
|
+
/** setq(reg, value) — store value in named register; returns "". */
|
|
5
|
+
setq: {
|
|
6
|
+
minArgs: 2, maxArgs: 2,
|
|
7
|
+
exec(args, ctx) {
|
|
8
|
+
const [reg, val] = args as string[];
|
|
9
|
+
ctx.registers.set(reg, val);
|
|
10
|
+
return "";
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
/** r(reg) — read named register (empty string if unset). */
|
|
15
|
+
r: {
|
|
16
|
+
minArgs: 1, maxArgs: 1,
|
|
17
|
+
exec(args, ctx) {
|
|
18
|
+
return ctx.registers.get((args as string[])[0]) ?? "";
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
/** setr(reg, value) — like setq but returns value. */
|
|
23
|
+
setr: {
|
|
24
|
+
minArgs: 2, maxArgs: 2,
|
|
25
|
+
exec(args, ctx) {
|
|
26
|
+
const [reg, val] = args as string[];
|
|
27
|
+
ctx.registers.set(reg, val);
|
|
28
|
+
return val;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Maximum number of characters any single string-building function may produce.
|
|
4
|
+
* Mirrors TinyMUX's per-expression output limit (~8 000 chars).
|
|
5
|
+
* Prevents unbounded memory allocation from player-authored softcode.
|
|
6
|
+
*/ export const MAX_STRING_LEN = 8_000;
|
|
7
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
8
|
+
function pad(str, width, fill, align) {
|
|
9
|
+
if (width > MAX_STRING_LEN) throw new Error("OUTPUT TOO LONG");
|
|
10
|
+
const ch = fill[0] ?? " ";
|
|
11
|
+
const len = str.length;
|
|
12
|
+
if (len >= width) return str;
|
|
13
|
+
const total = width - len;
|
|
14
|
+
if (align === "left") return str + ch.repeat(total);
|
|
15
|
+
if (align === "right") return ch.repeat(total) + str;
|
|
16
|
+
const lPad = Math.floor(total / 2);
|
|
17
|
+
return ch.repeat(lPad) + str + ch.repeat(total - lPad);
|
|
18
|
+
}
|
|
19
|
+
// ── String functions ──────────────────────────────────────────────────────────
|
|
20
|
+
export const stringFunctions = {
|
|
21
|
+
strlen: {
|
|
22
|
+
minArgs: 1,
|
|
23
|
+
maxArgs: 1,
|
|
24
|
+
exec (args) {
|
|
25
|
+
return String(args[0].length);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
/** mid(str, start, length) — 0-based start position (TinyMUX convention). Negative start clamped to 0. */ mid: {
|
|
29
|
+
minArgs: 3,
|
|
30
|
+
maxArgs: 3,
|
|
31
|
+
exec (args) {
|
|
32
|
+
const [str, startStr, lenStr] = args;
|
|
33
|
+
const start = parseInt(startStr, 10);
|
|
34
|
+
const len = parseInt(lenStr, 10);
|
|
35
|
+
if (isNaN(start) || isNaN(len)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
36
|
+
const s = Math.max(0, start);
|
|
37
|
+
return str.slice(s, s + Math.max(0, len));
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
left: {
|
|
41
|
+
minArgs: 2,
|
|
42
|
+
maxArgs: 2,
|
|
43
|
+
exec (args) {
|
|
44
|
+
const [str, nStr] = args;
|
|
45
|
+
const n = parseInt(nStr, 10);
|
|
46
|
+
if (isNaN(n)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
47
|
+
return str.slice(0, Math.max(0, n));
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
right: {
|
|
51
|
+
minArgs: 2,
|
|
52
|
+
maxArgs: 2,
|
|
53
|
+
exec (args) {
|
|
54
|
+
const [str, nStr] = args;
|
|
55
|
+
const n = parseInt(nStr, 10);
|
|
56
|
+
if (isNaN(n)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
57
|
+
return n > 0 ? str.slice(-n) : "";
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
/** trim(str[, side[, char]]) — side: "l"|"r"|"b" (both, default). */ trim: {
|
|
61
|
+
minArgs: 1,
|
|
62
|
+
maxArgs: 3,
|
|
63
|
+
exec (args) {
|
|
64
|
+
const [str, side = "b", char = " "] = args;
|
|
65
|
+
const ch = (char[0] ?? " ").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66
|
+
const s = side.toLowerCase();
|
|
67
|
+
if (s === "l") return str.replace(new RegExp(`^${ch}+`), "");
|
|
68
|
+
if (s === "r") return str.replace(new RegExp(`${ch}+$`), "");
|
|
69
|
+
return str.replace(new RegExp(`^${ch}+`), "").replace(new RegExp(`${ch}+$`), "");
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
ljust: {
|
|
73
|
+
minArgs: 2,
|
|
74
|
+
maxArgs: 3,
|
|
75
|
+
exec (args) {
|
|
76
|
+
const [str, wStr, fill = " "] = args;
|
|
77
|
+
const w = parseInt(wStr, 10);
|
|
78
|
+
if (isNaN(w)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
79
|
+
return pad(str, w, fill, "left");
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
rjust: {
|
|
83
|
+
minArgs: 2,
|
|
84
|
+
maxArgs: 3,
|
|
85
|
+
exec (args) {
|
|
86
|
+
const [str, wStr, fill = " "] = args;
|
|
87
|
+
const w = parseInt(wStr, 10);
|
|
88
|
+
if (isNaN(w)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
89
|
+
return pad(str, w, fill, "right");
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
center: {
|
|
93
|
+
minArgs: 2,
|
|
94
|
+
maxArgs: 3,
|
|
95
|
+
exec (args) {
|
|
96
|
+
const [str, wStr, fill = " "] = args;
|
|
97
|
+
const w = parseInt(wStr, 10);
|
|
98
|
+
if (isNaN(w)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
99
|
+
return pad(str, w, fill, "center");
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
ucstr: {
|
|
103
|
+
minArgs: 1,
|
|
104
|
+
maxArgs: 1,
|
|
105
|
+
exec (args) {
|
|
106
|
+
return args[0].toUpperCase();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
lcstr: {
|
|
110
|
+
minArgs: 1,
|
|
111
|
+
maxArgs: 1,
|
|
112
|
+
exec (args) {
|
|
113
|
+
return args[0].toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
capstr: {
|
|
117
|
+
minArgs: 1,
|
|
118
|
+
maxArgs: 1,
|
|
119
|
+
exec (args) {
|
|
120
|
+
const s = args[0];
|
|
121
|
+
return s ? s[0].toUpperCase() + s.slice(1) : "";
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
/** cat(str1, str2, ...) — joins with a single space. */ cat: {
|
|
125
|
+
minArgs: 2,
|
|
126
|
+
maxArgs: Infinity,
|
|
127
|
+
exec (args) {
|
|
128
|
+
return args.join(" ");
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
space: {
|
|
132
|
+
minArgs: 1,
|
|
133
|
+
maxArgs: 1,
|
|
134
|
+
exec (args) {
|
|
135
|
+
const n = parseInt(args[0], 10);
|
|
136
|
+
if (isNaN(n) || n < 0) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
137
|
+
if (n > MAX_STRING_LEN) throw new Error("OUTPUT TOO LONG");
|
|
138
|
+
return " ".repeat(n);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
repeat: {
|
|
142
|
+
minArgs: 2,
|
|
143
|
+
maxArgs: 2,
|
|
144
|
+
exec (args) {
|
|
145
|
+
const [str, nStr] = args;
|
|
146
|
+
const n = parseInt(nStr, 10);
|
|
147
|
+
if (isNaN(n) || n < 0) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
148
|
+
if (str.length * n > MAX_STRING_LEN) throw new Error("OUTPUT TOO LONG");
|
|
149
|
+
return str.repeat(n);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=string.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string.js","sources":["./string.ts"],"names":[],"mappings":"AAEA,iFAAiF;AAEjF;;;;CAIC,GACD,OAAO,MAAM,iBAAiB,MAAM;AAEpC,iFAAiF;AAEjF,SAAS,IAAI,GAAW,EAAE,KAAa,EAAE,IAAY,EAAE,KAAkC;EACvF,IAAI,QAAQ,gBAAgB,MAAM,IAAI,MAAM;EAC5C,MAAM,KAAM,IAAI,CAAC,EAAE,IAAI;EACvB,MAAM,MAAM,IAAI,MAAM;EACtB,IAAI,OAAO,OAAO,OAAO;EACzB,MAAM,QAAQ,QAAQ;EACtB,IAAI,UAAU,QAAU,OAAO,MAAM,GAAG,MAAM,CAAC;EAC/C,IAAI,UAAU,SAAU,OAAO,GAAG,MAAM,CAAC,SAAS;EAClD,MAAM,OAAO,KAAK,KAAK,CAAC,QAAQ;EAChC,OAAO,GAAG,MAAM,CAAC,QAAQ,MAAM,GAAG,MAAM,CAAC,QAAQ;AACnD;AAEA,iFAAiF;AAEjF,OAAO,MAAM,kBAAgD;EAC3D,QAAQ;IACN,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MAAI,OAAO,OAAO,AAAC,IAAiB,CAAC,EAAE,CAAC,MAAM;IAAG;EAC5D;EAEA,wGAAwG,GACxG,KAAK;IACH,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,UAAU,OAAO,GAAG;MAChC,MAAM,QAAQ,SAAS,UAAU;MACjC,MAAM,MAAQ,SAAS,QAAU;MACjC,IAAI,MAAM,UAAU,MAAM,MAAM,MAAM,IAAI,MAAM;MAChD,MAAM,IAAI,KAAK,GAAG,CAAC,GAAG;MACtB,OAAO,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,GAAG,CAAC,GAAG;IACtC;EACF;EAEA,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,KAAK,GAAG;MACpB,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM;MAC9B,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG;IAClC;EACF;EAEA,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,KAAK,GAAG;MACpB,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM;MAC9B,OAAO,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,KAAK;IACjC;EACF;EAEA,mEAAmE,GACnE,MAAM;IACJ,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,OAAO,GAAG,EAAE,OAAO,GAAG,CAAC,GAAG;MACtC,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,OAAO,CAAC,uBAAuB;MAC3D,MAAM,IAAK,KAAK,WAAW;MAC3B,IAAI,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG;MACzD,IAAI,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC,GAAG;MACzD,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC,GAAG;IAC/E;EACF;EAEA,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,MAAM,OAAO,GAAG,CAAC,GAAG;MAChC,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM;MAC9B,OAAO,IAAI,KAAK,GAAG,MAAM;IAC3B;EACF;EAEA,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,MAAM,OAAO,GAAG,CAAC,GAAG;MAChC,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM;MAC9B,OAAO,IAAI,KAAK,GAAG,MAAM;IAC3B;EACF;EAEA,QAAQ;IACN,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,MAAM,OAAO,GAAG,CAAC,GAAG;MAChC,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM;MAC9B,OAAO,IAAI,KAAK,GAAG,MAAM;IAC3B;EACF;EAEA,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MAAI,OAAO,AAAC,IAAiB,CAAC,EAAE,CAAC,WAAW;IAAI;EAC3D;EAEA,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MAAI,OAAO,AAAC,IAAiB,CAAC,EAAE,CAAC,WAAW;IAAI;EAC3D;EAEA,QAAQ;IACN,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,IAAI,AAAC,IAAiB,CAAC,EAAE;MAC/B,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC,WAAW,KAAK,EAAE,KAAK,CAAC,KAAK;IAC/C;EACF;EAEA,sDAAsD,GACtD,KAAK;IACH,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MAAI,OAAO,AAAC,KAAkB,IAAI,CAAC;IAAM;EACpD;EAEA,OAAO;IACL,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,IAAI,SAAS,AAAC,IAAiB,CAAC,EAAE,EAAE;MAC1C,IAAI,MAAM,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM;MACvC,IAAI,IAAI,gBAAgB,MAAM,IAAI,MAAM;MACxC,OAAO,IAAI,MAAM,CAAC;IACpB;EACF;EAEA,QAAQ;IACN,SAAS;IAAG,SAAS;IACrB,MAAK,IAAI;MACP,MAAM,CAAC,KAAK,KAAK,GAAG;MACpB,MAAM,IAAI,SAAS,MAAM;MACzB,IAAI,MAAM,MAAM,IAAI,GAAG,MAAM,IAAI,MAAM;MACvC,IAAI,IAAI,MAAM,GAAG,IAAI,gBAAgB,MAAM,IAAI,MAAM;MACrD,OAAO,IAAI,MAAM,CAAC;IACpB;EACF;AACF,EAAE"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { FunctionImpl } from "../context.ts";
|
|
2
|
+
|
|
3
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Maximum number of characters any single string-building function may produce.
|
|
7
|
+
* Mirrors TinyMUX's per-expression output limit (~8 000 chars).
|
|
8
|
+
* Prevents unbounded memory allocation from player-authored softcode.
|
|
9
|
+
*/
|
|
10
|
+
export const MAX_STRING_LEN = 8_000;
|
|
11
|
+
|
|
12
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
function pad(str: string, width: number, fill: string, align: "left" | "right" | "center"): string {
|
|
15
|
+
if (width > MAX_STRING_LEN) throw new Error("OUTPUT TOO LONG");
|
|
16
|
+
const ch = fill[0] ?? " ";
|
|
17
|
+
const len = str.length;
|
|
18
|
+
if (len >= width) return str;
|
|
19
|
+
const total = width - len;
|
|
20
|
+
if (align === "left") return str + ch.repeat(total);
|
|
21
|
+
if (align === "right") return ch.repeat(total) + str;
|
|
22
|
+
const lPad = Math.floor(total / 2);
|
|
23
|
+
return ch.repeat(lPad) + str + ch.repeat(total - lPad);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── String functions ──────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export const stringFunctions: Record<string, FunctionImpl> = {
|
|
29
|
+
strlen: {
|
|
30
|
+
minArgs: 1, maxArgs: 1,
|
|
31
|
+
exec(args) { return String((args as string[])[0].length); },
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/** mid(str, start, length) — 0-based start position (TinyMUX convention). Negative start clamped to 0. */
|
|
35
|
+
mid: {
|
|
36
|
+
minArgs: 3, maxArgs: 3,
|
|
37
|
+
exec(args) {
|
|
38
|
+
const [str, startStr, lenStr] = args as string[];
|
|
39
|
+
const start = parseInt(startStr, 10);
|
|
40
|
+
const len = parseInt(lenStr, 10);
|
|
41
|
+
if (isNaN(start) || isNaN(len)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
42
|
+
const s = Math.max(0, start);
|
|
43
|
+
return str.slice(s, s + Math.max(0, len));
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
left: {
|
|
48
|
+
minArgs: 2, maxArgs: 2,
|
|
49
|
+
exec(args) {
|
|
50
|
+
const [str, nStr] = args as string[];
|
|
51
|
+
const n = parseInt(nStr, 10);
|
|
52
|
+
if (isNaN(n)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
53
|
+
return str.slice(0, Math.max(0, n));
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
right: {
|
|
58
|
+
minArgs: 2, maxArgs: 2,
|
|
59
|
+
exec(args) {
|
|
60
|
+
const [str, nStr] = args as string[];
|
|
61
|
+
const n = parseInt(nStr, 10);
|
|
62
|
+
if (isNaN(n)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
63
|
+
return n > 0 ? str.slice(-n) : "";
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/** trim(str[, side[, char]]) — side: "l"|"r"|"b" (both, default). */
|
|
68
|
+
trim: {
|
|
69
|
+
minArgs: 1, maxArgs: 3,
|
|
70
|
+
exec(args) {
|
|
71
|
+
const [str, side = "b", char = " "] = args as string[];
|
|
72
|
+
const ch = (char[0] ?? " ").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
73
|
+
const s = side.toLowerCase();
|
|
74
|
+
if (s === "l") return str.replace(new RegExp(`^${ch}+`), "");
|
|
75
|
+
if (s === "r") return str.replace(new RegExp(`${ch}+$`), "");
|
|
76
|
+
return str.replace(new RegExp(`^${ch}+`), "").replace(new RegExp(`${ch}+$`), "");
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
ljust: {
|
|
81
|
+
minArgs: 2, maxArgs: 3,
|
|
82
|
+
exec(args) {
|
|
83
|
+
const [str, wStr, fill = " "] = args as string[];
|
|
84
|
+
const w = parseInt(wStr, 10);
|
|
85
|
+
if (isNaN(w)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
86
|
+
return pad(str, w, fill, "left");
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
rjust: {
|
|
91
|
+
minArgs: 2, maxArgs: 3,
|
|
92
|
+
exec(args) {
|
|
93
|
+
const [str, wStr, fill = " "] = args as string[];
|
|
94
|
+
const w = parseInt(wStr, 10);
|
|
95
|
+
if (isNaN(w)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
96
|
+
return pad(str, w, fill, "right");
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
center: {
|
|
101
|
+
minArgs: 2, maxArgs: 3,
|
|
102
|
+
exec(args) {
|
|
103
|
+
const [str, wStr, fill = " "] = args as string[];
|
|
104
|
+
const w = parseInt(wStr, 10);
|
|
105
|
+
if (isNaN(w)) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
106
|
+
return pad(str, w, fill, "center");
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
ucstr: {
|
|
111
|
+
minArgs: 1, maxArgs: 1,
|
|
112
|
+
exec(args) { return (args as string[])[0].toUpperCase(); },
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
lcstr: {
|
|
116
|
+
minArgs: 1, maxArgs: 1,
|
|
117
|
+
exec(args) { return (args as string[])[0].toLowerCase(); },
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
capstr: {
|
|
121
|
+
minArgs: 1, maxArgs: 1,
|
|
122
|
+
exec(args) {
|
|
123
|
+
const s = (args as string[])[0];
|
|
124
|
+
return s ? s[0].toUpperCase() + s.slice(1) : "";
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
/** cat(str1, str2, ...) — joins with a single space. */
|
|
129
|
+
cat: {
|
|
130
|
+
minArgs: 2, maxArgs: Infinity,
|
|
131
|
+
exec(args) { return (args as string[]).join(" "); },
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
space: {
|
|
135
|
+
minArgs: 1, maxArgs: 1,
|
|
136
|
+
exec(args) {
|
|
137
|
+
const n = parseInt((args as string[])[0], 10);
|
|
138
|
+
if (isNaN(n) || n < 0) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
139
|
+
if (n > MAX_STRING_LEN) throw new Error("OUTPUT TOO LONG");
|
|
140
|
+
return " ".repeat(n);
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
repeat: {
|
|
145
|
+
minArgs: 2, maxArgs: 2,
|
|
146
|
+
exec(args) {
|
|
147
|
+
const [str, nStr] = args as string[];
|
|
148
|
+
const n = parseInt(nStr, 10);
|
|
149
|
+
if (isNaN(n) || n < 0) throw new Error("ARGUMENT IS NOT A NUMBER");
|
|
150
|
+
if (str.length * n > MAX_STRING_LEN) throw new Error("OUTPUT TOO LONG");
|
|
151
|
+
return str.repeat(n);
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|