redscript-mc 3.0.1 → 3.0.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 (225) hide show
  1. package/.github/workflows/ci.yml +1 -0
  2. package/README.md +119 -313
  3. package/README.zh.md +118 -314
  4. package/ROADMAP.md +5 -5
  5. package/dist/data/impl_test/function/counter/get.mcfunction +5 -0
  6. package/dist/data/impl_test/function/counter/inc.mcfunction +7 -0
  7. package/dist/data/impl_test/function/counter/new.mcfunction +4 -0
  8. package/dist/data/impl_test/function/load.mcfunction +1 -0
  9. package/dist/data/impl_test/function/test_impl.mcfunction +10 -0
  10. package/dist/data/minecraft/tags/function/load.json +5 -0
  11. package/dist/data/playground/function/load.mcfunction +1 -0
  12. package/dist/data/playground/function/start.mcfunction +4 -0
  13. package/dist/data/playground/function/start__say_macro_t1.mcfunction +1 -0
  14. package/dist/data/playground/function/stop.mcfunction +5 -0
  15. package/dist/data/playground/function/stop__say_macro_t0.mcfunction +1 -0
  16. package/dist/data/stdlib_queue8_test/function/__queue_append_apply.mcfunction +4 -0
  17. package/dist/data/stdlib_queue8_test/function/__queue_peek_apply.mcfunction +4 -0
  18. package/dist/data/stdlib_queue8_test/function/__queue_size_raw_apply.mcfunction +4 -0
  19. package/dist/data/stdlib_queue8_test/function/load.mcfunction +1 -0
  20. package/dist/data/stdlib_queue8_test/function/queue_clear.mcfunction +6 -0
  21. package/dist/data/stdlib_queue8_test/function/queue_empty__merge_1.mcfunction +5 -0
  22. package/dist/data/stdlib_queue8_test/function/queue_empty__then_0.mcfunction +5 -0
  23. package/dist/data/stdlib_queue8_test/function/queue_peek__merge_1.mcfunction +13 -0
  24. package/dist/data/stdlib_queue8_test/function/queue_peek__then_0.mcfunction +5 -0
  25. package/dist/data/stdlib_queue8_test/function/queue_pop__merge_1.mcfunction +15 -0
  26. package/dist/data/stdlib_queue8_test/function/queue_pop__then_0.mcfunction +5 -0
  27. package/dist/data/stdlib_queue8_test/function/queue_push__const_11.mcfunction +6 -0
  28. package/dist/data/stdlib_queue8_test/function/queue_push__const_22.mcfunction +6 -0
  29. package/dist/data/stdlib_queue8_test/function/queue_size.mcfunction +13 -0
  30. package/dist/data/stdlib_queue8_test/function/test_queue_push_and_size.mcfunction +13 -0
  31. package/dist/data/test/function/load.mcfunction +1 -0
  32. package/dist/data/test/function/say_at.mcfunction +6 -0
  33. package/dist/data/test/function/test.mcfunction +4 -0
  34. package/dist/pack.mcmeta +6 -0
  35. package/dist/package.json +1 -1
  36. package/dist/src/__tests__/formatter-extra.test.d.ts +7 -0
  37. package/dist/src/__tests__/formatter-extra.test.js +123 -0
  38. package/dist/src/__tests__/global-vars.test.d.ts +13 -0
  39. package/dist/src/__tests__/global-vars.test.js +156 -0
  40. package/dist/src/__tests__/lint/new-rules.test.d.ts +9 -0
  41. package/dist/src/__tests__/lint/new-rules.test.js +402 -0
  42. package/dist/src/__tests__/lsp-rename.test.d.ts +8 -0
  43. package/dist/src/__tests__/lsp-rename.test.js +157 -0
  44. package/dist/src/__tests__/mc-integration/say-fstring.test.d.ts +11 -0
  45. package/dist/src/__tests__/mc-integration/say-fstring.test.js +220 -0
  46. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +1 -1
  47. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1 -1
  48. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1 -1
  49. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1 -1
  50. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +1 -1
  51. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +1 -1
  52. package/dist/src/__tests__/mc-integration/stdlib-coverage-8.test.js +1 -1
  53. package/dist/src/__tests__/mc-syntax.test.js +4 -1
  54. package/dist/src/__tests__/monomorphize-coverage.test.d.ts +9 -0
  55. package/dist/src/__tests__/monomorphize-coverage.test.js +204 -0
  56. package/dist/src/__tests__/optimizer-cse.test.d.ts +7 -0
  57. package/dist/src/__tests__/optimizer-cse.test.js +226 -0
  58. package/dist/src/__tests__/parser.test.js +4 -13
  59. package/dist/src/__tests__/repl-server-extra.test.js +6 -7
  60. package/dist/src/__tests__/repl-server.test.js +5 -7
  61. package/dist/src/__tests__/stdlib/queue.test.js +6 -6
  62. package/dist/src/cli.js +0 -0
  63. package/dist/src/lexer/index.js +2 -1
  64. package/dist/src/lint/index.d.ts +12 -5
  65. package/dist/src/lint/index.js +730 -5
  66. package/dist/src/lsp/main.js +0 -0
  67. package/dist/src/mc-test/client.d.ts +21 -0
  68. package/dist/src/mc-test/client.js +34 -0
  69. package/dist/src/mir/lower.js +108 -6
  70. package/dist/src/optimizer/interprocedural.js +37 -2
  71. package/dist/src/parser/decl-parser.d.ts +19 -0
  72. package/dist/src/parser/decl-parser.js +323 -0
  73. package/dist/src/parser/expr-parser.d.ts +46 -0
  74. package/dist/src/parser/expr-parser.js +759 -0
  75. package/dist/src/parser/index.d.ts +8 -129
  76. package/dist/src/parser/index.js +13 -2262
  77. package/dist/src/parser/stmt-parser.d.ts +28 -0
  78. package/dist/src/parser/stmt-parser.js +577 -0
  79. package/dist/src/parser/type-parser.d.ts +20 -0
  80. package/dist/src/parser/type-parser.js +257 -0
  81. package/dist/src/parser/utils.d.ts +34 -0
  82. package/dist/src/parser/utils.js +141 -0
  83. package/docs/dev/README-mc-integration-tests.md +141 -0
  84. package/docs/lint-rules.md +162 -0
  85. package/docs/stdlib/bigint.md +2 -0
  86. package/editors/vscode/README.md +63 -41
  87. package/editors/vscode/out/extension.js +1881 -1776
  88. package/editors/vscode/out/lsp-server.js +4257 -3651
  89. package/editors/vscode/package-lock.json +3 -3
  90. package/editors/vscode/package.json +1 -1
  91. package/examples/loops-demo.mcrs +87 -0
  92. package/package.json +1 -1
  93. package/redscript-docs/docs/en/stdlib/advanced.md +629 -0
  94. package/redscript-docs/docs/en/stdlib/bigint.md +316 -0
  95. package/redscript-docs/docs/en/stdlib/bits.md +292 -0
  96. package/redscript-docs/docs/en/stdlib/bossbar.md +177 -0
  97. package/redscript-docs/docs/en/stdlib/calculus.md +289 -0
  98. package/redscript-docs/docs/en/stdlib/color.md +353 -0
  99. package/redscript-docs/docs/en/stdlib/combat.md +88 -0
  100. package/redscript-docs/docs/en/stdlib/cooldown.md +82 -0
  101. package/redscript-docs/docs/en/stdlib/dialog.md +155 -0
  102. package/redscript-docs/docs/en/stdlib/easing.md +558 -0
  103. package/redscript-docs/docs/en/stdlib/ecs.md +475 -0
  104. package/redscript-docs/docs/en/stdlib/effects.md +324 -0
  105. package/redscript-docs/docs/en/stdlib/events.md +3 -0
  106. package/redscript-docs/docs/en/stdlib/expr.md +45 -0
  107. package/redscript-docs/docs/en/stdlib/fft.md +141 -0
  108. package/redscript-docs/docs/en/stdlib/geometry.md +430 -0
  109. package/redscript-docs/docs/en/stdlib/graph.md +259 -0
  110. package/redscript-docs/docs/en/stdlib/heap.md +185 -0
  111. package/redscript-docs/docs/en/stdlib/interactions.md +179 -0
  112. package/redscript-docs/docs/en/stdlib/inventory.md +97 -0
  113. package/redscript-docs/docs/en/stdlib/linalg.md +557 -0
  114. package/redscript-docs/docs/en/stdlib/list.md +559 -0
  115. package/redscript-docs/docs/en/stdlib/map.md +140 -0
  116. package/redscript-docs/docs/en/stdlib/math.md +193 -0
  117. package/redscript-docs/docs/en/stdlib/math_hp.md +149 -0
  118. package/redscript-docs/docs/en/stdlib/matrix.md +403 -0
  119. package/redscript-docs/docs/en/stdlib/mobs.md +965 -0
  120. package/redscript-docs/docs/en/stdlib/noise.md +244 -0
  121. package/redscript-docs/docs/en/stdlib/ode.md +253 -0
  122. package/redscript-docs/docs/en/stdlib/parabola.md +342 -0
  123. package/redscript-docs/docs/en/stdlib/particles.md +311 -0
  124. package/redscript-docs/docs/en/stdlib/pathfind.md +255 -0
  125. package/redscript-docs/docs/en/stdlib/physics.md +493 -0
  126. package/redscript-docs/docs/en/stdlib/player.md +78 -0
  127. package/redscript-docs/docs/en/stdlib/quaternion.md +673 -0
  128. package/redscript-docs/docs/en/stdlib/queue.md +134 -0
  129. package/redscript-docs/docs/en/stdlib/random.md +223 -0
  130. package/redscript-docs/docs/en/stdlib/result.md +143 -0
  131. package/redscript-docs/docs/en/stdlib/scheduler.md +183 -0
  132. package/redscript-docs/docs/en/stdlib/set_int.md +190 -0
  133. package/redscript-docs/docs/en/stdlib/sets.md +101 -0
  134. package/redscript-docs/docs/en/stdlib/signal.md +400 -0
  135. package/redscript-docs/docs/en/stdlib/sort.md +104 -0
  136. package/redscript-docs/docs/en/stdlib/spawn.md +147 -0
  137. package/redscript-docs/docs/en/stdlib/state.md +142 -0
  138. package/redscript-docs/docs/en/stdlib/strings.md +154 -0
  139. package/redscript-docs/docs/en/stdlib/tags.md +3451 -0
  140. package/redscript-docs/docs/en/stdlib/teams.md +153 -0
  141. package/redscript-docs/docs/en/stdlib/timer.md +246 -0
  142. package/redscript-docs/docs/en/stdlib/vec.md +158 -0
  143. package/redscript-docs/docs/en/stdlib/world.md +298 -0
  144. package/redscript-docs/docs/zh/stdlib/advanced.md +615 -0
  145. package/redscript-docs/docs/zh/stdlib/bigint.md +316 -0
  146. package/redscript-docs/docs/zh/stdlib/bits.md +292 -0
  147. package/redscript-docs/docs/zh/stdlib/bossbar.md +170 -0
  148. package/redscript-docs/docs/zh/stdlib/calculus.md +287 -0
  149. package/redscript-docs/docs/zh/stdlib/color.md +353 -0
  150. package/redscript-docs/docs/zh/stdlib/combat.md +88 -0
  151. package/redscript-docs/docs/zh/stdlib/cooldown.md +84 -0
  152. package/redscript-docs/docs/zh/stdlib/dialog.md +152 -0
  153. package/redscript-docs/docs/zh/stdlib/easing.md +558 -0
  154. package/redscript-docs/docs/zh/stdlib/ecs.md +472 -0
  155. package/redscript-docs/docs/zh/stdlib/effects.md +324 -0
  156. package/redscript-docs/docs/zh/stdlib/events.md +3 -0
  157. package/redscript-docs/docs/zh/stdlib/expr.md +37 -0
  158. package/redscript-docs/docs/zh/stdlib/fft.md +128 -0
  159. package/redscript-docs/docs/zh/stdlib/geometry.md +430 -0
  160. package/redscript-docs/docs/zh/stdlib/graph.md +259 -0
  161. package/redscript-docs/docs/zh/stdlib/heap.md +185 -0
  162. package/redscript-docs/docs/zh/stdlib/interactions.md +160 -0
  163. package/redscript-docs/docs/zh/stdlib/inventory.md +94 -0
  164. package/redscript-docs/docs/zh/stdlib/linalg.md +543 -0
  165. package/redscript-docs/docs/zh/stdlib/list.md +561 -0
  166. package/redscript-docs/docs/zh/stdlib/map.md +132 -0
  167. package/redscript-docs/docs/zh/stdlib/math.md +193 -0
  168. package/redscript-docs/docs/zh/stdlib/math_hp.md +143 -0
  169. package/redscript-docs/docs/zh/stdlib/matrix.md +396 -0
  170. package/redscript-docs/docs/zh/stdlib/mobs.md +965 -0
  171. package/redscript-docs/docs/zh/stdlib/noise.md +244 -0
  172. package/redscript-docs/docs/zh/stdlib/ode.md +243 -0
  173. package/redscript-docs/docs/zh/stdlib/parabola.md +337 -0
  174. package/redscript-docs/docs/zh/stdlib/particles.md +307 -0
  175. package/redscript-docs/docs/zh/stdlib/pathfind.md +255 -0
  176. package/redscript-docs/docs/zh/stdlib/physics.md +493 -0
  177. package/redscript-docs/docs/zh/stdlib/player.md +78 -0
  178. package/redscript-docs/docs/zh/stdlib/quaternion.md +669 -0
  179. package/redscript-docs/docs/zh/stdlib/queue.md +124 -0
  180. package/redscript-docs/docs/zh/stdlib/random.md +222 -0
  181. package/redscript-docs/docs/zh/stdlib/result.md +147 -0
  182. package/redscript-docs/docs/zh/stdlib/scheduler.md +173 -0
  183. package/redscript-docs/docs/zh/stdlib/set_int.md +180 -0
  184. package/redscript-docs/docs/zh/stdlib/sets.md +107 -0
  185. package/redscript-docs/docs/zh/stdlib/signal.md +373 -0
  186. package/redscript-docs/docs/zh/stdlib/sort.md +104 -0
  187. package/redscript-docs/docs/zh/stdlib/spawn.md +142 -0
  188. package/redscript-docs/docs/zh/stdlib/state.md +134 -0
  189. package/redscript-docs/docs/zh/stdlib/strings.md +107 -0
  190. package/redscript-docs/docs/zh/stdlib/tags.md +3451 -0
  191. package/redscript-docs/docs/zh/stdlib/teams.md +150 -0
  192. package/redscript-docs/docs/zh/stdlib/timer.md +254 -0
  193. package/redscript-docs/docs/zh/stdlib/vec.md +158 -0
  194. package/redscript-docs/docs/zh/stdlib/world.md +289 -0
  195. package/src/__tests__/formatter-extra.test.ts +139 -0
  196. package/src/__tests__/global-vars.test.ts +171 -0
  197. package/src/__tests__/lint/new-rules.test.ts +437 -0
  198. package/src/__tests__/lsp-rename.test.ts +171 -0
  199. package/src/__tests__/mc-integration/say-fstring.test.ts +211 -0
  200. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +1 -1
  201. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1 -1
  202. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1 -1
  203. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1 -1
  204. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +1 -1
  205. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +1 -1
  206. package/src/__tests__/mc-integration/stdlib-coverage-8.test.ts +1 -1
  207. package/src/__tests__/mc-syntax.test.ts +3 -0
  208. package/src/__tests__/monomorphize-coverage.test.ts +220 -0
  209. package/src/__tests__/optimizer-cse.test.ts +250 -0
  210. package/src/__tests__/parser.test.ts +4 -13
  211. package/src/__tests__/repl-server-extra.test.ts +6 -6
  212. package/src/__tests__/repl-server.test.ts +5 -6
  213. package/src/__tests__/stdlib/queue.test.ts +6 -6
  214. package/src/lexer/index.ts +2 -1
  215. package/src/lint/index.ts +713 -5
  216. package/src/mc-test/client.ts +40 -0
  217. package/src/mir/lower.ts +111 -2
  218. package/src/optimizer/interprocedural.ts +40 -2
  219. package/src/parser/decl-parser.ts +349 -0
  220. package/src/parser/expr-parser.ts +838 -0
  221. package/src/parser/index.ts +17 -2558
  222. package/src/parser/stmt-parser.ts +585 -0
  223. package/src/parser/type-parser.ts +276 -0
  224. package/src/parser/utils.ts +173 -0
  225. package/src/stdlib/queue.mcrs +19 -6
