@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,418 @@
1
+ # Stdlib
2
+
3
+ All functions listed here are registered by `registerStdlib(engine)`.
4
+
5
+ ```typescript
6
+ import { EvalEngine, registerStdlib, makeContext } from "jsr:@ursamu/mushcode/eval";
7
+
8
+ const engine = new EvalEngine(accessor);
9
+ registerStdlib(engine);
10
+ ```
11
+
12
+ Errors return `#-1 MESSAGE` strings, matching MUX server conventions.
13
+ Integer inputs and outputs use integer arithmetic; mixed or float inputs return
14
+ up to 6 significant digits.
15
+
16
+ ---
17
+
18
+ ## Math
19
+
20
+ ### `add(n, n, …)`
21
+
22
+ Sum of two or more numbers.
23
+
24
+ ```typescript
25
+ // [add(1,2,3)] → "6"
26
+ // [add(1.5,2.5)] → "4"
27
+ ```
28
+
29
+ ### `sub(a, b)`
30
+
31
+ Subtraction.
32
+
33
+ ```typescript
34
+ // [sub(10,3)] → "7"
35
+ ```
36
+
37
+ ### `mul(n, n, …)`
38
+
39
+ Product of two or more numbers.
40
+
41
+ ```typescript
42
+ // [mul(2,3,4)] → "24"
43
+ ```
44
+
45
+ ### `div(a, b)`
46
+
47
+ Division. Integer operands use integer (truncating) division. Returns `#-1
48
+ DIVIDE BY ZERO` if `b` is 0.
49
+
50
+ ```typescript
51
+ // [div(10,3)] → "3" (integer truncation)
52
+ // [div(10.0,3)] → "3.33333"
53
+ ```
54
+
55
+ ### `mod(a, b)`
56
+
57
+ Remainder. Returns `#-1 DIVIDE BY ZERO` if `b` is 0.
58
+
59
+ ```typescript
60
+ // [mod(10,3)] → "1"
61
+ ```
62
+
63
+ ### `abs(n)`
64
+
65
+ Absolute value.
66
+
67
+ ```typescript
68
+ // [abs(-5)] → "5"
69
+ ```
70
+
71
+ ### `round(n, places)`
72
+
73
+ Round `n` to `places` decimal places.
74
+
75
+ ```typescript
76
+ // [round(3.14159,2)] → "3.14"
77
+ // [round(3.5,0)] → "4"
78
+ ```
79
+
80
+ ### `floor(n)`
81
+
82
+ Floor (round down to nearest integer).
83
+
84
+ ```typescript
85
+ // [floor(3.9)] → "3"
86
+ // [floor(-1.1)] → "-2"
87
+ ```
88
+
89
+ ### `ceil(n)`
90
+
91
+ Ceiling (round up to nearest integer).
92
+
93
+ ```typescript
94
+ // [ceil(3.1)] → "4"
95
+ ```
96
+
97
+ ### `max(n, n, …)` / `min(n, n, …)`
98
+
99
+ Maximum or minimum of two or more values.
100
+
101
+ ```typescript
102
+ // [max(1,5,3)] → "5"
103
+ // [min(1,5,3)] → "1"
104
+ ```
105
+
106
+ ### `power(base, exp)`
107
+
108
+ Exponentiation.
109
+
110
+ ```typescript
111
+ // [power(2,8)] → "256"
112
+ ```
113
+
114
+ ### `sqrt(n)`
115
+
116
+ Square root. Returns `#-1 ARGUMENT OUT OF RANGE` for negative input.
117
+
118
+ ```typescript
119
+ // [sqrt(9)] → "3"
120
+ // [sqrt(2)] → "1.41421"
121
+ ```
122
+
123
+ ---
124
+
125
+ ## String
126
+
127
+ ### `strlen(str)`
128
+
129
+ Length of a string.
130
+
131
+ ```typescript
132
+ // [strlen(Hello)] → "5"
133
+ ```
134
+
135
+ ### `mid(str, start, len)`
136
+
137
+ Substring. `start` is 0-based.
138
+
139
+ ```typescript
140
+ // [mid(Hello,1,3)] → "ell"
141
+ ```
142
+
143
+ ### `left(str, n)` / `right(str, n)`
144
+
145
+ First or last `n` characters.
146
+
147
+ ```typescript
148
+ // [left(Hello,3)] → "Hel"
149
+ // [right(Hello,3)] → "llo"
150
+ ```
151
+
152
+ ### `trim(str)`
153
+
154
+ Strip leading and trailing whitespace.
155
+
156
+ ```typescript
157
+ // [trim( hi )] → "hi"
158
+ ```
159
+
160
+ ### `ljust(str, width[, fill])` / `rjust(str, width[, fill])` / `center(str, width[, fill])`
161
+
162
+ Pad a string to `width` characters. Default fill character is space.
163
+
164
+ ```typescript
165
+ // [ljust(hi,6)] → "hi "
166
+ // [rjust(hi,6)] → " hi"
167
+ // [center(hi,6)] → " hi "
168
+ // [ljust(hi,6,-)] → "hi----"
169
+ ```
170
+
171
+ ### `ucstr(str)` / `lcstr(str)` / `capstr(str)`
172
+
173
+ Uppercase, lowercase, or capitalise first letter.
174
+
175
+ ```typescript
176
+ // [ucstr(hello)] → "HELLO"
177
+ // [lcstr(HELLO)] → "hello"
178
+ // [capstr(hello world)] → "Hello world"
179
+ ```
180
+
181
+ ### `cat(str, …)`
182
+
183
+ Concatenate arguments with a single space between each.
184
+
185
+ ```typescript
186
+ // [cat(hello,world)] → "hello world"
187
+ ```
188
+
189
+ ### `space(n)`
190
+
191
+ A string of `n` spaces.
192
+
193
+ ```typescript
194
+ // [space(3)] → " "
195
+ ```
196
+
197
+ ### `repeat(str, n)`
198
+
199
+ Repeat `str` exactly `n` times.
200
+
201
+ ```typescript
202
+ // [repeat(-,5)] → "-----"
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Compare
208
+
209
+ All compare functions return `"1"` (true) or `"0"` (false).
210
+
211
+ ### `eq(a, b)` / `neq(a, b)`
212
+
213
+ Numeric equality / inequality.
214
+
215
+ ```typescript
216
+ // [eq(3,3)] → "1"
217
+ // [neq(3,4)] → "1"
218
+ ```
219
+
220
+ ### `gt(a, b)` / `gte(a, b)` / `lt(a, b)` / `lte(a, b)`
221
+
222
+ Numeric greater-than, greater-or-equal, less-than, less-or-equal.
223
+
224
+ ```typescript
225
+ // [gt(5,3)] → "1"
226
+ // [lte(3,3)] → "1"
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Logic
232
+
233
+ `if`, `ifelse`, `switch`, `and`, and `or` are **lazy** — they only evaluate the
234
+ branches they need.
235
+
236
+ ### `if(cond, then)`
237
+
238
+ If `cond` is truthy (non-zero, non-empty), return `then`. Otherwise return `""`.
239
+
240
+ ```typescript
241
+ // [if(1,yes)] → "yes"
242
+ // [if(0,yes)] → ""
243
+ ```
244
+
245
+ ### `ifelse(cond, then, else)`
246
+
247
+ If `cond` is truthy, return `then`; otherwise return `else`.
248
+
249
+ ```typescript
250
+ // [ifelse([gt(%0,10)],big,small)] → "big" or "small"
251
+ ```
252
+
253
+ ### `switch(value, match1, result1, match2, result2, …[, default])`
254
+
255
+ Exact-match switch. Compares `value` against each `match` string in order.
256
+ Returns the corresponding `result` for the first match, or `default` (or `""`)
257
+ if nothing matches.
258
+
259
+ ```typescript
260
+ // [switch(%0,1,one,2,two,other)] → "one" if %0 is "1"
261
+ ```
262
+
263
+ ### `and(a, b, …)` / `or(a, b, …)`
264
+
265
+ Short-circuit logical AND / OR. Return `"1"` or `"0"`.
266
+
267
+ ```typescript
268
+ // [and(1,1,0)] → "0"
269
+ // [or(0,0,1)] → "1"
270
+ ```
271
+
272
+ ### `not(a)`
273
+
274
+ Logical NOT.
275
+
276
+ ```typescript
277
+ // [not(0)] → "1"
278
+ // [not(1)] → "0"
279
+ ```
280
+
281
+ ### `t(a)`
282
+
283
+ Truthiness test — returns `"1"` if `a` is non-empty and non-`"0"`, else `"0"`.
284
+
285
+ ```typescript
286
+ // [t(hello)] → "1"
287
+ // [t(0)] → "0"
288
+ // [t()] → "0"
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Registers
294
+
295
+ ### `setq(reg, val)` / `setr(reg, val)`
296
+
297
+ Store `val` in register `reg`. Both forms are equivalent. Returns `""` and
298
+ mutates `ctx.registers`.
299
+
300
+ ```typescript
301
+ // [setq(0,hello)][r(0)] → "hello"
302
+ ```
303
+
304
+ ### `r(reg)`
305
+
306
+ Read register `reg`. Returns `""` if not set.
307
+
308
+ ```typescript
309
+ // [setq(name,Alice)][r(name)] → "Alice"
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Iter / List
315
+
316
+ ### `iter(list, body[, idelim[, odelim]])`
317
+
318
+ Evaluate `body` for each item in `list`. Inside `body`, `##` holds the current
319
+ item and `#@` holds its 1-based position. Default delimiter is space (which
320
+ also collapses consecutive whitespace, MUX-style).
321
+
322
+ `iter` is **lazy** — `body` is evaluated once per item.
323
+
324
+ ```typescript
325
+ // [iter(a b c,## is #@)] → "a is 1 b is 2 c is 3"
326
+ // [iter(a|b|c,##,|,;)] → "a;b;c"
327
+ ```
328
+
329
+ ### `words(str[, delim])`
330
+
331
+ Count of items in a space- (or delim-) separated list.
332
+
333
+ ```typescript
334
+ // [words(a b c)] → "3"
335
+ // [words(a|b,|)] → "2"
336
+ ```
337
+
338
+ ### `word(str, n[, delim])`
339
+
340
+ The nth word (1-based).
341
+
342
+ ```typescript
343
+ // [word(a b c,2)] → "b"
344
+ ```
345
+
346
+ ### `first(str[, delim])` / `last(str[, delim])`
347
+
348
+ First or last word.
349
+
350
+ ```typescript
351
+ // [first(a b c)] → "a"
352
+ // [last(a b c)] → "c"
353
+ ```
354
+
355
+ ### `rest(str[, delim])`
356
+
357
+ All words after the first.
358
+
359
+ ```typescript
360
+ // [rest(a b c)] → "b c"
361
+ ```
362
+
363
+ ---
364
+
365
+ ## DB
366
+
367
+ These functions call through to `ObjectAccessor` methods.
368
+
369
+ ### `get(obj/attr)`
370
+
371
+ Read the named attribute from `obj`. Returns `""` if the attribute is unset.
372
+ Returns `#-1 NO MATCH` if `obj` cannot be resolved, or `#-1 BAD ARGUMENT
373
+ FORMAT` if the argument has no `/`.
374
+
375
+ ```typescript
376
+ // [get(me/DESC)] — reads DESC from the enactor
377
+ // [get(#room/NAME)] — reads NAME from a TagRef-resolved object
378
+ ```
379
+
380
+ ### `name(obj)`
381
+
382
+ Display name of `obj`.
383
+
384
+ ```typescript
385
+ // [name(me)] → "Alice"
386
+ ```
387
+
388
+ ### `hasattr(obj, attr)`
389
+
390
+ `"1"` if the attribute exists on `obj`, `"0"` otherwise.
391
+
392
+ ```typescript
393
+ // [hasattr(me,DESC)] → "1" or "0"
394
+ ```
395
+
396
+ ### `hasflag(obj, flag)`
397
+
398
+ `"1"` if `obj` has `flag`, `"0"` otherwise.
399
+
400
+ ```typescript
401
+ // [hasflag(me,WIZARD)] → "1" or "0"
402
+ ```
403
+
404
+ ### `u(obj/attr[, arg0, arg1, …])`
405
+
406
+ Evaluate the named attribute as a function call. Creates a child `EvalContext`
407
+ where:
408
+ - `%0`–`%9` are the extra arguments passed to `u()`
409
+ - `%!` is the object that owns the attribute (new executor)
410
+ - `%@` is the previous executor (caller)
411
+ - `%#` is unchanged (original enactor)
412
+ - `depth` is incremented; returns `#-1 EVALUATION DEPTH EXCEEDED` at `maxDepth`
413
+
414
+ ```typescript
415
+ // [u(me/FN_DOUBLE,21)] → evaluates the FN_DOUBLE attr with %0="21"
416
+ ```
417
+
418
+ If `obj/` is omitted (bare attribute name), the executor is used as the target.
@@ -0,0 +1,167 @@
1
+ # Traverse
2
+
3
+ ```typescript
4
+ import { walk, transform, findAll, findFirst, findFirstOrNull } from "jsr:@ursamu/mushcode/traverse";
5
+ import type { Visitor, Transformer } from "jsr:@ursamu/mushcode/traverse";
6
+ ```
7
+
8
+ ## `walk(root, visitor)`
9
+
10
+ Depth-first walk over an AST. The visitor's `enter` hook is called before a
11
+ node's children are visited; `leave` is called after.
12
+
13
+ ```typescript
14
+ function walk(root: ASTNode, visitor: Visitor): void
15
+ ```
16
+
17
+ ```typescript
18
+ interface Visitor {
19
+ enter?: (node: ASTNode) => false | void;
20
+ leave?: (node: ASTNode) => void;
21
+ }
22
+ ```
23
+
24
+ Returning `false` from `enter` skips the node's children and suppresses the
25
+ `leave` call for that node.
26
+
27
+ Nodes are visited in place — you may mutate `node` fields inside a visitor, but
28
+ prefer `transform` when you need a new tree.
29
+
30
+ ### Example: collect all function names
31
+
32
+ ```typescript
33
+ import { parse } from "jsr:@ursamu/mushcode/parse";
34
+ import { walk } from "jsr:@ursamu/mushcode/traverse";
35
+
36
+ const ast = parse("[add(1,[mul(2,3)])]");
37
+ const names: string[] = [];
38
+
39
+ walk(ast, {
40
+ enter(node) {
41
+ if (node.type === "FunctionCall") names.push(node.name as string);
42
+ },
43
+ });
44
+
45
+ console.log(names); // ["add", "mul"]
46
+ ```
47
+
48
+ ### Example: skip subtrees
49
+
50
+ ```typescript
51
+ walk(ast, {
52
+ enter(node) {
53
+ // Do not descend into braced strings
54
+ if (node.type === "BracedString") return false;
55
+ if (node.type === "Literal") process(node);
56
+ },
57
+ });
58
+ ```
59
+
60
+ ## `transform(root, fn)`
61
+
62
+ Produce a new tree by applying `fn` to every node top-down. The original tree
63
+ is never mutated.
64
+
65
+ ```typescript
66
+ function transform(root: ASTNode, fn: Transformer): ASTNode
67
+
68
+ type Transformer = (node: ASTNode) => ASTNode | null | undefined;
69
+ ```
70
+
71
+ Return values from the transformer:
72
+
73
+ | Return value | Effect |
74
+ |--------------|-------------------------------------------------------------------------|
75
+ | `undefined` | Keep the node as-is and recurse into its children |
76
+ | `ASTNode` | Replace the node with the returned value, then recurse into its children |
77
+ | `null` | Remove the node (from an array slot) or set the field to `null` |
78
+
79
+ ### Example: replace TagRefs with Literal placeholders
80
+
81
+ ```typescript
82
+ import { parse } from "jsr:@ursamu/mushcode/parse";
83
+ import { transform } from "jsr:@ursamu/mushcode/traverse";
84
+
85
+ const ast = parse("@pemit %#=[get(#room/DESC)]");
86
+
87
+ const out = transform(ast, (node) => {
88
+ if (node.type === "TagRef") {
89
+ return { type: "Literal", value: `<tag:${node.name}>` };
90
+ }
91
+ });
92
+
93
+ // out contains a new tree with TagRef replaced by Literal
94
+ ```
95
+
96
+ ### Example: strip all EvalBlocks (flatten to their content)
97
+
98
+ ```typescript
99
+ const stripped = transform(ast, (node) => {
100
+ if (node.type === "EvalBlock") {
101
+ // Replace EvalBlock with a Literal that holds its printed content
102
+ // (or return null to remove it entirely)
103
+ return null;
104
+ }
105
+ });
106
+ ```
107
+
108
+ ## `findAll(root, type)`
109
+
110
+ Collect every node of the given type anywhere in the tree.
111
+
112
+ ```typescript
113
+ function findAll(root: ASTNode, type: string): ASTNode[]
114
+ ```
115
+
116
+ ```typescript
117
+ import { parse } from "jsr:@ursamu/mushcode/parse";
118
+ import { findAll } from "jsr:@ursamu/mushcode/traverse";
119
+
120
+ const ast = parse("$+finger *:@pemit %#=[u(me/FN,%0,#room)]");
121
+ const refs = findAll(ast, "TagRef");
122
+ console.log(refs.map(n => n.name)); // ["room"]
123
+
124
+ const subs = findAll(ast, "Substitution");
125
+ console.log(subs.map(n => n.code)); // ["#", "0"]
126
+ ```
127
+
128
+ Returns an empty array when no nodes of that type exist.
129
+
130
+ ## `findFirst(root, type)`
131
+
132
+ Return the first node of the given type. Throws `Error` if none is found.
133
+
134
+ ```typescript
135
+ function findFirst(root: ASTNode, type: string): ASTNode
136
+ ```
137
+
138
+ ```typescript
139
+ import { parse } from "jsr:@ursamu/mushcode/parse";
140
+ import { findFirst } from "jsr:@ursamu/mushcode/traverse";
141
+
142
+ const ast = parse("$do *:@pemit %#=[u(me/FN,%0)]");
143
+ const call = findFirst(ast, "FunctionCall");
144
+ console.log(call.name); // "u"
145
+ ```
146
+
147
+ ## `findFirstOrNull(root, type)`
148
+
149
+ Return the first node of the given type, or `null` if none exists. Prefer this
150
+ over `findFirst` when the node might not be present.
151
+
152
+ ```typescript
153
+ function findFirstOrNull(root: ASTNode, type: string): ASTNode | null
154
+ ```
155
+
156
+ ```typescript
157
+ import { parse } from "jsr:@ursamu/mushcode/parse";
158
+ import { findFirstOrNull } from "jsr:@ursamu/mushcode/traverse";
159
+
160
+ const ast = parse("@pemit %#=Hello");
161
+ const call = findFirstOrNull(ast, "FunctionCall");
162
+ if (call) {
163
+ console.log(call.name);
164
+ } else {
165
+ console.log("no function call found");
166
+ }
167
+ ```