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
@@ -18107,7 +18107,7 @@ var require_package = __commonJS({
18107
18107
  "../../dist/package.json"(exports2, module2) {
18108
18108
  module2.exports = {
18109
18109
  name: "redscript-mc",
18110
- version: "3.0.1",
18110
+ version: "3.0.2",
18111
18111
  description: "A high-level programming language that compiles to Minecraft datapacks",
18112
18112
  main: "dist/src/index.js",
18113
18113
  bin: {
@@ -18877,15 +18877,15 @@ var require_lexer = __commonJS({
18877
18877
  }
18878
18878
  });
18879
18879
 
18880
- // ../../dist/src/parser/index.js
18881
- var require_parser = __commonJS({
18882
- "../../dist/src/parser/index.js"(exports2) {
18880
+ // ../../dist/src/parser/utils.js
18881
+ var require_utils = __commonJS({
18882
+ "../../dist/src/parser/utils.js"(exports2) {
18883
18883
  "use strict";
18884
18884
  Object.defineProperty(exports2, "__esModule", { value: true });
18885
- exports2.Parser = void 0;
18885
+ exports2.ParserBase = exports2.BINARY_OPS = exports2.PRECEDENCE = void 0;
18886
18886
  var lexer_1 = require_lexer();
18887
18887
  var diagnostics_1 = require_diagnostics();
18888
- var PRECEDENCE = {
18888
+ exports2.PRECEDENCE = {
18889
18889
  "||": 1,
18890
18890
  "&&": 2,
18891
18891
  "==": 3,
@@ -18901,45 +18901,8 @@ var require_parser = __commonJS({
18901
18901
  "/": 6,
18902
18902
  "%": 6
18903
18903
  };
18904
- var BINARY_OPS = /* @__PURE__ */ new Set(["||", "&&", "==", "!=", "<", "<=", ">", ">=", "is", "+", "-", "*", "/", "%"]);
18905
- var ENTITY_TYPE_NAMES = /* @__PURE__ */ new Set([
18906
- "entity",
18907
- "Player",
18908
- "Mob",
18909
- "HostileMob",
18910
- "PassiveMob",
18911
- "Zombie",
18912
- "Skeleton",
18913
- "Creeper",
18914
- "Spider",
18915
- "Enderman",
18916
- "Blaze",
18917
- "Witch",
18918
- "Slime",
18919
- "ZombieVillager",
18920
- "Husk",
18921
- "Drowned",
18922
- "Stray",
18923
- "WitherSkeleton",
18924
- "CaveSpider",
18925
- "Pig",
18926
- "Cow",
18927
- "Sheep",
18928
- "Chicken",
18929
- "Villager",
18930
- "WanderingTrader",
18931
- "ArmorStand",
18932
- "Item",
18933
- "Arrow"
18934
- ]);
18935
- function computeIsSingle(raw) {
18936
- if (/^@[spr](\[|$)/.test(raw))
18937
- return true;
18938
- if (/[\[,\s]limit=1[,\]\s]/.test(raw))
18939
- return true;
18940
- return false;
18941
- }
18942
- var Parser = class _Parser {
18904
+ exports2.BINARY_OPS = /* @__PURE__ */ new Set(["||", "&&", "==", "!=", "<", "<=", ">", ">=", "is", "+", "-", "*", "/", "%"]);
18905
+ var ParserBase = class _ParserBase {
18943
18906
  constructor(tokens, source, filePath) {
18944
18907
  this.pos = 0;
18945
18908
  this.inLibraryMode = false;
@@ -18950,7 +18913,7 @@ var require_parser = __commonJS({
18950
18913
  this.filePath = filePath;
18951
18914
  }
18952
18915
  // -------------------------------------------------------------------------
18953
- // Utilities
18916
+ // Token navigation
18954
18917
  // -------------------------------------------------------------------------
18955
18918
  peek(offset = 0) {
18956
18919
  const idx = this.pos + offset;
@@ -19005,14 +18968,12 @@ var require_parser = __commonJS({
19005
18968
  }
19006
18969
  return { kind: "eof", value: "", line: span.line, col: span.col };
19007
18970
  }
18971
+ checkIdent(value) {
18972
+ return this.check("ident") && this.peek().value === value;
18973
+ }
19008
18974
  // -------------------------------------------------------------------------
19009
18975
  // Error Recovery
19010
18976
  // -------------------------------------------------------------------------
19011
- /**
19012
- * Synchronize to the next top-level declaration boundary after a parse error.
19013
- * Skips tokens until we find a keyword that starts a top-level declaration,
19014
- * or a `}` (end of a block), or EOF.
19015
- */
19016
18977
  syncToNextDecl() {
19017
18978
  const TOP_LEVEL_KEYWORDS = /* @__PURE__ */ new Set([
19018
18979
  "fn",
@@ -19042,10 +19003,6 @@ var require_parser = __commonJS({
19042
19003
  this.advance();
19043
19004
  }
19044
19005
  }
19045
- /**
19046
- * Synchronize to the next statement boundary inside a block after a parse error.
19047
- * Skips tokens until we reach `;`, `}`, or EOF.
19048
- */
19049
19006
  syncToNextStmt() {
19050
19007
  while (!this.check("eof")) {
19051
19008
  const kind = this.peek().kind;
@@ -19060,215 +19017,230 @@ var require_parser = __commonJS({
19060
19017
  }
19061
19018
  }
19062
19019
  // -------------------------------------------------------------------------
19063
- // Program
19020
+ // Sub-parser helper (used by string interpolation)
19064
19021
  // -------------------------------------------------------------------------
19065
- parse(defaultNamespace = "redscript") {
19066
- let namespace = defaultNamespace;
19067
- const globals = [];
19068
- const declarations = [];
19069
- const structs = [];
19070
- const implBlocks = [];
19071
- const enums = [];
19072
- const consts = [];
19073
- const imports = [];
19074
- const interfaces = [];
19075
- let isLibrary = false;
19076
- let moduleName;
19077
- if (this.check("namespace")) {
19022
+ makeSubParser(source) {
19023
+ const tokens = new lexer_1.Lexer(source, this.filePath).tokenize();
19024
+ return new _ParserBase(tokens, source, this.filePath);
19025
+ }
19026
+ };
19027
+ exports2.ParserBase = ParserBase;
19028
+ }
19029
+ });
19030
+
19031
+ // ../../dist/src/parser/type-parser.js
19032
+ var require_type_parser = __commonJS({
19033
+ "../../dist/src/parser/type-parser.js"(exports2) {
19034
+ "use strict";
19035
+ Object.defineProperty(exports2, "__esModule", { value: true });
19036
+ exports2.TypeParser = void 0;
19037
+ var utils_1 = require_utils();
19038
+ var TypeParser = class extends utils_1.ParserBase {
19039
+ // -------------------------------------------------------------------------
19040
+ // Type Parsing
19041
+ // -------------------------------------------------------------------------
19042
+ parseType() {
19043
+ const token = this.peek();
19044
+ let type;
19045
+ if (token.kind === "(") {
19046
+ const saved = this.pos;
19078
19047
  this.advance();
19079
- const name = this.expect("ident");
19080
- namespace = name.value;
19081
- this.match(";");
19048
+ const elements = [];
19049
+ if (!this.check(")")) {
19050
+ do {
19051
+ elements.push(this.parseType());
19052
+ } while (this.match(","));
19053
+ }
19054
+ this.expect(")");
19055
+ if (this.check("->")) {
19056
+ this.pos = saved;
19057
+ return this.parseFunctionType();
19058
+ }
19059
+ return { kind: "tuple", elements };
19082
19060
  }
19083
- if (this.check("module")) {
19061
+ if (token.kind === "float") {
19084
19062
  this.advance();
19085
- const modKind = this.expect("ident");
19086
- if (modKind.value === "library") {
19087
- isLibrary = true;
19088
- this.inLibraryMode = true;
19063
+ const filePart = this.filePath ? `${this.filePath}:` : "";
19064
+ this.warnings.push(`[DeprecatedType] ${filePart}line ${token.line}, col ${token.col}: 'float' is deprecated, use 'fixed' instead (\xD710000 fixed-point)`);
19065
+ type = { kind: "named", name: "float" };
19066
+ } else if (token.kind === "int" || token.kind === "bool" || token.kind === "fixed" || token.kind === "string" || token.kind === "void" || token.kind === "BlockPos") {
19067
+ this.advance();
19068
+ type = { kind: "named", name: token.kind };
19069
+ } else if (token.kind === "ident") {
19070
+ this.advance();
19071
+ if (token.value === "selector" && this.check("<")) {
19072
+ this.advance();
19073
+ const entityType = this.expect("ident").value;
19074
+ this.expect(">");
19075
+ type = { kind: "selector", entityType };
19076
+ } else if (token.value === "selector") {
19077
+ type = { kind: "selector" };
19078
+ } else if (token.value === "Option" && this.check("<")) {
19079
+ this.advance();
19080
+ const inner = this.parseType();
19081
+ this.expect(">");
19082
+ type = { kind: "option", inner };
19083
+ } else if (token.value === "double" || token.value === "byte" || token.value === "short" || token.value === "long" || token.value === "format_string") {
19084
+ type = { kind: "named", name: token.value };
19089
19085
  } else {
19090
- moduleName = modKind.value;
19086
+ type = { kind: "struct", name: token.value };
19091
19087
  }
19092
- this.match(";");
19088
+ } else {
19089
+ this.error(`Expected type, got '${token.value || token.kind}'. Valid types: int, float, bool, string, void, or a struct/enum name`);
19093
19090
  }
19094
- while (!this.check("eof")) {
19095
- try {
19096
- if (this.check("decorator") && this.peek().value.startsWith("@config")) {
19097
- const decorToken = this.advance();
19098
- const decorator = this.parseDecoratorValue(decorToken.value);
19099
- if (!this.check("let")) {
19100
- this.error("@config decorator must be followed by a let declaration");
19101
- }
19102
- const g = this.parseGlobalDecl(true);
19103
- g.configKey = decorator.args?.configKey;
19104
- g.configDefault = decorator.args?.configDefault;
19105
- globals.push(g);
19106
- } else if (this.check("let")) {
19107
- globals.push(this.parseGlobalDecl(true));
19108
- } else if (this.check("decorator") && this.peek().value === "@singleton") {
19109
- this.advance();
19110
- if (!this.check("struct")) {
19111
- this.error("@singleton decorator must be followed by a struct declaration");
19112
- }
19113
- const s = this.parseStructDecl();
19114
- s.isSingleton = true;
19115
- structs.push(s);
19116
- } else if (this.check("struct")) {
19117
- structs.push(this.parseStructDecl());
19118
- } else if (this.check("impl")) {
19119
- implBlocks.push(this.parseImplBlock());
19120
- } else if (this.check("interface")) {
19121
- interfaces.push(this.parseInterfaceDecl());
19122
- } else if (this.check("enum")) {
19123
- enums.push(this.parseEnumDecl());
19124
- } else if (this.check("const")) {
19125
- consts.push(this.parseConstDecl());
19126
- } else if (this.check("declare")) {
19127
- this.advance();
19128
- this.parseDeclareStub();
19129
- } else if (this.check("export")) {
19130
- declarations.push(this.parseExportedFnDecl());
19131
- } else if (this.check("import") || this.check("ident") && this.peek().value === "import") {
19132
- this.advance();
19133
- const importToken = this.peek();
19134
- const modName = this.expect("ident").value;
19135
- if (this.check("::")) {
19136
- this.advance();
19137
- let symbol;
19138
- if (this.check("*")) {
19139
- this.advance();
19140
- symbol = "*";
19141
- } else {
19142
- symbol = this.expect("ident").value;
19143
- }
19144
- this.match(";");
19145
- imports.push(this.withLoc({ moduleName: modName, symbol }, importToken));
19146
- } else {
19147
- this.match(";");
19148
- imports.push(this.withLoc({ moduleName: modName, symbol: void 0 }, importToken));
19149
- }
19150
- } else {
19151
- declarations.push(this.parseFnDecl());
19152
- }
19153
- } catch (err) {
19154
- if (err instanceof diagnostics_1.DiagnosticError) {
19155
- this.parseErrors.push(err);
19156
- this.syncToNextDecl();
19157
- } else {
19158
- throw err;
19159
- }
19091
+ while (this.match("[")) {
19092
+ this.expect("]");
19093
+ type = { kind: "array", elem: type };
19094
+ }
19095
+ return type;
19096
+ }
19097
+ parseFunctionType() {
19098
+ this.expect("(");
19099
+ const params = [];
19100
+ if (!this.check(")")) {
19101
+ do {
19102
+ params.push(this.parseType());
19103
+ } while (this.match(","));
19104
+ }
19105
+ this.expect(")");
19106
+ this.expect("->");
19107
+ const returnType = this.parseType();
19108
+ return { kind: "function_type", params, return: returnType };
19109
+ }
19110
+ /**
19111
+ * Try to parse `<Type, ...>` as explicit generic type arguments.
19112
+ * Returns the parsed type list if successful, null if this looks like a comparison.
19113
+ * Does NOT consume any tokens if it returns null.
19114
+ */
19115
+ tryParseTypeArgs() {
19116
+ const saved = this.pos;
19117
+ this.advance();
19118
+ const typeArgs = [];
19119
+ try {
19120
+ do {
19121
+ typeArgs.push(this.parseType());
19122
+ } while (this.match(","));
19123
+ if (!this.check(">")) {
19124
+ this.pos = saved;
19125
+ return null;
19160
19126
  }
19127
+ this.advance();
19128
+ return typeArgs;
19129
+ } catch {
19130
+ this.pos = saved;
19131
+ return null;
19161
19132
  }
19162
- return { namespace, moduleName, globals, declarations, structs, implBlocks, enums, consts, imports, interfaces, isLibrary };
19163
19133
  }
19164
19134
  // -------------------------------------------------------------------------
19165
- // Struct Declaration
19135
+ // Lambda lookahead helpers (needed by expr-parser)
19166
19136
  // -------------------------------------------------------------------------
19167
- parseStructDecl() {
19168
- const structToken = this.expect("struct");
19169
- const name = this.expect("ident").value;
19170
- const extendsName = this.match("extends") ? this.expect("ident").value : void 0;
19171
- this.expect("{");
19172
- const fields = [];
19173
- while (!this.check("}") && !this.check("eof")) {
19174
- const fieldName = this.expect("ident").value;
19175
- this.expect(":");
19176
- const fieldType = this.parseType();
19177
- fields.push({ name: fieldName, type: fieldType });
19178
- this.match(",");
19179
- }
19180
- this.expect("}");
19181
- return this.withLoc({ name, extends: extendsName, fields }, structToken);
19182
- }
19183
- parseEnumDecl() {
19184
- const enumToken = this.expect("enum");
19185
- const name = this.expect("ident").value;
19186
- this.expect("{");
19187
- const variants = [];
19188
- let nextValue = 0;
19189
- while (!this.check("}") && !this.check("eof")) {
19190
- const variantToken = this.expect("ident");
19191
- const variant = { name: variantToken.value };
19192
- if (this.check("(")) {
19193
- this.advance();
19194
- const fields = [];
19195
- while (!this.check(")") && !this.check("eof")) {
19196
- const fieldName = this.expect("ident").value;
19197
- this.expect(":");
19198
- const fieldType = this.parseType();
19199
- fields.push({ name: fieldName, type: fieldType });
19200
- if (!this.match(","))
19201
- break;
19137
+ isLambdaStart() {
19138
+ if (!this.check("("))
19139
+ return false;
19140
+ let offset = 1;
19141
+ if (this.peek(offset).kind !== ")") {
19142
+ while (true) {
19143
+ if (this.peek(offset).kind !== "ident") {
19144
+ return false;
19145
+ }
19146
+ offset += 1;
19147
+ if (this.peek(offset).kind === ":") {
19148
+ offset += 1;
19149
+ const consumed = this.typeTokenLength(offset);
19150
+ if (consumed === 0) {
19151
+ return false;
19152
+ }
19153
+ offset += consumed;
19154
+ }
19155
+ if (this.peek(offset).kind === ",") {
19156
+ offset += 1;
19157
+ continue;
19202
19158
  }
19203
- this.expect(")");
19204
- variant.fields = fields;
19205
- }
19206
- if (this.match("=")) {
19207
- const valueToken = this.expect("int_lit");
19208
- variant.value = parseInt(valueToken.value, 10);
19209
- nextValue = variant.value + 1;
19210
- } else {
19211
- variant.value = nextValue++;
19212
- }
19213
- variants.push(variant);
19214
- if (!this.match(",")) {
19215
19159
  break;
19216
19160
  }
19217
19161
  }
19218
- this.expect("}");
19219
- return this.withLoc({ name, variants }, enumToken);
19220
- }
19221
- parseImplBlock() {
19222
- const implToken = this.expect("impl");
19223
- let traitName;
19224
- let typeName;
19225
- const firstName = this.expect("ident").value;
19226
- if (this.match("for")) {
19227
- traitName = firstName;
19228
- typeName = this.expect("ident").value;
19229
- } else {
19230
- typeName = firstName;
19162
+ if (this.peek(offset).kind !== ")") {
19163
+ return false;
19231
19164
  }
19232
- this.expect("{");
19233
- const methods = [];
19234
- while (!this.check("}") && !this.check("eof")) {
19235
- methods.push(this.parseFnDecl(typeName));
19165
+ offset += 1;
19166
+ if (this.peek(offset).kind === "=>") {
19167
+ return true;
19236
19168
  }
19237
- this.expect("}");
19238
- return this.withLoc({ kind: "impl_block", traitName, typeName, methods }, implToken);
19169
+ if (this.peek(offset).kind === "->") {
19170
+ offset += 1;
19171
+ const consumed = this.typeTokenLength(offset);
19172
+ if (consumed === 0) {
19173
+ return false;
19174
+ }
19175
+ offset += consumed;
19176
+ return this.peek(offset).kind === "=>";
19177
+ }
19178
+ return false;
19239
19179
  }
19240
- /**
19241
- * Parse an interface declaration:
19242
- * interface <Name> {
19243
- * fn <method>(<params>): <retType>
19244
- * ...
19245
- * }
19246
- * Method signatures have no body — they are prototype-only.
19247
- */
19248
- parseInterfaceDecl() {
19249
- const ifaceToken = this.expect("interface");
19250
- const name = this.expect("ident").value;
19251
- this.expect("{");
19252
- const methods = [];
19253
- while (!this.check("}") && !this.check("eof")) {
19254
- const fnToken = this.expect("fn");
19255
- const methodName = this.expect("ident").value;
19256
- this.expect("(");
19257
- const params = this.parseInterfaceParams();
19258
- this.expect(")");
19259
- let returnType;
19260
- if (this.match(":")) {
19261
- returnType = this.parseType();
19180
+ typeTokenLength(offset) {
19181
+ const token = this.peek(offset);
19182
+ if (token.kind === "(") {
19183
+ let inner = offset + 1;
19184
+ if (this.peek(inner).kind !== ")") {
19185
+ while (true) {
19186
+ const consumed = this.typeTokenLength(inner);
19187
+ if (consumed === 0) {
19188
+ return 0;
19189
+ }
19190
+ inner += consumed;
19191
+ if (this.peek(inner).kind === ",") {
19192
+ inner += 1;
19193
+ continue;
19194
+ }
19195
+ break;
19196
+ }
19262
19197
  }
19263
- methods.push(this.withLoc({ name: methodName, params, returnType }, fnToken));
19198
+ if (this.peek(inner).kind !== ")") {
19199
+ return 0;
19200
+ }
19201
+ inner += 1;
19202
+ if (this.peek(inner).kind !== "->") {
19203
+ return 0;
19204
+ }
19205
+ inner += 1;
19206
+ const returnLen = this.typeTokenLength(inner);
19207
+ return returnLen === 0 ? 0 : inner + returnLen - offset;
19264
19208
  }
19265
- this.expect("}");
19266
- return this.withLoc({ name, methods }, ifaceToken);
19209
+ const isNamedType = token.kind === "int" || token.kind === "bool" || token.kind === "float" || token.kind === "fixed" || token.kind === "string" || token.kind === "void" || token.kind === "BlockPos" || token.kind === "ident";
19210
+ if (!isNamedType) {
19211
+ return 0;
19212
+ }
19213
+ let length = 1;
19214
+ while (this.peek(offset + length).kind === "[" && this.peek(offset + length + 1).kind === "]") {
19215
+ length += 2;
19216
+ }
19217
+ return length;
19218
+ }
19219
+ // -------------------------------------------------------------------------
19220
+ // Params parsing (used by decl-parser)
19221
+ // -------------------------------------------------------------------------
19222
+ parseParams(implTypeName) {
19223
+ const params = [];
19224
+ if (!this.check(")")) {
19225
+ do {
19226
+ const paramToken = this.expect("ident");
19227
+ const name = paramToken.value;
19228
+ let type;
19229
+ if (implTypeName && params.length === 0 && name === "self" && !this.check(":")) {
19230
+ type = { kind: "struct", name: implTypeName };
19231
+ } else {
19232
+ this.expect(":");
19233
+ type = this.parseType();
19234
+ }
19235
+ let defaultValue;
19236
+ if (this.match("=")) {
19237
+ defaultValue = this.parseExpr();
19238
+ }
19239
+ params.push(this.withLoc({ name, type, default: defaultValue }, paramToken));
19240
+ } while (this.match(","));
19241
+ }
19242
+ return params;
19267
19243
  }
19268
- /**
19269
- * Parse interface method params — like parseParams but allows bare `self`
19270
- * (no `:` required for the first param named 'self').
19271
- */
19272
19244
  parseInterfaceParams() {
19273
19245
  const params = [];
19274
19246
  if (!this.check(")")) {
@@ -19287,1731 +19259,1757 @@ var require_parser = __commonJS({
19287
19259
  }
19288
19260
  return params;
19289
19261
  }
19290
- parseConstDecl() {
19291
- const constToken = this.expect("const");
19292
- const name = this.expect("ident").value;
19293
- let type;
19294
- if (this.match(":")) {
19295
- type = this.parseType();
19296
- }
19297
- this.expect("=");
19298
- const value = this.parseLiteralExpr();
19299
- this.match(";");
19300
- const inferredType = type ?? (value.kind === "str_lit" ? { kind: "named", name: "string" } : value.kind === "bool_lit" ? { kind: "named", name: "bool" } : value.kind === "float_lit" ? { kind: "named", name: "fixed" } : { kind: "named", name: "int" });
19301
- return this.withLoc({ name, type: inferredType, value }, constToken);
19302
- }
19303
- parseGlobalDecl(mutable) {
19304
- const token = this.advance();
19305
- const name = this.expect("ident").value;
19306
- this.expect(":");
19307
- const type = this.parseType();
19308
- let init;
19309
- if (this.match("=")) {
19310
- init = this.parseExpr();
19311
- } else {
19312
- init = { kind: "int_lit", value: 0 };
19313
- }
19314
- this.match(";");
19315
- return this.withLoc({ kind: "global", name, type, init, mutable }, token);
19316
- }
19262
+ };
19263
+ exports2.TypeParser = TypeParser;
19264
+ }
19265
+ });
19266
+
19267
+ // ../../dist/src/parser/expr-parser.js
19268
+ var require_expr_parser = __commonJS({
19269
+ "../../dist/src/parser/expr-parser.js"(exports2) {
19270
+ "use strict";
19271
+ Object.defineProperty(exports2, "__esModule", { value: true });
19272
+ exports2.ExprParser = void 0;
19273
+ var lexer_1 = require_lexer();
19274
+ var type_parser_1 = require_type_parser();
19275
+ var utils_1 = require_utils();
19276
+ var ENTITY_TYPE_NAMES = /* @__PURE__ */ new Set([
19277
+ "entity",
19278
+ "Player",
19279
+ "Mob",
19280
+ "HostileMob",
19281
+ "PassiveMob",
19282
+ "Zombie",
19283
+ "Skeleton",
19284
+ "Creeper",
19285
+ "Spider",
19286
+ "Enderman",
19287
+ "Blaze",
19288
+ "Witch",
19289
+ "Slime",
19290
+ "ZombieVillager",
19291
+ "Husk",
19292
+ "Drowned",
19293
+ "Stray",
19294
+ "WitherSkeleton",
19295
+ "CaveSpider",
19296
+ "Pig",
19297
+ "Cow",
19298
+ "Sheep",
19299
+ "Chicken",
19300
+ "Villager",
19301
+ "WanderingTrader",
19302
+ "ArmorStand",
19303
+ "Item",
19304
+ "Arrow"
19305
+ ]);
19306
+ function computeIsSingle(raw) {
19307
+ if (/^@[spr](\[|$)/.test(raw))
19308
+ return true;
19309
+ if (/[\[,\s]limit=1[,\]\s]/.test(raw))
19310
+ return true;
19311
+ return false;
19312
+ }
19313
+ var ExprParser = class extends type_parser_1.TypeParser {
19317
19314
  // -------------------------------------------------------------------------
19318
- // Function Declaration
19315
+ // Expressions (Precedence Climbing)
19319
19316
  // -------------------------------------------------------------------------
19320
- /** Parse `export fn name(...)` — marks the function as exported (survives DCE). */
19321
- parseExportedFnDecl() {
19322
- this.expect("export");
19323
- const fn = this.parseFnDecl();
19324
- fn.isExported = true;
19325
- return fn;
19317
+ parseExpr() {
19318
+ return this.parseAssignment();
19326
19319
  }
19327
- parseFnDecl(implTypeName) {
19328
- const decorators = this.parseDecorators();
19329
- const watchObjective = decorators.find((decorator) => decorator.name === "watch")?.args?.objective;
19330
- let isExported;
19331
- const filteredDecorators = decorators.filter((d) => {
19332
- if (d.name === "keep") {
19333
- isExported = true;
19334
- return false;
19320
+ parseAssignment() {
19321
+ const left = this.parseBinaryExpr(1);
19322
+ const token = this.peek();
19323
+ if (token.kind === "=" || token.kind === "+=" || token.kind === "-=" || token.kind === "*=" || token.kind === "/=" || token.kind === "%=") {
19324
+ const op = this.advance().kind;
19325
+ if (left.kind === "ident") {
19326
+ const value = this.parseAssignment();
19327
+ return this.withLoc({ kind: "assign", target: left.name, op, value }, this.getLocToken(left) ?? token);
19328
+ }
19329
+ if (left.kind === "member") {
19330
+ const value = this.parseAssignment();
19331
+ return this.withLoc({ kind: "member_assign", obj: left.obj, field: left.field, op, value }, this.getLocToken(left) ?? token);
19332
+ }
19333
+ if (left.kind === "index") {
19334
+ const value = this.parseAssignment();
19335
+ return this.withLoc({ kind: "index_assign", obj: left.obj, index: left.index, op, value }, this.getLocToken(left) ?? token);
19335
19336
  }
19336
- return true;
19337
- });
19338
- const fnToken = this.expect("fn");
19339
- const name = this.expect("ident").value;
19340
- let typeParams;
19341
- if (this.check("<")) {
19342
- this.advance();
19343
- typeParams = [];
19344
- do {
19345
- typeParams.push(this.expect("ident").value);
19346
- } while (this.match(","));
19347
- this.expect(">");
19348
19337
  }
19349
- this.expect("(");
19350
- const params = this.parseParams(implTypeName);
19351
- this.expect(")");
19352
- let returnType = { kind: "named", name: "void" };
19353
- if (this.match("->") || this.match(":")) {
19354
- returnType = this.parseType();
19338
+ return left;
19339
+ }
19340
+ parseBinaryExpr(minPrec) {
19341
+ let left = this.parseUnaryExpr();
19342
+ while (true) {
19343
+ const op = this.peek().kind;
19344
+ if (!utils_1.BINARY_OPS.has(op))
19345
+ break;
19346
+ const prec = utils_1.PRECEDENCE[op];
19347
+ if (prec < minPrec)
19348
+ break;
19349
+ const opToken = this.advance();
19350
+ if (op === "is") {
19351
+ const entityType = this.parseEntityTypeName();
19352
+ left = this.withLoc({ kind: "is_check", expr: left, entityType }, this.getLocToken(left) ?? opToken);
19353
+ continue;
19354
+ }
19355
+ const right = this.parseBinaryExpr(prec + 1);
19356
+ left = this.withLoc({ kind: "binary", op, left, right }, this.getLocToken(left) ?? opToken);
19355
19357
  }
19356
- const body = this.parseBlock();
19357
- const closingBraceLine = this.tokens[this.pos - 1]?.line;
19358
- const fn = this.withLoc({
19359
- name,
19360
- typeParams,
19361
- params,
19362
- returnType,
19363
- decorators: filteredDecorators,
19364
- body,
19365
- isLibraryFn: this.inLibraryMode || void 0,
19366
- isExported,
19367
- watchObjective
19368
- }, fnToken);
19369
- if (fn.span && closingBraceLine)
19370
- fn.span.endLine = closingBraceLine;
19371
- return fn;
19358
+ return left;
19372
19359
  }
19373
- /** Parse a `declare fn name(params): returnType;` stub — no body, just discard. */
19374
- parseDeclareStub() {
19375
- this.expect("fn");
19376
- this.expect("ident");
19377
- this.expect("(");
19378
- let depth = 1;
19379
- while (!this.check("eof") && depth > 0) {
19380
- const t = this.advance();
19381
- if (t.kind === "(")
19382
- depth++;
19383
- else if (t.kind === ")")
19384
- depth--;
19360
+ parseUnaryExpr() {
19361
+ if (this.match("!")) {
19362
+ const bangToken = this.tokens[this.pos - 1];
19363
+ const operand = this.parseUnaryExpr();
19364
+ return this.withLoc({ kind: "unary", op: "!", operand }, bangToken);
19385
19365
  }
19386
- if (this.match(":") || this.match("->")) {
19387
- this.parseType();
19366
+ if (this.check("-") && !this.isSubtraction()) {
19367
+ const minusToken = this.advance();
19368
+ const operand = this.parseUnaryExpr();
19369
+ return this.withLoc({ kind: "unary", op: "-", operand }, minusToken);
19388
19370
  }
19389
- this.match(";");
19371
+ return this.parsePostfixExpr();
19390
19372
  }
19391
- parseDecorators() {
19392
- const decorators = [];
19393
- while (this.check("decorator")) {
19394
- const token = this.advance();
19395
- const decorator = this.parseDecoratorValue(token.value);
19396
- decorators.push(decorator);
19373
+ parseEntityTypeName() {
19374
+ const token = this.expect("ident");
19375
+ if (ENTITY_TYPE_NAMES.has(token.value)) {
19376
+ return token.value;
19397
19377
  }
19398
- return decorators;
19378
+ this.error(`Unknown entity type '${token.value}'. Valid types: ${[...ENTITY_TYPE_NAMES].slice(0, 6).join(", ")}, ...`);
19399
19379
  }
19400
- parseDecoratorValue(value) {
19401
- const match = value.match(/^@([A-Za-z_][A-Za-z0-9_-]*)(?:\((.*)\))?$/s);
19402
- if (!match) {
19403
- this.error(`Invalid decorator: ${value}`);
19404
- }
19405
- const name = match[1];
19406
- const argsStr = match[2];
19407
- if (!argsStr) {
19408
- return { name };
19409
- }
19410
- if (name === "profile" || name === "benchmark" || name === "memoize") {
19411
- this.error(`@${name} decorator does not accept arguments`);
19412
- }
19413
- const args = {};
19414
- if (name === "on") {
19415
- const eventTypeMatch = argsStr.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
19416
- if (eventTypeMatch) {
19417
- args.eventType = eventTypeMatch[1];
19418
- return { name, args };
19419
- }
19420
- }
19421
- if (name === "watch" || name === "on_trigger" || name === "on_advancement" || name === "on_craft" || name === "on_join_team") {
19422
- const strMatch = argsStr.match(/^"([^"]*)"$/);
19423
- if (strMatch) {
19424
- if (name === "watch") {
19425
- args.objective = strMatch[1];
19426
- } else if (name === "on_trigger") {
19427
- args.trigger = strMatch[1];
19428
- } else if (name === "on_advancement") {
19429
- args.advancement = strMatch[1];
19430
- } else if (name === "on_craft") {
19431
- args.item = strMatch[1];
19432
- } else if (name === "on_join_team") {
19433
- args.team = strMatch[1];
19380
+ isSubtraction() {
19381
+ if (this.pos === 0)
19382
+ return false;
19383
+ const prev = this.tokens[this.pos - 1];
19384
+ return ["int_lit", "float_lit", "ident", ")", "]"].includes(prev.kind);
19385
+ }
19386
+ parsePostfixExpr() {
19387
+ let expr = this.parsePrimaryExpr();
19388
+ while (true) {
19389
+ if (expr.kind === "ident" && this.check("<")) {
19390
+ const typeArgs = this.tryParseTypeArgs();
19391
+ if (typeArgs !== null && this.check("(")) {
19392
+ const openParenToken = this.peek();
19393
+ this.advance();
19394
+ const args = this.parseArgs();
19395
+ this.expect(")");
19396
+ expr = this.withLoc({ kind: "call", fn: expr.name, args, typeArgs }, this.getLocToken(expr) ?? openParenToken);
19397
+ continue;
19434
19398
  }
19435
- return { name, args };
19436
- }
19437
- }
19438
- if (name === "config") {
19439
- const configMatch = argsStr.match(/^"([^"]+)"\s*,\s*default\s*:\s*(-?\d+(?:\.\d+)?)$/);
19440
- if (configMatch) {
19441
- return { name, args: { configKey: configMatch[1], configDefault: parseFloat(configMatch[2]) } };
19442
- }
19443
- const keyOnlyMatch = argsStr.match(/^"([^"]+)"$/);
19444
- if (keyOnlyMatch) {
19445
- return { name, args: { configKey: keyOnlyMatch[1] } };
19446
- }
19447
- this.error(`Invalid @config syntax. Expected: @config("key", default: value) or @config("key")`);
19448
- }
19449
- if (name === "deprecated") {
19450
- const strMatch = argsStr.match(/^"([^"]*)"$/);
19451
- if (strMatch) {
19452
- return { name, args: { message: strMatch[1] } };
19453
19399
  }
19454
- return { name, args: {} };
19455
- }
19456
- if (name === "test") {
19457
- const strMatch = argsStr.match(/^"([^"]*)"$/);
19458
- if (strMatch) {
19459
- return { name, args: { testLabel: strMatch[1] } };
19460
- }
19461
- return { name, args: { testLabel: "" } };
19462
- }
19463
- if (name === "require_on_load") {
19464
- const rawArgs = [];
19465
- for (const part of argsStr.split(",")) {
19466
- const trimmed = part.trim();
19467
- const identMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
19468
- if (identMatch) {
19469
- rawArgs.push({ kind: "string", value: identMatch[1] });
19470
- } else {
19471
- const strMatch = trimmed.match(/^"([^"]*)"$/);
19472
- if (strMatch) {
19473
- rawArgs.push({ kind: "string", value: strMatch[1] });
19400
+ if (this.match("(")) {
19401
+ const openParenToken = this.tokens[this.pos - 1];
19402
+ if (expr.kind === "ident") {
19403
+ const args2 = this.parseArgs();
19404
+ this.expect(")");
19405
+ expr = this.withLoc({ kind: "call", fn: expr.name, args: args2 }, this.getLocToken(expr) ?? openParenToken);
19406
+ continue;
19407
+ }
19408
+ if (expr.kind === "member") {
19409
+ if (expr.field === "unwrap_or") {
19410
+ const defaultExpr = this.parseExpr();
19411
+ this.expect(")");
19412
+ expr = this.withLoc({ kind: "unwrap_or", opt: expr.obj, default_: defaultExpr }, this.getLocToken(expr) ?? openParenToken);
19413
+ continue;
19414
+ }
19415
+ const methodMap = {
19416
+ "tag": "__entity_tag",
19417
+ "untag": "__entity_untag",
19418
+ "has_tag": "__entity_has_tag",
19419
+ "push": "__array_push",
19420
+ "pop": "__array_pop",
19421
+ "add": "set_add",
19422
+ "contains": "set_contains",
19423
+ "remove": "set_remove",
19424
+ "clear": "set_clear"
19425
+ };
19426
+ const internalFn = methodMap[expr.field];
19427
+ if (internalFn) {
19428
+ const args3 = this.parseArgs();
19429
+ this.expect(")");
19430
+ expr = this.withLoc({ kind: "call", fn: internalFn, args: [expr.obj, ...args3] }, this.getLocToken(expr) ?? openParenToken);
19431
+ continue;
19474
19432
  }
19433
+ const args2 = this.parseArgs();
19434
+ this.expect(")");
19435
+ expr = this.withLoc({ kind: "call", fn: expr.field, args: [expr.obj, ...args2] }, this.getLocToken(expr) ?? openParenToken);
19436
+ continue;
19475
19437
  }
19438
+ const args = this.parseArgs();
19439
+ this.expect(")");
19440
+ expr = this.withLoc({ kind: "invoke", callee: expr, args }, this.getLocToken(expr) ?? openParenToken);
19441
+ continue;
19476
19442
  }
19477
- return { name, rawArgs };
19478
- }
19479
- for (const part of argsStr.split(",")) {
19480
- const [key, val] = part.split("=").map((s) => s.trim());
19481
- if (key === "rate") {
19482
- args.rate = parseInt(val, 10);
19483
- } else if (key === "ticks") {
19484
- args.ticks = parseInt(val, 10);
19485
- } else if (key === "batch") {
19486
- args.batch = parseInt(val, 10);
19487
- } else if (key === "onDone") {
19488
- args.onDone = val.replace(/^["']|["']$/g, "");
19489
- } else if (key === "trigger") {
19490
- args.trigger = val;
19491
- } else if (key === "advancement") {
19492
- args.advancement = val;
19493
- } else if (key === "item") {
19494
- args.item = val;
19495
- } else if (key === "team") {
19496
- args.team = val;
19497
- } else if (key === "max") {
19498
- args.max = parseInt(val, 10);
19443
+ if (this.match("[")) {
19444
+ const index = this.parseExpr();
19445
+ this.expect("]");
19446
+ expr = this.withLoc({ kind: "index", obj: expr, index }, this.getLocToken(expr) ?? this.tokens[this.pos - 1]);
19447
+ continue;
19448
+ }
19449
+ if (this.match(".")) {
19450
+ const field = this.expect("ident").value;
19451
+ expr = this.withLoc({ kind: "member", obj: expr, field }, this.getLocToken(expr) ?? this.tokens[this.pos - 1]);
19452
+ continue;
19453
+ }
19454
+ if (this.check("as") && this.isTypeCastAs()) {
19455
+ const asToken = this.advance();
19456
+ const targetType = this.parseType();
19457
+ expr = this.withLoc({ kind: "type_cast", expr, targetType }, this.getLocToken(expr) ?? asToken);
19458
+ continue;
19499
19459
  }
19460
+ break;
19500
19461
  }
19501
- return { name, args };
19462
+ return expr;
19502
19463
  }
19503
- parseParams(implTypeName) {
19504
- const params = [];
19505
- if (!this.check(")")) {
19506
- do {
19507
- const paramToken = this.expect("ident");
19508
- const name = paramToken.value;
19509
- let type;
19510
- if (implTypeName && params.length === 0 && name === "self" && !this.check(":")) {
19511
- type = { kind: "struct", name: implTypeName };
19512
- } else {
19513
- this.expect(":");
19514
- type = this.parseType();
19515
- }
19516
- let defaultValue;
19517
- if (this.match("=")) {
19518
- defaultValue = this.parseExpr();
19519
- }
19520
- params.push(this.withLoc({ name, type, default: defaultValue }, paramToken));
19521
- } while (this.match(","));
19522
- }
19523
- return params;
19524
- }
19525
- parseType() {
19526
- const token = this.peek();
19527
- let type;
19528
- if (token.kind === "(") {
19529
- const saved = this.pos;
19530
- this.advance();
19531
- const elements = [];
19532
- if (!this.check(")")) {
19533
- do {
19534
- elements.push(this.parseType());
19535
- } while (this.match(","));
19536
- }
19537
- this.expect(")");
19538
- if (this.check("->")) {
19539
- this.pos = saved;
19540
- return this.parseFunctionType();
19541
- }
19542
- return { kind: "tuple", elements };
19543
- }
19544
- if (token.kind === "float") {
19545
- this.advance();
19546
- const filePart = this.filePath ? `${this.filePath}:` : "";
19547
- this.warnings.push(`[DeprecatedType] ${filePart}line ${token.line}, col ${token.col}: 'float' is deprecated, use 'fixed' instead (\xD710000 fixed-point)`);
19548
- type = { kind: "named", name: "float" };
19549
- } else if (token.kind === "int" || token.kind === "bool" || token.kind === "fixed" || token.kind === "string" || token.kind === "void" || token.kind === "BlockPos") {
19550
- this.advance();
19551
- type = { kind: "named", name: token.kind };
19552
- } else if (token.kind === "ident") {
19553
- this.advance();
19554
- if (token.value === "selector" && this.check("<")) {
19555
- this.advance();
19556
- const entityType = this.expect("ident").value;
19557
- this.expect(">");
19558
- type = { kind: "selector", entityType };
19559
- } else if (token.value === "selector") {
19560
- type = { kind: "selector" };
19561
- } else if (token.value === "Option" && this.check("<")) {
19562
- this.advance();
19563
- const inner = this.parseType();
19564
- this.expect(">");
19565
- type = { kind: "option", inner };
19566
- } else if (token.value === "double" || token.value === "byte" || token.value === "short" || token.value === "long" || token.value === "format_string") {
19567
- type = { kind: "named", name: token.value };
19568
- } else {
19569
- type = { kind: "struct", name: token.value };
19570
- }
19571
- } else {
19572
- this.error(`Expected type, got '${token.kind}'`);
19573
- }
19574
- while (this.match("[")) {
19575
- this.expect("]");
19576
- type = { kind: "array", elem: type };
19577
- }
19578
- return type;
19464
+ isTypeCastAs() {
19465
+ const next = this.tokens[this.pos + 1];
19466
+ if (!next)
19467
+ return false;
19468
+ const typeStartTokens = /* @__PURE__ */ new Set(["int", "bool", "float", "fixed", "string", "void", "BlockPos", "("]);
19469
+ if (typeStartTokens.has(next.kind))
19470
+ return true;
19471
+ if (next.kind === "ident" && (next.value === "double" || next.value === "byte" || next.value === "short" || next.value === "long" || next.value === "selector" || next.value === "Option"))
19472
+ return true;
19473
+ return false;
19579
19474
  }
19580
- parseFunctionType() {
19581
- this.expect("(");
19582
- const params = [];
19475
+ parseArgs() {
19476
+ const args = [];
19583
19477
  if (!this.check(")")) {
19584
19478
  do {
19585
- params.push(this.parseType());
19479
+ args.push(this.parseExpr());
19586
19480
  } while (this.match(","));
19587
19481
  }
19588
- this.expect(")");
19589
- this.expect("->");
19590
- const returnType = this.parseType();
19591
- return { kind: "function_type", params, return: returnType };
19482
+ return args;
19592
19483
  }
19593
- // -------------------------------------------------------------------------
19594
- // Block & Statements
19595
- // -------------------------------------------------------------------------
19596
- parseBlock() {
19597
- this.expect("{");
19598
- const stmts = [];
19599
- while (!this.check("}") && !this.check("eof")) {
19600
- try {
19601
- stmts.push(this.parseStmt());
19602
- } catch (err) {
19603
- if (err instanceof diagnostics_1.DiagnosticError) {
19604
- this.parseErrors.push(err);
19605
- this.syncToNextStmt();
19606
- } else {
19607
- throw err;
19484
+ parsePrimaryExpr() {
19485
+ const token = this.peek();
19486
+ if (token.kind === "ident" && this.peek(1).kind === "::") {
19487
+ const typeToken = this.advance();
19488
+ this.expect("::");
19489
+ const memberToken = this.expect("ident");
19490
+ if (this.check("(")) {
19491
+ const isNamedArgs = this.peek(1).kind === "ident" && this.peek(2).kind === ":";
19492
+ if (isNamedArgs) {
19493
+ this.advance();
19494
+ const args2 = [];
19495
+ while (!this.check(")") && !this.check("eof")) {
19496
+ const fieldName = this.expect("ident").value;
19497
+ this.expect(":");
19498
+ const value = this.parseExpr();
19499
+ args2.push({ name: fieldName, value });
19500
+ if (!this.match(","))
19501
+ break;
19502
+ }
19503
+ this.expect(")");
19504
+ return this.withLoc({ kind: "enum_construct", enumName: typeToken.value, variant: memberToken.value, args: args2 }, typeToken);
19608
19505
  }
19506
+ this.advance();
19507
+ const args = this.parseArgs();
19508
+ this.expect(")");
19509
+ return this.withLoc({ kind: "static_call", type: typeToken.value, method: memberToken.value, args }, typeToken);
19609
19510
  }
19511
+ return this.withLoc({ kind: "path_expr", enumName: typeToken.value, variant: memberToken.value }, typeToken);
19610
19512
  }
19611
- this.expect("}");
19612
- return stmts;
19613
- }
19614
- parseStmt() {
19615
- if (this.check("let")) {
19616
- return this.parseLetStmt();
19513
+ if (token.kind === "ident" && this.peek(1).kind === "=>") {
19514
+ return this.parseSingleParamLambda();
19617
19515
  }
19618
- if (this.check("const")) {
19619
- return this.parseLocalConstDecl();
19516
+ if (token.kind === "int_lit") {
19517
+ this.advance();
19518
+ return this.withLoc({ kind: "int_lit", value: parseInt(token.value, 10) }, token);
19620
19519
  }
19621
- if (this.check("return")) {
19622
- return this.parseReturnStmt();
19520
+ if (token.kind === "float_lit") {
19521
+ this.advance();
19522
+ return this.withLoc({ kind: "float_lit", value: parseFloat(token.value) }, token);
19623
19523
  }
19624
- if (this.check("break")) {
19625
- const token = this.advance();
19626
- if (this.check("ident")) {
19627
- const labelToken = this.advance();
19628
- this.match(";");
19629
- return this.withLoc({ kind: "break_label", label: labelToken.value }, token);
19630
- }
19631
- this.match(";");
19632
- return this.withLoc({ kind: "break" }, token);
19524
+ if (token.kind === "rel_coord") {
19525
+ this.advance();
19526
+ return this.withLoc({ kind: "rel_coord", value: token.value }, token);
19633
19527
  }
19634
- if (this.check("continue")) {
19635
- const token = this.advance();
19636
- if (this.check("ident")) {
19637
- const labelToken = this.advance();
19638
- this.match(";");
19639
- return this.withLoc({ kind: "continue_label", label: labelToken.value }, token);
19640
- }
19641
- this.match(";");
19642
- return this.withLoc({ kind: "continue" }, token);
19528
+ if (token.kind === "local_coord") {
19529
+ this.advance();
19530
+ return this.withLoc({ kind: "local_coord", value: token.value }, token);
19643
19531
  }
19644
- if (this.check("if")) {
19645
- return this.parseIfStmt();
19532
+ if (token.kind === "byte_lit") {
19533
+ this.advance();
19534
+ return this.withLoc({ kind: "byte_lit", value: parseInt(token.value.slice(0, -1), 10) }, token);
19646
19535
  }
19647
- if (this.check("ident") && this.peek(1).kind === ":") {
19648
- const labelToken = this.advance();
19649
- const colonToken = this.advance();
19650
- let loopStmt;
19651
- if (this.check("while")) {
19652
- loopStmt = this.parseWhileStmt();
19653
- } else if (this.check("for")) {
19654
- loopStmt = this.parseForStmt();
19655
- } else if (this.check("foreach")) {
19656
- loopStmt = this.parseForeachStmt();
19657
- } else if (this.check("repeat")) {
19658
- loopStmt = this.parseRepeatStmt();
19659
- } else {
19660
- throw new diagnostics_1.DiagnosticError("ParseError", `Expected loop statement after label '${labelToken.value}:', found '${this.peek().kind}'`, { line: labelToken.line, col: labelToken.col });
19661
- }
19662
- return this.withLoc({ kind: "labeled_loop", label: labelToken.value, body: loopStmt }, labelToken);
19536
+ if (token.kind === "short_lit") {
19537
+ this.advance();
19538
+ return this.withLoc({ kind: "short_lit", value: parseInt(token.value.slice(0, -1), 10) }, token);
19663
19539
  }
19664
- if (this.check("while")) {
19665
- return this.parseWhileStmt();
19540
+ if (token.kind === "long_lit") {
19541
+ this.advance();
19542
+ return this.withLoc({ kind: "long_lit", value: parseInt(token.value.slice(0, -1), 10) }, token);
19666
19543
  }
19667
- if (this.check("do")) {
19668
- return this.parseDoWhileStmt();
19544
+ if (token.kind === "double_lit") {
19545
+ this.advance();
19546
+ return this.withLoc({ kind: "double_lit", value: parseFloat(token.value.slice(0, -1)) }, token);
19669
19547
  }
19670
- if (this.check("repeat")) {
19671
- return this.parseRepeatStmt();
19548
+ if (token.kind === "string_lit") {
19549
+ this.advance();
19550
+ return this.parseStringExpr(token);
19672
19551
  }
19673
- if (this.check("for")) {
19674
- return this.parseForStmt();
19552
+ if (token.kind === "f_string") {
19553
+ this.advance();
19554
+ return this.parseFStringExpr(token);
19675
19555
  }
19676
- if (this.check("foreach")) {
19677
- return this.parseForeachStmt();
19556
+ if (token.kind === "mc_name") {
19557
+ this.advance();
19558
+ return this.withLoc({ kind: "mc_name", value: token.value.slice(1) }, token);
19678
19559
  }
19679
- if (this.check("match")) {
19680
- return this.parseMatchStmt();
19560
+ if (token.kind === "true") {
19561
+ this.advance();
19562
+ return this.withLoc({ kind: "bool_lit", value: true }, token);
19681
19563
  }
19682
- if (this.check("as")) {
19683
- return this.parseAsStmt();
19564
+ if (token.kind === "false") {
19565
+ this.advance();
19566
+ return this.withLoc({ kind: "bool_lit", value: false }, token);
19684
19567
  }
19685
- if (this.check("at")) {
19686
- return this.parseAtStmt();
19568
+ if (token.kind === "range_lit") {
19569
+ this.advance();
19570
+ return this.withLoc({ kind: "range_lit", range: this.parseRangeValue(token.value) }, token);
19687
19571
  }
19688
- if (this.check("execute")) {
19689
- return this.parseExecuteStmt();
19572
+ if (token.kind === "selector") {
19573
+ this.advance();
19574
+ return this.withLoc({
19575
+ kind: "selector",
19576
+ raw: token.value,
19577
+ isSingle: computeIsSingle(token.value),
19578
+ sel: this.parseSelectorValue(token.value)
19579
+ }, token);
19690
19580
  }
19691
- if (this.check("raw_cmd")) {
19692
- const token = this.advance();
19693
- const cmd = token.value;
19694
- this.match(";");
19695
- return this.withLoc({ kind: "raw", cmd }, token);
19581
+ if (token.kind === "ident" && this.peek(1).kind === "{" && this.peek(2).kind === "ident" && this.peek(3).kind === ":") {
19582
+ this.advance();
19583
+ return this.parseStructLit();
19696
19584
  }
19697
- return this.parseExprStmt();
19698
- }
19699
- parseLetStmt() {
19700
- const letToken = this.expect("let");
19701
- if (this.check("(")) {
19585
+ if (token.kind === "ident" && token.value === "Some" && this.peek(1).kind === "(") {
19702
19586
  this.advance();
19703
- const names = [];
19704
- do {
19705
- names.push(this.expect("ident").value);
19706
- } while (this.match(","));
19587
+ this.advance();
19588
+ const value = this.parseExpr();
19707
19589
  this.expect(")");
19708
- let type2;
19709
- if (this.match(":")) {
19710
- type2 = this.parseType();
19711
- }
19712
- this.expect("=");
19713
- const init2 = this.parseExpr();
19714
- this.match(";");
19715
- return this.withLoc({ kind: "let_destruct", names, type: type2, init: init2 }, letToken);
19590
+ return this.withLoc({ kind: "some_lit", value }, token);
19716
19591
  }
19717
- const name = this.expect("ident").value;
19718
- let type;
19719
- if (this.match(":")) {
19720
- type = this.parseType();
19721
- }
19722
- this.expect("=");
19723
- const init = this.parseExpr();
19724
- this.match(";");
19725
- return this.withLoc({ kind: "let", name, type, init }, letToken);
19726
- }
19727
- parseLocalConstDecl() {
19728
- const constToken = this.expect("const");
19729
- const name = this.expect("ident").value;
19730
- this.expect(":");
19731
- const type = this.parseType();
19732
- this.expect("=");
19733
- const value = this.parseExpr();
19734
- this.match(";");
19735
- return this.withLoc({ kind: "const_decl", name, type, value }, constToken);
19736
- }
19737
- parseReturnStmt() {
19738
- const returnToken = this.expect("return");
19739
- let value;
19740
- if (!this.check(";") && !this.check("}") && !this.check("eof")) {
19741
- value = this.parseExpr();
19742
- }
19743
- this.match(";");
19744
- return this.withLoc({ kind: "return", value }, returnToken);
19745
- }
19746
- parseIfStmt() {
19747
- const ifToken = this.expect("if");
19748
- if (this.check("let") && this.peek(1).kind === "ident" && this.peek(1).value === "Some") {
19749
- this.advance();
19750
- this.advance();
19751
- this.expect("(");
19752
- const binding = this.expect("ident").value;
19753
- this.expect(")");
19754
- this.expect("=");
19755
- const init = this.parseExpr();
19756
- const then2 = this.parseBlock();
19757
- let else_2;
19758
- if (this.match("else")) {
19759
- if (this.check("if")) {
19760
- else_2 = [this.parseIfStmt()];
19761
- } else {
19762
- else_2 = this.parseBlock();
19763
- }
19764
- }
19765
- return this.withLoc({ kind: "if_let_some", binding, init, then: then2, else_: else_2 }, ifToken);
19766
- }
19767
- const cond = this.parseParenOptionalCond();
19768
- const then = this.parseBlock();
19769
- let else_;
19770
- if (this.match("else")) {
19771
- if (this.check("if")) {
19772
- else_ = [this.parseIfStmt()];
19773
- } else {
19774
- else_ = this.parseBlock();
19775
- }
19776
- }
19777
- return this.withLoc({ kind: "if", cond, then, else_ }, ifToken);
19778
- }
19779
- parseWhileStmt() {
19780
- const whileToken = this.expect("while");
19781
- if (this.check("let") && this.peek(1).kind === "ident" && this.peek(1).value === "Some") {
19782
- this.advance();
19592
+ if (token.kind === "ident" && token.value === "None") {
19783
19593
  this.advance();
19784
- this.expect("(");
19785
- const binding = this.expect("ident").value;
19786
- this.expect(")");
19787
- this.expect("=");
19788
- const init = this.parseExpr();
19789
- const body2 = this.parseBlock();
19790
- return this.withLoc({ kind: "while_let_some", binding, init, body: body2 }, whileToken);
19791
- }
19792
- const cond = this.parseParenOptionalCond();
19793
- const body = this.parseBlock();
19794
- return this.withLoc({ kind: "while", cond, body }, whileToken);
19795
- }
19796
- parseDoWhileStmt() {
19797
- const doToken = this.expect("do");
19798
- const body = this.parseBlock();
19799
- this.expect("while");
19800
- const cond = this.parseParenOptionalCond();
19801
- this.match(";");
19802
- return this.withLoc({ kind: "do_while", cond, body }, doToken);
19803
- }
19804
- parseRepeatStmt() {
19805
- const repeatToken = this.expect("repeat");
19806
- const countToken = this.expect("int_lit");
19807
- const count = parseInt(countToken.value, 10);
19808
- const body = this.parseBlock();
19809
- return this.withLoc({ kind: "repeat", count, body }, repeatToken);
19810
- }
19811
- parseParenOptionalCond() {
19812
- if (this.match("(")) {
19813
- const cond = this.parseExpr();
19814
- this.expect(")");
19815
- return cond;
19816
- }
19817
- return this.parseExpr();
19818
- }
19819
- parseForStmt() {
19820
- const forToken = this.expect("for");
19821
- if (this.check("ident") && this.peek(1).kind === "in") {
19822
- return this.parseForRangeStmt(forToken);
19594
+ return this.withLoc({ kind: "none_lit" }, token);
19823
19595
  }
19824
- this.expect("(");
19825
- if (this.check("let") && this.peek(1).kind === "ident" && this.peek(2).kind === "in" && this.peek(3).kind === "ident" && this.peek(4).kind === ",") {
19596
+ if (token.kind === "ident") {
19826
19597
  this.advance();
19827
- const binding = this.expect("ident").value;
19828
- this.expect("in");
19829
- const arrayName = this.expect("ident").value;
19830
- this.expect(",");
19831
- const lenExpr = this.parseExpr();
19832
- this.expect(")");
19833
- const body2 = this.parseBlock();
19834
- return this.withLoc({ kind: "for_in_array", binding, arrayName, lenExpr, body: body2 }, forToken);
19835
- }
19836
- let init;
19837
- if (this.check("let")) {
19838
- const letToken = this.expect("let");
19839
- const name = this.expect("ident").value;
19840
- let type;
19841
- if (this.match(":")) {
19842
- type = this.parseType();
19843
- }
19844
- this.expect("=");
19845
- const initExpr = this.parseExpr();
19846
- const initStmt = { kind: "let", name, type, init: initExpr };
19847
- init = this.withLoc(initStmt, letToken);
19598
+ return this.withLoc({ kind: "ident", name: token.value }, token);
19848
19599
  }
19849
- this.expect(";");
19850
- const cond = this.parseExpr();
19851
- this.expect(";");
19852
- const step = this.parseExpr();
19853
- this.expect(")");
19854
- const body = this.parseBlock();
19855
- return this.withLoc({ kind: "for", init, cond, step, body }, forToken);
19856
- }
19857
- parseForRangeStmt(forToken) {
19858
- const varName = this.expect("ident").value;
19859
- this.expect("in");
19860
- let start;
19861
- let end;
19862
- let inclusive = false;
19863
- if (this.check("range_lit")) {
19864
- const rangeToken = this.advance();
19865
- const raw = rangeToken.value;
19866
- const incl = raw.includes("..=");
19867
- inclusive = incl;
19868
- const range = this.parseRangeValue(raw);
19869
- start = this.withLoc({ kind: "int_lit", value: range.min ?? 0 }, rangeToken);
19870
- if (range.max !== null && range.max !== void 0) {
19871
- end = this.withLoc({ kind: "int_lit", value: range.max }, rangeToken);
19872
- } else {
19873
- end = this.parseUnaryExpr();
19600
+ if (token.kind === "(") {
19601
+ if (this.isBlockPosLiteral()) {
19602
+ return this.parseBlockPos();
19874
19603
  }
19875
- } else {
19876
- const arrayOrStart = this.parseExpr();
19877
- if (!this.check("range_lit")) {
19878
- const body2 = this.parseBlock();
19879
- return this.withLoc({ kind: "for_each", binding: varName, array: arrayOrStart, body: body2 }, forToken);
19604
+ if (this.isLambdaStart()) {
19605
+ return this.parseLambdaExpr();
19880
19606
  }
19881
- start = arrayOrStart;
19882
- if (this.check("range_lit")) {
19883
- const rangeOp = this.advance();
19884
- inclusive = rangeOp.value.includes("=");
19885
- const afterOp = rangeOp.value.replace(/^\.\.=?/, "");
19886
- if (afterOp.length > 0) {
19887
- end = this.withLoc({ kind: "int_lit", value: parseInt(afterOp, 10) }, rangeOp);
19888
- } else {
19889
- end = this.parseExpr();
19607
+ this.advance();
19608
+ const first = this.parseExpr();
19609
+ if (this.match(",")) {
19610
+ const elements = [first];
19611
+ if (!this.check(")")) {
19612
+ do {
19613
+ elements.push(this.parseExpr());
19614
+ } while (this.match(","));
19890
19615
  }
19891
- } else {
19892
- this.error("Expected .. or ..= in for-range expression");
19893
- start = this.withLoc({ kind: "int_lit", value: 0 }, this.peek());
19894
- end = this.withLoc({ kind: "int_lit", value: 0 }, this.peek());
19895
- }
19896
- }
19897
- const body = this.parseBlock();
19898
- return this.withLoc({ kind: "for_range", varName, start, end, inclusive, body }, forToken);
19899
- }
19900
- parseForeachStmt() {
19901
- const foreachToken = this.expect("foreach");
19902
- this.expect("(");
19903
- const binding = this.expect("ident").value;
19904
- this.expect("in");
19905
- const iterable = this.parseExpr();
19906
- this.expect(")");
19907
- let executeContext;
19908
- const execIdentKeywords = ["positioned", "rotated", "facing", "anchored", "align", "on", "summon"];
19909
- if (this.check("as") || this.check("at") || this.check("in") || this.check("ident") && execIdentKeywords.includes(this.peek().value)) {
19910
- let context = "";
19911
- while (!this.check("{") && !this.check("eof")) {
19912
- context += this.advance().value + " ";
19616
+ this.expect(")");
19617
+ return this.withLoc({ kind: "tuple_lit", elements }, token);
19913
19618
  }
19914
- executeContext = context.trim();
19619
+ this.expect(")");
19620
+ return first;
19915
19621
  }
19916
- const body = this.parseBlock();
19917
- return this.withLoc({ kind: "foreach", binding, iterable, body, executeContext }, foreachToken);
19918
- }
19919
- parseMatchPattern() {
19920
- if (this.check("ident") && this.peek().value === "_") {
19921
- this.advance();
19922
- return { kind: "PatWild" };
19622
+ if (token.kind === "{") {
19623
+ return this.parseStructLit();
19923
19624
  }
19924
- if (this.check("ident") && this.peek().value === "None") {
19925
- this.advance();
19926
- return { kind: "PatNone" };
19625
+ if (token.kind === "[") {
19626
+ return this.parseArrayLit();
19927
19627
  }
19928
- if (this.check("ident") && this.peek().value === "Some") {
19628
+ this.error(`Unexpected token '${token.value || token.kind}'. Expected an expression (identifier, literal, '(', '[', or '{')`);
19629
+ }
19630
+ parseLiteralExpr() {
19631
+ if (this.check("-")) {
19929
19632
  this.advance();
19930
- this.expect("(");
19931
- const binding = this.expect("ident").value;
19932
- this.expect(")");
19933
- return { kind: "PatSome", binding };
19934
- }
19935
- if (this.check("ident") && this.peek(1).kind === "::") {
19936
- const enumName = this.advance().value;
19937
- this.expect("::");
19938
- const variant = this.expect("ident").value;
19939
- const bindings = [];
19940
- if (this.check("(")) {
19633
+ const token = this.peek();
19634
+ if (token.kind === "int_lit") {
19941
19635
  this.advance();
19942
- while (!this.check(")") && !this.check("eof")) {
19943
- bindings.push(this.expect("ident").value);
19944
- if (!this.match(","))
19945
- break;
19946
- }
19947
- this.expect(")");
19636
+ return this.withLoc({ kind: "int_lit", value: -Number(token.value) }, token);
19948
19637
  }
19949
- return { kind: "PatEnum", enumName, variant, bindings };
19950
- }
19951
- if (this.check("int_lit")) {
19952
- const tok = this.advance();
19953
- return { kind: "PatInt", value: parseInt(tok.value, 10) };
19638
+ if (token.kind === "float_lit") {
19639
+ this.advance();
19640
+ return this.withLoc({ kind: "float_lit", value: -Number(token.value) }, token);
19641
+ }
19642
+ this.error("Expected number after unary minus (-). Const values must be numeric or string literals");
19954
19643
  }
19955
- if (this.check("-") && this.peek(1).kind === "int_lit") {
19956
- this.advance();
19957
- const tok = this.advance();
19958
- return { kind: "PatInt", value: -parseInt(tok.value, 10) };
19644
+ const expr = this.parsePrimaryExpr();
19645
+ if (expr.kind === "int_lit" || expr.kind === "float_lit" || expr.kind === "bool_lit" || expr.kind === "str_lit") {
19646
+ return expr;
19959
19647
  }
19960
- const e = this.parseExpr();
19961
- return { kind: "PatExpr", expr: e };
19648
+ this.error("Const value must be a literal");
19962
19649
  }
19963
- parseMatchStmt() {
19964
- const matchToken = this.expect("match");
19965
- let expr;
19966
- if (this.check("(")) {
19967
- this.advance();
19968
- expr = this.parseExpr();
19969
- this.expect(")");
19970
- } else {
19971
- expr = this.parseExpr();
19650
+ // -------------------------------------------------------------------------
19651
+ // Lambda
19652
+ // -------------------------------------------------------------------------
19653
+ parseSingleParamLambda() {
19654
+ const paramToken = this.expect("ident");
19655
+ const params = [{ name: paramToken.value }];
19656
+ this.expect("=>");
19657
+ return this.finishLambdaExpr(params, paramToken);
19658
+ }
19659
+ parseLambdaExpr() {
19660
+ const openParenToken = this.expect("(");
19661
+ const params = [];
19662
+ if (!this.check(")")) {
19663
+ do {
19664
+ const name = this.expect("ident").value;
19665
+ let type;
19666
+ if (this.match(":")) {
19667
+ type = this.parseType();
19668
+ }
19669
+ params.push({ name, type });
19670
+ } while (this.match(","));
19972
19671
  }
19973
- this.expect("{");
19974
- const arms = [];
19975
- while (!this.check("}") && !this.check("eof")) {
19976
- const pattern = this.parseMatchPattern();
19977
- this.expect("=>");
19978
- const body = this.parseBlock();
19979
- this.match(",");
19980
- arms.push({ pattern, body });
19672
+ this.expect(")");
19673
+ let returnType;
19674
+ if (this.match("->")) {
19675
+ returnType = this.parseType();
19981
19676
  }
19982
- this.expect("}");
19983
- return this.withLoc({ kind: "match", expr, arms }, matchToken);
19677
+ this.expect("=>");
19678
+ return this.finishLambdaExpr(params, openParenToken, returnType);
19984
19679
  }
19985
- parseAsStmt() {
19986
- const asToken = this.expect("as");
19987
- const as_sel = this.parseSelector();
19988
- if (this.match("at")) {
19989
- const at_sel = this.parseSelector();
19990
- const body2 = this.parseBlock();
19991
- return this.withLoc({ kind: "as_at", as_sel, at_sel, body: body2 }, asToken);
19992
- }
19993
- const body = this.parseBlock();
19994
- return this.withLoc({ kind: "as_block", selector: as_sel, body }, asToken);
19680
+ finishLambdaExpr(params, token, returnType) {
19681
+ const body = this.check("{") ? this.parseBlock() : this.parseExpr();
19682
+ return this.withLoc({ kind: "lambda", params, returnType, body }, token);
19995
19683
  }
19996
- parseAtStmt() {
19997
- const atToken = this.expect("at");
19998
- const selector = this.parseSelector();
19999
- const body = this.parseBlock();
20000
- return this.withLoc({ kind: "at_block", selector, body }, atToken);
19684
+ // -------------------------------------------------------------------------
19685
+ // String interpolation
19686
+ // -------------------------------------------------------------------------
19687
+ parseStringExpr(token) {
19688
+ return this.withLoc({ kind: "str_lit", value: token.value }, token);
20001
19689
  }
20002
- parseExecuteStmt() {
20003
- const executeToken = this.expect("execute");
20004
- const subcommands = [];
20005
- while (!this.check("run") && !this.check("eof")) {
20006
- if (this.match("as")) {
20007
- const selector = this.parseSelector();
20008
- subcommands.push({ kind: "as", selector });
20009
- } else if (this.match("at")) {
20010
- const selector = this.parseSelector();
20011
- subcommands.push({ kind: "at", selector });
20012
- } else if (this.checkIdent("positioned")) {
20013
- this.advance();
20014
- if (this.match("as")) {
20015
- const selector = this.parseSelector();
20016
- subcommands.push({ kind: "positioned_as", selector });
20017
- } else {
20018
- const x = this.parseCoordToken();
20019
- const y = this.parseCoordToken();
20020
- const z = this.parseCoordToken();
20021
- subcommands.push({ kind: "positioned", x, y, z });
20022
- }
20023
- } else if (this.checkIdent("rotated")) {
20024
- this.advance();
20025
- if (this.match("as")) {
20026
- const selector = this.parseSelector();
20027
- subcommands.push({ kind: "rotated_as", selector });
20028
- } else {
20029
- const yaw = this.parseCoordToken();
20030
- const pitch = this.parseCoordToken();
20031
- subcommands.push({ kind: "rotated", yaw, pitch });
20032
- }
20033
- } else if (this.checkIdent("facing")) {
20034
- this.advance();
20035
- if (this.checkIdent("entity")) {
20036
- this.advance();
20037
- const selector = this.parseSelector();
20038
- const anchor = this.checkIdent("eyes") || this.checkIdent("feet") ? this.advance().value : "feet";
20039
- subcommands.push({ kind: "facing_entity", selector, anchor });
20040
- } else {
20041
- const x = this.parseCoordToken();
20042
- const y = this.parseCoordToken();
20043
- const z = this.parseCoordToken();
20044
- subcommands.push({ kind: "facing", x, y, z });
19690
+ parseFStringExpr(token) {
19691
+ const parts = [];
19692
+ let current = "";
19693
+ let index = 0;
19694
+ while (index < token.value.length) {
19695
+ if (token.value[index] === "{") {
19696
+ if (current) {
19697
+ parts.push({ kind: "text", value: current });
19698
+ current = "";
20045
19699
  }
20046
- } else if (this.checkIdent("anchored")) {
20047
- this.advance();
20048
- const anchor = this.advance().value;
20049
- subcommands.push({ kind: "anchored", anchor });
20050
- } else if (this.checkIdent("align")) {
20051
- this.advance();
20052
- const axes = this.advance().value;
20053
- subcommands.push({ kind: "align", axes });
20054
- } else if (this.checkIdent("on")) {
20055
- this.advance();
20056
- const relation = this.advance().value;
20057
- subcommands.push({ kind: "on", relation });
20058
- } else if (this.checkIdent("summon")) {
20059
- this.advance();
20060
- const entity = this.advance().value;
20061
- subcommands.push({ kind: "summon", entity });
20062
- } else if (this.checkIdent("store")) {
20063
- this.advance();
20064
- const storeType = this.advance().value;
20065
- if (this.checkIdent("score")) {
20066
- this.advance();
20067
- const target = this.advance().value;
20068
- const targetObj = this.advance().value;
20069
- if (storeType === "result") {
20070
- subcommands.push({ kind: "store_result", target, targetObj });
20071
- } else {
20072
- subcommands.push({ kind: "store_success", target, targetObj });
19700
+ index++;
19701
+ let depth = 1;
19702
+ let exprSource = "";
19703
+ let inString = false;
19704
+ while (index < token.value.length && depth > 0) {
19705
+ const char = token.value[index];
19706
+ if (char === '"' && token.value[index - 1] !== "\\") {
19707
+ inString = !inString;
20073
19708
  }
20074
- } else {
20075
- this.error("store currently only supports score target");
20076
- }
20077
- } else if (this.match("if")) {
20078
- this.parseExecuteCondition(subcommands, "if");
20079
- } else if (this.match("unless")) {
20080
- this.parseExecuteCondition(subcommands, "unless");
20081
- } else if (this.match("in")) {
20082
- let dim = this.advance().value;
20083
- if (this.match(":")) {
20084
- dim += ":" + this.advance().value;
19709
+ if (!inString) {
19710
+ if (char === "{")
19711
+ depth++;
19712
+ else if (char === "}") {
19713
+ depth--;
19714
+ if (depth === 0) {
19715
+ index++;
19716
+ break;
19717
+ }
19718
+ }
19719
+ }
19720
+ if (depth > 0)
19721
+ exprSource += char;
19722
+ index++;
20085
19723
  }
20086
- subcommands.push({ kind: "in", dimension: dim });
20087
- } else {
20088
- this.error(`Unexpected token in execute statement: ${this.peek().kind} (${this.peek().value})`);
19724
+ if (depth !== 0)
19725
+ this.error("Unterminated f-string interpolation");
19726
+ parts.push({ kind: "expr", expr: this.parseEmbeddedExpr(exprSource) });
19727
+ continue;
20089
19728
  }
19729
+ current += token.value[index];
19730
+ index++;
20090
19731
  }
20091
- this.expect("run");
20092
- const body = this.parseBlock();
20093
- return this.withLoc({ kind: "execute", subcommands, body }, executeToken);
19732
+ if (current)
19733
+ parts.push({ kind: "text", value: current });
19734
+ return this.withLoc({ kind: "f_string", parts }, token);
20094
19735
  }
20095
- parseExecuteCondition(subcommands, type) {
20096
- if (this.checkIdent("entity") || this.check("selector")) {
20097
- if (this.checkIdent("entity"))
20098
- this.advance();
20099
- const selectorOrVar = this.parseSelectorOrVarSelector();
20100
- subcommands.push({ kind: type === "if" ? "if_entity" : "unless_entity", ...selectorOrVar });
20101
- } else if (this.checkIdent("block")) {
20102
- this.advance();
20103
- const x = this.parseCoordToken();
20104
- const y = this.parseCoordToken();
20105
- const z = this.parseCoordToken();
20106
- const block = this.parseBlockId();
20107
- subcommands.push({ kind: type === "if" ? "if_block" : "unless_block", pos: [x, y, z], block });
20108
- } else if (this.checkIdent("score")) {
20109
- this.advance();
20110
- const target = this.advance().value;
20111
- const targetObj = this.advance().value;
20112
- if (this.checkIdent("matches")) {
20113
- this.advance();
20114
- const range = this.advance().value;
20115
- subcommands.push({ kind: type === "if" ? "if_score_range" : "unless_score_range", target, targetObj, range });
20116
- } else {
20117
- const op = this.advance().value;
20118
- const source = this.advance().value;
20119
- const sourceObj = this.advance().value;
20120
- subcommands.push({
20121
- kind: type === "if" ? "if_score" : "unless_score",
20122
- target,
20123
- targetObj,
20124
- op,
20125
- source,
20126
- sourceObj
20127
- });
20128
- }
20129
- } else {
20130
- this.error(`Unknown condition type after ${type}`);
19736
+ parseEmbeddedExpr(source) {
19737
+ const { Parser } = require_parser();
19738
+ const tokens = new lexer_1.Lexer(source, this.filePath).tokenize();
19739
+ const parser = new Parser(tokens, source, this.filePath);
19740
+ const expr = parser.parseExpr();
19741
+ if (!parser.check("eof")) {
19742
+ parser.error(`Unexpected token '${parser.peek().kind}' in string interpolation`);
19743
+ }
19744
+ return expr;
19745
+ }
19746
+ // -------------------------------------------------------------------------
19747
+ // Struct / Array / BlockPos literals
19748
+ // -------------------------------------------------------------------------
19749
+ parseStructLit() {
19750
+ const braceToken = this.expect("{");
19751
+ const fields = [];
19752
+ if (!this.check("}")) {
19753
+ do {
19754
+ const name = this.expect("ident").value;
19755
+ this.expect(":");
19756
+ const value = this.parseExpr();
19757
+ fields.push({ name, value });
19758
+ } while (this.match(","));
20131
19759
  }
19760
+ this.expect("}");
19761
+ return this.withLoc({ kind: "struct_lit", fields }, braceToken);
20132
19762
  }
20133
- parseCoordToken() {
20134
- const token = this.peek();
20135
- if (token.kind === "rel_coord" || token.kind === "local_coord" || token.kind === "int_lit" || token.kind === "float_lit" || token.kind === "-" || token.kind === "ident") {
20136
- return this.advance().value;
19763
+ parseArrayLit() {
19764
+ const bracketToken = this.expect("[");
19765
+ const elements = [];
19766
+ if (!this.check("]")) {
19767
+ do {
19768
+ elements.push(this.parseExpr());
19769
+ } while (this.match(","));
20137
19770
  }
20138
- this.error(`Expected coordinate, got ${token.kind}`);
20139
- return "~";
19771
+ this.expect("]");
19772
+ return this.withLoc({ kind: "array_lit", elements }, bracketToken);
20140
19773
  }
20141
- parseBlockId() {
20142
- let id = this.advance().value;
20143
- if (this.match(":")) {
20144
- id += ":" + this.advance().value;
20145
- }
20146
- if (this.check("[")) {
20147
- id += this.advance().value;
20148
- while (!this.check("]") && !this.check("eof")) {
20149
- id += this.advance().value;
19774
+ isBlockPosLiteral() {
19775
+ if (!this.check("("))
19776
+ return false;
19777
+ let offset = 1;
19778
+ for (let i = 0; i < 3; i++) {
19779
+ const consumed = this.coordComponentTokenLength(offset);
19780
+ if (consumed === 0)
19781
+ return false;
19782
+ offset += consumed;
19783
+ if (i < 2) {
19784
+ if (this.peek(offset).kind !== ",")
19785
+ return false;
19786
+ offset += 1;
20150
19787
  }
20151
- id += this.advance().value;
20152
19788
  }
20153
- return id;
20154
- }
20155
- checkIdent(value) {
20156
- return this.check("ident") && this.peek().value === value;
19789
+ return this.peek(offset).kind === ")";
20157
19790
  }
20158
- parseExprStmt() {
20159
- const expr = this.parseExpr();
20160
- this.match(";");
20161
- const exprToken = this.getLocToken(expr) ?? this.peek();
20162
- return this.withLoc({ kind: "expr", expr }, exprToken);
19791
+ coordComponentTokenLength(offset) {
19792
+ const token = this.peek(offset);
19793
+ if (token.kind === "int_lit")
19794
+ return 1;
19795
+ if (token.kind === "-") {
19796
+ return this.peek(offset + 1).kind === "int_lit" ? 2 : 0;
19797
+ }
19798
+ if (token.kind === "rel_coord" || token.kind === "local_coord")
19799
+ return 1;
19800
+ return 0;
20163
19801
  }
20164
- // -------------------------------------------------------------------------
20165
- // Expressions (Precedence Climbing)
20166
- // -------------------------------------------------------------------------
20167
- parseExpr() {
20168
- return this.parseAssignment();
19802
+ parseBlockPos() {
19803
+ const openParenToken = this.expect("(");
19804
+ const x = this.parseCoordComponent();
19805
+ this.expect(",");
19806
+ const y = this.parseCoordComponent();
19807
+ this.expect(",");
19808
+ const z = this.parseCoordComponent();
19809
+ this.expect(")");
19810
+ return this.withLoc({ kind: "blockpos", x, y, z }, openParenToken);
20169
19811
  }
20170
- parseAssignment() {
20171
- const left = this.parseBinaryExpr(1);
19812
+ parseCoordComponent() {
20172
19813
  const token = this.peek();
20173
- if (token.kind === "=" || token.kind === "+=" || token.kind === "-=" || token.kind === "*=" || token.kind === "/=" || token.kind === "%=") {
20174
- const op = this.advance().kind;
20175
- if (left.kind === "ident") {
20176
- const value = this.parseAssignment();
20177
- return this.withLoc({ kind: "assign", target: left.name, op, value }, this.getLocToken(left) ?? token);
20178
- }
20179
- if (left.kind === "member") {
20180
- const value = this.parseAssignment();
20181
- return this.withLoc({ kind: "member_assign", obj: left.obj, field: left.field, op, value }, this.getLocToken(left) ?? token);
20182
- }
20183
- if (left.kind === "index") {
20184
- const value = this.parseAssignment();
20185
- return this.withLoc({ kind: "index_assign", obj: left.obj, index: left.index, op, value }, this.getLocToken(left) ?? token);
20186
- }
19814
+ if (token.kind === "rel_coord") {
19815
+ this.advance();
19816
+ return { kind: "relative", offset: this.parseCoordOffsetFromValue(token.value.slice(1)) };
20187
19817
  }
20188
- return left;
20189
- }
20190
- parseBinaryExpr(minPrec) {
20191
- let left = this.parseUnaryExpr();
20192
- while (true) {
20193
- const op = this.peek().kind;
20194
- if (!BINARY_OPS.has(op))
20195
- break;
20196
- const prec = PRECEDENCE[op];
20197
- if (prec < minPrec)
20198
- break;
20199
- const opToken = this.advance();
20200
- if (op === "is") {
20201
- const entityType = this.parseEntityTypeName();
20202
- left = this.withLoc({ kind: "is_check", expr: left, entityType }, this.getLocToken(left) ?? opToken);
20203
- continue;
20204
- }
20205
- const right = this.parseBinaryExpr(prec + 1);
20206
- left = this.withLoc({ kind: "binary", op, left, right }, this.getLocToken(left) ?? opToken);
19818
+ if (token.kind === "local_coord") {
19819
+ this.advance();
19820
+ return { kind: "local", offset: this.parseCoordOffsetFromValue(token.value.slice(1)) };
20207
19821
  }
20208
- return left;
19822
+ return { kind: "absolute", value: this.parseSignedCoordOffset(true) };
20209
19823
  }
20210
- parseUnaryExpr() {
20211
- if (this.match("!")) {
20212
- const bangToken = this.tokens[this.pos - 1];
20213
- const operand = this.parseUnaryExpr();
20214
- return this.withLoc({ kind: "unary", op: "!", operand }, bangToken);
20215
- }
20216
- if (this.check("-") && !this.isSubtraction()) {
20217
- const minusToken = this.advance();
20218
- const operand = this.parseUnaryExpr();
20219
- return this.withLoc({ kind: "unary", op: "-", operand }, minusToken);
20220
- }
20221
- return this.parsePostfixExpr();
19824
+ parseCoordOffsetFromValue(value) {
19825
+ if (value === "" || value === void 0)
19826
+ return 0;
19827
+ return parseFloat(value);
20222
19828
  }
20223
- parseEntityTypeName() {
20224
- const token = this.expect("ident");
20225
- if (ENTITY_TYPE_NAMES.has(token.value)) {
20226
- return token.value;
20227
- }
20228
- this.error(`Unknown entity type '${token.value}'`);
19829
+ parseSignedCoordOffset(requireValue = false) {
19830
+ let sign = 1;
19831
+ if (this.match("-"))
19832
+ sign = -1;
19833
+ if (this.check("int_lit"))
19834
+ return sign * parseInt(this.advance().value, 10);
19835
+ if (requireValue)
19836
+ this.error("Expected integer coordinate component");
19837
+ return 0;
20229
19838
  }
20230
- isSubtraction() {
20231
- if (this.pos === 0)
20232
- return false;
20233
- const prev = this.tokens[this.pos - 1];
20234
- return ["int_lit", "float_lit", "ident", ")", "]"].includes(prev.kind);
19839
+ // -------------------------------------------------------------------------
19840
+ // Selector parsing (also used by stmt-parser)
19841
+ // -------------------------------------------------------------------------
19842
+ parseSelector() {
19843
+ const token = this.expect("selector");
19844
+ return this.parseSelectorValue(token.value);
20235
19845
  }
20236
- /**
20237
- * Try to parse `<Type, ...>` as explicit generic type arguments.
20238
- * Returns the parsed type list if successful, null if this looks like a comparison.
20239
- * Does NOT consume any tokens if it returns null.
20240
- */
20241
- tryParseTypeArgs() {
20242
- const saved = this.pos;
20243
- this.advance();
20244
- const typeArgs = [];
20245
- try {
20246
- do {
20247
- typeArgs.push(this.parseType());
20248
- } while (this.match(","));
20249
- if (!this.check(">")) {
20250
- this.pos = saved;
20251
- return null;
20252
- }
20253
- this.advance();
20254
- return typeArgs;
20255
- } catch {
20256
- this.pos = saved;
20257
- return null;
19846
+ parseSelectorOrVarSelector() {
19847
+ if (this.check("selector")) {
19848
+ return { selector: this.parseSelector() };
20258
19849
  }
20259
- }
20260
- parsePostfixExpr() {
20261
- let expr = this.parsePrimaryExpr();
20262
- while (true) {
20263
- if (expr.kind === "ident" && this.check("<")) {
20264
- const typeArgs = this.tryParseTypeArgs();
20265
- if (typeArgs !== null && this.check("(")) {
20266
- const openParenToken = this.peek();
19850
+ const varToken = this.expect("ident");
19851
+ const varName = varToken.value;
19852
+ if (this.check("[")) {
19853
+ this.advance();
19854
+ let filterStr = "";
19855
+ let depth = 1;
19856
+ while (depth > 0 && !this.check("eof")) {
19857
+ if (this.check("["))
19858
+ depth++;
19859
+ else if (this.check("]"))
19860
+ depth--;
19861
+ if (depth > 0) {
19862
+ filterStr += this.peek().value ?? this.peek().kind;
20267
19863
  this.advance();
20268
- const args = this.parseArgs();
20269
- this.expect(")");
20270
- expr = this.withLoc({ kind: "call", fn: expr.name, args, typeArgs }, this.getLocToken(expr) ?? openParenToken);
20271
- continue;
20272
- }
20273
- }
20274
- if (this.match("(")) {
20275
- const openParenToken = this.tokens[this.pos - 1];
20276
- if (expr.kind === "ident") {
20277
- const args2 = this.parseArgs();
20278
- this.expect(")");
20279
- expr = this.withLoc({ kind: "call", fn: expr.name, args: args2 }, this.getLocToken(expr) ?? openParenToken);
20280
- continue;
20281
- }
20282
- if (expr.kind === "member") {
20283
- if (expr.field === "unwrap_or") {
20284
- const defaultExpr = this.parseExpr();
20285
- this.expect(")");
20286
- expr = this.withLoc({ kind: "unwrap_or", opt: expr.obj, default_: defaultExpr }, this.getLocToken(expr) ?? openParenToken);
20287
- continue;
20288
- }
20289
- const methodMap = {
20290
- "tag": "__entity_tag",
20291
- "untag": "__entity_untag",
20292
- "has_tag": "__entity_has_tag",
20293
- "push": "__array_push",
20294
- "pop": "__array_pop",
20295
- "add": "set_add",
20296
- "contains": "set_contains",
20297
- "remove": "set_remove",
20298
- "clear": "set_clear"
20299
- };
20300
- const internalFn = methodMap[expr.field];
20301
- if (internalFn) {
20302
- const args3 = this.parseArgs();
20303
- this.expect(")");
20304
- expr = this.withLoc({ kind: "call", fn: internalFn, args: [expr.obj, ...args3] }, this.getLocToken(expr) ?? openParenToken);
20305
- continue;
20306
- }
20307
- const args2 = this.parseArgs();
20308
- this.expect(")");
20309
- expr = this.withLoc({ kind: "call", fn: expr.field, args: [expr.obj, ...args2] }, this.getLocToken(expr) ?? openParenToken);
20310
- continue;
20311
19864
  }
20312
- const args = this.parseArgs();
20313
- this.expect(")");
20314
- expr = this.withLoc({ kind: "invoke", callee: expr, args }, this.getLocToken(expr) ?? openParenToken);
20315
- continue;
20316
19865
  }
20317
- if (this.match("[")) {
20318
- const index = this.parseExpr();
20319
- this.expect("]");
20320
- expr = this.withLoc({ kind: "index", obj: expr, index }, this.getLocToken(expr) ?? this.tokens[this.pos - 1]);
20321
- continue;
20322
- }
20323
- if (this.match(".")) {
20324
- const field = this.expect("ident").value;
20325
- expr = this.withLoc({ kind: "member", obj: expr, field }, this.getLocToken(expr) ?? this.tokens[this.pos - 1]);
19866
+ this.expect("]");
19867
+ const filters = this.parseSelectorFilters(filterStr);
19868
+ return { varName, filters };
19869
+ }
19870
+ return { varName };
19871
+ }
19872
+ parseSelectorValue(value) {
19873
+ const bracketIndex = value.indexOf("[");
19874
+ if (bracketIndex === -1) {
19875
+ return { kind: value };
19876
+ }
19877
+ const kind = value.slice(0, bracketIndex);
19878
+ const paramsStr = value.slice(bracketIndex + 1, -1);
19879
+ const filters = this.parseSelectorFilters(paramsStr);
19880
+ return { kind, filters };
19881
+ }
19882
+ parseSelectorFilters(paramsStr) {
19883
+ const filters = {};
19884
+ const parts = this.splitSelectorParams(paramsStr);
19885
+ for (const part of parts) {
19886
+ const eqIndex = part.indexOf("=");
19887
+ if (eqIndex === -1)
20326
19888
  continue;
19889
+ const key = part.slice(0, eqIndex).trim();
19890
+ const val = part.slice(eqIndex + 1).trim();
19891
+ switch (key) {
19892
+ case "type":
19893
+ filters.type = val;
19894
+ break;
19895
+ case "distance":
19896
+ filters.distance = this.parseRangeValue(val);
19897
+ break;
19898
+ case "tag":
19899
+ if (val.startsWith("!")) {
19900
+ filters.notTag = filters.notTag ?? [];
19901
+ filters.notTag.push(val.slice(1));
19902
+ } else {
19903
+ filters.tag = filters.tag ?? [];
19904
+ filters.tag.push(val);
19905
+ }
19906
+ break;
19907
+ case "limit":
19908
+ filters.limit = parseInt(val, 10);
19909
+ break;
19910
+ case "sort":
19911
+ filters.sort = val;
19912
+ break;
19913
+ case "nbt":
19914
+ filters.nbt = val;
19915
+ break;
19916
+ case "gamemode":
19917
+ filters.gamemode = val;
19918
+ break;
19919
+ case "scores":
19920
+ filters.scores = this.parseScoresFilter(val);
19921
+ break;
19922
+ case "x":
19923
+ filters.x = this.parseRangeValue(val);
19924
+ break;
19925
+ case "y":
19926
+ filters.y = this.parseRangeValue(val);
19927
+ break;
19928
+ case "z":
19929
+ filters.z = this.parseRangeValue(val);
19930
+ break;
19931
+ case "x_rotation":
19932
+ filters.x_rotation = this.parseRangeValue(val);
19933
+ break;
19934
+ case "y_rotation":
19935
+ filters.y_rotation = this.parseRangeValue(val);
19936
+ break;
20327
19937
  }
20328
- if (this.check("as") && this.isTypeCastAs()) {
20329
- const asToken = this.advance();
20330
- const targetType = this.parseType();
20331
- expr = this.withLoc({ kind: "type_cast", expr, targetType }, this.getLocToken(expr) ?? asToken);
19938
+ }
19939
+ return filters;
19940
+ }
19941
+ splitSelectorParams(str) {
19942
+ const parts = [];
19943
+ let current = "";
19944
+ let depth = 0;
19945
+ for (const char of str) {
19946
+ if (char === "{" || char === "[")
19947
+ depth++;
19948
+ else if (char === "}" || char === "]")
19949
+ depth--;
19950
+ else if (char === "," && depth === 0) {
19951
+ parts.push(current.trim());
19952
+ current = "";
20332
19953
  continue;
20333
19954
  }
20334
- break;
19955
+ current += char;
20335
19956
  }
20336
- return expr;
19957
+ if (current.trim())
19958
+ parts.push(current.trim());
19959
+ return parts;
20337
19960
  }
20338
- /** Returns true if the current 'as' token is a type cast (not a context block) */
20339
- isTypeCastAs() {
20340
- const next = this.tokens[this.pos + 1];
20341
- if (!next)
20342
- return false;
20343
- const typeStartTokens = /* @__PURE__ */ new Set(["int", "bool", "float", "fixed", "string", "void", "BlockPos", "("]);
20344
- if (typeStartTokens.has(next.kind))
20345
- return true;
20346
- if (next.kind === "ident" && (next.value === "double" || next.value === "byte" || next.value === "short" || next.value === "long" || next.value === "selector" || next.value === "Option"))
20347
- return true;
20348
- return false;
19961
+ parseScoresFilter(val) {
19962
+ const scores = {};
19963
+ const inner = val.slice(1, -1);
19964
+ const parts = inner.split(",");
19965
+ for (const part of parts) {
19966
+ const [name, range] = part.split("=").map((s) => s.trim());
19967
+ scores[name] = this.parseRangeValue(range);
19968
+ }
19969
+ return scores;
20349
19970
  }
20350
- parseArgs() {
20351
- const args = [];
20352
- if (!this.check(")")) {
20353
- do {
20354
- args.push(this.parseExpr());
20355
- } while (this.match(","));
19971
+ parseRangeValue(value) {
19972
+ if (value.startsWith("..=")) {
19973
+ const rest = value.slice(3);
19974
+ if (!rest)
19975
+ return {};
19976
+ return { max: parseInt(rest, 10) };
20356
19977
  }
20357
- return args;
19978
+ if (value.startsWith("..")) {
19979
+ const rest = value.slice(2);
19980
+ if (!rest)
19981
+ return {};
19982
+ return { max: parseInt(rest, 10) };
19983
+ }
19984
+ const inclIdx = value.indexOf("..=");
19985
+ if (inclIdx !== -1) {
19986
+ const min = parseInt(value.slice(0, inclIdx), 10);
19987
+ const rest = value.slice(inclIdx + 3);
19988
+ if (!rest)
19989
+ return { min };
19990
+ return { min, max: parseInt(rest, 10) };
19991
+ }
19992
+ const dotIndex = value.indexOf("..");
19993
+ if (dotIndex !== -1) {
19994
+ const min = parseInt(value.slice(0, dotIndex), 10);
19995
+ const rest = value.slice(dotIndex + 2);
19996
+ if (!rest)
19997
+ return { min };
19998
+ return { min, max: parseInt(rest, 10) };
19999
+ }
20000
+ const val = parseInt(value, 10);
20001
+ return { min: val, max: val };
20358
20002
  }
20359
- parsePrimaryExpr() {
20003
+ // -------------------------------------------------------------------------
20004
+ // Coord token (used by stmt-parser for execute subcommands)
20005
+ // -------------------------------------------------------------------------
20006
+ parseCoordToken() {
20360
20007
  const token = this.peek();
20361
- if (token.kind === "ident" && this.peek(1).kind === "::") {
20362
- const typeToken = this.advance();
20363
- this.expect("::");
20364
- const memberToken = this.expect("ident");
20365
- if (this.check("(")) {
20366
- const isNamedArgs = this.peek(1).kind === "ident" && this.peek(2).kind === ":";
20367
- if (isNamedArgs) {
20368
- this.advance();
20369
- const args2 = [];
20370
- while (!this.check(")") && !this.check("eof")) {
20371
- const fieldName = this.expect("ident").value;
20372
- this.expect(":");
20373
- const value = this.parseExpr();
20374
- args2.push({ name: fieldName, value });
20375
- if (!this.match(","))
20376
- break;
20377
- }
20378
- this.expect(")");
20379
- return this.withLoc({ kind: "enum_construct", enumName: typeToken.value, variant: memberToken.value, args: args2 }, typeToken);
20008
+ if (token.kind === "rel_coord" || token.kind === "local_coord" || token.kind === "int_lit" || token.kind === "float_lit" || token.kind === "-" || token.kind === "ident") {
20009
+ return this.advance().value;
20010
+ }
20011
+ return this.error(`Expected coordinate, got ${token.kind}`);
20012
+ }
20013
+ parseBlockId() {
20014
+ let id = this.advance().value;
20015
+ if (this.match(":"))
20016
+ id += ":" + this.advance().value;
20017
+ if (this.check("[")) {
20018
+ id += this.advance().value;
20019
+ while (!this.check("]") && !this.check("eof"))
20020
+ id += this.advance().value;
20021
+ id += this.advance().value;
20022
+ }
20023
+ return id;
20024
+ }
20025
+ };
20026
+ exports2.ExprParser = ExprParser;
20027
+ }
20028
+ });
20029
+
20030
+ // ../../dist/src/parser/stmt-parser.js
20031
+ var require_stmt_parser = __commonJS({
20032
+ "../../dist/src/parser/stmt-parser.js"(exports2) {
20033
+ "use strict";
20034
+ Object.defineProperty(exports2, "__esModule", { value: true });
20035
+ exports2.StmtParser = void 0;
20036
+ var diagnostics_1 = require_diagnostics();
20037
+ var expr_parser_1 = require_expr_parser();
20038
+ var StmtParser = class extends expr_parser_1.ExprParser {
20039
+ // -------------------------------------------------------------------------
20040
+ // Block
20041
+ // -------------------------------------------------------------------------
20042
+ parseBlock() {
20043
+ this.expect("{");
20044
+ const stmts = [];
20045
+ while (!this.check("}") && !this.check("eof")) {
20046
+ try {
20047
+ stmts.push(this.parseStmt());
20048
+ } catch (err) {
20049
+ if (err instanceof diagnostics_1.DiagnosticError) {
20050
+ this.parseErrors.push(err);
20051
+ this.syncToNextStmt();
20052
+ } else {
20053
+ throw err;
20380
20054
  }
20381
- this.advance();
20382
- const args = this.parseArgs();
20383
- this.expect(")");
20384
- return this.withLoc({ kind: "static_call", type: typeToken.value, method: memberToken.value, args }, typeToken);
20385
20055
  }
20386
- return this.withLoc({ kind: "path_expr", enumName: typeToken.value, variant: memberToken.value }, typeToken);
20387
- }
20388
- if (token.kind === "ident" && this.peek(1).kind === "=>") {
20389
- return this.parseSingleParamLambda();
20390
20056
  }
20391
- if (token.kind === "int_lit") {
20392
- this.advance();
20393
- return this.withLoc({ kind: "int_lit", value: parseInt(token.value, 10) }, token);
20057
+ this.expect("}");
20058
+ return stmts;
20059
+ }
20060
+ // -------------------------------------------------------------------------
20061
+ // Statement dispatch
20062
+ // -------------------------------------------------------------------------
20063
+ parseStmt() {
20064
+ if (this.check("let"))
20065
+ return this.parseLetStmt();
20066
+ if (this.check("const"))
20067
+ return this.parseLocalConstDecl();
20068
+ if (this.check("return"))
20069
+ return this.parseReturnStmt();
20070
+ if (this.check("break")) {
20071
+ const token = this.advance();
20072
+ if (this.check("ident")) {
20073
+ const labelToken = this.advance();
20074
+ this.match(";");
20075
+ return this.withLoc({ kind: "break_label", label: labelToken.value }, token);
20076
+ }
20077
+ this.match(";");
20078
+ return this.withLoc({ kind: "break" }, token);
20394
20079
  }
20395
- if (token.kind === "float_lit") {
20396
- this.advance();
20397
- return this.withLoc({ kind: "float_lit", value: parseFloat(token.value) }, token);
20080
+ if (this.check("continue")) {
20081
+ const token = this.advance();
20082
+ if (this.check("ident")) {
20083
+ const labelToken = this.advance();
20084
+ this.match(";");
20085
+ return this.withLoc({ kind: "continue_label", label: labelToken.value }, token);
20086
+ }
20087
+ this.match(";");
20088
+ return this.withLoc({ kind: "continue" }, token);
20398
20089
  }
20399
- if (token.kind === "rel_coord") {
20090
+ if (this.check("if"))
20091
+ return this.parseIfStmt();
20092
+ if (this.check("ident") && this.peek(1).kind === ":") {
20093
+ const labelToken = this.advance();
20400
20094
  this.advance();
20401
- return this.withLoc({ kind: "rel_coord", value: token.value }, token);
20095
+ let loopStmt;
20096
+ if (this.check("while")) {
20097
+ loopStmt = this.parseWhileStmt();
20098
+ } else if (this.check("for")) {
20099
+ loopStmt = this.parseForStmt();
20100
+ } else if (this.check("foreach")) {
20101
+ loopStmt = this.parseForeachStmt();
20102
+ } else if (this.check("repeat")) {
20103
+ loopStmt = this.parseRepeatStmt();
20104
+ } else {
20105
+ throw new diagnostics_1.DiagnosticError("ParseError", `Expected loop statement after label '${labelToken.value}:', found '${this.peek().kind}'`, { line: labelToken.line, col: labelToken.col });
20106
+ }
20107
+ return this.withLoc({ kind: "labeled_loop", label: labelToken.value, body: loopStmt }, labelToken);
20402
20108
  }
20403
- if (token.kind === "local_coord") {
20404
- this.advance();
20405
- return this.withLoc({ kind: "local_coord", value: token.value }, token);
20109
+ if (this.check("while"))
20110
+ return this.parseWhileStmt();
20111
+ if (this.check("do"))
20112
+ return this.parseDoWhileStmt();
20113
+ if (this.check("repeat"))
20114
+ return this.parseRepeatStmt();
20115
+ if (this.check("for"))
20116
+ return this.parseForStmt();
20117
+ if (this.check("foreach"))
20118
+ return this.parseForeachStmt();
20119
+ if (this.check("match"))
20120
+ return this.parseMatchStmt();
20121
+ if (this.check("as"))
20122
+ return this.parseAsStmt();
20123
+ if (this.check("at"))
20124
+ return this.parseAtStmt();
20125
+ if (this.check("execute"))
20126
+ return this.parseExecuteStmt();
20127
+ if (this.check("raw_cmd")) {
20128
+ const token = this.advance();
20129
+ const cmd = token.value;
20130
+ this.match(";");
20131
+ return this.withLoc({ kind: "raw", cmd }, token);
20406
20132
  }
20407
- if (token.kind === "byte_lit") {
20133
+ return this.parseExprStmt();
20134
+ }
20135
+ // -------------------------------------------------------------------------
20136
+ // Individual statement parsers
20137
+ // -------------------------------------------------------------------------
20138
+ parseLetStmt() {
20139
+ const letToken = this.expect("let");
20140
+ if (this.check("(")) {
20408
20141
  this.advance();
20409
- return this.withLoc({ kind: "byte_lit", value: parseInt(token.value.slice(0, -1), 10) }, token);
20142
+ const names = [];
20143
+ do {
20144
+ names.push(this.expect("ident").value);
20145
+ } while (this.match(","));
20146
+ this.expect(")");
20147
+ let type2;
20148
+ if (this.match(":"))
20149
+ type2 = this.parseType();
20150
+ this.expect("=");
20151
+ const init2 = this.parseExpr();
20152
+ this.match(";");
20153
+ return this.withLoc({ kind: "let_destruct", names, type: type2, init: init2 }, letToken);
20410
20154
  }
20411
- if (token.kind === "short_lit") {
20412
- this.advance();
20413
- return this.withLoc({ kind: "short_lit", value: parseInt(token.value.slice(0, -1), 10) }, token);
20155
+ const name = this.expect("ident").value;
20156
+ let type;
20157
+ if (this.match(":"))
20158
+ type = this.parseType();
20159
+ this.expect("=");
20160
+ const init = this.parseExpr();
20161
+ this.match(";");
20162
+ return this.withLoc({ kind: "let", name, type, init }, letToken);
20163
+ }
20164
+ parseLocalConstDecl() {
20165
+ const constToken = this.expect("const");
20166
+ const name = this.expect("ident").value;
20167
+ this.expect(":");
20168
+ const type = this.parseType();
20169
+ this.expect("=");
20170
+ const value = this.parseExpr();
20171
+ this.match(";");
20172
+ return this.withLoc({ kind: "const_decl", name, type, value }, constToken);
20173
+ }
20174
+ parseReturnStmt() {
20175
+ const returnToken = this.expect("return");
20176
+ let value;
20177
+ if (!this.check(";") && !this.check("}") && !this.check("eof")) {
20178
+ value = this.parseExpr();
20414
20179
  }
20415
- if (token.kind === "long_lit") {
20180
+ this.match(";");
20181
+ return this.withLoc({ kind: "return", value }, returnToken);
20182
+ }
20183
+ parseIfStmt() {
20184
+ const ifToken = this.expect("if");
20185
+ if (this.check("let") && this.peek(1).kind === "ident" && this.peek(1).value === "Some") {
20416
20186
  this.advance();
20417
- return this.withLoc({ kind: "long_lit", value: parseInt(token.value.slice(0, -1), 10) }, token);
20418
- }
20419
- if (token.kind === "double_lit") {
20420
20187
  this.advance();
20421
- return this.withLoc({ kind: "double_lit", value: parseFloat(token.value.slice(0, -1)) }, token);
20188
+ this.expect("(");
20189
+ const binding = this.expect("ident").value;
20190
+ this.expect(")");
20191
+ this.expect("=");
20192
+ const init = this.parseExpr();
20193
+ const then2 = this.parseBlock();
20194
+ let else_2;
20195
+ if (this.match("else")) {
20196
+ else_2 = this.check("if") ? [this.parseIfStmt()] : this.parseBlock();
20197
+ }
20198
+ return this.withLoc({ kind: "if_let_some", binding, init, then: then2, else_: else_2 }, ifToken);
20422
20199
  }
20423
- if (token.kind === "string_lit") {
20424
- this.advance();
20425
- return this.parseStringExpr(token);
20200
+ const cond = this.parseParenOptionalCond();
20201
+ const then = this.parseBlock();
20202
+ let else_;
20203
+ if (this.match("else")) {
20204
+ else_ = this.check("if") ? [this.parseIfStmt()] : this.parseBlock();
20426
20205
  }
20427
- if (token.kind === "f_string") {
20206
+ return this.withLoc({ kind: "if", cond, then, else_ }, ifToken);
20207
+ }
20208
+ parseWhileStmt() {
20209
+ const whileToken = this.expect("while");
20210
+ if (this.check("let") && this.peek(1).kind === "ident" && this.peek(1).value === "Some") {
20428
20211
  this.advance();
20429
- return this.parseFStringExpr(token);
20430
- }
20431
- if (token.kind === "mc_name") {
20432
20212
  this.advance();
20433
- return this.withLoc({ kind: "mc_name", value: token.value.slice(1) }, token);
20213
+ this.expect("(");
20214
+ const binding = this.expect("ident").value;
20215
+ this.expect(")");
20216
+ this.expect("=");
20217
+ const init = this.parseExpr();
20218
+ const body2 = this.parseBlock();
20219
+ return this.withLoc({ kind: "while_let_some", binding, init, body: body2 }, whileToken);
20434
20220
  }
20435
- if (token.kind === "true") {
20436
- this.advance();
20437
- return this.withLoc({ kind: "bool_lit", value: true }, token);
20221
+ const cond = this.parseParenOptionalCond();
20222
+ const body = this.parseBlock();
20223
+ return this.withLoc({ kind: "while", cond, body }, whileToken);
20224
+ }
20225
+ parseDoWhileStmt() {
20226
+ const doToken = this.expect("do");
20227
+ const body = this.parseBlock();
20228
+ this.expect("while");
20229
+ const cond = this.parseParenOptionalCond();
20230
+ this.match(";");
20231
+ return this.withLoc({ kind: "do_while", cond, body }, doToken);
20232
+ }
20233
+ parseRepeatStmt() {
20234
+ const repeatToken = this.expect("repeat");
20235
+ const countToken = this.expect("int_lit");
20236
+ const count = parseInt(countToken.value, 10);
20237
+ const body = this.parseBlock();
20238
+ return this.withLoc({ kind: "repeat", count, body }, repeatToken);
20239
+ }
20240
+ parseParenOptionalCond() {
20241
+ if (this.match("(")) {
20242
+ const cond = this.parseExpr();
20243
+ this.expect(")");
20244
+ return cond;
20438
20245
  }
20439
- if (token.kind === "false") {
20440
- this.advance();
20441
- return this.withLoc({ kind: "bool_lit", value: false }, token);
20246
+ return this.parseExpr();
20247
+ }
20248
+ parseForStmt() {
20249
+ const forToken = this.expect("for");
20250
+ if (this.check("ident") && this.peek(1).kind === "in") {
20251
+ return this.parseForRangeStmt(forToken);
20442
20252
  }
20443
- if (token.kind === "range_lit") {
20253
+ this.expect("(");
20254
+ if (this.check("let") && this.peek(1).kind === "ident" && this.peek(2).kind === "in" && this.peek(3).kind === "ident" && this.peek(4).kind === ",") {
20444
20255
  this.advance();
20445
- return this.withLoc({ kind: "range_lit", range: this.parseRangeValue(token.value) }, token);
20256
+ const binding = this.expect("ident").value;
20257
+ this.expect("in");
20258
+ const arrayName = this.expect("ident").value;
20259
+ this.expect(",");
20260
+ const lenExpr = this.parseExpr();
20261
+ this.expect(")");
20262
+ const body2 = this.parseBlock();
20263
+ return this.withLoc({ kind: "for_in_array", binding, arrayName, lenExpr, body: body2 }, forToken);
20446
20264
  }
20447
- if (token.kind === "selector") {
20448
- this.advance();
20449
- return this.withLoc({
20450
- kind: "selector",
20451
- raw: token.value,
20452
- isSingle: computeIsSingle(token.value),
20453
- sel: this.parseSelectorValue(token.value)
20454
- }, token);
20265
+ let init;
20266
+ if (this.check("let")) {
20267
+ const letToken = this.expect("let");
20268
+ const name = this.expect("ident").value;
20269
+ let type;
20270
+ if (this.match(":"))
20271
+ type = this.parseType();
20272
+ this.expect("=");
20273
+ const initExpr = this.parseExpr();
20274
+ const initStmt = { kind: "let", name, type, init: initExpr };
20275
+ init = this.withLoc(initStmt, letToken);
20455
20276
  }
20456
- if (token.kind === "ident" && this.peek(1).kind === "{" && this.peek(2).kind === "ident" && this.peek(3).kind === ":") {
20457
- this.advance();
20458
- return this.parseStructLit();
20277
+ this.expect(";");
20278
+ const cond = this.parseExpr();
20279
+ this.expect(";");
20280
+ const step = this.parseExpr();
20281
+ this.expect(")");
20282
+ const body = this.parseBlock();
20283
+ return this.withLoc({ kind: "for", init, cond, step, body }, forToken);
20284
+ }
20285
+ parseForRangeStmt(forToken) {
20286
+ const varName = this.expect("ident").value;
20287
+ this.expect("in");
20288
+ let start;
20289
+ let end;
20290
+ let inclusive = false;
20291
+ if (this.check("range_lit")) {
20292
+ const rangeToken = this.advance();
20293
+ const raw = rangeToken.value;
20294
+ inclusive = raw.includes("..=");
20295
+ const range = this.parseRangeValue(raw);
20296
+ start = this.withLoc({ kind: "int_lit", value: range.min ?? 0 }, rangeToken);
20297
+ if (range.max !== null && range.max !== void 0) {
20298
+ end = this.withLoc({ kind: "int_lit", value: range.max }, rangeToken);
20299
+ } else {
20300
+ end = this.parseUnaryExpr();
20301
+ }
20302
+ } else {
20303
+ const arrayOrStart = this.parseExpr();
20304
+ if (!this.check("range_lit")) {
20305
+ const body2 = this.parseBlock();
20306
+ return this.withLoc({ kind: "for_each", binding: varName, array: arrayOrStart, body: body2 }, forToken);
20307
+ }
20308
+ start = arrayOrStart;
20309
+ if (this.check("range_lit")) {
20310
+ const rangeOp = this.advance();
20311
+ inclusive = rangeOp.value.includes("=");
20312
+ const afterOp = rangeOp.value.replace(/^\.\.=?/, "");
20313
+ if (afterOp.length > 0) {
20314
+ end = this.withLoc({ kind: "int_lit", value: parseInt(afterOp, 10) }, rangeOp);
20315
+ } else {
20316
+ end = this.parseExpr();
20317
+ }
20318
+ } else {
20319
+ this.error("Expected .. or ..= in for-range expression. Example: for i in 0..10 { ... }");
20320
+ }
20459
20321
  }
20460
- if (token.kind === "ident" && token.value === "Some" && this.peek(1).kind === "(") {
20461
- this.advance();
20462
- this.advance();
20463
- const value = this.parseExpr();
20464
- this.expect(")");
20465
- return this.withLoc({ kind: "some_lit", value }, token);
20322
+ const body = this.parseBlock();
20323
+ return this.withLoc({ kind: "for_range", varName, start, end, inclusive, body }, forToken);
20324
+ }
20325
+ parseForeachStmt() {
20326
+ const foreachToken = this.expect("foreach");
20327
+ this.expect("(");
20328
+ const binding = this.expect("ident").value;
20329
+ this.expect("in");
20330
+ const iterable = this.parseExpr();
20331
+ this.expect(")");
20332
+ let executeContext;
20333
+ const execIdentKeywords = ["positioned", "rotated", "facing", "anchored", "align", "on", "summon"];
20334
+ if (this.check("as") || this.check("at") || this.check("in") || this.check("ident") && execIdentKeywords.includes(this.peek().value)) {
20335
+ let context = "";
20336
+ while (!this.check("{") && !this.check("eof")) {
20337
+ context += this.advance().value + " ";
20338
+ }
20339
+ executeContext = context.trim();
20466
20340
  }
20467
- if (token.kind === "ident" && token.value === "None") {
20341
+ const body = this.parseBlock();
20342
+ return this.withLoc({ kind: "foreach", binding, iterable, body, executeContext }, foreachToken);
20343
+ }
20344
+ // -------------------------------------------------------------------------
20345
+ // Match
20346
+ // -------------------------------------------------------------------------
20347
+ parseMatchPattern() {
20348
+ if (this.check("ident") && this.peek().value === "_") {
20468
20349
  this.advance();
20469
- return this.withLoc({ kind: "none_lit" }, token);
20350
+ return { kind: "PatWild" };
20470
20351
  }
20471
- if (token.kind === "ident") {
20352
+ if (this.check("ident") && this.peek().value === "None") {
20472
20353
  this.advance();
20473
- return this.withLoc({ kind: "ident", name: token.value }, token);
20354
+ return { kind: "PatNone" };
20474
20355
  }
20475
- if (token.kind === "(") {
20476
- if (this.isBlockPosLiteral()) {
20477
- return this.parseBlockPos();
20478
- }
20479
- if (this.isLambdaStart()) {
20480
- return this.parseLambdaExpr();
20481
- }
20356
+ if (this.check("ident") && this.peek().value === "Some") {
20482
20357
  this.advance();
20483
- const first = this.parseExpr();
20484
- if (this.match(",")) {
20485
- const elements = [first];
20486
- if (!this.check(")")) {
20487
- do {
20488
- elements.push(this.parseExpr());
20489
- } while (this.match(","));
20358
+ this.expect("(");
20359
+ const binding = this.expect("ident").value;
20360
+ this.expect(")");
20361
+ return { kind: "PatSome", binding };
20362
+ }
20363
+ if (this.check("ident") && this.peek(1).kind === "::") {
20364
+ const enumName = this.advance().value;
20365
+ this.expect("::");
20366
+ const variant = this.expect("ident").value;
20367
+ const bindings = [];
20368
+ if (this.check("(")) {
20369
+ this.advance();
20370
+ while (!this.check(")") && !this.check("eof")) {
20371
+ bindings.push(this.expect("ident").value);
20372
+ if (!this.match(","))
20373
+ break;
20490
20374
  }
20491
20375
  this.expect(")");
20492
- return this.withLoc({ kind: "tuple_lit", elements }, token);
20493
20376
  }
20494
- this.expect(")");
20495
- return first;
20377
+ return { kind: "PatEnum", enumName, variant, bindings };
20496
20378
  }
20497
- if (token.kind === "{") {
20498
- return this.parseStructLit();
20379
+ if (this.check("int_lit")) {
20380
+ const tok = this.advance();
20381
+ return { kind: "PatInt", value: parseInt(tok.value, 10) };
20499
20382
  }
20500
- if (token.kind === "[") {
20501
- return this.parseArrayLit();
20383
+ if (this.check("-") && this.peek(1).kind === "int_lit") {
20384
+ this.advance();
20385
+ const tok = this.advance();
20386
+ return { kind: "PatInt", value: -parseInt(tok.value, 10) };
20502
20387
  }
20503
- this.error(`Unexpected token '${token.kind}'`);
20388
+ const e = this.parseExpr();
20389
+ return { kind: "PatExpr", expr: e };
20504
20390
  }
20505
- parseLiteralExpr() {
20506
- if (this.check("-")) {
20391
+ parseMatchStmt() {
20392
+ const matchToken = this.expect("match");
20393
+ let expr;
20394
+ if (this.check("(")) {
20507
20395
  this.advance();
20508
- const token = this.peek();
20509
- if (token.kind === "int_lit") {
20510
- this.advance();
20511
- return this.withLoc({ kind: "int_lit", value: -Number(token.value) }, token);
20512
- }
20513
- if (token.kind === "float_lit") {
20514
- this.advance();
20515
- return this.withLoc({ kind: "float_lit", value: -Number(token.value) }, token);
20516
- }
20517
- this.error("Expected number after unary -");
20396
+ expr = this.parseExpr();
20397
+ this.expect(")");
20398
+ } else {
20399
+ expr = this.parseExpr();
20518
20400
  }
20519
- const expr = this.parsePrimaryExpr();
20520
- if (expr.kind === "int_lit" || expr.kind === "float_lit" || expr.kind === "bool_lit" || expr.kind === "str_lit") {
20521
- return expr;
20401
+ this.expect("{");
20402
+ const arms = [];
20403
+ while (!this.check("}") && !this.check("eof")) {
20404
+ const pattern = this.parseMatchPattern();
20405
+ this.expect("=>");
20406
+ const body = this.parseBlock();
20407
+ this.match(",");
20408
+ arms.push({ pattern, body });
20522
20409
  }
20523
- this.error("Const value must be a literal");
20524
- }
20525
- parseSingleParamLambda() {
20526
- const paramToken = this.expect("ident");
20527
- const params = [{ name: paramToken.value }];
20528
- this.expect("=>");
20529
- return this.finishLambdaExpr(params, paramToken);
20410
+ this.expect("}");
20411
+ return this.withLoc({ kind: "match", expr, arms }, matchToken);
20530
20412
  }
20531
- parseLambdaExpr() {
20532
- const openParenToken = this.expect("(");
20533
- const params = [];
20534
- if (!this.check(")")) {
20535
- do {
20536
- const name = this.expect("ident").value;
20537
- let type;
20538
- if (this.match(":")) {
20539
- type = this.parseType();
20540
- }
20541
- params.push({ name, type });
20542
- } while (this.match(","));
20543
- }
20544
- this.expect(")");
20545
- let returnType;
20546
- if (this.match("->")) {
20547
- returnType = this.parseType();
20413
+ // -------------------------------------------------------------------------
20414
+ // As / At / Execute
20415
+ // -------------------------------------------------------------------------
20416
+ parseAsStmt() {
20417
+ const asToken = this.expect("as");
20418
+ const as_sel = this.parseSelector();
20419
+ if (this.match("at")) {
20420
+ const at_sel = this.parseSelector();
20421
+ const body2 = this.parseBlock();
20422
+ return this.withLoc({ kind: "as_at", as_sel, at_sel, body: body2 }, asToken);
20548
20423
  }
20549
- this.expect("=>");
20550
- return this.finishLambdaExpr(params, openParenToken, returnType);
20424
+ const body = this.parseBlock();
20425
+ return this.withLoc({ kind: "as_block", selector: as_sel, body }, asToken);
20551
20426
  }
20552
- finishLambdaExpr(params, token, returnType) {
20553
- const body = this.check("{") ? this.parseBlock() : this.parseExpr();
20554
- return this.withLoc({ kind: "lambda", params, returnType, body }, token);
20427
+ parseAtStmt() {
20428
+ const atToken = this.expect("at");
20429
+ const selector = this.parseSelector();
20430
+ const body = this.parseBlock();
20431
+ return this.withLoc({ kind: "at_block", selector, body }, atToken);
20555
20432
  }
20556
- parseStringExpr(token) {
20557
- if (!token.value.includes("${")) {
20558
- return this.withLoc({ kind: "str_lit", value: token.value }, token);
20559
- }
20560
- const parts = [];
20561
- let current = "";
20562
- let index = 0;
20563
- while (index < token.value.length) {
20564
- if (token.value[index] === "$" && token.value[index + 1] === "{") {
20565
- if (current) {
20566
- parts.push(current);
20567
- current = "";
20568
- }
20569
- index += 2;
20570
- let depth = 1;
20571
- let exprSource = "";
20572
- let inString = false;
20573
- while (index < token.value.length && depth > 0) {
20574
- const char = token.value[index];
20575
- if (char === '"' && token.value[index - 1] !== "\\") {
20576
- inString = !inString;
20577
- }
20578
- if (!inString) {
20579
- if (char === "{") {
20580
- depth++;
20581
- } else if (char === "}") {
20582
- depth--;
20583
- if (depth === 0) {
20584
- index++;
20585
- break;
20586
- }
20587
- }
20588
- }
20589
- if (depth > 0) {
20590
- exprSource += char;
20591
- }
20592
- index++;
20433
+ parseExecuteStmt() {
20434
+ const executeToken = this.expect("execute");
20435
+ const subcommands = [];
20436
+ while (!this.check("run") && !this.check("eof")) {
20437
+ if (this.match("as")) {
20438
+ const selector = this.parseSelector();
20439
+ subcommands.push({ kind: "as", selector });
20440
+ } else if (this.match("at")) {
20441
+ const selector = this.parseSelector();
20442
+ subcommands.push({ kind: "at", selector });
20443
+ } else if (this.checkIdent("positioned")) {
20444
+ this.advance();
20445
+ if (this.match("as")) {
20446
+ const selector = this.parseSelector();
20447
+ subcommands.push({ kind: "positioned_as", selector });
20448
+ } else {
20449
+ const x = this.parseCoordToken();
20450
+ const y = this.parseCoordToken();
20451
+ const z = this.parseCoordToken();
20452
+ subcommands.push({ kind: "positioned", x, y, z });
20593
20453
  }
20594
- if (depth !== 0) {
20595
- this.error("Unterminated string interpolation");
20454
+ } else if (this.checkIdent("rotated")) {
20455
+ this.advance();
20456
+ if (this.match("as")) {
20457
+ const selector = this.parseSelector();
20458
+ subcommands.push({ kind: "rotated_as", selector });
20459
+ } else {
20460
+ const yaw = this.parseCoordToken();
20461
+ const pitch = this.parseCoordToken();
20462
+ subcommands.push({ kind: "rotated", yaw, pitch });
20596
20463
  }
20597
- parts.push(this.parseEmbeddedExpr(exprSource));
20598
- continue;
20599
- }
20600
- current += token.value[index];
20601
- index++;
20602
- }
20603
- if (current) {
20604
- parts.push(current);
20605
- }
20606
- return this.withLoc({ kind: "str_interp", parts }, token);
20607
- }
20608
- parseFStringExpr(token) {
20609
- const parts = [];
20610
- let current = "";
20611
- let index = 0;
20612
- while (index < token.value.length) {
20613
- if (token.value[index] === "{") {
20614
- if (current) {
20615
- parts.push({ kind: "text", value: current });
20616
- current = "";
20464
+ } else if (this.checkIdent("facing")) {
20465
+ this.advance();
20466
+ if (this.checkIdent("entity")) {
20467
+ this.advance();
20468
+ const selector = this.parseSelector();
20469
+ const anchor = this.checkIdent("eyes") || this.checkIdent("feet") ? this.advance().value : "feet";
20470
+ subcommands.push({ kind: "facing_entity", selector, anchor });
20471
+ } else {
20472
+ const x = this.parseCoordToken();
20473
+ const y = this.parseCoordToken();
20474
+ const z = this.parseCoordToken();
20475
+ subcommands.push({ kind: "facing", x, y, z });
20617
20476
  }
20618
- index++;
20619
- let depth = 1;
20620
- let exprSource = "";
20621
- let inString = false;
20622
- while (index < token.value.length && depth > 0) {
20623
- const char = token.value[index];
20624
- if (char === '"' && token.value[index - 1] !== "\\") {
20625
- inString = !inString;
20626
- }
20627
- if (!inString) {
20628
- if (char === "{") {
20629
- depth++;
20630
- } else if (char === "}") {
20631
- depth--;
20632
- if (depth === 0) {
20633
- index++;
20634
- break;
20635
- }
20636
- }
20637
- }
20638
- if (depth > 0) {
20639
- exprSource += char;
20477
+ } else if (this.checkIdent("anchored")) {
20478
+ this.advance();
20479
+ const anchor = this.advance().value;
20480
+ subcommands.push({ kind: "anchored", anchor });
20481
+ } else if (this.checkIdent("align")) {
20482
+ this.advance();
20483
+ const axes = this.advance().value;
20484
+ subcommands.push({ kind: "align", axes });
20485
+ } else if (this.checkIdent("on")) {
20486
+ this.advance();
20487
+ const relation = this.advance().value;
20488
+ subcommands.push({ kind: "on", relation });
20489
+ } else if (this.checkIdent("summon")) {
20490
+ this.advance();
20491
+ const entity = this.advance().value;
20492
+ subcommands.push({ kind: "summon", entity });
20493
+ } else if (this.checkIdent("store")) {
20494
+ this.advance();
20495
+ const storeType = this.advance().value;
20496
+ if (this.checkIdent("score")) {
20497
+ this.advance();
20498
+ const target = this.advance().value;
20499
+ const targetObj = this.advance().value;
20500
+ if (storeType === "result") {
20501
+ subcommands.push({ kind: "store_result", target, targetObj });
20502
+ } else {
20503
+ subcommands.push({ kind: "store_success", target, targetObj });
20640
20504
  }
20641
- index++;
20642
- }
20643
- if (depth !== 0) {
20644
- this.error("Unterminated f-string interpolation");
20505
+ } else {
20506
+ this.error("store currently only supports score target");
20645
20507
  }
20646
- parts.push({ kind: "expr", expr: this.parseEmbeddedExpr(exprSource) });
20647
- continue;
20508
+ } else if (this.match("if")) {
20509
+ this.parseExecuteCondition(subcommands, "if");
20510
+ } else if (this.match("unless")) {
20511
+ this.parseExecuteCondition(subcommands, "unless");
20512
+ } else if (this.match("in")) {
20513
+ let dim = this.advance().value;
20514
+ if (this.match(":"))
20515
+ dim += ":" + this.advance().value;
20516
+ subcommands.push({ kind: "in", dimension: dim });
20517
+ } else {
20518
+ this.error(`Unexpected token in execute statement: '${this.peek().value || this.peek().kind}'. Valid subcommands: as, at, positioned, align, facing, rotated, anchored, if, unless, in, store`);
20648
20519
  }
20649
- current += token.value[index];
20650
- index++;
20651
- }
20652
- if (current) {
20653
- parts.push({ kind: "text", value: current });
20654
20520
  }
20655
- return this.withLoc({ kind: "f_string", parts }, token);
20521
+ this.expect("run");
20522
+ const body = this.parseBlock();
20523
+ return this.withLoc({ kind: "execute", subcommands, body }, executeToken);
20656
20524
  }
20657
- parseEmbeddedExpr(source) {
20658
- const tokens = new lexer_1.Lexer(source, this.filePath).tokenize();
20659
- const parser = new _Parser(tokens, source, this.filePath);
20660
- const expr = parser.parseExpr();
20661
- if (!parser.check("eof")) {
20662
- parser.error(`Unexpected token '${parser.peek().kind}' in string interpolation`);
20525
+ parseExecuteCondition(subcommands, type) {
20526
+ if (this.checkIdent("entity") || this.check("selector")) {
20527
+ if (this.checkIdent("entity"))
20528
+ this.advance();
20529
+ const selectorOrVar = this.parseSelectorOrVarSelector();
20530
+ subcommands.push({ kind: type === "if" ? "if_entity" : "unless_entity", ...selectorOrVar });
20531
+ } else if (this.checkIdent("block")) {
20532
+ this.advance();
20533
+ const x = this.parseCoordToken();
20534
+ const y = this.parseCoordToken();
20535
+ const z = this.parseCoordToken();
20536
+ const block = this.parseBlockId();
20537
+ subcommands.push({ kind: type === "if" ? "if_block" : "unless_block", pos: [x, y, z], block });
20538
+ } else if (this.checkIdent("score")) {
20539
+ this.advance();
20540
+ const target = this.advance().value;
20541
+ const targetObj = this.advance().value;
20542
+ if (this.checkIdent("matches")) {
20543
+ this.advance();
20544
+ const range = this.advance().value;
20545
+ subcommands.push({ kind: type === "if" ? "if_score_range" : "unless_score_range", target, targetObj, range });
20546
+ } else {
20547
+ const op = this.advance().value;
20548
+ const source = this.advance().value;
20549
+ const sourceObj = this.advance().value;
20550
+ subcommands.push({
20551
+ kind: type === "if" ? "if_score" : "unless_score",
20552
+ target,
20553
+ targetObj,
20554
+ op,
20555
+ source,
20556
+ sourceObj
20557
+ });
20558
+ }
20559
+ } else {
20560
+ this.error(`Unknown condition type after ${type}`);
20663
20561
  }
20664
- return expr;
20665
20562
  }
20666
- parseStructLit() {
20667
- const braceToken = this.expect("{");
20563
+ // -------------------------------------------------------------------------
20564
+ // Expression statement
20565
+ // -------------------------------------------------------------------------
20566
+ parseExprStmt() {
20567
+ const expr = this.parseExpr();
20568
+ this.match(";");
20569
+ const exprToken = this.getLocToken(expr) ?? this.peek();
20570
+ return this.withLoc({ kind: "expr", expr }, exprToken);
20571
+ }
20572
+ };
20573
+ exports2.StmtParser = StmtParser;
20574
+ }
20575
+ });
20576
+
20577
+ // ../../dist/src/parser/decl-parser.js
20578
+ var require_decl_parser = __commonJS({
20579
+ "../../dist/src/parser/decl-parser.js"(exports2) {
20580
+ "use strict";
20581
+ Object.defineProperty(exports2, "__esModule", { value: true });
20582
+ exports2.DeclParser = void 0;
20583
+ var stmt_parser_1 = require_stmt_parser();
20584
+ var DeclParser = class extends stmt_parser_1.StmtParser {
20585
+ // -------------------------------------------------------------------------
20586
+ // Struct
20587
+ // -------------------------------------------------------------------------
20588
+ parseStructDecl() {
20589
+ const structToken = this.expect("struct");
20590
+ const name = this.expect("ident").value;
20591
+ const extendsName = this.match("extends") ? this.expect("ident").value : void 0;
20592
+ this.expect("{");
20668
20593
  const fields = [];
20669
- if (!this.check("}")) {
20670
- do {
20671
- const name = this.expect("ident").value;
20672
- this.expect(":");
20673
- const value = this.parseExpr();
20674
- fields.push({ name, value });
20675
- } while (this.match(","));
20594
+ while (!this.check("}") && !this.check("eof")) {
20595
+ const fieldName = this.expect("ident").value;
20596
+ this.expect(":");
20597
+ const fieldType = this.parseType();
20598
+ fields.push({ name: fieldName, type: fieldType });
20599
+ this.match(",");
20676
20600
  }
20677
20601
  this.expect("}");
20678
- return this.withLoc({ kind: "struct_lit", fields }, braceToken);
20679
- }
20680
- parseArrayLit() {
20681
- const bracketToken = this.expect("[");
20682
- const elements = [];
20683
- if (!this.check("]")) {
20684
- do {
20685
- elements.push(this.parseExpr());
20686
- } while (this.match(","));
20687
- }
20688
- this.expect("]");
20689
- return this.withLoc({ kind: "array_lit", elements }, bracketToken);
20602
+ return this.withLoc({ name, extends: extendsName, fields }, structToken);
20690
20603
  }
20691
- isLambdaStart() {
20692
- if (!this.check("("))
20693
- return false;
20694
- let offset = 1;
20695
- if (this.peek(offset).kind !== ")") {
20696
- while (true) {
20697
- if (this.peek(offset).kind !== "ident") {
20698
- return false;
20699
- }
20700
- offset += 1;
20701
- if (this.peek(offset).kind === ":") {
20702
- offset += 1;
20703
- const consumed = this.typeTokenLength(offset);
20704
- if (consumed === 0) {
20705
- return false;
20706
- }
20707
- offset += consumed;
20708
- }
20709
- if (this.peek(offset).kind === ",") {
20710
- offset += 1;
20711
- continue;
20604
+ // -------------------------------------------------------------------------
20605
+ // Enum
20606
+ // -------------------------------------------------------------------------
20607
+ parseEnumDecl() {
20608
+ const enumToken = this.expect("enum");
20609
+ const name = this.expect("ident").value;
20610
+ this.expect("{");
20611
+ const variants = [];
20612
+ let nextValue = 0;
20613
+ while (!this.check("}") && !this.check("eof")) {
20614
+ const variantToken = this.expect("ident");
20615
+ const variant = { name: variantToken.value };
20616
+ if (this.check("(")) {
20617
+ this.advance();
20618
+ const fields = [];
20619
+ while (!this.check(")") && !this.check("eof")) {
20620
+ const fieldName = this.expect("ident").value;
20621
+ this.expect(":");
20622
+ const fieldType = this.parseType();
20623
+ fields.push({ name: fieldName, type: fieldType });
20624
+ if (!this.match(","))
20625
+ break;
20712
20626
  }
20713
- break;
20627
+ this.expect(")");
20628
+ variant.fields = fields;
20714
20629
  }
20715
- }
20716
- if (this.peek(offset).kind !== ")") {
20717
- return false;
20718
- }
20719
- offset += 1;
20720
- if (this.peek(offset).kind === "=>") {
20721
- return true;
20722
- }
20723
- if (this.peek(offset).kind === "->") {
20724
- offset += 1;
20725
- const consumed = this.typeTokenLength(offset);
20726
- if (consumed === 0) {
20727
- return false;
20630
+ if (this.match("=")) {
20631
+ const valueToken = this.expect("int_lit");
20632
+ variant.value = parseInt(valueToken.value, 10);
20633
+ nextValue = variant.value + 1;
20634
+ } else {
20635
+ variant.value = nextValue++;
20728
20636
  }
20729
- offset += consumed;
20730
- return this.peek(offset).kind === "=>";
20637
+ variants.push(variant);
20638
+ if (!this.match(","))
20639
+ break;
20731
20640
  }
20732
- return false;
20641
+ this.expect("}");
20642
+ return this.withLoc({ name, variants }, enumToken);
20733
20643
  }
20734
- typeTokenLength(offset) {
20735
- const token = this.peek(offset);
20736
- if (token.kind === "(") {
20737
- let inner = offset + 1;
20738
- if (this.peek(inner).kind !== ")") {
20739
- while (true) {
20740
- const consumed = this.typeTokenLength(inner);
20741
- if (consumed === 0) {
20742
- return 0;
20743
- }
20744
- inner += consumed;
20745
- if (this.peek(inner).kind === ",") {
20746
- inner += 1;
20747
- continue;
20748
- }
20749
- break;
20750
- }
20751
- }
20752
- if (this.peek(inner).kind !== ")") {
20753
- return 0;
20754
- }
20755
- inner += 1;
20756
- if (this.peek(inner).kind !== "->") {
20757
- return 0;
20758
- }
20759
- inner += 1;
20760
- const returnLen = this.typeTokenLength(inner);
20761
- return returnLen === 0 ? 0 : inner + returnLen - offset;
20762
- }
20763
- const isNamedType = token.kind === "int" || token.kind === "bool" || token.kind === "float" || token.kind === "fixed" || token.kind === "string" || token.kind === "void" || token.kind === "BlockPos" || token.kind === "ident";
20764
- if (!isNamedType) {
20765
- return 0;
20766
- }
20767
- let length = 1;
20768
- while (this.peek(offset + length).kind === "[" && this.peek(offset + length + 1).kind === "]") {
20769
- length += 2;
20644
+ // -------------------------------------------------------------------------
20645
+ // Impl Block
20646
+ // -------------------------------------------------------------------------
20647
+ parseImplBlock() {
20648
+ const implToken = this.expect("impl");
20649
+ let traitName;
20650
+ let typeName;
20651
+ const firstName = this.expect("ident").value;
20652
+ if (this.match("for")) {
20653
+ traitName = firstName;
20654
+ typeName = this.expect("ident").value;
20655
+ } else {
20656
+ typeName = firstName;
20770
20657
  }
20771
- return length;
20772
- }
20773
- isBlockPosLiteral() {
20774
- if (!this.check("("))
20775
- return false;
20776
- let offset = 1;
20777
- for (let i = 0; i < 3; i++) {
20778
- const consumed = this.coordComponentTokenLength(offset);
20779
- if (consumed === 0)
20780
- return false;
20781
- offset += consumed;
20782
- if (i < 2) {
20783
- if (this.peek(offset).kind !== ",")
20784
- return false;
20785
- offset += 1;
20786
- }
20658
+ this.expect("{");
20659
+ const methods = [];
20660
+ while (!this.check("}") && !this.check("eof")) {
20661
+ methods.push(this.parseFnDecl(typeName));
20787
20662
  }
20788
- return this.peek(offset).kind === ")";
20663
+ this.expect("}");
20664
+ return this.withLoc({ kind: "impl_block", traitName, typeName, methods }, implToken);
20789
20665
  }
20790
- coordComponentTokenLength(offset) {
20791
- const token = this.peek(offset);
20792
- if (token.kind === "int_lit") {
20793
- return 1;
20794
- }
20795
- if (token.kind === "-") {
20796
- return this.peek(offset + 1).kind === "int_lit" ? 2 : 0;
20666
+ // -------------------------------------------------------------------------
20667
+ // Interface
20668
+ // -------------------------------------------------------------------------
20669
+ parseInterfaceDecl() {
20670
+ const ifaceToken = this.expect("interface");
20671
+ const name = this.expect("ident").value;
20672
+ this.expect("{");
20673
+ const methods = [];
20674
+ while (!this.check("}") && !this.check("eof")) {
20675
+ const fnToken = this.expect("fn");
20676
+ const methodName = this.expect("ident").value;
20677
+ this.expect("(");
20678
+ const params = this.parseInterfaceParams();
20679
+ this.expect(")");
20680
+ let returnType;
20681
+ if (this.match(":"))
20682
+ returnType = this.parseType();
20683
+ methods.push(this.withLoc({ name: methodName, params, returnType }, fnToken));
20797
20684
  }
20798
- if (token.kind === "rel_coord" || token.kind === "local_coord") {
20799
- return 1;
20685
+ this.expect("}");
20686
+ return this.withLoc({ name, methods }, ifaceToken);
20687
+ }
20688
+ // -------------------------------------------------------------------------
20689
+ // Const / Global
20690
+ // -------------------------------------------------------------------------
20691
+ parseConstDecl() {
20692
+ const constToken = this.expect("const");
20693
+ const name = this.expect("ident").value;
20694
+ let type;
20695
+ if (this.match(":"))
20696
+ type = this.parseType();
20697
+ this.expect("=");
20698
+ const value = this.parseLiteralExpr();
20699
+ this.match(";");
20700
+ const inferredType = type ?? (value.kind === "str_lit" ? { kind: "named", name: "string" } : value.kind === "bool_lit" ? { kind: "named", name: "bool" } : value.kind === "float_lit" ? { kind: "named", name: "fixed" } : { kind: "named", name: "int" });
20701
+ return this.withLoc({ name, type: inferredType, value }, constToken);
20702
+ }
20703
+ parseGlobalDecl(mutable) {
20704
+ const token = this.advance();
20705
+ const name = this.expect("ident").value;
20706
+ this.expect(":");
20707
+ const type = this.parseType();
20708
+ let init;
20709
+ if (this.match("=")) {
20710
+ init = this.parseExpr();
20711
+ } else {
20712
+ init = { kind: "int_lit", value: 0 };
20800
20713
  }
20801
- return 0;
20714
+ this.match(";");
20715
+ return this.withLoc({ kind: "global", name, type, init, mutable }, token);
20802
20716
  }
20803
- parseBlockPos() {
20804
- const openParenToken = this.expect("(");
20805
- const x = this.parseCoordComponent();
20806
- this.expect(",");
20807
- const y = this.parseCoordComponent();
20808
- this.expect(",");
20809
- const z = this.parseCoordComponent();
20810
- this.expect(")");
20811
- return this.withLoc({ kind: "blockpos", x, y, z }, openParenToken);
20717
+ // -------------------------------------------------------------------------
20718
+ // Function
20719
+ // -------------------------------------------------------------------------
20720
+ parseExportedFnDecl() {
20721
+ this.expect("export");
20722
+ const fn = this.parseFnDecl();
20723
+ fn.isExported = true;
20724
+ return fn;
20812
20725
  }
20813
- parseCoordComponent() {
20814
- const token = this.peek();
20815
- if (token.kind === "rel_coord") {
20726
+ parseFnDecl(implTypeName) {
20727
+ const decorators = this.parseDecorators();
20728
+ const watchObjective = decorators.find((decorator) => decorator.name === "watch")?.args?.objective;
20729
+ let isExported;
20730
+ const filteredDecorators = decorators.filter((d) => {
20731
+ if (d.name === "keep") {
20732
+ isExported = true;
20733
+ return false;
20734
+ }
20735
+ return true;
20736
+ });
20737
+ const fnToken = this.expect("fn");
20738
+ const name = this.expect("ident").value;
20739
+ let typeParams;
20740
+ if (this.check("<")) {
20816
20741
  this.advance();
20817
- const offset = this.parseCoordOffsetFromValue(token.value.slice(1));
20818
- return { kind: "relative", offset };
20742
+ typeParams = [];
20743
+ do {
20744
+ typeParams.push(this.expect("ident").value);
20745
+ } while (this.match(","));
20746
+ this.expect(">");
20819
20747
  }
20820
- if (token.kind === "local_coord") {
20821
- this.advance();
20822
- const offset = this.parseCoordOffsetFromValue(token.value.slice(1));
20823
- return { kind: "local", offset };
20748
+ this.expect("(");
20749
+ const params = this.parseParams(implTypeName);
20750
+ this.expect(")");
20751
+ let returnType = { kind: "named", name: "void" };
20752
+ if (this.match("->") || this.match(":")) {
20753
+ returnType = this.parseType();
20824
20754
  }
20825
- return { kind: "absolute", value: this.parseSignedCoordOffset(true) };
20826
- }
20827
- parseCoordOffsetFromValue(value) {
20828
- if (value === "" || value === void 0)
20829
- return 0;
20830
- return parseFloat(value);
20755
+ const body = this.parseBlock();
20756
+ const closingBraceLine = this.tokens[this.pos - 1]?.line;
20757
+ const fn = this.withLoc({
20758
+ name,
20759
+ typeParams,
20760
+ params,
20761
+ returnType,
20762
+ decorators: filteredDecorators,
20763
+ body,
20764
+ isLibraryFn: this.inLibraryMode || void 0,
20765
+ isExported,
20766
+ watchObjective
20767
+ }, fnToken);
20768
+ if (fn.span && closingBraceLine)
20769
+ fn.span.endLine = closingBraceLine;
20770
+ return fn;
20831
20771
  }
20832
- parseSignedCoordOffset(requireValue = false) {
20833
- let sign = 1;
20834
- if (this.match("-")) {
20835
- sign = -1;
20836
- }
20837
- if (this.check("int_lit")) {
20838
- return sign * parseInt(this.advance().value, 10);
20772
+ parseDeclareStub() {
20773
+ this.expect("fn");
20774
+ this.expect("ident");
20775
+ this.expect("(");
20776
+ let depth = 1;
20777
+ while (!this.check("eof") && depth > 0) {
20778
+ const t = this.advance();
20779
+ if (t.kind === "(")
20780
+ depth++;
20781
+ else if (t.kind === ")")
20782
+ depth--;
20839
20783
  }
20840
- if (requireValue) {
20841
- this.error("Expected integer coordinate component");
20784
+ if (this.match(":") || this.match("->")) {
20785
+ this.parseType();
20842
20786
  }
20843
- return 0;
20787
+ this.match(";");
20844
20788
  }
20845
20789
  // -------------------------------------------------------------------------
20846
- // Selector Parsing
20790
+ // Decorators
20847
20791
  // -------------------------------------------------------------------------
20848
- parseSelector() {
20849
- const token = this.expect("selector");
20850
- return this.parseSelectorValue(token.value);
20851
- }
20852
- // Parse either a selector (@a[...]) or a variable with filters (p[...])
20853
- // Returns { selector } for selectors or { varName, filters } for variables
20854
- parseSelectorOrVarSelector() {
20855
- if (this.check("selector")) {
20856
- return { selector: this.parseSelector() };
20857
- }
20858
- const varToken = this.expect("ident");
20859
- const varName = varToken.value;
20860
- if (this.check("[")) {
20861
- this.advance();
20862
- let filterStr = "";
20863
- let depth = 1;
20864
- while (depth > 0 && !this.check("eof")) {
20865
- if (this.check("["))
20866
- depth++;
20867
- else if (this.check("]"))
20868
- depth--;
20869
- if (depth > 0) {
20870
- filterStr += this.peek().value ?? this.peek().kind;
20871
- this.advance();
20872
- }
20873
- }
20874
- this.expect("]");
20875
- const filters = this.parseSelectorFilters(filterStr);
20876
- return { varName, filters };
20792
+ parseDecorators() {
20793
+ const decorators = [];
20794
+ while (this.check("decorator")) {
20795
+ const token = this.advance();
20796
+ const decorator = this.parseDecoratorValue(token.value);
20797
+ decorators.push(decorator);
20877
20798
  }
20878
- return { varName };
20799
+ return decorators;
20879
20800
  }
20880
- parseSelectorValue(value) {
20881
- const bracketIndex = value.indexOf("[");
20882
- if (bracketIndex === -1) {
20883
- return { kind: value };
20801
+ parseDecoratorValue(value) {
20802
+ const match = value.match(/^@([A-Za-z_][A-Za-z0-9_-]*)(?:\((.*)\))?$/s);
20803
+ if (!match) {
20804
+ this.error(`Invalid decorator: ${value}`);
20884
20805
  }
20885
- const kind = value.slice(0, bracketIndex);
20886
- const paramsStr = value.slice(bracketIndex + 1, -1);
20887
- const filters = this.parseSelectorFilters(paramsStr);
20888
- return { kind, filters };
20889
- }
20890
- parseSelectorFilters(paramsStr) {
20891
- const filters = {};
20892
- const parts = this.splitSelectorParams(paramsStr);
20893
- for (const part of parts) {
20894
- const eqIndex = part.indexOf("=");
20895
- if (eqIndex === -1)
20896
- continue;
20897
- const key = part.slice(0, eqIndex).trim();
20898
- const val = part.slice(eqIndex + 1).trim();
20899
- switch (key) {
20900
- case "type":
20901
- filters.type = val;
20902
- break;
20903
- case "distance":
20904
- filters.distance = this.parseRangeValue(val);
20905
- break;
20906
- case "tag":
20907
- if (val.startsWith("!")) {
20908
- filters.notTag = filters.notTag ?? [];
20909
- filters.notTag.push(val.slice(1));
20910
- } else {
20911
- filters.tag = filters.tag ?? [];
20912
- filters.tag.push(val);
20913
- }
20914
- break;
20915
- case "limit":
20916
- filters.limit = parseInt(val, 10);
20917
- break;
20918
- case "sort":
20919
- filters.sort = val;
20920
- break;
20921
- case "nbt":
20922
- filters.nbt = val;
20923
- break;
20924
- case "gamemode":
20925
- filters.gamemode = val;
20926
- break;
20927
- case "scores":
20928
- filters.scores = this.parseScoresFilter(val);
20929
- break;
20930
- case "x":
20931
- filters.x = this.parseRangeValue(val);
20932
- break;
20933
- case "y":
20934
- filters.y = this.parseRangeValue(val);
20935
- break;
20936
- case "z":
20937
- filters.z = this.parseRangeValue(val);
20938
- break;
20939
- case "x_rotation":
20940
- filters.x_rotation = this.parseRangeValue(val);
20941
- break;
20942
- case "y_rotation":
20943
- filters.y_rotation = this.parseRangeValue(val);
20944
- break;
20806
+ const name = match[1];
20807
+ const argsStr = match[2];
20808
+ if (!argsStr)
20809
+ return { name };
20810
+ if (name === "profile" || name === "benchmark" || name === "memoize") {
20811
+ this.error(`@${name} decorator does not accept arguments`);
20812
+ }
20813
+ const args = {};
20814
+ if (name === "on") {
20815
+ const eventTypeMatch = argsStr.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
20816
+ if (eventTypeMatch) {
20817
+ args.eventType = eventTypeMatch[1];
20818
+ return { name, args };
20945
20819
  }
20946
20820
  }
20947
- return filters;
20948
- }
20949
- splitSelectorParams(str) {
20950
- const parts = [];
20951
- let current = "";
20952
- let depth = 0;
20953
- for (const char of str) {
20954
- if (char === "{" || char === "[")
20955
- depth++;
20956
- else if (char === "}" || char === "]")
20957
- depth--;
20958
- else if (char === "," && depth === 0) {
20959
- parts.push(current.trim());
20960
- current = "";
20961
- continue;
20821
+ if (name === "watch" || name === "on_trigger" || name === "on_advancement" || name === "on_craft" || name === "on_join_team") {
20822
+ const strMatch = argsStr.match(/^"([^"]*)"$/);
20823
+ if (strMatch) {
20824
+ if (name === "watch")
20825
+ args.objective = strMatch[1];
20826
+ else if (name === "on_trigger")
20827
+ args.trigger = strMatch[1];
20828
+ else if (name === "on_advancement")
20829
+ args.advancement = strMatch[1];
20830
+ else if (name === "on_craft")
20831
+ args.item = strMatch[1];
20832
+ else if (name === "on_join_team")
20833
+ args.team = strMatch[1];
20834
+ return { name, args };
20962
20835
  }
20963
- current += char;
20964
20836
  }
20965
- if (current.trim()) {
20966
- parts.push(current.trim());
20837
+ if (name === "config") {
20838
+ const configMatch = argsStr.match(/^"([^"]+)"\s*,\s*default\s*:\s*(-?\d+(?:\.\d+)?)$/);
20839
+ if (configMatch) {
20840
+ return { name, args: { configKey: configMatch[1], configDefault: parseFloat(configMatch[2]) } };
20841
+ }
20842
+ const keyOnlyMatch = argsStr.match(/^"([^"]+)"$/);
20843
+ if (keyOnlyMatch) {
20844
+ return { name, args: { configKey: keyOnlyMatch[1] } };
20845
+ }
20846
+ this.error(`Invalid @config syntax. Expected: @config("key", default: value) or @config("key")`);
20967
20847
  }
20968
- return parts;
20969
- }
20970
- parseScoresFilter(val) {
20971
- const scores = {};
20972
- const inner = val.slice(1, -1);
20973
- const parts = inner.split(",");
20974
- for (const part of parts) {
20975
- const [name, range] = part.split("=").map((s) => s.trim());
20976
- scores[name] = this.parseRangeValue(range);
20848
+ if (name === "deprecated") {
20849
+ const strMatch = argsStr.match(/^"([^"]*)"$/);
20850
+ if (strMatch)
20851
+ return { name, args: { message: strMatch[1] } };
20852
+ return { name, args: {} };
20977
20853
  }
20978
- return scores;
20979
- }
20980
- parseRangeValue(value) {
20981
- if (value.startsWith("..=")) {
20982
- const rest = value.slice(3);
20983
- if (!rest)
20984
- return {};
20985
- const max = parseInt(rest, 10);
20986
- return { max };
20854
+ if (name === "test") {
20855
+ const strMatch = argsStr.match(/^"([^"]*)"$/);
20856
+ if (strMatch)
20857
+ return { name, args: { testLabel: strMatch[1] } };
20858
+ return { name, args: { testLabel: "" } };
20987
20859
  }
20988
- if (value.startsWith("..")) {
20989
- const rest = value.slice(2);
20990
- if (!rest)
20991
- return {};
20992
- const max = parseInt(rest, 10);
20993
- return { max };
20860
+ if (name === "require_on_load") {
20861
+ const rawArgs = [];
20862
+ for (const part of argsStr.split(",")) {
20863
+ const trimmed = part.trim();
20864
+ const identMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
20865
+ if (identMatch) {
20866
+ rawArgs.push({ kind: "string", value: identMatch[1] });
20867
+ } else {
20868
+ const strMatch = trimmed.match(/^"([^"]*)"$/);
20869
+ if (strMatch)
20870
+ rawArgs.push({ kind: "string", value: strMatch[1] });
20871
+ }
20872
+ }
20873
+ return { name, rawArgs };
20994
20874
  }
20995
- const inclIdx = value.indexOf("..=");
20996
- if (inclIdx !== -1) {
20997
- const min = parseInt(value.slice(0, inclIdx), 10);
20998
- const rest = value.slice(inclIdx + 3);
20999
- if (!rest)
21000
- return { min };
21001
- const max = parseInt(rest, 10);
21002
- return { min, max };
20875
+ for (const part of argsStr.split(",")) {
20876
+ const [key, val] = part.split("=").map((s) => s.trim());
20877
+ if (key === "rate")
20878
+ args.rate = parseInt(val, 10);
20879
+ else if (key === "ticks")
20880
+ args.ticks = parseInt(val, 10);
20881
+ else if (key === "batch")
20882
+ args.batch = parseInt(val, 10);
20883
+ else if (key === "onDone")
20884
+ args.onDone = val.replace(/^["']|["']$/g, "");
20885
+ else if (key === "trigger")
20886
+ args.trigger = val;
20887
+ else if (key === "advancement")
20888
+ args.advancement = val;
20889
+ else if (key === "item")
20890
+ args.item = val;
20891
+ else if (key === "team")
20892
+ args.team = val;
20893
+ else if (key === "max")
20894
+ args.max = parseInt(val, 10);
21003
20895
  }
21004
- const dotIndex = value.indexOf("..");
21005
- if (dotIndex !== -1) {
21006
- const min = parseInt(value.slice(0, dotIndex), 10);
21007
- const rest = value.slice(dotIndex + 2);
21008
- if (!rest)
21009
- return { min };
21010
- const max = parseInt(rest, 10);
21011
- return { min, max };
20896
+ return { name, args };
20897
+ }
20898
+ };
20899
+ exports2.DeclParser = DeclParser;
20900
+ }
20901
+ });
20902
+
20903
+ // ../../dist/src/parser/index.js
20904
+ var require_parser = __commonJS({
20905
+ "../../dist/src/parser/index.js"(exports2) {
20906
+ "use strict";
20907
+ Object.defineProperty(exports2, "__esModule", { value: true });
20908
+ exports2.Parser = void 0;
20909
+ var diagnostics_1 = require_diagnostics();
20910
+ var decl_parser_1 = require_decl_parser();
20911
+ var Parser = class extends decl_parser_1.DeclParser {
20912
+ // -------------------------------------------------------------------------
20913
+ // Program (top-level entry point)
20914
+ // -------------------------------------------------------------------------
20915
+ parse(defaultNamespace = "redscript") {
20916
+ let namespace = defaultNamespace;
20917
+ const globals = [];
20918
+ const declarations = [];
20919
+ const structs = [];
20920
+ const implBlocks = [];
20921
+ const enums = [];
20922
+ const consts = [];
20923
+ const imports = [];
20924
+ const interfaces = [];
20925
+ let isLibrary = false;
20926
+ let moduleName;
20927
+ if (this.check("namespace")) {
20928
+ this.advance();
20929
+ const name = this.expect("ident");
20930
+ namespace = name.value;
20931
+ this.match(";");
21012
20932
  }
21013
- const val = parseInt(value, 10);
21014
- return { min: val, max: val };
20933
+ if (this.check("module")) {
20934
+ this.advance();
20935
+ const modKind = this.expect("ident");
20936
+ if (modKind.value === "library") {
20937
+ isLibrary = true;
20938
+ this.inLibraryMode = true;
20939
+ } else {
20940
+ moduleName = modKind.value;
20941
+ }
20942
+ this.match(";");
20943
+ }
20944
+ while (!this.check("eof")) {
20945
+ try {
20946
+ if (this.check("decorator") && this.peek().value.startsWith("@config")) {
20947
+ const decorToken = this.advance();
20948
+ const decorator = this.parseDecoratorValue(decorToken.value);
20949
+ if (!this.check("let")) {
20950
+ this.error("@config decorator must be followed by a let declaration");
20951
+ }
20952
+ const g = this.parseGlobalDecl(true);
20953
+ g.configKey = decorator.args?.configKey;
20954
+ g.configDefault = decorator.args?.configDefault;
20955
+ globals.push(g);
20956
+ } else if (this.check("let")) {
20957
+ globals.push(this.parseGlobalDecl(true));
20958
+ } else if (this.check("decorator") && this.peek().value === "@singleton") {
20959
+ this.advance();
20960
+ if (!this.check("struct")) {
20961
+ this.error("@singleton decorator must be followed by a struct declaration");
20962
+ }
20963
+ const s = this.parseStructDecl();
20964
+ s.isSingleton = true;
20965
+ structs.push(s);
20966
+ } else if (this.check("struct")) {
20967
+ structs.push(this.parseStructDecl());
20968
+ } else if (this.check("impl")) {
20969
+ implBlocks.push(this.parseImplBlock());
20970
+ } else if (this.check("interface")) {
20971
+ interfaces.push(this.parseInterfaceDecl());
20972
+ } else if (this.check("enum")) {
20973
+ enums.push(this.parseEnumDecl());
20974
+ } else if (this.check("const")) {
20975
+ consts.push(this.parseConstDecl());
20976
+ } else if (this.check("declare")) {
20977
+ this.advance();
20978
+ this.parseDeclareStub();
20979
+ } else if (this.check("export")) {
20980
+ declarations.push(this.parseExportedFnDecl());
20981
+ } else if (this.check("import") || this.check("ident") && this.peek().value === "import") {
20982
+ this.advance();
20983
+ const importToken = this.peek();
20984
+ const modName = this.expect("ident").value;
20985
+ if (this.check("::")) {
20986
+ this.advance();
20987
+ let symbol;
20988
+ if (this.check("*")) {
20989
+ this.advance();
20990
+ symbol = "*";
20991
+ } else {
20992
+ symbol = this.expect("ident").value;
20993
+ }
20994
+ this.match(";");
20995
+ imports.push(this.withLoc({ moduleName: modName, symbol }, importToken));
20996
+ } else {
20997
+ this.match(";");
20998
+ imports.push(this.withLoc({ moduleName: modName, symbol: void 0 }, importToken));
20999
+ }
21000
+ } else {
21001
+ declarations.push(this.parseFnDecl());
21002
+ }
21003
+ } catch (err) {
21004
+ if (err instanceof diagnostics_1.DiagnosticError) {
21005
+ this.parseErrors.push(err);
21006
+ this.syncToNextDecl();
21007
+ } else {
21008
+ throw err;
21009
+ }
21010
+ }
21011
+ }
21012
+ return { namespace, moduleName, globals, declarations, structs, implBlocks, enums, consts, imports, interfaces, isLibrary };
21015
21013
  }
21016
21014
  };
21017
21015
  exports2.Parser = Parser;
@@ -22779,16 +22777,17 @@ var require_lower2 = __commonJS({
22779
22777
  else if (c.value.kind === "float_lit")
22780
22778
  constValues.set(c.name, Math.round(c.value.value * 1e4));
22781
22779
  }
22780
+ const globalVarNames = new Set(hir.globals.map((g) => g.name));
22782
22781
  const allFunctions = [];
22783
22782
  for (const f of hir.functions) {
22784
- const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, void 0, hirFnMap, specializedFnsRegistry, void 0, enumPayloads, constValues, singletonStructs, displayImpls);
22783
+ const { fn, helpers } = lowerFunction(f, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, void 0, hirFnMap, specializedFnsRegistry, void 0, enumPayloads, constValues, singletonStructs, displayImpls, globalVarNames);
22785
22784
  allFunctions.push(fn, ...helpers);
22786
22785
  }
22787
22786
  for (const ib of hir.implBlocks) {
22788
22787
  if (ib.traitName === "Display")
22789
22788
  continue;
22790
22789
  for (const m of ib.methods) {
22791
- const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, enumPayloads, constValues);
22790
+ const { fn, helpers } = lowerImplMethod(m, ib.typeName, hir.namespace, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, sourceFile, timerCounter, enumPayloads, constValues, globalVarNames);
22792
22791
  allFunctions.push(fn, ...helpers);
22793
22792
  }
22794
22793
  }
@@ -22825,6 +22824,7 @@ var require_lower2 = __commonJS({
22825
22824
  this.constValues = /* @__PURE__ */ new Map();
22826
22825
  this.singletonStructs = /* @__PURE__ */ new Set();
22827
22826
  this.displayImpls = /* @__PURE__ */ new Map();
22827
+ this.globalVarNames = /* @__PURE__ */ new Set();
22828
22828
  this.namespace = namespace;
22829
22829
  this.fnName = fnName;
22830
22830
  this.structDefs = structDefs;
@@ -22912,13 +22912,14 @@ var require_lower2 = __commonJS({
22912
22912
  return `${this.namespace}_${this.fnName}_${varName}_${this.stringVarCount++}`;
22913
22913
  }
22914
22914
  };
22915
- function lowerFunction(fn, namespace, structDefs = /* @__PURE__ */ new Map(), implMethods = /* @__PURE__ */ new Map(), macroInfo = /* @__PURE__ */ new Map(), fnParamInfo = /* @__PURE__ */ new Map(), enumDefs = /* @__PURE__ */ new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, arrayArgBindings, hirFnMap, specializedFnsRegistry, overrideName, enumPayloads = /* @__PURE__ */ new Map(), constValues = /* @__PURE__ */ new Map(), singletonStructs = /* @__PURE__ */ new Set(), displayImpls = /* @__PURE__ */ new Map()) {
22915
+ function lowerFunction(fn, namespace, structDefs = /* @__PURE__ */ new Map(), implMethods = /* @__PURE__ */ new Map(), macroInfo = /* @__PURE__ */ new Map(), fnParamInfo = /* @__PURE__ */ new Map(), enumDefs = /* @__PURE__ */ new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, arrayArgBindings, hirFnMap, specializedFnsRegistry, overrideName, enumPayloads = /* @__PURE__ */ new Map(), constValues = /* @__PURE__ */ new Map(), singletonStructs = /* @__PURE__ */ new Set(), displayImpls = /* @__PURE__ */ new Map(), globalVarNames = /* @__PURE__ */ new Set()) {
22916
22916
  const mirFnName = overrideName ?? fn.name;
22917
22917
  const ctx = new FnContext(namespace, mirFnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads);
22918
22918
  ctx.sourceFile = fn.sourceFile ?? sourceFile;
22919
22919
  ctx.constValues = constValues;
22920
22920
  ctx.singletonStructs = singletonStructs;
22921
22921
  ctx.displayImpls = displayImpls;
22922
+ ctx.globalVarNames = globalVarNames;
22922
22923
  if (hirFnMap)
22923
22924
  ctx.hirFunctions = hirFnMap;
22924
22925
  if (specializedFnsRegistry)
@@ -22969,11 +22970,12 @@ var require_lower2 = __commonJS({
22969
22970
  };
22970
22971
  return { fn: result, helpers: ctx.helperFunctions };
22971
22972
  }
22972
- function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, macroInfo = /* @__PURE__ */ new Map(), fnParamInfo = /* @__PURE__ */ new Map(), enumDefs = /* @__PURE__ */ new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, enumPayloads = /* @__PURE__ */ new Map(), constValues = /* @__PURE__ */ new Map()) {
22973
+ function lowerImplMethod(method, typeName, namespace, structDefs, implMethods, macroInfo = /* @__PURE__ */ new Map(), fnParamInfo = /* @__PURE__ */ new Map(), enumDefs = /* @__PURE__ */ new Map(), sourceFile, timerCounter = { count: 0, timerId: 0 }, enumPayloads = /* @__PURE__ */ new Map(), constValues = /* @__PURE__ */ new Map(), globalVarNames = /* @__PURE__ */ new Set()) {
22973
22974
  const fnName = `${typeName}::${method.name}`;
22974
22975
  const ctx = new FnContext(namespace, fnName, structDefs, implMethods, macroInfo, fnParamInfo, enumDefs, timerCounter, enumPayloads);
22975
22976
  ctx.sourceFile = method.sourceFile ?? sourceFile;
22976
22977
  ctx.constValues = constValues;
22978
+ ctx.globalVarNames = globalVarNames;
22977
22979
  const fields = structDefs.get(typeName) ?? [];
22978
22980
  const hasSelf = method.params.length > 0 && method.params[0].name === "self";
22979
22981
  const params = [];
@@ -23674,7 +23676,7 @@ var require_lower2 = __commonJS({
23674
23676
  }
23675
23677
  case "raw": {
23676
23678
  const ns = ctx.getNamespace();
23677
- const rawCmd = stmt.cmd.replace(/__NS__/g, ns).replace(/__OBJ__/g, `__${ns}`);
23679
+ const rawCmd = stmt.cmd.replace(/__NS__/g, ns).replace(/__OBJ__/g, `__${ns}`).replace(/__RS__/g, "rs");
23678
23680
  ctx.emit({ kind: "call", dst: null, fn: `__raw:${rawCmd}`, args: [] });
23679
23681
  break;
23680
23682
  }
@@ -23828,6 +23830,12 @@ var require_lower2 = __commonJS({
23828
23830
  if (ctx.constValues.has(expr.name)) {
23829
23831
  return { kind: "const", value: ctx.constValues.get(expr.name) };
23830
23832
  }
23833
+ if (ctx.globalVarNames.has(expr.name)) {
23834
+ const t2 = ctx.freshTemp();
23835
+ ctx.emit({ kind: "score_read", dst: t2, player: expr.name, obj: `__${ctx.getNamespace()}` });
23836
+ scope.set(expr.name, t2);
23837
+ return { kind: "temp", name: t2 };
23838
+ }
23831
23839
  const t = ctx.freshTemp();
23832
23840
  ctx.emit({ kind: "copy", dst: t, src: { kind: "const", value: 0 } });
23833
23841
  scope.set(expr.name, t);
@@ -23921,6 +23929,25 @@ var require_lower2 = __commonJS({
23921
23929
  }
23922
23930
  case "assign": {
23923
23931
  const val = lowerExpr(expr.value, ctx, scope);
23932
+ const sv = ctx.structVars.get(expr.target);
23933
+ if (sv) {
23934
+ const fields = ctx.structDefs.get(sv.typeName) ?? [];
23935
+ for (const fieldName of fields) {
23936
+ const existingFieldTemp = sv.fields.get(fieldName);
23937
+ const fieldTemp = existingFieldTemp ?? ctx.freshTemp();
23938
+ ctx.emit({ kind: "copy", dst: fieldTemp, src: { kind: "temp", name: `__rf_${fieldName}` } });
23939
+ sv.fields.set(fieldName, fieldTemp);
23940
+ }
23941
+ return val;
23942
+ }
23943
+ if (ctx.globalVarNames.has(expr.target)) {
23944
+ const globalObj = `__${ctx.getNamespace()}`;
23945
+ ctx.emit({ kind: "score_write", player: expr.target, obj: globalObj, src: val });
23946
+ const t2 = ctx.freshTemp();
23947
+ ctx.emit({ kind: "score_read", dst: t2, player: expr.target, obj: globalObj });
23948
+ scope.set(expr.target, t2);
23949
+ return val;
23950
+ }
23924
23951
  const existing = scope.get(expr.target);
23925
23952
  const t = existing ?? ctx.freshTemp();
23926
23953
  ctx.emit({ kind: "copy", dst: t, src: val });
@@ -24259,6 +24286,59 @@ var require_lower2 = __commonJS({
24259
24286
  return { kind: "temp", name: t2 };
24260
24287
  }
24261
24288
  if (macro_1.BUILTIN_SET.has(expr.fn)) {
24289
+ if (expr.fn === "say" && expr.args[0]?.kind === "f_string") {
24290
+ const fstr = precomputeFStringParts(expr.args[0], ctx, scope);
24291
+ if (fstr.kind === "f_string") {
24292
+ const ns = ctx.getNamespace();
24293
+ const obj = `__${ns}`;
24294
+ const helperName = `${ctx.getFnName()}__say_macro_${ctx.freshTemp()}`;
24295
+ let template = "say ";
24296
+ const macroVarNames = [];
24297
+ for (const part of fstr.parts) {
24298
+ if (part.kind === "text") {
24299
+ template += part.value;
24300
+ } else {
24301
+ const inner = part.expr;
24302
+ if (inner.kind === "ident") {
24303
+ const varName = inner.name.startsWith("$") ? inner.name.slice(1) : inner.name;
24304
+ template += `$(${varName})`;
24305
+ macroVarNames.push(inner.name);
24306
+ } else if (inner.kind === "int_lit") {
24307
+ template += String(inner.value);
24308
+ } else {
24309
+ template += "?";
24310
+ }
24311
+ }
24312
+ }
24313
+ for (const varName of macroVarNames) {
24314
+ const cleanName = varName.startsWith("$") ? varName.slice(1) : varName;
24315
+ ctx.emit({
24316
+ kind: "call",
24317
+ dst: null,
24318
+ fn: `__raw:execute store result storage rs:macro_args ${cleanName} int 1 run scoreboard players get ${varName} ${obj}`,
24319
+ args: []
24320
+ });
24321
+ }
24322
+ const helperCtx = new FnContext(ns, helperName, ctx.structDefs, ctx.implMethods);
24323
+ helperCtx.emit({ kind: "call", dst: null, fn: `__raw:$${template}`, args: [] });
24324
+ helperCtx.terminate({ kind: "return", value: null });
24325
+ const helperReachable = computeReachable(helperCtx.blocks, "entry");
24326
+ const helperBlocks = helperCtx.blocks.filter((b) => helperReachable.has(b.id));
24327
+ computePreds(helperBlocks);
24328
+ ctx.helperFunctions.push({
24329
+ name: helperName,
24330
+ params: [],
24331
+ blocks: helperBlocks,
24332
+ entry: "entry",
24333
+ isMacro: true,
24334
+ sourceSnippet: "say macro helper"
24335
+ });
24336
+ ctx.emit({ kind: "call", dst: null, fn: `__raw:function ${ns}:${helperName} with storage rs:macro_args`, args: [] });
24337
+ const t3 = ctx.freshTemp();
24338
+ ctx.emit({ kind: "const", dst: t3, value: 0 });
24339
+ return { kind: "temp", name: t3 };
24340
+ }
24341
+ }
24262
24342
  const TEXT_BUILTINS_SET = /* @__PURE__ */ new Set(["tell", "tellraw", "title", "subtitle", "actionbar", "announce"]);
24263
24343
  let resolvedArgs = expr.args;
24264
24344
  if (TEXT_BUILTINS_SET.has(expr.fn)) {
@@ -24389,7 +24469,7 @@ var require_lower2 = __commonJS({
24389
24469
  const specializedName = `${expr.fn}__arr_${bindingKey}`;
24390
24470
  if (!ctx.specializedFnsRegistry.has(specializedName)) {
24391
24471
  ctx.specializedFnsRegistry.set(specializedName, []);
24392
- 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);
24472
+ 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);
24393
24473
  ctx.specializedFnsRegistry.set(specializedName, [specFn, ...specHelpers]);
24394
24474
  }
24395
24475
  const nonArrayArgs = [];
@@ -26616,7 +26696,7 @@ var require_interprocedural = __commonJS({
26616
26696
  const mangledName = mangleName(instr.fn, constArgs.map((a) => a.value));
26617
26697
  if (fnMap.has(mangledName) || added.has(mangledName))
26618
26698
  continue;
26619
- const specialized = specialize(callee, constArgs.map((a) => a.value), mangledName);
26699
+ const specialized = specialize(callee, constArgs.map((a) => a.value), mangledName, mod.objective);
26620
26700
  newFunctions.push(specialized);
26621
26701
  added.add(mangledName);
26622
26702
  fnMap.set(mangledName, specialized);
@@ -26642,12 +26722,37 @@ var require_interprocedural = __commonJS({
26642
26722
  function mangleName(name, args) {
26643
26723
  return `${name}__const_${args.map((v) => v < 0 ? `n${Math.abs(v)}` : String(v)).join("_")}`;
26644
26724
  }
26645
- function specialize(fn, args, newName) {
26725
+ function hasRawParamRefs(fn, paramCount) {
26726
+ for (let i = 0; i < paramCount; i++) {
26727
+ const pattern = `$p${i}`;
26728
+ for (const block of fn.blocks) {
26729
+ for (const instr of block.instrs) {
26730
+ if (instr.kind === "call" && instr.fn.startsWith("__raw:") && instr.fn.includes(pattern)) {
26731
+ return true;
26732
+ }
26733
+ }
26734
+ }
26735
+ }
26736
+ return false;
26737
+ }
26738
+ function specialize(fn, args, newName, objective) {
26646
26739
  const sub = /* @__PURE__ */ new Map();
26647
26740
  for (let i = 0; i < fn.params.length; i++) {
26648
26741
  sub.set(fn.params[i].name, { kind: "const", value: args[i] });
26649
26742
  }
26650
26743
  const newBlocks = fn.blocks.map((block) => substituteBlock(block, sub));
26744
+ if (hasRawParamRefs(fn, args.length)) {
26745
+ const entryBlock = newBlocks.find((b) => b.id === fn.entry);
26746
+ if (entryBlock) {
26747
+ const scoreWrites = args.map((value, i) => ({
26748
+ kind: "score_write",
26749
+ player: `$p${i}`,
26750
+ obj: objective,
26751
+ src: { kind: "const", value }
26752
+ }));
26753
+ entryBlock.instrs = [...scoreWrites, ...entryBlock.instrs];
26754
+ }
26755
+ }
26651
26756
  const specialized = {
26652
26757
  ...fn,
26653
26758
  name: newName,