File without changes
@@ -133,6 +133,27 @@ export declare class MCTestClient {
133
133
  * Assert chat log contains a message matching substring.
134
134
  */
135
135
  assertChatContains(substring: string, since?: number): Promise<void>;
136
+ /**
137
+ * Dump all scoreboard entries for a namespace's objective (__<ns>).
138
+ * Returns { "$p0": 0, "$val": 11, "#result": 42, ... }
139
+ */
140
+ dumpScores(ns: string): Promise<Record<string, number>>;
141
+ /**
142
+ * Dump all scoreboard entries for a specific objective.
143
+ */
144
+ dumpScoresByObj(obj: string): Promise<Record<string, number>>;
145
+ /**
146
+ * Dump the raw NBT of an entire storage namespace.
147
+ */
148
+ dumpStorage(storage: string): Promise<{
149
+ raw: string;
150
+ ok: boolean;
151
+ }>;
152
+ /**
153
+ * Assert multiple scoreboard values at once.
154
+ * Usage: await mc.assertScoreMap('ns', { '$p0': 11, '$ret': 2 })
155
+ */
156
+ assertScoreMap(ns: string, expected: Record<string, number>): Promise<void>;
136
157
  /**
137
158
  * Wait until a scoreboard value equals expected, up to timeout ms.
138
159
  */
@@ -180,6 +180,40 @@ class MCTestClient {
180
180
  throw new Error(`assertChatContains: "${substring}" not found in chat. Recent: [${recent}]`);
181
181
  }
182
182
  }
