@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,40 @@
|
|
|
1
|
+
import type { ASTNode } from "../../../parser/mod.ts";
|
|
2
|
+
import type { Diagnostic } from "../mod.ts";
|
|
3
|
+
import { findAll } from "../../traverse/walk.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* missing-wildcard
|
|
7
|
+
*
|
|
8
|
+
* Reports when a DollarPattern or ListenPattern action uses a positional
|
|
9
|
+
* substitution %N (N ≥ 1) but the pattern contains fewer than N wildcards.
|
|
10
|
+
*
|
|
11
|
+
* In TinyMUX:
|
|
12
|
+
* %0 = the full matched input (always available)
|
|
13
|
+
* %1 = first wildcard match, %2 = second, … %9 = ninth
|
|
14
|
+
*
|
|
15
|
+
* So if the action uses %2, the pattern must have at least 2 wildcards.
|
|
16
|
+
*/
|
|
17
|
+
export function checkMissingWildcard(node: ASTNode): Diagnostic[] {
|
|
18
|
+
if (node.type !== "DollarPattern" && node.type !== "ListenPattern") return [];
|
|
19
|
+
|
|
20
|
+
const pattern = node.pattern as ASTNode;
|
|
21
|
+
const action = node.action as ASTNode;
|
|
22
|
+
|
|
23
|
+
const wildcardCount = findAll(pattern, "Wildcard").length;
|
|
24
|
+
|
|
25
|
+
const maxPositional = findAll(action, "Substitution")
|
|
26
|
+
.map(s => s.code as string)
|
|
27
|
+
.filter(code => /^[1-9]$/.test(code))
|
|
28
|
+
.reduce((max, code) => Math.max(max, parseInt(code)), 0);
|
|
29
|
+
|
|
30
|
+
if (maxPositional > wildcardCount) {
|
|
31
|
+
return [{
|
|
32
|
+
rule: "missing-wildcard",
|
|
33
|
+
severity: "warning",
|
|
34
|
+
message: `Pattern has ${wildcardCount} wildcard(s) but action uses %${maxPositional} `
|
|
35
|
+
+ `— needs at least ${maxPositional} wildcard(s) in pattern`,
|
|
36
|
+
node: pattern,
|
|
37
|
+
}];
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { findAll } from "../../traverse/walk.js";
|
|
2
|
+
/**
|
|
3
|
+
* register-before-set
|
|
4
|
+
*
|
|
5
|
+
* Reports %q<name> substitutions and r(<name>) calls where the named register
|
|
6
|
+
* has no corresponding setq(<name>, ...) anywhere in the same attribute value.
|
|
7
|
+
*
|
|
8
|
+
* This catches typos (using %q0 when you meant %0) and missing initialisation.
|
|
9
|
+
*
|
|
10
|
+
* Limitation: purely static — cannot detect registers set in other attributes
|
|
11
|
+
* called via u() before this one runs. Use severity "info" to reflect this.
|
|
12
|
+
*/ export function checkRegisterBeforeSet(root) {
|
|
13
|
+
const setRegisters = new Set();
|
|
14
|
+
const readNodes = [];
|
|
15
|
+
// Collect all setq() writes
|
|
16
|
+
for (const node of findAll(root, "FunctionCall")){
|
|
17
|
+
if (node.name.toLowerCase() !== "setq") continue;
|
|
18
|
+
const args = node.args;
|
|
19
|
+
if (args.length < 1) continue;
|
|
20
|
+
const nameStr = literalText(args[0]);
|
|
21
|
+
if (nameStr !== null) setRegisters.add(nameStr);
|
|
22
|
+
}
|
|
23
|
+
// Collect all register reads — %q<name> substitutions
|
|
24
|
+
for (const node of findAll(root, "Substitution")){
|
|
25
|
+
const code = node.code;
|
|
26
|
+
if (!code.startsWith("q")) continue;
|
|
27
|
+
const name = code.slice(1); // "q0" → "0", "qfoo" → "foo"
|
|
28
|
+
if (name) readNodes.push({
|
|
29
|
+
name,
|
|
30
|
+
node
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Collect r(<name>) function calls
|
|
34
|
+
for (const node of findAll(root, "FunctionCall")){
|
|
35
|
+
if (node.name.toLowerCase() !== "r") continue;
|
|
36
|
+
const args = node.args;
|
|
37
|
+
if (args.length !== 1) continue;
|
|
38
|
+
const name = literalText(args[0]);
|
|
39
|
+
if (name !== null) readNodes.push({
|
|
40
|
+
name,
|
|
41
|
+
node
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return readNodes.filter(({ name })=>!setRegisters.has(name)).map(({ name, node })=>({
|
|
45
|
+
rule: "register-before-set",
|
|
46
|
+
severity: "info",
|
|
47
|
+
message: `Register "%q${name}" is read but no setq("${name}", …) found in this attribute`,
|
|
48
|
+
node
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/** Extract the literal string value of a single-literal Arg node, or null. */ function literalText(arg) {
|
|
52
|
+
if (arg.type !== "Arg") return null;
|
|
53
|
+
const parts = arg.parts;
|
|
54
|
+
if (parts.length === 1 && parts[0].type === "Literal") {
|
|
55
|
+
return parts[0].value;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=register_before_set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register_before_set.js","sources":["./register_before_set.ts"],"names":[],"mappings":"AAEA,SAAS,OAAO,iCAA0C;AAE1D;;;;;;;;;;CAUC,GACD,OAAO,SAAS,uBAAuB,IAAa;EAClD,MAAM,eAAe,IAAI;EACzB,MAAM,YAAoD,EAAE;EAE5D,4BAA4B;EAC5B,KAAK,MAAM,QAAQ,QAAQ,MAAM,gBAAiB;IAChD,IAAI,AAAC,KAAK,IAAI,CAAY,WAAW,OAAO,QAAQ;IACpD,MAAM,OAAO,KAAK,IAAI;IACtB,IAAI,KAAK,MAAM,GAAG,GAAG;IACrB,MAAM,UAAU,YAAY,IAAI,CAAC,EAAE;IACnC,IAAI,YAAY,MAAM,aAAa,GAAG,CAAC;EACzC;EAEA,sDAAsD;EACtD,KAAK,MAAM,QAAQ,QAAQ,MAAM,gBAAiB;IAChD,MAAM,OAAO,KAAK,IAAI;IACtB,IAAI,CAAC,KAAK,UAAU,CAAC,MAAM;IAC3B,MAAM,OAAO,KAAK,KAAK,CAAC,IAAI,6BAA6B;IACzD,IAAI,MAAM,UAAU,IAAI,CAAC;MAAE;MAAM;IAAK;EACxC;EAEA,mCAAmC;EACnC,KAAK,MAAM,QAAQ,QAAQ,MAAM,gBAAiB;IAChD,IAAI,AAAC,KAAK,IAAI,CAAY,WAAW,OAAO,KAAK;IACjD,MAAM,OAAO,KAAK,IAAI;IACtB,IAAI,KAAK,MAAM,KAAK,GAAG;IACvB,MAAM,OAAO,YAAY,IAAI,CAAC,EAAE;IAChC,IAAI,SAAS,MAAM,UAAU,IAAI,CAAC;MAAE;MAAM;IAAK;EACjD;EAEA,OAAO,UACJ,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,GAAK,CAAC,aAAa,GAAG,CAAC,OACvC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAK,CAAC;MACxB,MAAU;MACV,UAAU;MACV,SAAU,CAAC,YAAY,EAAE,KAAK,uBAAuB,EAAE,KAAK,6BAA6B,CAAC;MAC1F;IACF,CAAC;AACL;AAEA,4EAA4E,GAC5E,SAAS,YAAY,GAAY;EAC/B,IAAI,IAAI,IAAI,KAAK,OAAO,OAAO;EAC/B,MAAM,QAAQ,IAAI,KAAK;EACvB,IAAI,MAAM,MAAM,KAAK,KAAK,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,WAAW;IACrD,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK;EACvB;EACA,OAAO;AACT"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ASTNode } from "../../../parser/mod.ts";
|
|
2
|
+
import type { Diagnostic } from "../mod.ts";
|
|
3
|
+
import { findAll } from "../../traverse/walk.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* register-before-set
|
|
7
|
+
*
|
|
8
|
+
* Reports %q<name> substitutions and r(<name>) calls where the named register
|
|
9
|
+
* has no corresponding setq(<name>, ...) anywhere in the same attribute value.
|
|
10
|
+
*
|
|
11
|
+
* This catches typos (using %q0 when you meant %0) and missing initialisation.
|
|
12
|
+
*
|
|
13
|
+
* Limitation: purely static — cannot detect registers set in other attributes
|
|
14
|
+
* called via u() before this one runs. Use severity "info" to reflect this.
|
|
15
|
+
*/
|
|
16
|
+
export function checkRegisterBeforeSet(root: ASTNode): Diagnostic[] {
|
|
17
|
+
const setRegisters = new Set<string>();
|
|
18
|
+
const readNodes: Array<{ name: string; node: ASTNode }> = [];
|
|
19
|
+
|
|
20
|
+
// Collect all setq() writes
|
|
21
|
+
for (const node of findAll(root, "FunctionCall")) {
|
|
22
|
+
if ((node.name as string).toLowerCase() !== "setq") continue;
|
|
23
|
+
const args = node.args as ASTNode[];
|
|
24
|
+
if (args.length < 1) continue;
|
|
25
|
+
const nameStr = literalText(args[0]);
|
|
26
|
+
if (nameStr !== null) setRegisters.add(nameStr);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Collect all register reads — %q<name> substitutions
|
|
30
|
+
for (const node of findAll(root, "Substitution")) {
|
|
31
|
+
const code = node.code as string;
|
|
32
|
+
if (!code.startsWith("q")) continue;
|
|
33
|
+
const name = code.slice(1); // "q0" → "0", "qfoo" → "foo"
|
|
34
|
+
if (name) readNodes.push({ name, node });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Collect r(<name>) function calls
|
|
38
|
+
for (const node of findAll(root, "FunctionCall")) {
|
|
39
|
+
if ((node.name as string).toLowerCase() !== "r") continue;
|
|
40
|
+
const args = node.args as ASTNode[];
|
|
41
|
+
if (args.length !== 1) continue;
|
|
42
|
+
const name = literalText(args[0]);
|
|
43
|
+
if (name !== null) readNodes.push({ name, node });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return readNodes
|
|
47
|
+
.filter(({ name }) => !setRegisters.has(name))
|
|
48
|
+
.map(({ name, node }) => ({
|
|
49
|
+
rule: "register-before-set",
|
|
50
|
+
severity: "info" as const,
|
|
51
|
+
message: `Register "%q${name}" is read but no setq("${name}", …) found in this attribute`,
|
|
52
|
+
node,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Extract the literal string value of a single-literal Arg node, or null. */
|
|
57
|
+
function literalText(arg: ASTNode): string | null {
|
|
58
|
+
if (arg.type !== "Arg") return null;
|
|
59
|
+
const parts = arg.parts as ASTNode[];
|
|
60
|
+
if (parts.length === 1 && parts[0].type === "Literal") {
|
|
61
|
+
return parts[0].value as string;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Print a lock expression subtree back to its canonical string form.
|
|
3
|
+
* Only called when the root was parsed with the "LockExpr" start rule.
|
|
4
|
+
*/ export function printLock(node) {
|
|
5
|
+
return printLockNode(node, null);
|
|
6
|
+
}
|
|
7
|
+
function printLockNode(node, parentType) {
|
|
8
|
+
switch(node.type){
|
|
9
|
+
case "LockOr":
|
|
10
|
+
{
|
|
11
|
+
const inner = node.operands.map((op)=>printLockNode(op, "LockOr")).join("|");
|
|
12
|
+
// Parens needed when OR appears inside AND context
|
|
13
|
+
return parentType === "LockAnd" ? `(${inner})` : inner;
|
|
14
|
+
}
|
|
15
|
+
case "LockAnd":
|
|
16
|
+
{
|
|
17
|
+
return node.operands.map((op)=>printLockNode(op, "LockAnd")).join("&");
|
|
18
|
+
}
|
|
19
|
+
case "LockNot":
|
|
20
|
+
{
|
|
21
|
+
const operand = node.operand;
|
|
22
|
+
const inner = printLockNode(operand, "LockNot");
|
|
23
|
+
// Re-add parens if the operand is a compound expression
|
|
24
|
+
const needsParens = operand.type === "LockOr" || operand.type === "LockAnd";
|
|
25
|
+
return needsParens ? `!(${inner})` : `!${inner}`;
|
|
26
|
+
}
|
|
27
|
+
case "LockMe":
|
|
28
|
+
return "me";
|
|
29
|
+
case "LockDbref":
|
|
30
|
+
return node.dbref;
|
|
31
|
+
case "LockFlagCheck":
|
|
32
|
+
return `flag^${node.flag}`;
|
|
33
|
+
case "LockTypeCheck":
|
|
34
|
+
return `type^${node.typeName}`;
|
|
35
|
+
case "LockAttrCheck":
|
|
36
|
+
return `attr^${node.attribute}`;
|
|
37
|
+
case "LockPlayerName":
|
|
38
|
+
return `=${node.name}`;
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(`printLock: unexpected node type "${node.type}"`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=lock_printer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock_printer.js","sources":["./lock_printer.ts"],"names":[],"mappings":"AAEA;;;CAGC,GACD,OAAO,SAAS,UAAU,IAAa;EACrC,OAAO,cAAc,MAAM;AAC7B;AAEA,SAAS,cAAc,IAAa,EAAE,UAAyB;EAC7D,OAAQ,KAAK,IAAI;IACf,KAAK;MAAU;QACb,MAAM,QAAQ,AAAC,KAAK,QAAQ,CACzB,GAAG,CAAC,CAAA,KAAM,cAAc,IAAI,WAC5B,IAAI,CAAC;QACR,mDAAmD;QACnD,OAAO,eAAe,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG;MACnD;IACA,KAAK;MAAW;QACd,OAAO,AAAC,KAAK,QAAQ,CAClB,GAAG,CAAC,CAAA,KAAM,cAAc,IAAI,YAC5B,IAAI,CAAC;MACV;IACA,KAAK;MAAW;QACd,MAAM,UAAU,KAAK,OAAO;QAC5B,MAAM,QAAQ,cAAc,SAAS;QACrC,wDAAwD;QACxD,MAAM,cAAc,QAAQ,IAAI,KAAK,YAAY,QAAQ,IAAI,KAAK;QAClE,OAAO,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO;MAClD;IACA,KAAK;MAAkB,OAAO;IAC9B,KAAK;MAAkB,OAAO,KAAK,KAAK;IACxC,KAAK;MAAkB,OAAO,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;IACjD,KAAK;MAAkB,OAAO,CAAC,KAAK,EAAE,KAAK,QAAQ,EAAE;IACrD,KAAK;MAAkB,OAAO,CAAC,KAAK,EAAE,KAAK,SAAS,EAAE;IACtD,KAAK;MAAkB,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,EAAE;IAC7C;MACE,MAAM,IAAI,MAAM,CAAC,iCAAiC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;EACpE;AACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ASTNode } from "../../parser/mod.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Print a lock expression subtree back to its canonical string form.
|
|
5
|
+
* Only called when the root was parsed with the "LockExpr" start rule.
|
|
6
|
+
*/
|
|
7
|
+
export function printLock(node: ASTNode): string {
|
|
8
|
+
return printLockNode(node, null);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function printLockNode(node: ASTNode, parentType: string | null): string {
|
|
12
|
+
switch (node.type) {
|
|
13
|
+
case "LockOr": {
|
|
14
|
+
const inner = (node.operands as ASTNode[])
|
|
15
|
+
.map(op => printLockNode(op, "LockOr"))
|
|
16
|
+
.join("|");
|
|
17
|
+
// Parens needed when OR appears inside AND context
|
|
18
|
+
return parentType === "LockAnd" ? `(${inner})` : inner;
|
|
19
|
+
}
|
|
20
|
+
case "LockAnd": {
|
|
21
|
+
return (node.operands as ASTNode[])
|
|
22
|
+
.map(op => printLockNode(op, "LockAnd"))
|
|
23
|
+
.join("&");
|
|
24
|
+
}
|
|
25
|
+
case "LockNot": {
|
|
26
|
+
const operand = node.operand as ASTNode;
|
|
27
|
+
const inner = printLockNode(operand, "LockNot");
|
|
28
|
+
// Re-add parens if the operand is a compound expression
|
|
29
|
+
const needsParens = operand.type === "LockOr" || operand.type === "LockAnd";
|
|
30
|
+
return needsParens ? `!(${inner})` : `!${inner}`;
|
|
31
|
+
}
|
|
32
|
+
case "LockMe": return "me";
|
|
33
|
+
case "LockDbref": return node.dbref as string;
|
|
34
|
+
case "LockFlagCheck": return `flag^${node.flag}`;
|
|
35
|
+
case "LockTypeCheck": return `type^${node.typeName}`;
|
|
36
|
+
case "LockAttrCheck": return `attr^${node.attribute}`;
|
|
37
|
+
case "LockPlayerName": return `=${node.name}`;
|
|
38
|
+
default:
|
|
39
|
+
throw new Error(`printLock: unexpected node type "${node.type}"`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Converts an AST back to a canonical softcode string via `print()`.
|
|
4
|
+
* Supports compact (semicolon-separated) and pretty (newline-separated) modes.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { parse } from "@ursamu/mushcode/parse";
|
|
9
|
+
* import { print } from "@ursamu/mushcode/print";
|
|
10
|
+
*
|
|
11
|
+
* const ast = parse("@pemit %#=Hello;@pemit %#=World");
|
|
12
|
+
* console.log(print(ast, { mode: "pretty" }));
|
|
13
|
+
* // @pemit %#=Hello
|
|
14
|
+
* // @pemit %#=World
|
|
15
|
+
* ```
|
|
16
|
+
*/ export { print } from "./printer.js";
|
|
17
|
+
//# sourceMappingURL=mod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.js","sources":["./mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAAS,KAAK,uBAA8C"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Converts an AST back to a canonical softcode string via `print()`.
|
|
4
|
+
* Supports compact (semicolon-separated) and pretty (newline-separated) modes.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { parse } from "@ursamu/mushcode/parse";
|
|
9
|
+
* import { print } from "@ursamu/mushcode/print";
|
|
10
|
+
*
|
|
11
|
+
* const ast = parse("@pemit %#=Hello;@pemit %#=World");
|
|
12
|
+
* console.log(print(ast, { mode: "pretty" }));
|
|
13
|
+
* // @pemit %#=Hello
|
|
14
|
+
* // @pemit %#=World
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export type { PrintMode, PrintOptions } from "./printer.js";
|
|
18
|
+
export { print } from "./printer.js";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { printLock } from "./lock_printer.js";
|
|
2
|
+
// ── Entry point ───────────────────────────────────────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* Convert an AST node back to a canonical softcode string.
|
|
5
|
+
*
|
|
6
|
+
* The output is semantically equivalent to the original source but whitespace
|
|
7
|
+
* may be normalised (e.g. `@pemit%#=x` becomes `@pemit %#=x`).
|
|
8
|
+
* Lock-expression nodes (LockOr, LockAnd, …) produced by the "LockExpr" start
|
|
9
|
+
* rule are printed via the lock printer.
|
|
10
|
+
*/ export function print(node, opts) {
|
|
11
|
+
return printNode(node, opts);
|
|
12
|
+
}
|
|
13
|
+
// ── Dispatcher ────────────────────────────────────────────────────────────────
|
|
14
|
+
function printNode(node, opts) {
|
|
15
|
+
// deno-lint-ignore no-explicit-any
|
|
16
|
+
const n = node;
|
|
17
|
+
switch(node.type){
|
|
18
|
+
// ── Commands ──────────────────────────────────────────────────────────────
|
|
19
|
+
case "CommandList":
|
|
20
|
+
{
|
|
21
|
+
const sep = opts?.mode === "pretty" ? ";\n" : ";";
|
|
22
|
+
return n.commands.map((c)=>printNode(c, opts)).join(sep);
|
|
23
|
+
}
|
|
24
|
+
case "AtCommand":
|
|
25
|
+
{
|
|
26
|
+
const sw = n.switches.map((s)=>`/${s}`).join("");
|
|
27
|
+
const obj = n.object ? " " + printNode(n.object, opts) : "";
|
|
28
|
+
const val = n.value != null ? "=" + printNode(n.value, opts) : "";
|
|
29
|
+
return `@${n.name}${sw}${obj}${val}`;
|
|
30
|
+
}
|
|
31
|
+
case "AttributeSet":
|
|
32
|
+
{
|
|
33
|
+
const obj = printNode(n.object, opts);
|
|
34
|
+
const val = n.value != null ? "=" + printNode(n.value, opts) : "";
|
|
35
|
+
return `&${n.attribute} ${obj}${val}`;
|
|
36
|
+
}
|
|
37
|
+
case "UserCommand":
|
|
38
|
+
return n.parts.map((p)=>printNode(p, opts)).join("");
|
|
39
|
+
// ── Patterns ──────────────────────────────────────────────────────────────
|
|
40
|
+
case "DollarPattern":
|
|
41
|
+
return "$" + printNode(n.pattern, opts) + ":" + printNode(n.action, opts);
|
|
42
|
+
case "ListenPattern":
|
|
43
|
+
return "^" + printNode(n.pattern, opts) + ":" + printNode(n.action, opts);
|
|
44
|
+
case "PatternAlts":
|
|
45
|
+
return n.patterns.map((p)=>printNode(p, opts)).join(";");
|
|
46
|
+
case "Pattern":
|
|
47
|
+
return n.parts.map((p)=>printNode(p, opts)).join("");
|
|
48
|
+
case "Wildcard":
|
|
49
|
+
return n.wildcard; // "*" or "?"
|
|
50
|
+
// ── Expression containers ─────────────────────────────────────────────────
|
|
51
|
+
case "EvalBlock":
|
|
52
|
+
return "[" + n.parts.map((p)=>printNode(p, opts)).join("") + "]";
|
|
53
|
+
case "BracedString":
|
|
54
|
+
return "{" + n.parts.map((p)=>printNode(p, opts)).join("") + "}";
|
|
55
|
+
case "FunctionCall":
|
|
56
|
+
{
|
|
57
|
+
const args = n.args.map((a)=>printNode(a, opts)).join(",");
|
|
58
|
+
return `${n.name}(${args})`;
|
|
59
|
+
}
|
|
60
|
+
case "Arg":
|
|
61
|
+
return n.parts.map((p)=>printNode(p, opts)).join("");
|
|
62
|
+
case "Text":
|
|
63
|
+
return n.parts.map((p)=>printNode(p, opts)).join("");
|
|
64
|
+
// ── Leaves ────────────────────────────────────────────────────────────────
|
|
65
|
+
case "Literal":
|
|
66
|
+
return n.value;
|
|
67
|
+
case "Escape":
|
|
68
|
+
return "\\" + n.char;
|
|
69
|
+
case "Substitution":
|
|
70
|
+
return "%" + n.code;
|
|
71
|
+
case "SpecialVar":
|
|
72
|
+
return n.code; // "##", "#@", "#$" — already includes #
|
|
73
|
+
case "TagRef":
|
|
74
|
+
return "#" + n.name;
|
|
75
|
+
// ── Lock expressions ──────────────────────────────────────────────────────
|
|
76
|
+
// These appear when using the "LockExpr" start rule.
|
|
77
|
+
case "LockOr":
|
|
78
|
+
case "LockAnd":
|
|
79
|
+
case "LockNot":
|
|
80
|
+
case "LockMe":
|
|
81
|
+
case "LockDbref":
|
|
82
|
+
case "LockFlagCheck":
|
|
83
|
+
case "LockTypeCheck":
|
|
84
|
+
case "LockAttrCheck":
|
|
85
|
+
case "LockPlayerName":
|
|
86
|
+
return printLock(node);
|
|
87
|
+
default:
|
|
88
|
+
throw new Error(`print: unhandled node type "${node.type}"`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=printer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"printer.js","sources":["./printer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { ASTNode } from "../../parser/mod.ts";
|
|
2
|
+
import { printLock } from "./lock_printer.js";
|
|
3
|
+
|
|
4
|
+
// ── Options ───────────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
/** Controls how `CommandList` separators are rendered in the output. */
|
|
7
|
+
export type PrintMode = "compact" | "pretty";
|
|
8
|
+
|
|
9
|
+
/** Options for the {@link print} function. */
|
|
10
|
+
export interface PrintOptions {
|
|
11
|
+
/** "compact" (default): CommandList joined by ";".
|
|
12
|
+
* "pretty": CommandList joined by ";\n". */
|
|
13
|
+
mode?: PrintMode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ── Entry point ───────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convert an AST node back to a canonical softcode string.
|
|
20
|
+
*
|
|
21
|
+
* The output is semantically equivalent to the original source but whitespace
|
|
22
|
+
* may be normalised (e.g. `@pemit%#=x` becomes `@pemit %#=x`).
|
|
23
|
+
* Lock-expression nodes (LockOr, LockAnd, …) produced by the "LockExpr" start
|
|
24
|
+
* rule are printed via the lock printer.
|
|
25
|
+
*/
|
|
26
|
+
export function print(node: ASTNode, opts?: PrintOptions): string {
|
|
27
|
+
return printNode(node, opts);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Dispatcher ────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
function printNode(node: ASTNode, opts?: PrintOptions): string {
|
|
33
|
+
// deno-lint-ignore no-explicit-any
|
|
34
|
+
const n = node as any;
|
|
35
|
+
switch (node.type) {
|
|
36
|
+
|
|
37
|
+
// ── Commands ──────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
case "CommandList": {
|
|
40
|
+
const sep = opts?.mode === "pretty" ? ";\n" : ";";
|
|
41
|
+
return (n.commands as ASTNode[]).map(c => printNode(c, opts)).join(sep);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
case "AtCommand": {
|
|
45
|
+
const sw = (n.switches as string[]).map((s: string) => `/${s}`).join("");
|
|
46
|
+
const obj = n.object ? " " + printNode(n.object as ASTNode, opts) : "";
|
|
47
|
+
const val = n.value != null ? "=" + printNode(n.value as ASTNode, opts) : "";
|
|
48
|
+
return `@${n.name}${sw}${obj}${val}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
case "AttributeSet": {
|
|
52
|
+
const obj = printNode(n.object as ASTNode, opts);
|
|
53
|
+
const val = n.value != null ? "=" + printNode(n.value as ASTNode, opts) : "";
|
|
54
|
+
return `&${n.attribute} ${obj}${val}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
case "UserCommand":
|
|
58
|
+
return (n.parts as ASTNode[]).map(p => printNode(p, opts)).join("");
|
|
59
|
+
|
|
60
|
+
// ── Patterns ──────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
case "DollarPattern":
|
|
63
|
+
return "$" + printNode(n.pattern as ASTNode, opts)
|
|
64
|
+
+ ":" + printNode(n.action as ASTNode, opts);
|
|
65
|
+
|
|
66
|
+
case "ListenPattern":
|
|
67
|
+
return "^" + printNode(n.pattern as ASTNode, opts)
|
|
68
|
+
+ ":" + printNode(n.action as ASTNode, opts);
|
|
69
|
+
|
|
70
|
+
case "PatternAlts":
|
|
71
|
+
return (n.patterns as ASTNode[]).map(p => printNode(p, opts)).join(";");
|
|
72
|
+
|
|
73
|
+
case "Pattern":
|
|
74
|
+
return (n.parts as ASTNode[]).map(p => printNode(p, opts)).join("");
|
|
75
|
+
|
|
76
|
+
case "Wildcard":
|
|
77
|
+
return n.wildcard as string; // "*" or "?"
|
|
78
|
+
|
|
79
|
+
// ── Expression containers ─────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
case "EvalBlock":
|
|
82
|
+
return "[" + (n.parts as ASTNode[]).map(p => printNode(p, opts)).join("") + "]";
|
|
83
|
+
|
|
84
|
+
case "BracedString":
|
|
85
|
+
return "{" + (n.parts as ASTNode[]).map(p => printNode(p, opts)).join("") + "}";
|
|
86
|
+
|
|
87
|
+
case "FunctionCall": {
|
|
88
|
+
const args = (n.args as ASTNode[]).map(a => printNode(a, opts)).join(",");
|
|
89
|
+
return `${n.name}(${args})`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case "Arg":
|
|
93
|
+
return (n.parts as ASTNode[]).map(p => printNode(p, opts)).join("");
|
|
94
|
+
|
|
95
|
+
case "Text":
|
|
96
|
+
return (n.parts as ASTNode[]).map(p => printNode(p, opts)).join("");
|
|
97
|
+
|
|
98
|
+
// ── Leaves ────────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
case "Literal":
|
|
101
|
+
return n.value as string;
|
|
102
|
+
|
|
103
|
+
case "Escape":
|
|
104
|
+
return "\\" + (n.char as string);
|
|
105
|
+
|
|
106
|
+
case "Substitution":
|
|
107
|
+
return "%" + (n.code as string);
|
|
108
|
+
|
|
109
|
+
case "SpecialVar":
|
|
110
|
+
return n.code as string; // "##", "#@", "#$" — already includes #
|
|
111
|
+
|
|
112
|
+
case "TagRef":
|
|
113
|
+
return "#" + (n.name as string);
|
|
114
|
+
|
|
115
|
+
// ── Lock expressions ──────────────────────────────────────────────────────
|
|
116
|
+
// These appear when using the "LockExpr" start rule.
|
|
117
|
+
|
|
118
|
+
case "LockOr":
|
|
119
|
+
case "LockAnd":
|
|
120
|
+
case "LockNot":
|
|
121
|
+
case "LockMe":
|
|
122
|
+
case "LockDbref":
|
|
123
|
+
case "LockFlagCheck":
|
|
124
|
+
case "LockTypeCheck":
|
|
125
|
+
case "LockAttrCheck":
|
|
126
|
+
case "LockPlayerName":
|
|
127
|
+
return printLock(node);
|
|
128
|
+
|
|
129
|
+
default:
|
|
130
|
+
throw new Error(`print: unhandled node type "${node.type}"`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// child_slots.ts — authoritative child-field registry for every AST node type
|
|
3
|
+
//
|
|
4
|
+
// Used by walk() and transform() to know exactly which fields hold child nodes,
|
|
5
|
+
// whether each field is a single node (possibly null) or an array of nodes.
|
|
6
|
+
//
|
|
7
|
+
// Leaf types (Literal, Escape, Substitution, SpecialVar, Wildcard, TagRef,
|
|
8
|
+
// LockMe, LockDbref, LockFlagCheck, LockTypeCheck, LockAttrCheck,
|
|
9
|
+
// LockPlayerName) have no entry — the walker treats missing entries as leaves.
|
|
10
|
+
// ============================================================================
|
|
11
|
+
export const CHILD_SLOTS = {
|
|
12
|
+
// ── Commands ────────────────────────────────────────────────────────────────
|
|
13
|
+
CommandList: [
|
|
14
|
+
{
|
|
15
|
+
field: "commands",
|
|
16
|
+
kind: "array"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
AtCommand: [
|
|
20
|
+
{
|
|
21
|
+
field: "object",
|
|
22
|
+
kind: "single"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
field: "value",
|
|
26
|
+
kind: "single"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
AttributeSet: [
|
|
30
|
+
{
|
|
31
|
+
field: "object",
|
|
32
|
+
kind: "single"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
field: "value",
|
|
36
|
+
kind: "single"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
UserCommand: [
|
|
40
|
+
{
|
|
41
|
+
field: "parts",
|
|
42
|
+
kind: "array"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
// ── Patterns ────────────────────────────────────────────────────────────────
|
|
46
|
+
DollarPattern: [
|
|
47
|
+
{
|
|
48
|
+
field: "pattern",
|
|
49
|
+
kind: "single"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
field: "action",
|
|
53
|
+
kind: "single"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
ListenPattern: [
|
|
57
|
+
{
|
|
58
|
+
field: "pattern",
|
|
59
|
+
kind: "single"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
field: "action",
|
|
63
|
+
kind: "single"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
PatternAlts: [
|
|
67
|
+
{
|
|
68
|
+
field: "patterns",
|
|
69
|
+
kind: "array"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
Pattern: [
|
|
73
|
+
{
|
|
74
|
+
field: "parts",
|
|
75
|
+
kind: "array"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
// ── Expression containers ───────────────────────────────────────────────────
|
|
79
|
+
EvalBlock: [
|
|
80
|
+
{
|
|
81
|
+
field: "parts",
|
|
82
|
+
kind: "array"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
BracedString: [
|
|
86
|
+
{
|
|
87
|
+
field: "parts",
|
|
88
|
+
kind: "array"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
FunctionCall: [
|
|
92
|
+
{
|
|
93
|
+
field: "args",
|
|
94
|
+
kind: "array"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
Arg: [
|
|
98
|
+
{
|
|
99
|
+
field: "parts",
|
|
100
|
+
kind: "array"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
Text: [
|
|
104
|
+
{
|
|
105
|
+
field: "parts",
|
|
106
|
+
kind: "array"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
// ── Lock expressions ────────────────────────────────────────────────────────
|
|
110
|
+
LockOr: [
|
|
111
|
+
{
|
|
112
|
+
field: "operands",
|
|
113
|
+
kind: "array"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
LockAnd: [
|
|
117
|
+
{
|
|
118
|
+
field: "operands",
|
|
119
|
+
kind: "array"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
LockNot: [
|
|
123
|
+
{
|
|
124
|
+
field: "operand",
|
|
125
|
+
kind: "single"
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=child_slots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child_slots.js","sources":["./child_slots.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,2EAA2E;AAC3E,kEAAkE;AAClE,+EAA+E;AAC/E,+EAA+E;AAS/E,OAAO,MAAM,cAAmD;EAC9D,+EAA+E;EAC/E,aAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,WAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;IACrC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,cAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;IACrC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,aAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EAEvD,+EAA+E;EAC/E,eAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;IACrC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,eAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;IACrC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,aAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,SAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EAEvD,+EAA+E;EAC/E,WAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,cAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,cAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,KAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,MAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EAEvD,+EAA+E;EAC/E,QAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,SAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;EACvD,SAAe;IAAC;MAAE,OAAO;MAAa,MAAM;IAAS;GAAE;AAKzD,EAAE"}
|