@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.
Files changed (157) hide show
  1. package/node_modules/@ursamu/mushcode/.github/workflows/publish.yml +36 -0
  2. package/node_modules/@ursamu/mushcode/LICENSE +21 -0
  3. package/node_modules/@ursamu/mushcode/README.md +110 -0
  4. package/node_modules/@ursamu/mushcode/_dist/mod.d.ts +36 -0
  5. package/node_modules/@ursamu/mushcode/_dist/mod.d.ts.map +1 -0
  6. package/node_modules/@ursamu/mushcode/_dist/parser/mod.d.ts +41 -0
  7. package/node_modules/@ursamu/mushcode/_dist/parser/mod.d.ts.map +1 -0
  8. package/node_modules/@ursamu/mushcode/_dist/src/analyze/commands.d.ts +15 -0
  9. package/node_modules/@ursamu/mushcode/_dist/src/analyze/commands.d.ts.map +1 -0
  10. package/node_modules/@ursamu/mushcode/_dist/src/analyze/deps.d.ts +18 -0
  11. package/node_modules/@ursamu/mushcode/_dist/src/analyze/deps.d.ts.map +1 -0
  12. package/node_modules/@ursamu/mushcode/_dist/src/analyze/mod.d.ts +20 -0
  13. package/node_modules/@ursamu/mushcode/_dist/src/analyze/mod.d.ts.map +1 -0
  14. package/node_modules/@ursamu/mushcode/_dist/src/analyze/tags.d.ts +6 -0
  15. package/node_modules/@ursamu/mushcode/_dist/src/analyze/tags.d.ts.map +1 -0
  16. package/node_modules/@ursamu/mushcode/_dist/src/eval/context.d.ts +85 -0
  17. package/node_modules/@ursamu/mushcode/_dist/src/eval/context.d.ts.map +1 -0
  18. package/node_modules/@ursamu/mushcode/_dist/src/eval/engine.d.ts +48 -0
  19. package/node_modules/@ursamu/mushcode/_dist/src/eval/engine.d.ts.map +1 -0
  20. package/node_modules/@ursamu/mushcode/_dist/src/eval/mod.d.ts +26 -0
  21. package/node_modules/@ursamu/mushcode/_dist/src/eval/mod.d.ts.map +1 -0
  22. package/node_modules/@ursamu/mushcode/_dist/src/eval/stdlib/mod.d.ts +3 -0
  23. package/node_modules/@ursamu/mushcode/_dist/src/eval/stdlib/mod.d.ts.map +1 -0
  24. package/node_modules/@ursamu/mushcode/_dist/src/lint/mod.d.ts +38 -0
  25. package/node_modules/@ursamu/mushcode/_dist/src/lint/mod.d.ts.map +1 -0
  26. package/node_modules/@ursamu/mushcode/_dist/src/print/mod.d.ts +18 -0
  27. package/node_modules/@ursamu/mushcode/_dist/src/print/mod.d.ts.map +1 -0
  28. package/node_modules/@ursamu/mushcode/_dist/src/print/printer.d.ts +15 -0
  29. package/node_modules/@ursamu/mushcode/_dist/src/print/printer.d.ts.map +1 -0
  30. package/node_modules/@ursamu/mushcode/_dist/src/traverse/mod.d.ts +19 -0
  31. package/node_modules/@ursamu/mushcode/_dist/src/traverse/mod.d.ts.map +1 -0
  32. package/node_modules/@ursamu/mushcode/_dist/src/traverse/transform.d.ts +27 -0
  33. package/node_modules/@ursamu/mushcode/_dist/src/traverse/transform.d.ts.map +1 -0
  34. package/node_modules/@ursamu/mushcode/_dist/src/traverse/walk.d.ts +27 -0
  35. package/node_modules/@ursamu/mushcode/_dist/src/traverse/walk.d.ts.map +1 -0
  36. package/node_modules/@ursamu/mushcode/deno.json +26 -0
  37. package/node_modules/@ursamu/mushcode/deno.lock +42 -0
  38. package/node_modules/@ursamu/mushcode/docs/analyze.md +145 -0
  39. package/node_modules/@ursamu/mushcode/docs/eval.md +312 -0
  40. package/node_modules/@ursamu/mushcode/docs/lint.md +152 -0
  41. package/node_modules/@ursamu/mushcode/docs/parser.md +196 -0
  42. package/node_modules/@ursamu/mushcode/docs/print.md +84 -0
  43. package/node_modules/@ursamu/mushcode/docs/stdlib.md +418 -0
  44. package/node_modules/@ursamu/mushcode/docs/traverse.md +167 -0
  45. package/node_modules/@ursamu/mushcode/grammar/mux-softcode.pegjs +781 -0
  46. package/node_modules/@ursamu/mushcode/mod.js +44 -0
  47. package/node_modules/@ursamu/mushcode/mod.js.map +1 -0
  48. package/node_modules/@ursamu/mushcode/mod.ts +63 -0
  49. package/node_modules/@ursamu/mushcode/package.json +38 -0
  50. package/node_modules/@ursamu/mushcode/parser/mod.js +47 -0
  51. package/node_modules/@ursamu/mushcode/parser/mod.js.map +1 -0
  52. package/node_modules/@ursamu/mushcode/parser/mod.ts +99 -0
  53. package/node_modules/@ursamu/mushcode/parser/mux-softcode.js +3833 -0
  54. package/node_modules/@ursamu/mushcode/parser/mux-softcode.mjs +3837 -0
  55. package/node_modules/@ursamu/mushcode/src/analyze/commands.js +29 -0
  56. package/node_modules/@ursamu/mushcode/src/analyze/commands.js.map +1 -0
  57. package/node_modules/@ursamu/mushcode/src/analyze/commands.ts +46 -0
  58. package/node_modules/@ursamu/mushcode/src/analyze/deps.js +45 -0
  59. package/node_modules/@ursamu/mushcode/src/analyze/deps.js.map +1 -0
  60. package/node_modules/@ursamu/mushcode/src/analyze/deps.ts +51 -0
  61. package/node_modules/@ursamu/mushcode/src/analyze/mod.js +18 -0
  62. package/node_modules/@ursamu/mushcode/src/analyze/mod.js.map +1 -0
  63. package/node_modules/@ursamu/mushcode/src/analyze/mod.ts +20 -0
  64. package/node_modules/@ursamu/mushcode/src/analyze/tags.js +11 -0
  65. package/node_modules/@ursamu/mushcode/src/analyze/tags.js.map +1 -0
  66. package/node_modules/@ursamu/mushcode/src/analyze/tags.ts +11 -0
  67. package/node_modules/@ursamu/mushcode/src/eval/context.js +22 -0
  68. package/node_modules/@ursamu/mushcode/src/eval/context.js.map +1 -0
  69. package/node_modules/@ursamu/mushcode/src/eval/context.ts +177 -0
  70. package/node_modules/@ursamu/mushcode/src/eval/engine.js +238 -0
  71. package/node_modules/@ursamu/mushcode/src/eval/engine.js.map +1 -0
  72. package/node_modules/@ursamu/mushcode/src/eval/engine.ts +276 -0
  73. package/node_modules/@ursamu/mushcode/src/eval/mod.js +25 -0
  74. package/node_modules/@ursamu/mushcode/src/eval/mod.js.map +1 -0
  75. package/node_modules/@ursamu/mushcode/src/eval/mod.ts +31 -0
  76. package/node_modules/@ursamu/mushcode/src/eval/stdlib/compare.js +56 -0
  77. package/node_modules/@ursamu/mushcode/src/eval/stdlib/compare.js.map +1 -0
  78. package/node_modules/@ursamu/mushcode/src/eval/stdlib/compare.ts +16 -0
  79. package/node_modules/@ursamu/mushcode/src/eval/stdlib/db.js +91 -0
  80. package/node_modules/@ursamu/mushcode/src/eval/stdlib/db.js.map +1 -0
  81. package/node_modules/@ursamu/mushcode/src/eval/stdlib/db.ts +104 -0
  82. package/node_modules/@ursamu/mushcode/src/eval/stdlib/iter.js +91 -0
  83. package/node_modules/@ursamu/mushcode/src/eval/stdlib/iter.js.map +1 -0
  84. package/node_modules/@ursamu/mushcode/src/eval/stdlib/iter.ts +98 -0
  85. package/node_modules/@ursamu/mushcode/src/eval/stdlib/logic.js +79 -0
  86. package/node_modules/@ursamu/mushcode/src/eval/stdlib/logic.js.map +1 -0
  87. package/node_modules/@ursamu/mushcode/src/eval/stdlib/logic.ts +84 -0
  88. package/node_modules/@ursamu/mushcode/src/eval/stdlib/math.js +120 -0
  89. package/node_modules/@ursamu/mushcode/src/eval/stdlib/math.js.map +1 -0
  90. package/node_modules/@ursamu/mushcode/src/eval/stdlib/math.ts +115 -0
  91. package/node_modules/@ursamu/mushcode/src/eval/stdlib/mod.js +17 -0
  92. package/node_modules/@ursamu/mushcode/src/eval/stdlib/mod.js.map +1 -0
  93. package/node_modules/@ursamu/mushcode/src/eval/stdlib/mod.ts +19 -0
  94. package/node_modules/@ursamu/mushcode/src/eval/stdlib/register.js +28 -0
  95. package/node_modules/@ursamu/mushcode/src/eval/stdlib/register.js.map +1 -0
  96. package/node_modules/@ursamu/mushcode/src/eval/stdlib/register.ts +31 -0
  97. package/node_modules/@ursamu/mushcode/src/eval/stdlib/string.js +153 -0
  98. package/node_modules/@ursamu/mushcode/src/eval/stdlib/string.js.map +1 -0
  99. package/node_modules/@ursamu/mushcode/src/eval/stdlib/string.ts +154 -0
  100. package/node_modules/@ursamu/mushcode/src/lint/builtin_arities.js +212 -0
  101. package/node_modules/@ursamu/mushcode/src/lint/builtin_arities.js.map +1 -0
  102. package/node_modules/@ursamu/mushcode/src/lint/builtin_arities.ts +68 -0
  103. package/node_modules/@ursamu/mushcode/src/lint/mod.js +60 -0
  104. package/node_modules/@ursamu/mushcode/src/lint/mod.js.map +1 -0
  105. package/node_modules/@ursamu/mushcode/src/lint/mod.ts +96 -0
  106. package/node_modules/@ursamu/mushcode/src/lint/rules/arg_count.js +37 -0
  107. package/node_modules/@ursamu/mushcode/src/lint/rules/arg_count.js.map +1 -0
  108. package/node_modules/@ursamu/mushcode/src/lint/rules/arg_count.ts +44 -0
  109. package/node_modules/@ursamu/mushcode/src/lint/rules/iter_var_outside_iter.js +55 -0
  110. package/node_modules/@ursamu/mushcode/src/lint/rules/iter_var_outside_iter.js.map +1 -0
  111. package/node_modules/@ursamu/mushcode/src/lint/rules/iter_var_outside_iter.ts +60 -0
  112. package/node_modules/@ursamu/mushcode/src/lint/rules/missing_wildcard.js +31 -0
  113. package/node_modules/@ursamu/mushcode/src/lint/rules/missing_wildcard.js.map +1 -0
  114. package/node_modules/@ursamu/mushcode/src/lint/rules/missing_wildcard.ts +40 -0
  115. package/node_modules/@ursamu/mushcode/src/lint/rules/register_before_set.js +59 -0
  116. package/node_modules/@ursamu/mushcode/src/lint/rules/register_before_set.js.map +1 -0
  117. package/node_modules/@ursamu/mushcode/src/lint/rules/register_before_set.ts +64 -0
  118. package/node_modules/@ursamu/mushcode/src/print/lock_printer.js +43 -0
  119. package/node_modules/@ursamu/mushcode/src/print/lock_printer.js.map +1 -0
  120. package/node_modules/@ursamu/mushcode/src/print/lock_printer.ts +41 -0
  121. package/node_modules/@ursamu/mushcode/src/print/mod.js +17 -0
  122. package/node_modules/@ursamu/mushcode/src/print/mod.js.map +1 -0
  123. package/node_modules/@ursamu/mushcode/src/print/mod.ts +18 -0
  124. package/node_modules/@ursamu/mushcode/src/print/printer.js +91 -0
  125. package/node_modules/@ursamu/mushcode/src/print/printer.js.map +1 -0
  126. package/node_modules/@ursamu/mushcode/src/print/printer.ts +132 -0
  127. package/node_modules/@ursamu/mushcode/src/traverse/child_slots.js +129 -0
  128. package/node_modules/@ursamu/mushcode/src/traverse/child_slots.js.map +1 -0
  129. package/node_modules/@ursamu/mushcode/src/traverse/child_slots.ts +51 -0
  130. package/node_modules/@ursamu/mushcode/src/traverse/mod.js +17 -0
  131. package/node_modules/@ursamu/mushcode/src/traverse/mod.js.map +1 -0
  132. package/node_modules/@ursamu/mushcode/src/traverse/mod.ts +19 -0
  133. package/node_modules/@ursamu/mushcode/src/traverse/transform.js +70 -0
  134. package/node_modules/@ursamu/mushcode/src/traverse/transform.js.map +1 -0
  135. package/node_modules/@ursamu/mushcode/src/traverse/transform.ts +84 -0
  136. package/node_modules/@ursamu/mushcode/src/traverse/walk.js +55 -0
  137. package/node_modules/@ursamu/mushcode/src/traverse/walk.js.map +1 -0
  138. package/node_modules/@ursamu/mushcode/src/traverse/walk.ts +82 -0
  139. package/node_modules/@ursamu/mushcode/tests/01-literals.test.ts +105 -0
  140. package/node_modules/@ursamu/mushcode/tests/02-substitutions.test.ts +145 -0
  141. package/node_modules/@ursamu/mushcode/tests/03-function-calls.test.ts +184 -0
  142. package/node_modules/@ursamu/mushcode/tests/04-eval-blocks.test.ts +110 -0
  143. package/node_modules/@ursamu/mushcode/tests/05-braced-strings.test.ts +119 -0
  144. package/node_modules/@ursamu/mushcode/tests/06-commands.test.ts +222 -0
  145. package/node_modules/@ursamu/mushcode/tests/07-dollar-patterns.test.ts +156 -0
  146. package/node_modules/@ursamu/mushcode/tests/08-lock-expressions.test.ts +159 -0
  147. package/node_modules/@ursamu/mushcode/tests/09-edge-cases.test.ts +162 -0
  148. package/node_modules/@ursamu/mushcode/tests/10-regression.test.ts +211 -0
  149. package/node_modules/@ursamu/mushcode/tests/11-tags.test.ts +357 -0
  150. package/node_modules/@ursamu/mushcode/tests/12-locations.test.ts +162 -0
  151. package/node_modules/@ursamu/mushcode/tests/13-eval.test.ts +389 -0
  152. package/node_modules/@ursamu/mushcode/tests/analyze.test.ts +194 -0
  153. package/node_modules/@ursamu/mushcode/tests/helpers.ts +69 -0
  154. package/node_modules/@ursamu/mushcode/tests/lint.test.ts +232 -0
  155. package/node_modules/@ursamu/mushcode/tests/print.test.ts +204 -0
  156. package/node_modules/@ursamu/mushcode/tests/traverse.test.ts +211 -0
  157. package/package.json +4 -1
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Known arity ranges for TinyMUX / RhostMUSH built-in functions.
3
+ * [minArgs, maxArgs] — use Infinity for unlimited upper bound.
4
+ *
5
+ * Only strictly-bounded functions are listed. Omitting a function means the
6
+ * arg-count rule skips it entirely (no false positives for unknown functions).
7
+ */ export const ARITIES = {
8
+ // Arithmetic
9
+ add: [
10
+ 1,
11
+ Infinity
12
+ ],
13
+ sub: [
14
+ 1,
15
+ Infinity
16
+ ],
17
+ mul: [
18
+ 1,
19
+ Infinity
20
+ ],
21
+ div: [
22
+ 2,
23
+ 2
24
+ ],
25
+ mod: [
26
+ 2,
27
+ 2
28
+ ],
29
+ abs: [
30
+ 1,
31
+ 1
32
+ ],
33
+ // Comparison
34
+ eq: [
35
+ 2,
36
+ 2
37
+ ],
38
+ neq: [
39
+ 2,
40
+ 2
41
+ ],
42
+ gt: [
43
+ 2,
44
+ 2
45
+ ],
46
+ gte: [
47
+ 2,
48
+ 2
49
+ ],
50
+ lt: [
51
+ 2,
52
+ 2
53
+ ],
54
+ lte: [
55
+ 2,
56
+ 2
57
+ ],
58
+ // Logic
59
+ and: [
60
+ 1,
61
+ Infinity
62
+ ],
63
+ or: [
64
+ 1,
65
+ Infinity
66
+ ],
67
+ not: [
68
+ 1,
69
+ 1
70
+ ],
71
+ // Control flow
72
+ if: [
73
+ 2,
74
+ 3
75
+ ],
76
+ ifelse: [
77
+ 3,
78
+ 3
79
+ ],
80
+ switch: [
81
+ 3,
82
+ Infinity
83
+ ],
84
+ // Registers
85
+ setq: [
86
+ 2,
87
+ 2
88
+ ],
89
+ r: [
90
+ 1,
91
+ 1
92
+ ],
93
+ // Iteration
94
+ iter: [
95
+ 2,
96
+ 4
97
+ ],
98
+ map: [
99
+ 2,
100
+ 4
101
+ ],
102
+ filter: [
103
+ 2,
104
+ 3
105
+ ],
106
+ // Object / attribute
107
+ name: [
108
+ 1,
109
+ 1
110
+ ],
111
+ loc: [
112
+ 1,
113
+ 1
114
+ ],
115
+ get: [
116
+ 1,
117
+ 1
118
+ ],
119
+ set: [
120
+ 2,
121
+ 2
122
+ ],
123
+ v: [
124
+ 1,
125
+ 1
126
+ ],
127
+ u: [
128
+ 1,
129
+ Infinity
130
+ ],
131
+ hasflag: [
132
+ 2,
133
+ 2
134
+ ],
135
+ hasattr: [
136
+ 2,
137
+ 2
138
+ ],
139
+ // String
140
+ strlen: [
141
+ 1,
142
+ 1
143
+ ],
144
+ mid: [
145
+ 3,
146
+ 3
147
+ ],
148
+ left: [
149
+ 2,
150
+ 2
151
+ ],
152
+ right: [
153
+ 2,
154
+ 2
155
+ ],
156
+ trim: [
157
+ 1,
158
+ 3
159
+ ],
160
+ ljust: [
161
+ 2,
162
+ 3
163
+ ],
164
+ rjust: [
165
+ 2,
166
+ 3
167
+ ],
168
+ center: [
169
+ 2,
170
+ 3
171
+ ],
172
+ // List / matching
173
+ words: [
174
+ 1,
175
+ 2
176
+ ],
177
+ match: [
178
+ 2,
179
+ 3
180
+ ],
181
+ pmatch: [
182
+ 1,
183
+ 1
184
+ ],
185
+ lcon: [
186
+ 1,
187
+ 2
188
+ ],
189
+ lwho: [
190
+ 0,
191
+ 1
192
+ ],
193
+ // Formatting
194
+ ansi: [
195
+ 2,
196
+ Infinity
197
+ ],
198
+ // Tags (RhostMUSH)
199
+ tag: [
200
+ 1,
201
+ 1
202
+ ],
203
+ listtags: [
204
+ 0,
205
+ 1
206
+ ],
207
+ tagmatch: [
208
+ 1,
209
+ 1
210
+ ]
211
+ };
212
+ //# sourceMappingURL=builtin_arities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin_arities.js","sources":["./builtin_arities.ts"],"names":[],"mappings":"AAAA;;;;;;CAMC,GACD,OAAO,MAAM,UAAsD;EACjE,aAAa;EACb,KAAS;IAAC;IAAG;GAAS;EACtB,KAAS;IAAC;IAAG;GAAS;EACtB,KAAS;IAAC;IAAG;GAAS;EACtB,KAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,aAAa;EACb,IAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,IAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,IAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,QAAQ;EACR,KAAS;IAAC;IAAG;GAAS;EACtB,IAAS;IAAC;IAAG;GAAS;EACtB,KAAS;IAAC;IAAG;GAAE;EACf,eAAe;EACf,IAAS;IAAC;IAAG;GAAE;EACf,QAAS;IAAC;IAAG;GAAE;EACf,QAAS;IAAC;IAAG;GAAS;EACtB,YAAY;EACZ,MAAS;IAAC;IAAG;GAAE;EACf,GAAS;IAAC;IAAG;GAAE;EACf,YAAY;EACZ,MAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,QAAS;IAAC;IAAG;GAAE;EACf,qBAAqB;EACrB,MAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,GAAS;IAAC;IAAG;GAAE;EACf,GAAS;IAAC;IAAG;GAAS;EACtB,SAAS;IAAC;IAAG;GAAE;EACf,SAAS;IAAC;IAAG;GAAE;EACf,SAAS;EACT,QAAS;IAAC;IAAG;GAAE;EACf,KAAS;IAAC;IAAG;GAAE;EACf,MAAS;IAAC;IAAG;GAAE;EACf,OAAS;IAAC;IAAG;GAAE;EACf,MAAS;IAAC;IAAG;GAAE;EACf,OAAS;IAAC;IAAG;GAAE;EACf,OAAS;IAAC;IAAG;GAAE;EACf,QAAS;IAAC;IAAG;GAAE;EACf,kBAAkB;EAClB,OAAS;IAAC;IAAG;GAAE;EACf,OAAS;IAAC;IAAG;GAAE;EACf,QAAS;IAAC;IAAG;GAAE;EACf,MAAS;IAAC;IAAG;GAAE;EACf,MAAS;IAAC;IAAG;GAAE;EACf,aAAa;EACb,MAAS;IAAC;IAAG;GAAS;EACtB,mBAAmB;EACnB,KAAa;IAAC;IAAG;GAAE;EACnB,UAAa;IAAC;IAAG;GAAE;EACnB,UAAa;IAAC;IAAG;GAAE;AACrB,EAAE"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Known arity ranges for TinyMUX / RhostMUSH built-in functions.
3
+ * [minArgs, maxArgs] — use Infinity for unlimited upper bound.
4
+ *
5
+ * Only strictly-bounded functions are listed. Omitting a function means the
6
+ * arg-count rule skips it entirely (no false positives for unknown functions).
7
+ */
8
+ export const ARITIES: Readonly<Record<string, [number, number]>> = {
9
+ // Arithmetic
10
+ add: [1, Infinity],
11
+ sub: [1, Infinity],
12
+ mul: [1, Infinity],
13
+ div: [2, 2],
14
+ mod: [2, 2],
15
+ abs: [1, 1],
16
+ // Comparison
17
+ eq: [2, 2],
18
+ neq: [2, 2],
19
+ gt: [2, 2],
20
+ gte: [2, 2],
21
+ lt: [2, 2],
22
+ lte: [2, 2],
23
+ // Logic
24
+ and: [1, Infinity],
25
+ or: [1, Infinity],
26
+ not: [1, 1],
27
+ // Control flow
28
+ if: [2, 3],
29
+ ifelse: [3, 3],
30
+ switch: [3, Infinity],
31
+ // Registers
32
+ setq: [2, 2],
33
+ r: [1, 1],
34
+ // Iteration
35
+ iter: [2, 4],
36
+ map: [2, 4],
37
+ filter: [2, 3],
38
+ // Object / attribute
39
+ name: [1, 1],
40
+ loc: [1, 1],
41
+ get: [1, 1],
42
+ set: [2, 2],
43
+ v: [1, 1],
44
+ u: [1, Infinity],
45
+ hasflag: [2, 2],
46
+ hasattr: [2, 2],
47
+ // String
48
+ strlen: [1, 1],
49
+ mid: [3, 3],
50
+ left: [2, 2],
51
+ right: [2, 2],
52
+ trim: [1, 3],
53
+ ljust: [2, 3],
54
+ rjust: [2, 3],
55
+ center: [2, 3],
56
+ // List / matching
57
+ words: [1, 2],
58
+ match: [2, 3],
59
+ pmatch: [1, 1],
60
+ lcon: [1, 2],
61
+ lwho: [0, 1],
62
+ // Formatting
63
+ ansi: [2, Infinity],
64
+ // Tags (RhostMUSH)
65
+ tag: [1, 1],
66
+ listtags: [0, 1],
67
+ tagmatch: [1, 1],
68
+ };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @module
3
+ * Static analysis for softcode: `lint()` runs built-in rules and returns
4
+ * `Diagnostic` findings with severity, rule ID, and the offending AST node.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { parse } from "@ursamu/mushcode/parse";
9
+ * import { lint } from "@ursamu/mushcode/lint";
10
+ *
11
+ * const ast = parse("$finger:@pemit %#=[u(me/FN)]");
12
+ * const diags = lint(ast);
13
+ * diags.forEach(d => console.log(`[${d.severity}] ${d.rule}: ${d.message}`));
14
+ * ```
15
+ */ import { walk } from "../traverse/walk.js";
16
+ import { checkMissingWildcard } from "./rules/missing_wildcard.js";
17
+ import { checkIterVarOutsideIter } from "./rules/iter_var_outside_iter.js";
18
+ import { checkArgCount } from "./rules/arg_count.js";
19
+ import { checkRegisterBeforeSet } from "./rules/register_before_set.js";
20
+ // ── Available rules ───────────────────────────────────────────────────────────
21
+ /** All built-in rule IDs. Pass a subset to `LintOptions.rules` to enable only those rules. */ export const RULES = [
22
+ "missing-wildcard",
23
+ "iter-var-outside-iter",
24
+ "arg-count",
25
+ "register-before-set"
26
+ ];
27
+ // ── lint ──────────────────────────────────────────────────────────────────────
28
+ /**
29
+ * Run static analysis on an AST and return an array of diagnostics.
30
+ *
31
+ * Rules are applied at every DollarPattern/ListenPattern/FunctionCall/… node
32
+ * anywhere in the tree, so this works correctly on CommandList roots too.
33
+ *
34
+ * @example
35
+ * const diags = lint(parse("$+finger *:@pemit %#=[u(me/FN_FINGER,%0)]"));
36
+ * diags.forEach(d => console.log(`[${d.severity}] ${d.rule}: ${d.message}`));
37
+ */ export function lint(root, opts) {
38
+ const enabled = new Set(opts?.rules ?? RULES);
39
+ const diags = [];
40
+ // Rules that examine individual nodes as we walk
41
+ walk(root, {
42
+ enter (node) {
43
+ if (enabled.has("missing-wildcard")) {
44
+ diags.push(...checkMissingWildcard(node));
45
+ }
46
+ }
47
+ });
48
+ // Rules that need a full-tree view (run once on the root)
49
+ if (enabled.has("iter-var-outside-iter")) {
50
+ diags.push(...checkIterVarOutsideIter(root));
51
+ }
52
+ if (enabled.has("arg-count")) {
53
+ diags.push(...checkArgCount(root));
54
+ }
55
+ if (enabled.has("register-before-set")) {
56
+ diags.push(...checkRegisterBeforeSet(root));
57
+ }
58
+ return diags;
59
+ }
60
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sources":["./mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;CAcC,GAED,SAAS,IAAI,8BAAsD;AACnE,SAAS,oBAAoB,sCAA8C;AAC3E,SAAS,uBAAuB,2CAAgD;AAChF,SAAS,aAAa,+BAA8C;AACpE,SAAS,sBAAsB,yCAA+C;AAyB9E,iFAAiF;AAEjF,6FAA6F,GAC7F,OAAO,MAAM,QAAQ;EACnB;EACA;EACA;EACA;CACD,CAAU;AAKX,iFAAiF;AAEjF;;;;;;;;;CASC,GACD,OAAO,SAAS,KAAK,IAAa,EAAE,IAAkB;EACpD,MAAM,UAAU,IAAI,IAAI,MAAM,SAAS;EACvC,MAAM,QAAsB,EAAE;EAE9B,iDAAiD;EACjD,KAAK,MAAM;IACT,OAAM,IAAI;MACR,IAAI,QAAQ,GAAG,CAAC,qBAAqB;QACnC,MAAM,IAAI,IAAI,qBAAqB;MACrC;IACF;EACF;EAEA,0DAA0D;EAC1D,IAAI,QAAQ,GAAG,CAAC,0BAA0B;IACxC,MAAM,IAAI,IAAI,wBAAwB;EACxC;EACA,IAAI,QAAQ,GAAG,CAAC,cAAc;IAC5B,MAAM,IAAI,IAAI,cAAc;EAC9B;EACA,IAAI,QAAQ,GAAG,CAAC,wBAAwB;IACtC,MAAM,IAAI,IAAI,uBAAuB;EACvC;EAEA,OAAO;AACT"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @module
3
+ * Static analysis for softcode: `lint()` runs built-in rules and returns
4
+ * `Diagnostic` findings with severity, rule ID, and the offending AST node.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { parse } from "@ursamu/mushcode/parse";
9
+ * import { lint } from "@ursamu/mushcode/lint";
10
+ *
11
+ * const ast = parse("$finger:@pemit %#=[u(me/FN)]");
12
+ * const diags = lint(ast);
13
+ * diags.forEach(d => console.log(`[${d.severity}] ${d.rule}: ${d.message}`));
14
+ * ```
15
+ */
16
+ import type { ASTNode } from "../../parser/mod.ts";
17
+ import { walk } from "../traverse/walk.js";
18
+ import { checkMissingWildcard } from "./rules/missing_wildcard.js";
19
+ import { checkIterVarOutsideIter } from "./rules/iter_var_outside_iter.js";
20
+ import { checkArgCount } from "./rules/arg_count.js";
21
+ import { checkRegisterBeforeSet } from "./rules/register_before_set.js";
22
+
23
+ // ── Types ─────────────────────────────────────────────────────────────────────
24
+
25
+ /** Severity level of a lint diagnostic. */
26
+ export type Severity = "error" | "warning" | "info";
27
+
28
+ /** A single lint finding produced by a rule. */
29
+ export interface Diagnostic {
30
+ /** Identifier for the rule that produced this diagnostic. */
31
+ rule: string;
32
+ /** How serious the finding is. */
33
+ severity: Severity;
34
+ /** Human-readable description of the issue. */
35
+ message: string;
36
+ /** The AST node most closely associated with the issue. */
37
+ node: ASTNode;
38
+ }
39
+
40
+ /** Options passed to the {@link lint} function. */
41
+ export interface LintOptions {
42
+ /** Whitelist of rule IDs to run. Omit to run all rules. */
43
+ rules?: string[];
44
+ }
45
+
46
+ // ── Available rules ───────────────────────────────────────────────────────────
47
+
48
+ /** All built-in rule IDs. Pass a subset to `LintOptions.rules` to enable only those rules. */
49
+ export const RULES = [
50
+ "missing-wildcard",
51
+ "iter-var-outside-iter",
52
+ "arg-count",
53
+ "register-before-set",
54
+ ] as const;
55
+
56
+ /** Union type of every known rule ID string. */
57
+ export type RuleId = (typeof RULES)[number];
58
+
59
+ // ── lint ──────────────────────────────────────────────────────────────────────
60
+
61
+ /**
62
+ * Run static analysis on an AST and return an array of diagnostics.
63
+ *
64
+ * Rules are applied at every DollarPattern/ListenPattern/FunctionCall/… node
65
+ * anywhere in the tree, so this works correctly on CommandList roots too.
66
+ *
67
+ * @example
68
+ * const diags = lint(parse("$+finger *:@pemit %#=[u(me/FN_FINGER,%0)]"));
69
+ * diags.forEach(d => console.log(`[${d.severity}] ${d.rule}: ${d.message}`));
70
+ */
71
+ export function lint(root: ASTNode, opts?: LintOptions): Diagnostic[] {
72
+ const enabled = new Set(opts?.rules ?? RULES);
73
+ const diags: Diagnostic[] = [];
74
+
75
+ // Rules that examine individual nodes as we walk
76
+ walk(root, {
77
+ enter(node) {
78
+ if (enabled.has("missing-wildcard")) {
79
+ diags.push(...checkMissingWildcard(node));
80
+ }
81
+ },
82
+ });
83
+
84
+ // Rules that need a full-tree view (run once on the root)
85
+ if (enabled.has("iter-var-outside-iter")) {
86
+ diags.push(...checkIterVarOutsideIter(root));
87
+ }
88
+ if (enabled.has("arg-count")) {
89
+ diags.push(...checkArgCount(root));
90
+ }
91
+ if (enabled.has("register-before-set")) {
92
+ diags.push(...checkRegisterBeforeSet(root));
93
+ }
94
+
95
+ return diags;
96
+ }
@@ -0,0 +1,37 @@
1
+ import { findAll } from "../../traverse/walk.js";
2
+ import { ARITIES } from "../builtin_arities.js";
3
+ /**
4
+ * arg-count
5
+ *
6
+ * Reports FunctionCall nodes where the number of arguments falls outside the
7
+ * known [minArgs, maxArgs] range for that built-in function.
8
+ *
9
+ * Only functions listed in ARITIES are checked; user-defined functions and
10
+ * unrecognised names are silently skipped.
11
+ */ export function checkArgCount(root) {
12
+ const diagnostics = [];
13
+ for (const node of findAll(root, "FunctionCall")){
14
+ const name = node.name.toLowerCase();
15
+ const arity = ARITIES[name];
16
+ if (!arity) continue; // not a known built-in — skip
17
+ const [min, max] = arity;
18
+ const argc = node.args.length;
19
+ if (argc < min) {
20
+ diagnostics.push({
21
+ rule: "arg-count",
22
+ severity: "warning",
23
+ message: `${node.name}() requires at least ${min} argument(s), got ${argc}`,
24
+ node
25
+ });
26
+ } else if (argc > max) {
27
+ diagnostics.push({
28
+ rule: "arg-count",
29
+ severity: "warning",
30
+ message: `${node.name}() accepts at most ${max} argument(s), got ${argc}`,
31
+ node
32
+ });
33
+ }
34
+ }
35
+ return diagnostics;
36
+ }
37
+ //# sourceMappingURL=arg_count.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arg_count.js","sources":["./arg_count.ts"],"names":[],"mappings":"AAEA,SAAS,OAAO,iCAA0C;AAC1D,SAAS,OAAO,gCAAyC;AAEzD;;;;;;;;CAQC,GACD,OAAO,SAAS,cAAc,IAAa;EACzC,MAAM,cAA4B,EAAE;EAEpC,KAAK,MAAM,QAAQ,QAAQ,MAAM,gBAAiB;IAChD,MAAM,OAAQ,AAAC,KAAK,IAAI,CAAY,WAAW;IAC/C,MAAM,QAAQ,OAAO,CAAC,KAAK;IAC3B,IAAI,CAAC,OAAO,UAAU,8BAA8B;IAEpD,MAAM,CAAC,KAAK,IAAI,GAAG;IACnB,MAAM,OAAO,AAAC,KAAK,IAAI,CAAe,MAAM;IAE5C,IAAI,OAAO,KAAK;MACd,YAAY,IAAI,CAAC;QACf,MAAU;QACV,UAAU;QACV,SAAU,GAAG,KAAK,IAAI,CAAC,qBAAqB,EAAE,IAAI,kBAAkB,EAAE,MAAM;QAC5E;MACF;IACF,OAAO,IAAI,OAAO,KAAK;MACrB,YAAY,IAAI,CAAC;QACf,MAAU;QACV,UAAU;QACV,SAAU,GAAG,KAAK,IAAI,CAAC,mBAAmB,EAAE,IAAI,kBAAkB,EAAE,MAAM;QAC1E;MACF;IACF;EACF;EAEA,OAAO;AACT"}
@@ -0,0 +1,44 @@
1
+ import type { ASTNode } from "../../../parser/mod.ts";
2
+ import type { Diagnostic } from "../mod.ts";
3
+ import { findAll } from "../../traverse/walk.js";
4
+ import { ARITIES } from "../builtin_arities.js";
5
+
6
+ /**
7
+ * arg-count
8
+ *
9
+ * Reports FunctionCall nodes where the number of arguments falls outside the
10
+ * known [minArgs, maxArgs] range for that built-in function.
11
+ *
12
+ * Only functions listed in ARITIES are checked; user-defined functions and
13
+ * unrecognised names are silently skipped.
14
+ */
15
+ export function checkArgCount(root: ASTNode): Diagnostic[] {
16
+ const diagnostics: Diagnostic[] = [];
17
+
18
+ for (const node of findAll(root, "FunctionCall")) {
19
+ const name = (node.name as string).toLowerCase();
20
+ const arity = ARITIES[name];
21
+ if (!arity) continue; // not a known built-in — skip
22
+
23
+ const [min, max] = arity;
24
+ const argc = (node.args as ASTNode[]).length;
25
+
26
+ if (argc < min) {
27
+ diagnostics.push({
28
+ rule: "arg-count",
29
+ severity: "warning",
30
+ message: `${node.name}() requires at least ${min} argument(s), got ${argc}`,
31
+ node,
32
+ });
33
+ } else if (argc > max) {
34
+ diagnostics.push({
35
+ rule: "arg-count",
36
+ severity: "warning",
37
+ message: `${node.name}() accepts at most ${max} argument(s), got ${argc}`,
38
+ node,
39
+ });
40
+ }
41
+ }
42
+
43
+ return diagnostics;
44
+ }
@@ -0,0 +1,55 @@
1
+ import { walk } from "../../traverse/walk.js";
2
+ const ITER_VARS = new Set([
3
+ "##",
4
+ "#@",
5
+ "#$"
6
+ ]);
7
+ /**
8
+ * iter-var-outside-iter
9
+ *
10
+ * Reports when ##, #@, or #$ (iteration SpecialVars) appear outside an
11
+ * iter() function call or @dolist command, where they have no defined value.
12
+ *
13
+ * Valid contexts:
14
+ * iter(list, body) — ## is the current element in `body`
15
+ * @dolist list={body} — ## is the current element in `body`
16
+ *
17
+ * The rule is conservative: any nesting under an iter/dolist suppresses the
18
+ * warning for the entire subtree, avoiding false positives on patterns like
19
+ * iter(iter(inner, ##), ##).
20
+ */ export function checkIterVarOutsideIter(root) {
21
+ const diagnostics = [];
22
+ let iterDepth = 0;
23
+ walk(root, {
24
+ enter (node) {
25
+ if (isIterContext(node)) {
26
+ iterDepth++;
27
+ return; // continue into children
28
+ }
29
+ if (node.type === "SpecialVar" && ITER_VARS.has(node.code)) {
30
+ if (iterDepth === 0) {
31
+ diagnostics.push({
32
+ rule: "iter-var-outside-iter",
33
+ severity: "warning",
34
+ message: `"${node.code}" is only meaningful inside iter() or @dolist`,
35
+ node
36
+ });
37
+ }
38
+ }
39
+ },
40
+ leave (node) {
41
+ if (isIterContext(node)) iterDepth--;
42
+ }
43
+ });
44
+ return diagnostics;
45
+ }
46
+ function isIterContext(node) {
47
+ if (node.type === "FunctionCall") {
48
+ return node.name.toLowerCase() === "iter" || node.name.toLowerCase() === "ilist" || node.name.toLowerCase() === "map";
49
+ }
50
+ if (node.type === "AtCommand") {
51
+ return node.name.toLowerCase() === "dolist";
52
+ }
53
+ return false;
54
+ }
55
+ //# sourceMappingURL=iter_var_outside_iter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iter_var_outside_iter.js","sources":["./iter_var_outside_iter.ts"],"names":[],"mappings":"AAEA,SAAS,IAAI,iCAA6C;AAE1D,MAAM,YAAY,IAAI,IAAI;EAAC;EAAM;EAAM;CAAK;AAE5C;;;;;;;;;;;;;CAaC,GACD,OAAO,SAAS,wBAAwB,IAAa;EACnD,MAAM,cAA4B,EAAE;EACpC,IAAI,YAAY;EAEhB,KAAK,MAAM;IACT,OAAM,IAAI;MACR,IAAI,cAAc,OAAO;QACvB;QACA,QAAQ,yBAAyB;MACnC;MACA,IAAI,KAAK,IAAI,KAAK,gBAAgB,UAAU,GAAG,CAAC,KAAK,IAAI,GAAa;QACpE,IAAI,cAAc,GAAG;UACnB,YAAY,IAAI,CAAC;YACf,MAAU;YACV,UAAU;YACV,SAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,6CAA6C,CAAC;YACtE;UACF;QACF;MACF;IACF;IACA,OAAM,IAAI;MACR,IAAI,cAAc,OAAO;IAC3B;EACF;EAEA,OAAO;AACT;AAEA,SAAS,cAAc,IAAa;EAClC,IAAI,KAAK,IAAI,KAAK,gBAAgB;IAChC,OAAO,AAAC,KAAK,IAAI,CAAY,WAAW,OAAO,UACxC,AAAC,KAAK,IAAI,CAAY,WAAW,OAAO,WACxC,AAAC,KAAK,IAAI,CAAY,WAAW,OAAO;EACjD;EACA,IAAI,KAAK,IAAI,KAAK,aAAa;IAC7B,OAAO,AAAC,KAAK,IAAI,CAAY,WAAW,OAAO;EACjD;EACA,OAAO;AACT"}
@@ -0,0 +1,60 @@
1
+ import type { ASTNode } from "../../../parser/mod.ts";
2
+ import type { Diagnostic } from "../mod.ts";
3
+ import { walk } from "../../traverse/walk.js";
4
+
5
+ const ITER_VARS = new Set(["##", "#@", "#$"]);
6
+
7
+ /**
8
+ * iter-var-outside-iter
9
+ *
10
+ * Reports when ##, #@, or #$ (iteration SpecialVars) appear outside an
11
+ * iter() function call or @dolist command, where they have no defined value.
12
+ *
13
+ * Valid contexts:
14
+ * iter(list, body) — ## is the current element in `body`
15
+ * @dolist list={body} — ## is the current element in `body`
16
+ *
17
+ * The rule is conservative: any nesting under an iter/dolist suppresses the
18
+ * warning for the entire subtree, avoiding false positives on patterns like
19
+ * iter(iter(inner, ##), ##).
20
+ */
21
+ export function checkIterVarOutsideIter(root: ASTNode): Diagnostic[] {
22
+ const diagnostics: Diagnostic[] = [];
23
+ let iterDepth = 0;
24
+
25
+ walk(root, {
26
+ enter(node) {
27
+ if (isIterContext(node)) {
28
+ iterDepth++;
29
+ return; // continue into children
30
+ }
31
+ if (node.type === "SpecialVar" && ITER_VARS.has(node.code as string)) {
32
+ if (iterDepth === 0) {
33
+ diagnostics.push({
34
+ rule: "iter-var-outside-iter",
35
+ severity: "warning",
36
+ message: `"${node.code}" is only meaningful inside iter() or @dolist`,
37
+ node,
38
+ });
39
+ }
40
+ }
41
+ },
42
+ leave(node) {
43
+ if (isIterContext(node)) iterDepth--;
44
+ },
45
+ });
46
+
47
+ return diagnostics;
48
+ }
49
+
50
+ function isIterContext(node: ASTNode): boolean {
51
+ if (node.type === "FunctionCall") {
52
+ return (node.name as string).toLowerCase() === "iter" ||
53
+ (node.name as string).toLowerCase() === "ilist" ||
54
+ (node.name as string).toLowerCase() === "map";
55
+ }
56
+ if (node.type === "AtCommand") {
57
+ return (node.name as string).toLowerCase() === "dolist";
58
+ }
59
+ return false;
60
+ }
@@ -0,0 +1,31 @@
1
+ import { findAll } from "../../traverse/walk.js";
2
+ /**
3
+ * missing-wildcard
4
+ *
5
+ * Reports when a DollarPattern or ListenPattern action uses a positional
6
+ * substitution %N (N ≥ 1) but the pattern contains fewer than N wildcards.
7
+ *
8
+ * In TinyMUX:
9
+ * %0 = the full matched input (always available)
10
+ * %1 = first wildcard match, %2 = second, … %9 = ninth
11
+ *
12
+ * So if the action uses %2, the pattern must have at least 2 wildcards.
13
+ */ export function checkMissingWildcard(node) {
14
+ if (node.type !== "DollarPattern" && node.type !== "ListenPattern") return [];
15
+ const pattern = node.pattern;
16
+ const action = node.action;
17
+ const wildcardCount = findAll(pattern, "Wildcard").length;
18
+ const maxPositional = findAll(action, "Substitution").map((s)=>s.code).filter((code)=>/^[1-9]$/.test(code)).reduce((max, code)=>Math.max(max, parseInt(code)), 0);
19
+ if (maxPositional > wildcardCount) {
20
+ return [
21
+ {
22
+ rule: "missing-wildcard",
23
+ severity: "warning",
24
+ message: `Pattern has ${wildcardCount} wildcard(s) but action uses %${maxPositional} ` + `— needs at least ${maxPositional} wildcard(s) in pattern`,
25
+ node: pattern
26
+ }
27
+ ];
28
+ }
29
+ return [];
30
+ }
31
+ //# sourceMappingURL=missing_wildcard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing_wildcard.js","sources":["./missing_wildcard.ts"],"names":[],"mappings":"AAEA,SAAS,OAAO,iCAA0C;AAE1D;;;;;;;;;;;CAWC,GACD,OAAO,SAAS,qBAAqB,IAAa;EAChD,IAAI,KAAK,IAAI,KAAK,mBAAmB,KAAK,IAAI,KAAK,iBAAiB,OAAO,EAAE;EAE7E,MAAM,UAAU,KAAK,OAAO;EAC5B,MAAM,SAAU,KAAK,MAAM;EAE3B,MAAM,gBAAgB,QAAQ,SAAS,YAAY,MAAM;EAEzD,MAAM,gBAAgB,QAAQ,QAAQ,gBACnC,GAAG,CAAC,CAAA,IAAK,EAAE,IAAI,EACf,MAAM,CAAC,CAAA,OAAQ,UAAU,IAAI,CAAC,OAC9B,MAAM,CAAC,CAAC,KAAK,OAAS,KAAK,GAAG,CAAC,KAAK,SAAS,QAAQ;EAExD,IAAI,gBAAgB,eAAe;IACjC,OAAO;MAAC;QACN,MAAU;QACV,UAAU;QACV,SAAU,CAAC,YAAY,EAAE,cAAc,8BAA8B,EAAE,cAAc,CAAC,CAAC,GAC7E,CAAC,iBAAiB,EAAE,cAAc,uBAAuB,CAAC;QACpE,MAAM;MACR;KAAE;EACJ;EACA,OAAO,EAAE;AACX"}