183
+ /**
184
+ * Dump all scoreboard entries for a namespace's objective (__<ns>).
185
+ * Returns { "$p0": 0, "$val": 11, "#result": 42, ... }
186
+ */
187
+ async dumpScores(ns) {
188
+ return this.get('/scoreboard/dump', { ns })
189
+ .then((r) => r.entries ?? {});
190
+ }
191
+ /**
192
+ * Dump all scoreboard entries for a specific objective.
193
+ */
194
+ async dumpScoresByObj(obj) {
195
+ return this.get('/scoreboard/dump', { obj })
196
+ .then((r) => r.entries ?? {});
197
+ }
198
+ /**
199
+ * Dump the raw NBT of an entire storage namespace.
200
+ */
201
+ async dumpStorage(storage) {
202
+ return this.get('/storage/dump', { storage });
203
+ }
204
+ /**
205
+ * Assert multiple scoreboard values at once.
206
+ * Usage: await mc.assertScoreMap('ns', { '$p0': 11, '$ret': 2 })
207
+ */
208
+ async assertScoreMap(ns, expected) {
209
+ const actual = await this.dumpScores(ns);
210
+ for (const [key, val] of Object.entries(expected)) {
211
+ if (actual[key] !== val) {
212
+ throw new Error(`assertScoreMap[${ns}] ${key}: expected ${val}, got ${actual[key] ?? '(unset)'}\n` +
213
+ `Full dump: ${JSON.stringify(actual)}`);
214
+ }
215
+ }
216
+ }
183
217
  /**
184
218
  * Wait until a scoreboard value equals expected, up to timeout ms.
185
219
  */
@@ -121,9 +121,11 @@ function lowerToMIR(hir, sourceFile) {
121
121
  else if (c.value.kind === 'float_lit')
122
122
  constValues.set(c.name, Math.round(c.value.value * 10000));
123
123
  }
124
+ // Collect module-level global variable names (mutable lets at top level)
125
+ const globalVarNames = new Set(hir.globals.map(g => g.name));
124
126
  const allFunctions = [];
125
127
  for (const f of hir.functions) {
126
- const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, undefined, hirFnMap, specializedFnsRegistry, undefined, enumPayloads, constValues, singletonStructs, displayImpls);
128
+ const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, undefined, hirFnMap, specializedFnsRegistry, undefined, enumPayloads, constValues, singletonStructs, displayImpls, globalVarNames);
127
129
  allFunctions.push(fn, ...helpers);
128
130
  }
129
131
  // Lower impl block methods (skip Display::to_string — inlined at call sites instead)
@@ -131,7 +133,7 @@ function lowerToMIR(hir, sourceFile) {
131
133
  if (ib.traitName === 'Display')
132
134
  continue; // Display impls are inlined, not emitted as functions
133
135
  for (const m of ib.methods) {
134
- const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, enumPayloads, constValues);
136
+ const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, enumPayloads, constValues, globalVarNames);
135
137
  allFunctions.push(fn, ...helpers);
136
138
  }
137
139
  }
@@ -189,6 +191,8 @@ class FnContext {
189
191
  this.singletonStructs = new Set();
190
192
  /** Display trait impls: typeName → f-string parts from to_string body (inlined at call sites) */
191
193
  this.displayImpls = new Map();
194
+ /** Module-level global variable names — reads/writes must go through scoreboard */
195
+ this.globalVarNames = new Set();
192
196
  this.namespace = namespace;
193
197
  this.fnName = fnName;
194
198
  this.structDefs = structDefs;
@@ -286,13 +290,14 @@ hirFnMap,
286
290
  /** Shared registry of already-generated specialized MIR functions */
287
291
  specializedFnsRegistry,
288
292
  /** Override the MIR function name (used when generating specialized versions) */
289
- overrideName, enumPayloads = new Map(), constValues = new Map(), singletonStructs = new Set(), displayImpls = new Map()) {
293
+ overrideName, enumPayloads = new Map(), constValues = new Map(), singletonStructs = new Set(), displayImpls = new Map(), globalVarNames = new Set()) {
290
294
  const mirFnName = overrideName ?? fn.name;
291
295
  const ctx = new FnContext(namespace, mirFnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads);
292
296
  ctx.sourceFile = fn.sourceFile ?? sourceFile;
293
297
  ctx.constValues = constValues;
294
298
  ctx.singletonStructs = singletonStructs;
295
299
  ctx.displayImpls = displayImpls;
300
+ ctx.globalVarNames = globalVarNames;
296
301
  if (hirFnMap)
297
302
  ctx.hirFunctions = hirFnMap;
298
303
  if (specializedFnsRegistry)
@@ -352,11 +357,12 @@ overrideName, enumPayloads = new Map(), constValues = new Map(), singletonStruct
352
357
  };
353
358
  return { fn: result, helpers: ctx.helperFunctions };
354
359
  }
355
- function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, macroInfo = new Map(), fnParamInfo = new Map(), enumDefs = new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, enumPayloads = new Map(), constValues = new Map()) {
360
+ function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, macroInfo = new Map(), fnParamInfo = new Map(), enumDefs = new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, enumPayloads = new Map(), constValues = new Map(), globalVarNames = new Set()) {
356
361
  const fnName = `${typeName}::${method.name}`;
357
362
  const ctx = new FnContext(namespace, fnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads);
358
363
  ctx.sourceFile = method.sourceFile ?? sourceFile;
359
364
  ctx.constValues = constValues;
365
+ ctx.globalVarNames = globalVarNames;
360
366
  const fields = structDefs.get(typeName) ?? [];
361
367
  const hasSelf = method.params.length > 0 && method.params[0].name === 'self';
362
368
  const params = [];
@@ -1188,7 +1194,8 @@ function lowerStmt(stmt, ctx, scope) {
1188
1194
  const ns = ctx.getNamespace();
1189
1195
  const rawCmd = stmt.cmd
1190
1196
  .replace(/__NS__/g, ns)
1191
- .replace(/__OBJ__/g, `__${ns}`);
1197
+ .replace(/__OBJ__/g, `__${ns}`)
1198
+ .replace(/__RS__/g, 'rs');
1192
1199
  ctx.emit({ kind: 'call', dst: null, fn: `__raw:${rawCmd}`, args: [] });
1193
1200
  break;
1194
1201
  }
@@ -1367,6 +1374,13 @@ function lowerExpr(expr, ctx, scope) {
1367
1374
  if (ctx.constValues.has(expr.name)) {
1368
1375
  return { kind: 'const', value: ctx.constValues.get(expr.name) };
1369
1376
  }
1377
+ // Module-level global variable: read from scoreboard slot
1378
+ if (ctx.globalVarNames.has(expr.name)) {
1379
+ const t = ctx.freshTemp();
1380
+ ctx.emit({ kind: 'score_read', dst: t, player: expr.name, obj: `__${ctx.getNamespace()}` });
1381
+ scope.set(expr.name, t);
1382
+ return { kind: 'temp', name: t };
1383
+ }
1370
1384
  // Unresolved ident — could be a global or external reference
1371
1385
  const t = ctx.freshTemp();
1372
1386
  ctx.emit({ kind: 'copy', dst: t, src: { kind: 'const', value: 0 } });
@@ -1465,6 +1479,31 @@ function lowerExpr(expr, ctx, scope) {
1465
1479
  }
1466
1480
  case 'assign': {
1467
1481
  const val = lowerExpr(expr.value, ctx, scope);
1482
+ // Check if the target is a struct variable — if so, update its field temps
1483
+ // from the __rf_<field> return slots that the callee populated.
1484
+ const sv = ctx.structVars.get(expr.target);
1485
+ if (sv) {
1486
+ const fields = ctx.structDefs.get(sv.typeName) ?? [];
1487
+ for (const fieldName of fields) {
1488
+ const existingFieldTemp = sv.fields.get(fieldName);
1489
+ // Reuse the existing field temp if it exists (so scoreboard slot stays stable),
1490
+ // otherwise allocate a fresh one.
1491
+ const fieldTemp = existingFieldTemp ?? ctx.freshTemp();
1492
+ ctx.emit({ kind: 'copy', dst: fieldTemp, src: { kind: 'temp', name: `__rf_${fieldName}` } });
1493
+ sv.fields.set(fieldName, fieldTemp);
1494
+ }
1495
+ return val;
1496
+ }
1497
+ // Global variable assignment: write to scoreboard (score_write has side effects, DCE-safe)
1498
+ if (ctx.globalVarNames.has(expr.target)) {
1499
+ const globalObj = `__${ctx.getNamespace()}`;
1500
+ ctx.emit({ kind: 'score_write', player: expr.target, obj: globalObj, src: val });
1501
+ // Also update scope temp so subsequent reads in this function see the new value
1502
+ const t = ctx.freshTemp();
1503
+ ctx.emit({ kind: 'score_read', dst: t, player: expr.target, obj: globalObj });
1504
+ scope.set(expr.target, t);
1505
+ return val;
1506
+ }
1468
1507
  // Reuse the existing temp for this variable so that updates inside
1469
1508
  // if/while bodies are visible to outer code (we target mutable
1470
1509
  // scoreboard slots, not true SSA registers).
@@ -1887,6 +1926,69 @@ function lowerExpr(expr, ctx, scope) {
1887
1926
  }
1888
1927
  // Handle builtin calls → raw MC commands
1889
1928
  if (macro_1.BUILTIN_SET.has(expr.fn)) {
1929
+ // Special case: say() with f_string → MC macro function ($say template)
1930
+ // MC `say` is plain text and cannot reference scoreboards directly;
1931
+ // use function macros (MC 1.20.2+) to interpolate variables.
1932
+ if (expr.fn === 'say' && expr.args[0]?.kind === 'f_string') {
1933
+ const fstr = precomputeFStringParts(expr.args[0], ctx, scope);
1934
+ if (fstr.kind === 'f_string') {
1935
+ const ns = ctx.getNamespace();
1936
+ const obj = `__${ns}`;
1937
+ const helperName = `${ctx.getFnName()}__say_macro_${ctx.freshTemp()}`;
1938
+ // Build macro template: "text $(var) more text"
1939
+ let template = 'say ';
1940
+ const macroVarNames = [];
1941
+ for (const part of fstr.parts) {
1942
+ if (part.kind === 'text') {
1943
+ template += part.value;
1944
+ }
1945
+ else {
1946
+ const inner = part.expr;
1947
+ if (inner.kind === 'ident') {
1948
+ // Strip leading $ from temp names for macro param names
1949
+ const varName = inner.name.startsWith('$') ? inner.name.slice(1) : inner.name;
1950
+ template += `$(${varName})`;
1951
+ macroVarNames.push(inner.name);
1952
+ }
1953
+ else if (inner.kind === 'int_lit') {
1954
+ template += String(inner.value);
1955
+ }
1956
+ else {
1957
+ template += '?';
1958
+ }
1959
+ }
1960
+ }
1961
+ // Emit: copy each scoreboard var to rs:macro_args storage
1962
+ for (const varName of macroVarNames) {
1963
+ const cleanName = varName.startsWith('$') ? varName.slice(1) : varName;
1964
+ ctx.emit({
1965
+ kind: 'call', dst: null,
1966
+ fn: `__raw:execute store result storage rs:macro_args ${cleanName} int 1 run scoreboard players get ${varName} ${obj}`,
1967
+ args: [],
1968
+ });
1969
+ }
1970
+ // Build helper MIR function with isMacro: true
1971
+ const helperCtx = new FnContext(ns, helperName, ctx.structDefs, ctx.implMethods);
1972
+ helperCtx.emit({ kind: 'call', dst: null, fn: `__raw:$${template}`, args: [] });
1973
+ helperCtx.terminate({ kind: 'return', value: null });
1974
+ const helperReachable = computeReachable(helperCtx.blocks, 'entry');
1975
+ const helperBlocks = helperCtx.blocks.filter(b => helperReachable.has(b.id));
1976
+ computePreds(helperBlocks);
1977
+ ctx.helperFunctions.push({
1978
+ name: helperName,
1979
+ params: [],
1980
+ blocks: helperBlocks,
1981
+ entry: 'entry',
1982
+ isMacro: true,
1983
+ sourceSnippet: 'say macro helper',
1984
+ });
1985
+ // Emit: function <helper> with storage rs:macro_args
1986
+ ctx.emit({ kind: 'call', dst: null, fn: `__raw:function ${ns}:${helperName} with storage rs:macro_args`, args: [] });
1987
+ const t = ctx.freshTemp();
1988
+ ctx.emit({ kind: 'const', dst: t, value: 0 });
1989
+ return { kind: 'temp', name: t };
1990
+ }
1991
+ }
1890
1992
  // For text builtins with f-string args, precompute complex expressions to temp vars
1891
1993
  const TEXT_BUILTINS_SET = new Set(['tell', 'tellraw', 'title', 'subtitle', 'actionbar', 'announce']);
1892
1994
  let resolvedArgs = expr.args;
@@ -2040,7 +2142,7 @@ function lowerExpr(expr, ctx, scope) {
2040
2142
  if (!ctx.specializedFnsRegistry.has(specializedName)) {
2041
2143
  // Placeholder to prevent re-entry
2042
2144
  ctx.specializedFnsRegistry.set(specializedName, []);
2043
- const { fn: specFn, helpers: specHelpers } = lowerFunction(targetHirFn, ctx.getNamespace(), ctx.structDefs, ctx.implMethods, ctx.macroInfo, ctx.fnParamInfo, ctx.enumDefs, ctx.sourceFile, ctx.timerCounter, arrayArgBindings, ctx.hirFunctions, ctx.specializedFnsRegistry, specializedName, ctx.enumPayloads, ctx.constValues, ctx.singletonStructs, ctx.displayImpls);
2145
+ const { fn: specFn, helpers: specHelpers } = lowerFunction(targetHirFn, ctx.getNamespace(), ctx.structDefs, ctx.implMethods, ctx.macroInfo, ctx.fnParamInfo, ctx.enumDefs, ctx.sourceFile, ctx.timerCounter, arrayArgBindings, ctx.hirFunctions, ctx.specializedFnsRegistry, specializedName, ctx.enumPayloads, ctx.constValues, ctx.singletonStructs, ctx.displayImpls, ctx.globalVarNames);
2044
2146
  ctx.specializedFnsRegistry.set(specializedName, [specFn, ...specHelpers]);
2045
2147
  }
2046
2148
  // Emit call to the specialized function, passing only non-array args
@@ -62,7 +62,7 @@ function runOnePass(mod) {
62
62
  if (fnMap.has(mangledName) || added.has(mangledName))
63
63
  continue;
64
64
  // Create specialized clone
65
- const specialized = specialize(callee, constArgs.map(a => a.value), mangledName);
65
+ const specialized = specialize(callee, constArgs.map(a => a.value), mangledName, mod.objective);
66
66
  newFunctions.push(specialized);
67
67
  added.add(mangledName);
68
68
  fnMap.set(mangledName, specialized);
@@ -90,13 +90,48 @@ function isSelfRecursive(fn) {
90
90
  function mangleName(name, args) {
91
91
  return `${name}__const_${args.map(v => v < 0 ? `n${Math.abs(v)}` : String(v)).join('_')}`;
92
92
  }
93
- function specialize(fn, args, newName) {
93
+ /**
94
+ * Returns true if the function has any __raw: calls that directly reference
95
+ * scoreboard param slots ($p0, $p1, ...) by name in the raw command string.
96
+ * Such functions use the raw() pattern to read params via scoreboard, so the
97
+ * specialized clone must pre-set those slots before executing the body.
98
+ */
99
+ function hasRawParamRefs(fn, paramCount) {
100
+ for (let i = 0; i < paramCount; i++) {
101
+ const pattern = `$p${i}`;
102
+ for (const block of fn.blocks) {
103
+ for (const instr of block.instrs) {
104
+ if (instr.kind === 'call' && instr.fn.startsWith('__raw:') && instr.fn.includes(pattern)) {
105
+ return true;
106
+ }
107
+ }
108
+ }
109
+ }
110
+ return false;
111
+ }
112
+ function specialize(fn, args, newName, objective) {
94
113
  // Build substitution map: param.name → const operand
95
114
  const sub = new Map();
96
115
  for (let i = 0; i < fn.params.length; i++) {
97
116
  sub.set(fn.params[i].name, { kind: 'const', value: args[i] });
98
117
  }
99
118
  const newBlocks = fn.blocks.map(block => substituteBlock(block, sub));
119
+ // If the function uses raw() commands that read from $p<i> scoreboard slots
120
+ // directly, we must pre-set those slots in the entry block so the raw commands
121
+ // see the correct values (the normal call convention sets $p<i> at the call
122
+ // site, but the specialized function is called with no args).
123
+ if (hasRawParamRefs(fn, args.length)) {
124
+ const entryBlock = newBlocks.find(b => b.id === fn.entry);
125
+ if (entryBlock) {
126
+ const scoreWrites = args.map((value, i) => ({
127
+ kind: 'score_write',
128
+ player: `$p${i}`,
129
+ obj: objective,
130
+ src: { kind: 'const', value },
131
+ }));
132
+ entryBlock.instrs = [...scoreWrites, ...entryBlock.instrs];
133
+ }
134
+ }
100
135
  const specialized = {
101
136
  ...fn,
102
137
  name: newName,
@@ -0,0 +1,19 @@
1
+ /**
2
+ * DeclParser — declaration parsing (fn/struct/enum/impl/interface/const/global/import).
3
+ * Extends StmtParser so declaration methods can call block/statement methods.
4
+ */
5
+ import type { FnDecl, StructDecl, EnumDecl, ImplBlock, InterfaceDecl, ConstDecl, GlobalDecl, Decorator } from '../ast/types';
6
+ import { StmtParser } from './stmt-parser';
7
+ export declare class DeclParser extends StmtParser {
8
+ parseStructDecl(): StructDecl;
9
+ parseEnumDecl(): EnumDecl;
10
+ parseImplBlock(): ImplBlock;
11
+ parseInterfaceDecl(): InterfaceDecl;
12
+ parseConstDecl(): ConstDecl;
13
+ parseGlobalDecl(mutable: boolean): GlobalDecl;
14
+ parseExportedFnDecl(): FnDecl;
15
+ parseFnDecl(implTypeName?: string): FnDecl;
16
+ parseDeclareStub(): void;
17
+ private parseDecorators;
18
+ parseDecoratorValue(value: string): Decorator;
19
+ }
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ /**
3
+ * DeclParser — declaration parsing (fn/struct/enum/impl/interface/const/global/import).
4
+ * Extends StmtParser so declaration methods can call block/statement methods.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DeclParser = void 0;
8
+ const stmt_parser_1 = require("./stmt-parser");
9
+ class DeclParser extends stmt_parser_1.StmtParser {
10
+ // -------------------------------------------------------------------------
11
+ // Struct
12
+ // -------------------------------------------------------------------------
13
+ parseStructDecl() {
14
+ const structToken = this.expect('struct');
15
+ const name = this.expect('ident').value;
16
+ const extendsName = this.match('extends') ? this.expect('ident').value : undefined;
17
+ this.expect('{');
18
+ const fields = [];
19
+ while (!this.check('}') && !this.check('eof')) {
20
+ const fieldName = this.expect('ident').value;
21
+ this.expect(':');
22
+ const fieldType = this.parseType();
23
+ fields.push({ name: fieldName, type: fieldType });
24
+ this.match(',');
25
+ }
26
+ this.expect('}');
27
+ return this.withLoc({ name, extends: extendsName, fields }, structToken);
28
+ }
29
+ // -------------------------------------------------------------------------
30
+ // Enum
31
+ // -------------------------------------------------------------------------
32
+ parseEnumDecl() {
33
+ const enumToken = this.expect('enum');
34
+ const name = this.expect('ident').value;
35
+ this.expect('{');
36
+ const variants = [];
37
+ let nextValue = 0;
38
+ while (!this.check('}') && !this.check('eof')) {
39
+ const variantToken = this.expect('ident');
40
+ const variant = { name: variantToken.value };
41
+ if (this.check('(')) {
42
+ this.advance();
43
+ const fields = [];
44
+ while (!this.check(')') && !this.check('eof')) {
45
+ const fieldName = this.expect('ident').value;
46
+ this.expect(':');
47
+ const fieldType = this.parseType();
48
+ fields.push({ name: fieldName, type: fieldType });
49
+ if (!this.match(','))
50
+ break;
51
+ }
52
+ this.expect(')');
53
+ variant.fields = fields;
54
+ }
55
+ if (this.match('=')) {
56
+ const valueToken = this.expect('int_lit');
57
+ variant.value = parseInt(valueToken.value, 10);
58
+ nextValue = variant.value + 1;
59
+ }
60
+ else {
61
+ variant.value = nextValue++;
62
+ }
63
+ variants.push(variant);
64
+ if (!this.match(','))
65
+ break;
66
+ }
67
+ this.expect('}');
68
+ return this.withLoc({ name, variants }, enumToken);
69
+ }
70
+ // -------------------------------------------------------------------------
71
+ // Impl Block
72
+ // -------------------------------------------------------------------------
73
+ parseImplBlock() {
74
+ const implToken = this.expect('impl');
75
+ let traitName;
76
+ let typeName;
77
+ const firstName = this.expect('ident').value;
78
+ if (this.match('for')) {
79
+ traitName = firstName;
80
+ typeName = this.expect('ident').value;
81
+ }
82
+ else {
83
+ typeName = firstName;
84
+ }
85
+ this.expect('{');
86
+ const methods = [];
87
+ while (!this.check('}') && !this.check('eof')) {
88
+ methods.push(this.parseFnDecl(typeName));
89
+ }
90
+ this.expect('}');
91
+ return this.withLoc({ kind: 'impl_block', traitName, typeName, methods }, implToken);
92
+ }
93
+ // -------------------------------------------------------------------------
94
+ // Interface
95
+ // -------------------------------------------------------------------------
96
+ parseInterfaceDecl() {
97
+ const ifaceToken = this.expect('interface');
98
+ const name = this.expect('ident').value;
99
+ this.expect('{');
100
+ const methods = [];
101
+ while (!this.check('}') && !this.check('eof')) {
102
+ const fnToken = this.expect('fn');
103
+ const methodName = this.expect('ident').value;
104
+ this.expect('(');
105
+ const params = this.parseInterfaceParams();
106
+ this.expect(')');
107
+ let returnType;
108
+ if (this.match(':'))
109
+ returnType = this.parseType();
110
+ methods.push(this.withLoc({ name: methodName, params, returnType }, fnToken));
111
+ }
112
+ this.expect('}');
113
+ return this.withLoc({ name, methods }, ifaceToken);
114
+ }
115
+ // -------------------------------------------------------------------------
116
+ // Const / Global
117
+ // -------------------------------------------------------------------------
118
+ parseConstDecl() {
119
+ const constToken = this.expect('const');
120
+ const name = this.expect('ident').value;
121
+ let type;
122
+ if (this.match(':'))
123
+ type = this.parseType();
124
+ this.expect('=');
125
+ const value = this.parseLiteralExpr();
126
+ this.match(';');
127
+ const inferredType = type ?? (value.kind === 'str_lit' ? { kind: 'named', name: 'string' } :
128
+ value.kind === 'bool_lit' ? { kind: 'named', name: 'bool' } :
129
+ value.kind === 'float_lit' ? { kind: 'named', name: 'fixed' } :
130
+ { kind: 'named', name: 'int' });
131
+ return this.withLoc({ name, type: inferredType, value }, constToken);
132
+ }
133
+ parseGlobalDecl(mutable) {
134
+ const token = this.advance(); // consume 'let'
135
+ const name = this.expect('ident').value;
136
+ this.expect(':');
137
+ const type = this.parseType();
138
+ let init;
139
+ if (this.match('=')) {
140
+ init = this.parseExpr();
141
+ }
142
+ else {
143
+ init = { kind: 'int_lit', value: 0 };
144
+ }
145
+ this.match(';');
146
+ return this.withLoc({ kind: 'global', name, type, init, mutable }, token);
147
+ }
148
+ // -------------------------------------------------------------------------
149
+ // Function
150
+ // -------------------------------------------------------------------------
151
+ parseExportedFnDecl() {
152
+ this.expect('export');
153
+ const fn = this.parseFnDecl();
154
+ fn.isExported = true;
155
+ return fn;
156
+ }
157
+ parseFnDecl(implTypeName) {
158
+ const decorators = this.parseDecorators();
159
+ const watchObjective = decorators.find(decorator => decorator.name === 'watch')?.args?.objective;
160
+ let isExported;
161
+ const filteredDecorators = decorators.filter(d => {
162
+ if (d.name === 'keep') {
163
+ isExported = true;
164
+ return false;
165
+ }
166
+ return true;
167
+ });
168
+ const fnToken = this.expect('fn');
169
+ const name = this.expect('ident').value;
170
+ let typeParams;
171
+ if (this.check('<')) {
172
+ this.advance();
173
+ typeParams = [];
174
+ do {
175
+ typeParams.push(this.expect('ident').value);
176
+ } while (this.match(','));
177
+ this.expect('>');
178
+ }
179
+ this.expect('(');
180
+ const params = this.parseParams(implTypeName);
181
+ this.expect(')');
182
+ let returnType = { kind: 'named', name: 'void' };
183
+ if (this.match('->') || this.match(':')) {
184
+ returnType = this.parseType();
185
+ }
186
+ const body = this.parseBlock();
187
+ const closingBraceLine = this.tokens[this.pos - 1]?.line;
188
+ const fn = this.withLoc({ name, typeParams, params, returnType, decorators: filteredDecorators, body,
189
+ isLibraryFn: this.inLibraryMode || undefined, isExported, watchObjective }, fnToken);
190
+ if (fn.span && closingBraceLine)
191
+ fn.span.endLine = closingBraceLine;
192
+ return fn;
193
+ }
194
+ parseDeclareStub() {
195
+ this.expect('fn');
196
+ this.expect('ident');
197
+ this.expect('(');
198
+ let depth = 1;
199
+ while (!this.check('eof') && depth > 0) {
200
+ const t = this.advance();
201
+ if (t.kind === '(')
202
+ depth++;
203
+ else if (t.kind === ')')
204
+ depth--;
205
+ }
206
+ if (this.match(':') || this.match('->')) {
207
+ this.parseType();
208
+ }
209
+ this.match(';');
210
+ }
211
+ // -------------------------------------------------------------------------
212
+ // Decorators
213
+ // -------------------------------------------------------------------------
214
+ parseDecorators() {
215
+ const decorators = [];
216
+ while (this.check('decorator')) {
217
+ const token = this.advance();
218
+ const decorator = this.parseDecoratorValue(token.value);
219
+ decorators.push(decorator);
220
+ }
221
+ return decorators;
222
+ }
223
+ parseDecoratorValue(value) {
224
+ const match = value.match(/^@([A-Za-z_][A-Za-z0-9_-]*)(?:\((.*)\))?$/s);
225
+ if (!match) {
226
+ this.error(`Invalid decorator: ${value}`);
227
+ }
228
+ const name = match[1];
229
+ const argsStr = match[2];
230
+ if (!argsStr)
231
+ return { name };
232
+ if (name === 'profile' || name === 'benchmark' || name === 'memoize') {
233
+ this.error(`@${name} decorator does not accept arguments`);
234
+ }
235
+ const args = {};
236
+ if (name === 'on') {
237
+ const eventTypeMatch = argsStr.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
238
+ if (eventTypeMatch) {
239
+ args.eventType = eventTypeMatch[1];
240
+ return { name, args };
241
+ }
242
+ }
243
+ if (name === 'watch' || name === 'on_trigger' || name === 'on_advancement' || name === 'on_craft' || name === 'on_join_team') {
244
+ const strMatch = argsStr.match(/^"([^"]*)"$/);
245
+ if (strMatch) {
246
+ if (name === 'watch')
247
+ args.objective = strMatch[1];
248
+ else if (name === 'on_trigger')
249
+ args.trigger = strMatch[1];
250
+ else if (name === 'on_advancement')
251
+ args.advancement = strMatch[1];
252
+ else if (name === 'on_craft')
253
+ args.item = strMatch[1];
254
+ else if (name === 'on_join_team')
255
+ args.team = strMatch[1];
256
+ return { name, args };
257
+ }
258
+ }
259
+ if (name === 'config') {
260
+ const configMatch = argsStr.match(/^"([^"]+)"\s*,\s*default\s*:\s*(-?\d+(?:\.\d+)?)$/);
261
+ if (configMatch) {
262
+ return { name, args: { configKey: configMatch[1], configDefault: parseFloat(configMatch[2]) } };
263
+ }
264
+ const keyOnlyMatch = argsStr.match(/^"([^"]+)"$/);
265
+ if (keyOnlyMatch) {
266
+ return { name, args: { configKey: keyOnlyMatch[1] } };
267
+ }
268
+ this.error(`Invalid @config syntax. Expected: @config("key", default: value) or @config("key")`);
269
+ }
270
+ if (name === 'deprecated') {
271
+ const strMatch = argsStr.match(/^"([^"]*)"$/);
272
+ if (strMatch)
273
+ return { name, args: { message: strMatch[1] } };
274
+ return { name, args: {} };
275
+ }
276
+ if (name === 'test') {
277
+ const strMatch = argsStr.match(/^"([^"]*)"$/);
278
+ if (strMatch)
279
+ return { name, args: { testLabel: strMatch[1] } };
280
+ return { name, args: { testLabel: '' } };
281
+ }
282
+ if (name === 'require_on_load') {
283
+ const rawArgs = [];
284
+ for (const part of argsStr.split(',')) {
285
+ const trimmed = part.trim();
286
+ const identMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
287
+ if (identMatch) {
288
+ rawArgs.push({ kind: 'string', value: identMatch[1] });
289
+ }
290
+ else {
291
+ const strMatch = trimmed.match(/^"([^"]*)"$/);
292
+ if (strMatch)
293
+ rawArgs.push({ kind: 'string', value: strMatch[1] });
294
+ }
295
+ }
296
+ return { name, rawArgs };
297
+ }
298
+ for (const part of argsStr.split(',')) {
299
+ const [key, val] = part.split('=').map(s => s.trim());
300
+ if (key === 'rate')
301
+ args.rate = parseInt(val, 10);
302
+ else if (key === 'ticks')
303
+ args.ticks = parseInt(val, 10);
304
+ else if (key === 'batch')
305
+ args.batch = parseInt(val, 10);
306
+ else if (key === 'onDone')
307
+ args.onDone = val.replace(/^["']|["']$/g, '');
308
+ else if (key === 'trigger')
309
+ args.trigger = val;
310
+ else if (key === 'advancement')
311
+ args.advancement = val;
312
+ else if (key === 'item')
313
+ args.item = val;
314
+ else if (key === 'team')
315
+ args.team = val;
316
+ else if (key === 'max')
317
+ args.max = parseInt(val, 10);
318
+ }
319
+ return { name, args };
320
+ }
321
+ }
322
+ exports.DeclParser = DeclParser;
323
+ //# sourceMappingURL=decl-parser.js.map