redscript-mc 1.0.0 → 1.2.0

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 (136) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.yml +57 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +17 -25
  4. package/CHANGELOG.md +112 -0
  5. package/CONTRIBUTING.md +140 -0
  6. package/README.md +28 -19
  7. package/README.zh.md +28 -19
  8. package/dist/__tests__/cli.test.js +148 -10
  9. package/dist/__tests__/codegen.test.js +26 -1
  10. package/dist/__tests__/diagnostics.test.js +5 -5
  11. package/dist/__tests__/e2e.test.js +336 -17
  12. package/dist/__tests__/formatter.test.d.ts +1 -0
  13. package/dist/__tests__/formatter.test.js +40 -0
  14. package/dist/__tests__/lexer.test.js +12 -2
  15. package/dist/__tests__/lowering.test.js +200 -12
  16. package/dist/__tests__/mc-integration.test.js +370 -31
  17. package/dist/__tests__/mc-syntax.test.js +3 -3
  18. package/dist/__tests__/nbt.test.js +2 -2
  19. package/dist/__tests__/optimizer-advanced.test.js +5 -5
  20. package/dist/__tests__/parser.test.js +80 -0
  21. package/dist/__tests__/runtime.test.js +9 -9
  22. package/dist/__tests__/typechecker.test.js +158 -0
  23. package/dist/ast/types.d.ts +40 -3
  24. package/dist/cli.js +25 -7
  25. package/dist/codegen/mcfunction/index.d.ts +1 -1
  26. package/dist/codegen/mcfunction/index.js +38 -3
  27. package/dist/codegen/structure/index.js +32 -1
  28. package/dist/compile.d.ts +10 -0
  29. package/dist/compile.js +36 -5
  30. package/dist/events/types.d.ts +35 -0
  31. package/dist/events/types.js +59 -0
  32. package/dist/formatter/index.d.ts +1 -0
  33. package/dist/formatter/index.js +26 -0
  34. package/dist/index.js +3 -2
  35. package/dist/ir/builder.d.ts +2 -1
  36. package/dist/ir/types.d.ts +11 -2
  37. package/dist/ir/types.js +1 -1
  38. package/dist/lexer/index.d.ts +1 -1
  39. package/dist/lexer/index.js +2 -0
  40. package/dist/lowering/index.d.ts +34 -1
  41. package/dist/lowering/index.js +622 -23
  42. package/dist/mc-test/runner.d.ts +2 -2
  43. package/dist/mc-test/runner.js +3 -3
  44. package/dist/mc-test/setup.js +2 -2
  45. package/dist/parser/index.d.ts +4 -0
  46. package/dist/parser/index.js +153 -16
  47. package/dist/typechecker/index.d.ts +17 -0
  48. package/dist/typechecker/index.js +343 -17
  49. package/docs/COMPILATION_STATS.md +24 -24
  50. package/docs/ENTITY_TYPE_SYSTEM.md +242 -0
  51. package/docs/IMPLEMENTATION_GUIDE.md +1 -1
  52. package/docs/STRUCTURE_TARGET.md +1 -1
  53. package/editors/vscode/.vscodeignore +1 -0
  54. package/editors/vscode/CHANGELOG.md +9 -0
  55. package/editors/vscode/icons/mcrs.svg +7 -0
  56. package/editors/vscode/icons/redscript-icons.json +10 -0
  57. package/editors/vscode/out/extension.js +1295 -80
  58. package/editors/vscode/package-lock.json +2 -2
  59. package/editors/vscode/package.json +10 -3
  60. package/editors/vscode/src/hover.ts +55 -2
  61. package/editors/vscode/src/symbols.ts +42 -0
  62. package/package.json +1 -1
  63. package/src/__tests__/cli.test.ts +176 -10
  64. package/src/__tests__/codegen.test.ts +28 -1
  65. package/src/__tests__/diagnostics.test.ts +5 -5
  66. package/src/__tests__/e2e.test.ts +335 -17
  67. package/src/__tests__/fixtures/event-test.mcrs +13 -0
  68. package/src/__tests__/fixtures/impl-test.mcrs +46 -0
  69. package/src/__tests__/fixtures/interval-test.mcrs +11 -0
  70. package/src/__tests__/fixtures/is-check-test.mcrs +20 -0
  71. package/src/__tests__/fixtures/timeout-test.mcrs +7 -0
  72. package/src/__tests__/lexer.test.ts +14 -2
  73. package/src/__tests__/lowering.test.ts +226 -12
  74. package/src/__tests__/mc-integration.test.ts +421 -31
  75. package/src/__tests__/mc-syntax.test.ts +3 -3
  76. package/src/__tests__/nbt.test.ts +2 -2
  77. package/src/__tests__/optimizer-advanced.test.ts +5 -5
  78. package/src/__tests__/parser.test.ts +91 -5
  79. package/src/__tests__/runtime.test.ts +9 -9
  80. package/src/__tests__/typechecker.test.ts +171 -0
  81. package/src/ast/types.ts +44 -3
  82. package/src/cli.ts +10 -10
  83. package/src/codegen/mcfunction/index.ts +40 -3
  84. package/src/codegen/structure/index.ts +35 -1
  85. package/src/compile.ts +54 -6
  86. package/src/events/types.ts +69 -0
  87. package/src/examples/capture_the_flag.mcrs +208 -0
  88. package/src/examples/{counter.rs → counter.mcrs} +1 -1
  89. package/src/examples/hunger_games.mcrs +301 -0
  90. package/src/examples/new_features_demo.mcrs +193 -0
  91. package/src/examples/parkour_race.mcrs +233 -0
  92. package/src/examples/rpg.mcrs +13 -0
  93. package/src/examples/{shop.rs → shop.mcrs} +1 -1
  94. package/src/examples/{showcase_game.rs → showcase_game.mcrs} +3 -3
  95. package/src/examples/{turret.rs → turret.mcrs} +1 -1
  96. package/src/examples/zombie_survival.mcrs +314 -0
  97. package/src/index.ts +4 -3
  98. package/src/ir/builder.ts +3 -1
  99. package/src/ir/types.ts +12 -2
  100. package/src/lexer/index.ts +3 -1
  101. package/src/lowering/index.ts +684 -24
  102. package/src/mc-test/runner.ts +3 -3
  103. package/src/mc-test/setup.ts +2 -2
  104. package/src/parser/index.ts +170 -19
  105. package/src/stdlib/README.md +178 -140
  106. package/src/stdlib/bossbar.mcrs +68 -0
  107. package/src/stdlib/{cooldown.rs → cooldown.mcrs} +1 -1
  108. package/src/stdlib/effects.mcrs +64 -0
  109. package/src/stdlib/interactions.mcrs +195 -0
  110. package/src/stdlib/inventory.mcrs +38 -0
  111. package/src/stdlib/mobs.mcrs +99 -0
  112. package/src/stdlib/particles.mcrs +52 -0
  113. package/src/stdlib/sets.mcrs +20 -0
  114. package/src/stdlib/spawn.mcrs +41 -0
  115. package/src/stdlib/tags.mcrs +951 -0
  116. package/src/stdlib/teams.mcrs +68 -0
  117. package/src/stdlib/timer.mcrs +72 -0
  118. package/src/stdlib/world.mcrs +92 -0
  119. package/src/typechecker/index.ts +404 -18
  120. package/src/examples/rpg.rs +0 -13
  121. package/src/stdlib/mobs.rs +0 -99
  122. package/src/stdlib/timer.rs +0 -51
  123. /package/src/examples/{arena.rs → arena.mcrs} +0 -0
  124. /package/src/examples/{pvp_arena.rs → pvp_arena.mcrs} +0 -0
  125. /package/src/examples/{quiz.rs → quiz.mcrs} +0 -0
  126. /package/src/examples/{stdlib_demo.rs → stdlib_demo.mcrs} +0 -0
  127. /package/src/examples/{world_manager.rs → world_manager.mcrs} +0 -0
  128. /package/src/stdlib/{combat.rs → combat.mcrs} +0 -0
  129. /package/src/stdlib/{math.rs → math.mcrs} +0 -0
  130. /package/src/stdlib/{player.rs → player.mcrs} +0 -0
  131. /package/src/stdlib/{strings.rs → strings.mcrs} +0 -0
  132. /package/src/templates/{combat.rs → combat.mcrs} +0 -0
  133. /package/src/templates/{economy.rs → economy.mcrs} +0 -0
  134. /package/src/templates/{mini-game-framework.rs → mini-game-framework.mcrs} +0 -0
  135. /package/src/templates/{quest.rs → quest.mcrs} +0 -0
  136. /package/src/test_programs/{zombie_game.rs → zombie_game.mcrs} +0 -0
@@ -175,7 +175,9 @@ var require_lexer = __commonJS({
175
175
  as: "as",
176
176
  at: "at",
177
177
  in: "in",
178
+ is: "is",
178
179
  struct: "struct",
180
+ impl: "impl",
179
181
  enum: "enum",
180
182
  trigger: "trigger",
181
183
  namespace: "namespace",
@@ -608,13 +610,34 @@ var require_parser = __commonJS({
608
610
  "<=": 4,
609
611
  ">": 4,
610
612
  ">=": 4,
613
+ "is": 4,
611
614
  "+": 5,
612
615
  "-": 5,
613
616
  "*": 6,
614
617
  "/": 6,
615
618
  "%": 6
616
619
  };
617
- var BINARY_OPS = /* @__PURE__ */ new Set(["||", "&&", "==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "%"]);
620
+ var BINARY_OPS = /* @__PURE__ */ new Set(["||", "&&", "==", "!=", "<", "<=", ">", ">=", "is", "+", "-", "*", "/", "%"]);
621
+ var ENTITY_TYPE_NAMES = /* @__PURE__ */ new Set([
622
+ "entity",
623
+ "Player",
624
+ "Mob",
625
+ "HostileMob",
626
+ "PassiveMob",
627
+ "Zombie",
628
+ "Skeleton",
629
+ "Creeper",
630
+ "Spider",
631
+ "Enderman",
632
+ "Pig",
633
+ "Cow",
634
+ "Sheep",
635
+ "Chicken",
636
+ "Villager",
637
+ "ArmorStand",
638
+ "Item",
639
+ "Arrow"
640
+ ]);
618
641
  function computeIsSingle(raw) {
619
642
  if (/^@[spr](\[|$)/.test(raw))
620
643
  return true;
@@ -690,8 +713,10 @@ var require_parser = __commonJS({
690
713
  // -------------------------------------------------------------------------
691
714
  parse(defaultNamespace = "redscript") {
692
715
  let namespace = defaultNamespace;
716
+ const globals = [];
693
717
  const declarations = [];
694
718
  const structs = [];
719
+ const implBlocks = [];
695
720
  const enums = [];
696
721
  const consts = [];
697
722
  if (this.check("namespace")) {
@@ -701,8 +726,12 @@ var require_parser = __commonJS({
701
726
  this.expect(";");
702
727
  }
703
728
  while (!this.check("eof")) {
704
- if (this.check("struct")) {
729
+ if (this.check("let")) {
730
+ globals.push(this.parseGlobalDecl(true));
731
+ } else if (this.check("struct")) {
705
732
  structs.push(this.parseStructDecl());
733
+ } else if (this.check("impl")) {
734
+ implBlocks.push(this.parseImplBlock());
706
735
  } else if (this.check("enum")) {
707
736
  enums.push(this.parseEnumDecl());
708
737
  } else if (this.check("const")) {
@@ -711,7 +740,7 @@ var require_parser = __commonJS({
711
740
  declarations.push(this.parseFnDecl());
712
741
  }
713
742
  }
714
- return { namespace, declarations, structs, enums, consts };
743
+ return { namespace, globals, declarations, structs, implBlocks, enums, consts };
715
744
  }
716
745
  // -------------------------------------------------------------------------
717
746
  // Struct Declaration
@@ -755,6 +784,17 @@ var require_parser = __commonJS({
755
784
  this.expect("}");
756
785
  return this.withLoc({ name, variants }, enumToken);
757
786
  }
787
+ parseImplBlock() {
788
+ const implToken = this.expect("impl");
789
+ const typeName = this.expect("ident").value;
790
+ this.expect("{");
791
+ const methods = [];
792
+ while (!this.check("}") && !this.check("eof")) {
793
+ methods.push(this.parseFnDecl(typeName));
794
+ }
795
+ this.expect("}");
796
+ return this.withLoc({ kind: "impl_block", typeName, methods }, implToken);
797
+ }
758
798
  parseConstDecl() {
759
799
  const constToken = this.expect("const");
760
800
  const name = this.expect("ident").value;
@@ -765,18 +805,28 @@ var require_parser = __commonJS({
765
805
  this.match(";");
766
806
  return this.withLoc({ name, type, value }, constToken);
767
807
  }
808
+ parseGlobalDecl(mutable) {
809
+ const token = this.advance();
810
+ const name = this.expect("ident").value;
811
+ this.expect(":");
812
+ const type = this.parseType();
813
+ this.expect("=");
814
+ const init = this.parseExpr();
815
+ this.expect(";");
816
+ return this.withLoc({ kind: "global", name, type, init, mutable }, token);
817
+ }
768
818
  // -------------------------------------------------------------------------
769
819
  // Function Declaration
770
820
  // -------------------------------------------------------------------------
771
- parseFnDecl() {
821
+ parseFnDecl(implTypeName) {
772
822
  const decorators = this.parseDecorators();
773
823
  const fnToken = this.expect("fn");
774
824
  const name = this.expect("ident").value;
775
825
  this.expect("(");
776
- const params = this.parseParams();
826
+ const params = this.parseParams(implTypeName);
777
827
  this.expect(")");
778
828
  let returnType = { kind: "named", name: "void" };
779
- if (this.match("->")) {
829
+ if (this.match("->") || this.match(":")) {
780
830
  returnType = this.parseType();
781
831
  }
782
832
  const body = this.parseBlock();
@@ -802,6 +852,13 @@ var require_parser = __commonJS({
802
852
  return { name };
803
853
  }
804
854
  const args = {};
855
+ if (name === "on") {
856
+ const eventTypeMatch = argsStr.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
857
+ if (eventTypeMatch) {
858
+ args.eventType = eventTypeMatch[1];
859
+ return { name, args };
860
+ }
861
+ }
805
862
  if (name === "on_trigger" || name === "on_advancement" || name === "on_craft" || name === "on_join_team") {
806
863
  const strMatch = argsStr.match(/^"([^"]*)"$/);
807
864
  if (strMatch) {
@@ -833,14 +890,19 @@ var require_parser = __commonJS({
833
890
  }
834
891
  return { name, args };
835
892
  }
836
- parseParams() {
893
+ parseParams(implTypeName) {
837
894
  const params = [];
838
895
  if (!this.check(")")) {
839
896
  do {
840
897
  const paramToken = this.expect("ident");
841
898
  const name = paramToken.value;
842
- this.expect(":");
843
- const type = this.parseType();
899
+ let type;
900
+ if (implTypeName && params.length === 0 && name === "self" && !this.check(":")) {
901
+ type = { kind: "struct", name: implTypeName };
902
+ } else {
903
+ this.expect(":");
904
+ type = this.parseType();
905
+ }
844
906
  let defaultValue;
845
907
  if (this.match("=")) {
846
908
  defaultValue = this.parseExpr();
@@ -1080,14 +1142,14 @@ var require_parser = __commonJS({
1080
1142
  if (this.peek().kind === "ident" && this.peek().value === "entity") {
1081
1143
  this.advance();
1082
1144
  }
1083
- const selector = this.parseSelector();
1084
- subcommands.push({ kind: "if_entity", selector });
1145
+ const selectorOrVar = this.parseSelectorOrVarSelector();
1146
+ subcommands.push({ kind: "if_entity", ...selectorOrVar });
1085
1147
  } else if (this.match("unless")) {
1086
1148
  if (this.peek().kind === "ident" && this.peek().value === "entity") {
1087
1149
  this.advance();
1088
1150
  }
1089
- const selector = this.parseSelector();
1090
- subcommands.push({ kind: "unless_entity", selector });
1151
+ const selectorOrVar = this.parseSelectorOrVarSelector();
1152
+ subcommands.push({ kind: "unless_entity", ...selectorOrVar });
1091
1153
  } else if (this.match("in")) {
1092
1154
  const dim = this.expect("ident").value;
1093
1155
  subcommands.push({ kind: "in", dimension: dim });
@@ -1137,6 +1199,11 @@ var require_parser = __commonJS({
1137
1199
  if (prec < minPrec)
1138
1200
  break;
1139
1201
  const opToken = this.advance();
1202
+ if (op === "is") {
1203
+ const entityType = this.parseEntityTypeName();
1204
+ left = this.withLoc({ kind: "is_check", expr: left, entityType }, this.getLocToken(left) ?? opToken);
1205
+ continue;
1206
+ }
1140
1207
  const right = this.parseBinaryExpr(prec + 1);
1141
1208
  left = this.withLoc({ kind: "binary", op, left, right }, this.getLocToken(left) ?? opToken);
1142
1209
  }
@@ -1155,6 +1222,13 @@ var require_parser = __commonJS({
1155
1222
  }
1156
1223
  return this.parsePostfixExpr();
1157
1224
  }
1225
+ parseEntityTypeName() {
1226
+ const token = this.expect("ident");
1227
+ if (ENTITY_TYPE_NAMES.has(token.value)) {
1228
+ return token.value;
1229
+ }
1230
+ this.error(`Unknown entity type '${token.value}'`);
1231
+ }
1158
1232
  isSubtraction() {
1159
1233
  if (this.pos === 0)
1160
1234
  return false;
@@ -1178,16 +1252,23 @@ var require_parser = __commonJS({
1178
1252
  "untag": "__entity_untag",
1179
1253
  "has_tag": "__entity_has_tag",
1180
1254
  "push": "__array_push",
1181
- "pop": "__array_pop"
1255
+ "pop": "__array_pop",
1256
+ "add": "set_add",
1257
+ "contains": "set_contains",
1258
+ "remove": "set_remove",
1259
+ "clear": "set_clear"
1182
1260
  };
1183
1261
  const internalFn = methodMap[expr.field];
1184
1262
  if (internalFn) {
1185
- const args2 = this.parseArgs();
1263
+ const args3 = this.parseArgs();
1186
1264
  this.expect(")");
1187
- expr = this.withLoc({ kind: "call", fn: internalFn, args: [expr.obj, ...args2] }, this.getLocToken(expr) ?? openParenToken);
1265
+ expr = this.withLoc({ kind: "call", fn: internalFn, args: [expr.obj, ...args3] }, this.getLocToken(expr) ?? openParenToken);
1188
1266
  continue;
1189
1267
  }
1190
- this.error(`Unknown method '${expr.field}'`);
1268
+ const args2 = this.parseArgs();
1269
+ this.expect(")");
1270
+ expr = this.withLoc({ kind: "call", fn: expr.field, args: [expr.obj, ...args2] }, this.getLocToken(expr) ?? openParenToken);
1271
+ continue;
1191
1272
  }
1192
1273
  const args = this.parseArgs();
1193
1274
  this.expect(")");
@@ -1220,6 +1301,15 @@ var require_parser = __commonJS({
1220
1301
  }
1221
1302
  parsePrimaryExpr() {
1222
1303
  const token = this.peek();
1304
+ if (token.kind === "ident" && this.peek(1).kind === "::") {
1305
+ const typeToken = this.advance();
1306
+ this.expect("::");
1307
+ const methodToken = this.expect("ident");
1308
+ this.expect("(");
1309
+ const args = this.parseArgs();
1310
+ this.expect(")");
1311
+ return this.withLoc({ kind: "static_call", type: typeToken.value, method: methodToken.value, args }, typeToken);
1312
+ }
1223
1313
  if (token.kind === "ident" && this.peek(1).kind === "=>") {
1224
1314
  return this.parseSingleParamLambda();
1225
1315
  }
@@ -1585,6 +1675,34 @@ var require_parser = __commonJS({
1585
1675
  const token = this.expect("selector");
1586
1676
  return this.parseSelectorValue(token.value);
1587
1677
  }
1678
+ // Parse either a selector (@a[...]) or a variable with filters (p[...])
1679
+ // Returns { selector } for selectors or { varName, filters } for variables
1680
+ parseSelectorOrVarSelector() {
1681
+ if (this.check("selector")) {
1682
+ return { selector: this.parseSelector() };
1683
+ }
1684
+ const varToken = this.expect("ident");
1685
+ const varName = varToken.value;
1686
+ if (this.check("[")) {
1687
+ this.advance();
1688
+ let filterStr = "";
1689
+ let depth = 1;
1690
+ while (depth > 0 && !this.check("eof")) {
1691
+ if (this.check("["))
1692
+ depth++;
1693
+ else if (this.check("]"))
1694
+ depth--;
1695
+ if (depth > 0) {
1696
+ filterStr += this.peek().value ?? this.peek().kind;
1697
+ this.advance();
1698
+ }
1699
+ }
1700
+ this.expect("]");
1701
+ const filters = this.parseSelectorFilters(filterStr);
1702
+ return { varName, filters };
1703
+ }
1704
+ return { varName };
1705
+ }
1588
1706
  parseSelectorValue(value) {
1589
1707
  const bracketIndex = value.indexOf("[");
1590
1708
  if (bracketIndex === -1) {
@@ -1635,6 +1753,21 @@ var require_parser = __commonJS({
1635
1753
  case "scores":
1636
1754
  filters.scores = this.parseScoresFilter(val);
1637
1755
  break;
1756
+ case "x":
1757
+ filters.x = this.parseRangeValue(val);
1758
+ break;
1759
+ case "y":
1760
+ filters.y = this.parseRangeValue(val);
1761
+ break;
1762
+ case "z":
1763
+ filters.z = this.parseRangeValue(val);
1764
+ break;
1765
+ case "x_rotation":
1766
+ filters.x_rotation = this.parseRangeValue(val);
1767
+ break;
1768
+ case "y_rotation":
1769
+ filters.y_rotation = this.parseRangeValue(val);
1770
+ break;
1638
1771
  }
1639
1772
  }
1640
1773
  return filters;
@@ -1693,6 +1826,70 @@ var require_parser = __commonJS({
1693
1826
  }
1694
1827
  });
1695
1828
 
1829
+ // ../../dist/events/types.js
1830
+ var require_types = __commonJS({
1831
+ "../../dist/events/types.js"(exports2) {
1832
+ "use strict";
1833
+ Object.defineProperty(exports2, "__esModule", { value: true });
1834
+ exports2.EVENT_TYPES = void 0;
1835
+ exports2.isEventTypeName = isEventTypeName;
1836
+ exports2.getEventParamSpecs = getEventParamSpecs;
1837
+ exports2.EVENT_TYPES = {
1838
+ PlayerDeath: {
1839
+ tag: "rs.just_died",
1840
+ params: ["player: Player"],
1841
+ detection: "scoreboard"
1842
+ },
1843
+ PlayerJoin: {
1844
+ tag: "rs.just_joined",
1845
+ params: ["player: Player"],
1846
+ detection: "tag"
1847
+ },
1848
+ BlockBreak: {
1849
+ tag: "rs.just_broke_block",
1850
+ params: ["player: Player", "block: string"],
1851
+ detection: "advancement"
1852
+ },
1853
+ EntityKill: {
1854
+ tag: "rs.just_killed",
1855
+ params: ["player: Player"],
1856
+ detection: "scoreboard"
1857
+ },
1858
+ ItemUse: {
1859
+ tag: "rs.just_used_item",
1860
+ params: ["player: Player"],
1861
+ detection: "scoreboard"
1862
+ }
1863
+ };
1864
+ function isEventTypeName(value) {
1865
+ return value in exports2.EVENT_TYPES;
1866
+ }
1867
+ function getEventParamSpecs(eventType) {
1868
+ return exports2.EVENT_TYPES[eventType].params.map(parseEventParam);
1869
+ }
1870
+ function parseEventParam(spec) {
1871
+ const match = spec.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_]*)$/);
1872
+ if (!match) {
1873
+ throw new Error(`Invalid event parameter spec: ${spec}`);
1874
+ }
1875
+ const [, name, typeName] = match;
1876
+ return {
1877
+ name,
1878
+ type: toTypeNode(typeName)
1879
+ };
1880
+ }
1881
+ function toTypeNode(typeName) {
1882
+ if (typeName === "Player") {
1883
+ return { kind: "entity", entityType: "Player" };
1884
+ }
1885
+ if (typeName === "string" || typeName === "int" || typeName === "bool" || typeName === "float" || typeName === "void" || typeName === "BlockPos" || typeName === "byte" || typeName === "short" || typeName === "long" || typeName === "double") {
1886
+ return { kind: "named", name: typeName };
1887
+ }
1888
+ return { kind: "struct", name: typeName };
1889
+ }
1890
+ }
1891
+ });
1892
+
1696
1893
  // ../../dist/typechecker/index.js
1697
1894
  var require_typechecker = __commonJS({
1698
1895
  "../../dist/typechecker/index.js"(exports2) {
@@ -1700,15 +1897,82 @@ var require_typechecker = __commonJS({
1700
1897
  Object.defineProperty(exports2, "__esModule", { value: true });
1701
1898
  exports2.TypeChecker = void 0;
1702
1899
  var diagnostics_1 = require_diagnostics();
1900
+ var types_1 = require_types();
1901
+ var ENTITY_HIERARCHY = {
1902
+ "entity": null,
1903
+ "Player": "entity",
1904
+ "Mob": "entity",
1905
+ "HostileMob": "Mob",
1906
+ "PassiveMob": "Mob",
1907
+ "Zombie": "HostileMob",
1908
+ "Skeleton": "HostileMob",
1909
+ "Creeper": "HostileMob",
1910
+ "Spider": "HostileMob",
1911
+ "Enderman": "HostileMob",
1912
+ "Pig": "PassiveMob",
1913
+ "Cow": "PassiveMob",
1914
+ "Sheep": "PassiveMob",
1915
+ "Chicken": "PassiveMob",
1916
+ "Villager": "PassiveMob",
1917
+ "ArmorStand": "entity",
1918
+ "Item": "entity",
1919
+ "Arrow": "entity"
1920
+ };
1921
+ var MC_TYPE_TO_ENTITY = {
1922
+ "zombie": "Zombie",
1923
+ "minecraft:zombie": "Zombie",
1924
+ "skeleton": "Skeleton",
1925
+ "minecraft:skeleton": "Skeleton",
1926
+ "creeper": "Creeper",
1927
+ "minecraft:creeper": "Creeper",
1928
+ "spider": "Spider",
1929
+ "minecraft:spider": "Spider",
1930
+ "enderman": "Enderman",
1931
+ "minecraft:enderman": "Enderman",
1932
+ "pig": "Pig",
1933
+ "minecraft:pig": "Pig",
1934
+ "cow": "Cow",
1935
+ "minecraft:cow": "Cow",
1936
+ "sheep": "Sheep",
1937
+ "minecraft:sheep": "Sheep",
1938
+ "chicken": "Chicken",
1939
+ "minecraft:chicken": "Chicken",
1940
+ "villager": "Villager",
1941
+ "minecraft:villager": "Villager",
1942
+ "armor_stand": "ArmorStand",
1943
+ "minecraft:armor_stand": "ArmorStand",
1944
+ "item": "Item",
1945
+ "minecraft:item": "Item",
1946
+ "arrow": "Arrow",
1947
+ "minecraft:arrow": "Arrow"
1948
+ };
1949
+ var VOID_TYPE = { kind: "named", name: "void" };
1950
+ var INT_TYPE = { kind: "named", name: "int" };
1951
+ var BUILTIN_SIGNATURES = {
1952
+ setTimeout: {
1953
+ params: [INT_TYPE, { kind: "function_type", params: [], return: VOID_TYPE }],
1954
+ return: VOID_TYPE
1955
+ },
1956
+ setInterval: {
1957
+ params: [INT_TYPE, { kind: "function_type", params: [], return: VOID_TYPE }],
1958
+ return: INT_TYPE
1959
+ },
1960
+ clearInterval: {
1961
+ params: [INT_TYPE],
1962
+ return: VOID_TYPE
1963
+ }
1964
+ };
1703
1965
  var TypeChecker = class {
1704
1966
  constructor(source, filePath) {
1705
1967
  this.functions = /* @__PURE__ */ new Map();
1968
+ this.implMethods = /* @__PURE__ */ new Map();
1706
1969
  this.structs = /* @__PURE__ */ new Map();
1707
1970
  this.enums = /* @__PURE__ */ new Map();
1708
1971
  this.consts = /* @__PURE__ */ new Map();
1709
1972
  this.currentFn = null;
1710
1973
  this.currentReturnType = null;
1711
1974
  this.scope = /* @__PURE__ */ new Map();
1975
+ this.selfTypeStack = ["entity"];
1712
1976
  this.collector = new diagnostics_1.DiagnosticCollector(source, filePath);
1713
1977
  }
1714
1978
  getNodeLocation(node) {
@@ -1729,6 +1993,26 @@ var require_typechecker = __commonJS({
1729
1993
  for (const fn of program.declarations) {
1730
1994
  this.functions.set(fn.name, fn);
1731
1995
  }
1996
+ for (const implBlock of program.implBlocks ?? []) {
1997
+ let methods = this.implMethods.get(implBlock.typeName);
1998
+ if (!methods) {
1999
+ methods = /* @__PURE__ */ new Map();
2000
+ this.implMethods.set(implBlock.typeName, methods);
2001
+ }
2002
+ for (const method of implBlock.methods) {
2003
+ const selfIndex = method.params.findIndex((param) => param.name === "self");
2004
+ if (selfIndex > 0) {
2005
+ this.report(`Method '${method.name}' must declare 'self' as the first parameter`, method.params[selfIndex]);
2006
+ }
2007
+ if (selfIndex === 0) {
2008
+ const selfType = this.normalizeType(method.params[0].type);
2009
+ if (selfType.kind !== "struct" || selfType.name !== implBlock.typeName) {
2010
+ this.report(`Method '${method.name}' has invalid 'self' type`, method.params[0]);
2011
+ }
2012
+ }
2013
+ methods.set(method.name, method);
2014
+ }
2015
+ }
1732
2016
  for (const struct of program.structs ?? []) {
1733
2017
  const fields = /* @__PURE__ */ new Map();
1734
2018
  for (const field of struct.fields) {
@@ -1754,6 +2038,11 @@ var require_typechecker = __commonJS({
1754
2038
  for (const fn of program.declarations) {
1755
2039
  this.checkFunction(fn);
1756
2040
  }
2041
+ for (const implBlock of program.implBlocks ?? []) {
2042
+ for (const method of implBlock.methods) {
2043
+ this.checkFunction(method);
2044
+ }
2045
+ }
1757
2046
  return this.collector.getErrors();
1758
2047
  }
1759
2048
  checkFunction(fn) {
@@ -1761,6 +2050,7 @@ var require_typechecker = __commonJS({
1761
2050
  this.currentReturnType = this.normalizeType(fn.returnType);
1762
2051
  this.scope = /* @__PURE__ */ new Map();
1763
2052
  let seenDefault = false;
2053
+ this.checkFunctionDecorators(fn);
1764
2054
  for (const [name, type] of this.consts.entries()) {
1765
2055
  this.scope.set(name, { type, mutable: false });
1766
2056
  }
@@ -1782,6 +2072,37 @@ var require_typechecker = __commonJS({
1782
2072
  this.currentFn = null;
1783
2073
  this.currentReturnType = null;
1784
2074
  }
2075
+ checkFunctionDecorators(fn) {
2076
+ const eventDecorators = fn.decorators.filter((decorator) => decorator.name === "on");
2077
+ if (eventDecorators.length === 0) {
2078
+ return;
2079
+ }
2080
+ if (eventDecorators.length > 1) {
2081
+ this.report(`Function '${fn.name}' cannot have multiple @on decorators`, fn);
2082
+ return;
2083
+ }
2084
+ const eventType = eventDecorators[0].args?.eventType;
2085
+ if (!eventType) {
2086
+ this.report(`Function '${fn.name}' is missing an event type in @on(...)`, fn);
2087
+ return;
2088
+ }
2089
+ if (!(0, types_1.isEventTypeName)(eventType)) {
2090
+ this.report(`Unknown event type '${eventType}'`, fn);
2091
+ return;
2092
+ }
2093
+ const expectedParams = (0, types_1.getEventParamSpecs)(eventType);
2094
+ if (fn.params.length !== expectedParams.length) {
2095
+ this.report(`Event handler '${fn.name}' for ${eventType} must declare ${expectedParams.length} parameter(s), got ${fn.params.length}`, fn);
2096
+ return;
2097
+ }
2098
+ for (let i = 0; i < expectedParams.length; i++) {
2099
+ const actual = this.normalizeType(fn.params[i].type);
2100
+ const expected = this.normalizeType(expectedParams[i].type);
2101
+ if (!this.typesMatch(expected, actual)) {
2102
+ this.report(`Event handler '${fn.name}' parameter ${i + 1} must be ${this.typeToString(expected)}, got ${this.typeToString(actual)}`, fn.params[i]);
2103
+ }
2104
+ }
2105
+ }
1785
2106
  checkBlock(stmts) {
1786
2107
  for (const stmt of stmts) {
1787
2108
  this.checkStmt(stmt);
@@ -1797,9 +2118,7 @@ var require_typechecker = __commonJS({
1797
2118
  break;
1798
2119
  case "if":
1799
2120
  this.checkExpr(stmt.cond);
1800
- this.checkBlock(stmt.then);
1801
- if (stmt.else_)
1802
- this.checkBlock(stmt.else_);
2121
+ this.checkIfBranches(stmt);
1803
2122
  break;
1804
2123
  case "while":
1805
2124
  this.checkExpr(stmt.cond);
@@ -1815,7 +2134,15 @@ var require_typechecker = __commonJS({
1815
2134
  case "foreach":
1816
2135
  this.checkExpr(stmt.iterable);
1817
2136
  if (stmt.iterable.kind === "selector") {
1818
- this.scope.set(stmt.binding, { type: { kind: "named", name: "void" }, mutable: true });
2137
+ const entityType = this.inferEntityTypeFromSelector(stmt.iterable.sel);
2138
+ this.scope.set(stmt.binding, {
2139
+ type: { kind: "entity", entityType },
2140
+ mutable: false
2141
+ // Entity bindings are not reassignable
2142
+ });
2143
+ this.pushSelfType(entityType);
2144
+ this.checkBlock(stmt.body);
2145
+ this.popSelfType();
1819
2146
  } else {
1820
2147
  const iterableType = this.inferType(stmt.iterable);
1821
2148
  if (iterableType.kind === "array") {
@@ -1823,8 +2150,8 @@ var require_typechecker = __commonJS({
1823
2150
  } else {
1824
2151
  this.scope.set(stmt.binding, { type: { kind: "named", name: "void" }, mutable: true });
1825
2152
  }
2153
+ this.checkBlock(stmt.body);
1826
2154
  }
1827
- this.checkBlock(stmt.body);
1828
2155
  break;
1829
2156
  case "match":
1830
2157
  this.checkExpr(stmt.expr);
@@ -1838,15 +2165,36 @@ var require_typechecker = __commonJS({
1838
2165
  this.checkBlock(arm.body);
1839
2166
  }
1840
2167
  break;
1841
- case "as_block":
2168
+ case "as_block": {
2169
+ const entityType = this.inferEntityTypeFromSelector(stmt.selector);
2170
+ this.pushSelfType(entityType);
2171
+ this.checkBlock(stmt.body);
2172
+ this.popSelfType();
2173
+ break;
2174
+ }
1842
2175
  case "at_block":
1843
2176
  this.checkBlock(stmt.body);
1844
2177
  break;
1845
- case "as_at":
2178
+ case "as_at": {
2179
+ const entityType = this.inferEntityTypeFromSelector(stmt.as_sel);
2180
+ this.pushSelfType(entityType);
1846
2181
  this.checkBlock(stmt.body);
2182
+ this.popSelfType();
1847
2183
  break;
2184
+ }
1848
2185
  case "execute":
2186
+ for (const sub of stmt.subcommands) {
2187
+ if (sub.kind === "as" && sub.selector) {
2188
+ const entityType = this.inferEntityTypeFromSelector(sub.selector);
2189
+ this.pushSelfType(entityType);
2190
+ }
2191
+ }
1849
2192
  this.checkBlock(stmt.body);
2193
+ for (const sub of stmt.subcommands) {
2194
+ if (sub.kind === "as") {
2195
+ this.popSelfType();
2196
+ }
2197
+ }
1850
2198
  break;
1851
2199
  case "expr":
1852
2200
  this.checkExpr(stmt.expr);
@@ -1897,10 +2245,21 @@ var require_typechecker = __commonJS({
1897
2245
  case "member":
1898
2246
  this.checkMemberExpr(expr);
1899
2247
  break;
2248
+ case "static_call":
2249
+ this.checkStaticCallExpr(expr);
2250
+ break;
1900
2251
  case "binary":
1901
2252
  this.checkExpr(expr.left);
1902
2253
  this.checkExpr(expr.right);
1903
2254
  break;
2255
+ case "is_check": {
2256
+ this.checkExpr(expr.expr);
2257
+ const checkedType = this.inferType(expr.expr);
2258
+ if (checkedType.kind !== "entity") {
2259
+ this.report(`'is' checks require an entity expression, got ${this.typeToString(checkedType)}`, expr.expr);
2260
+ }
2261
+ break;
2262
+ }
1904
2263
  case "unary":
1905
2264
  this.checkExpr(expr.operand);
1906
2265
  break;
@@ -1946,11 +2305,6 @@ var require_typechecker = __commonJS({
1946
2305
  break;
1947
2306
  case "blockpos":
1948
2307
  break;
1949
- case "static_call":
1950
- for (const arg of expr.args) {
1951
- this.checkExpr(arg);
1952
- }
1953
- break;
1954
2308
  // Literals don't need checking
1955
2309
  case "int_lit":
1956
2310
  case "float_lit":
@@ -1970,6 +2324,11 @@ var require_typechecker = __commonJS({
1970
2324
  if (expr.fn === "tp" || expr.fn === "tp_to") {
1971
2325
  this.checkTpCall(expr);
1972
2326
  }
2327
+ const builtin = BUILTIN_SIGNATURES[expr.fn];
2328
+ if (builtin) {
2329
+ this.checkFunctionCallArgs(expr.args, builtin.params, expr.fn, expr);
2330
+ return;
2331
+ }
1973
2332
  const fn = this.functions.get(expr.fn);
1974
2333
  if (fn) {
1975
2334
  const requiredParams = fn.params.filter((param) => !param.default).length;
@@ -1994,6 +2353,11 @@ var require_typechecker = __commonJS({
1994
2353
  this.checkFunctionCallArgs(expr.args, varType.params, expr.fn, expr);
1995
2354
  return;
1996
2355
  }
2356
+ const implMethod = this.resolveInstanceMethod(expr);
2357
+ if (implMethod) {
2358
+ this.checkFunctionCallArgs(expr.args, implMethod.params.map((param) => this.normalizeType(param.type)), implMethod.name, expr);
2359
+ return;
2360
+ }
1997
2361
  for (const arg of expr.args) {
1998
2362
  this.checkExpr(arg);
1999
2363
  }
@@ -2074,6 +2438,21 @@ var require_typechecker = __commonJS({
2074
2438
  }
2075
2439
  }
2076
2440
  }
2441
+ checkStaticCallExpr(expr) {
2442
+ const method = this.implMethods.get(expr.type)?.get(expr.method);
2443
+ if (!method) {
2444
+ this.report(`Type '${expr.type}' has no static method '${expr.method}'`, expr);
2445
+ for (const arg of expr.args) {
2446
+ this.checkExpr(arg);
2447
+ }
2448
+ return;
2449
+ }
2450
+ if (method.params[0]?.name === "self") {
2451
+ this.report(`Method '${expr.type}::${expr.method}' is an instance method`, expr);
2452
+ return;
2453
+ }
2454
+ this.checkFunctionCallArgs(expr.args, method.params.map((param) => this.normalizeType(param.type)), `${expr.type}::${expr.method}`, expr);
2455
+ }
2077
2456
  checkLambdaExpr(expr, expectedType) {
2078
2457
  const normalizedExpected = expectedType ? this.normalizeType(expectedType) : void 0;
2079
2458
  const expectedFnType = normalizedExpected?.kind === "function_type" ? normalizedExpected : void 0;
@@ -2106,6 +2485,36 @@ var require_typechecker = __commonJS({
2106
2485
  this.scope = outerScope;
2107
2486
  this.currentReturnType = outerReturnType;
2108
2487
  }
2488
+ checkIfBranches(stmt) {
2489
+ const narrowed = this.getThenBranchNarrowing(stmt.cond);
2490
+ if (narrowed) {
2491
+ const thenScope = new Map(this.scope);
2492
+ thenScope.set(narrowed.name, { type: narrowed.type, mutable: narrowed.mutable });
2493
+ const outerScope = this.scope;
2494
+ this.scope = thenScope;
2495
+ this.checkBlock(stmt.then);
2496
+ this.scope = outerScope;
2497
+ } else {
2498
+ this.checkBlock(stmt.then);
2499
+ }
2500
+ if (stmt.else_) {
2501
+ this.checkBlock(stmt.else_);
2502
+ }
2503
+ }
2504
+ getThenBranchNarrowing(cond) {
2505
+ if (cond.kind !== "is_check" || cond.expr.kind !== "ident") {
2506
+ return null;
2507
+ }
2508
+ const symbol = this.scope.get(cond.expr.name);
2509
+ if (!symbol || symbol.type.kind !== "entity") {
2510
+ return null;
2511
+ }
2512
+ return {
2513
+ name: cond.expr.name,
2514
+ type: { kind: "entity", entityType: cond.entityType },
2515
+ mutable: symbol.mutable
2516
+ };
2517
+ }
2109
2518
  inferType(expr, expectedType) {
2110
2519
  switch (expr.kind) {
2111
2520
  case "int_lit":
@@ -2137,8 +2546,12 @@ var require_typechecker = __commonJS({
2137
2546
  case "ident":
2138
2547
  return this.scope.get(expr.name)?.type ?? { kind: "named", name: "void" };
2139
2548
  case "call": {
2549
+ const builtin = BUILTIN_SIGNATURES[expr.fn];
2550
+ if (builtin) {
2551
+ return builtin.return;
2552
+ }
2140
2553
  if (expr.fn === "__array_push") {
2141
- return { kind: "named", name: "void" };
2554
+ return VOID_TYPE;
2142
2555
  }
2143
2556
  if (expr.fn === "__array_pop") {
2144
2557
  const target = expr.args[0];
@@ -2147,20 +2560,28 @@ var require_typechecker = __commonJS({
2147
2560
  if (targetType?.kind === "array")
2148
2561
  return targetType.elem;
2149
2562
  }
2150
- return { kind: "named", name: "int" };
2563
+ return INT_TYPE;
2151
2564
  }
2152
2565
  if (expr.fn === "bossbar_get_value") {
2153
- return { kind: "named", name: "int" };
2566
+ return INT_TYPE;
2154
2567
  }
2155
2568
  if (expr.fn === "random_sequence") {
2156
- return { kind: "named", name: "void" };
2569
+ return VOID_TYPE;
2157
2570
  }
2158
2571
  const varType = this.scope.get(expr.fn)?.type;
2159
2572
  if (varType?.kind === "function_type") {
2160
2573
  return varType.return;
2161
2574
  }
2575
+ const implMethod = this.resolveInstanceMethod(expr);
2576
+ if (implMethod) {
2577
+ return this.normalizeType(implMethod.returnType);
2578
+ }
2162
2579
  const fn = this.functions.get(expr.fn);
2163
- return fn?.returnType ?? { kind: "named", name: "int" };
2580
+ return fn?.returnType ?? INT_TYPE;
2581
+ }
2582
+ case "static_call": {
2583
+ const method = this.implMethods.get(expr.type)?.get(expr.method);
2584
+ return method ? this.normalizeType(method.returnType) : { kind: "named", name: "void" };
2164
2585
  }
2165
2586
  case "invoke": {
2166
2587
  const calleeType = this.inferType(expr.callee);
@@ -2191,6 +2612,8 @@ var require_typechecker = __commonJS({
2191
2612
  return { kind: "named", name: "bool" };
2192
2613
  }
2193
2614
  return this.inferType(expr.left);
2615
+ case "is_check":
2616
+ return { kind: "named", name: "bool" };
2194
2617
  case "unary":
2195
2618
  if (expr.op === "!")
2196
2619
  return { kind: "named", name: "bool" };
@@ -2200,6 +2623,14 @@ var require_typechecker = __commonJS({
2200
2623
  return { kind: "array", elem: this.inferType(expr.elements[0]) };
2201
2624
  }
2202
2625
  return { kind: "array", elem: { kind: "named", name: "int" } };
2626
+ case "struct_lit":
2627
+ if (expectedType) {
2628
+ const normalized = this.normalizeType(expectedType);
2629
+ if (normalized.kind === "struct") {
2630
+ return normalized;
2631
+ }
2632
+ }
2633
+ return { kind: "named", name: "void" };
2203
2634
  case "lambda":
2204
2635
  return this.inferLambdaType(expr, expectedType && this.normalizeType(expectedType).kind === "function_type" ? this.normalizeType(expectedType) : void 0);
2205
2636
  default:
@@ -2224,6 +2655,64 @@ var require_typechecker = __commonJS({
2224
2655
  }
2225
2656
  return { kind: "function_type", params, return: returnType };
2226
2657
  }
2658
+ // ---------------------------------------------------------------------------
2659
+ // Entity Type Helpers
2660
+ // ---------------------------------------------------------------------------
2661
+ /** Infer entity type from a selector */
2662
+ inferEntityTypeFromSelector(selector) {
2663
+ if (selector.kind === "@a" || selector.kind === "@p" || selector.kind === "@r") {
2664
+ return "Player";
2665
+ }
2666
+ if (selector.filters?.type) {
2667
+ const mcType = selector.filters.type.toLowerCase();
2668
+ return MC_TYPE_TO_ENTITY[mcType] ?? "entity";
2669
+ }
2670
+ if (selector.kind === "@s") {
2671
+ return this.selfTypeStack[this.selfTypeStack.length - 1];
2672
+ }
2673
+ return "entity";
2674
+ }
2675
+ resolveInstanceMethod(expr) {
2676
+ const receiver = expr.args[0];
2677
+ if (!receiver) {
2678
+ return null;
2679
+ }
2680
+ const receiverType = this.inferType(receiver);
2681
+ if (receiverType.kind !== "struct") {
2682
+ return null;
2683
+ }
2684
+ const method = this.implMethods.get(receiverType.name)?.get(expr.fn);
2685
+ if (!method || method.params[0]?.name !== "self") {
2686
+ return null;
2687
+ }
2688
+ return method;
2689
+ }
2690
+ /** Check if childType is a subtype of parentType */
2691
+ isEntitySubtype(childType, parentType) {
2692
+ if (childType === parentType)
2693
+ return true;
2694
+ let current = childType;
2695
+ while (current !== null) {
2696
+ if (current === parentType)
2697
+ return true;
2698
+ current = ENTITY_HIERARCHY[current];
2699
+ }
2700
+ return false;
2701
+ }
2702
+ /** Push a new self type context */
2703
+ pushSelfType(entityType) {
2704
+ this.selfTypeStack.push(entityType);
2705
+ }
2706
+ /** Pop self type context */
2707
+ popSelfType() {
2708
+ if (this.selfTypeStack.length > 1) {
2709
+ this.selfTypeStack.pop();
2710
+ }
2711
+ }
2712
+ /** Get current @s type */
2713
+ getCurrentSelfType() {
2714
+ return this.selfTypeStack[this.selfTypeStack.length - 1];
2715
+ }
2227
2716
  typesMatch(expected, actual) {
2228
2717
  if (expected.kind !== actual.kind)
2229
2718
  return false;
@@ -2244,6 +2733,12 @@ var require_typechecker = __commonJS({
2244
2733
  if (expected.kind === "function_type" && actual.kind === "function_type") {
2245
2734
  return expected.params.length === actual.params.length && expected.params.every((param, index) => this.typesMatch(param, actual.params[index])) && this.typesMatch(expected.return, actual.return);
2246
2735
  }
2736
+ if (expected.kind === "entity" && actual.kind === "entity") {
2737
+ return this.isEntitySubtype(actual.entityType, expected.entityType);
2738
+ }
2739
+ if (expected.kind === "selector" && actual.kind === "entity") {
2740
+ return true;
2741
+ }
2247
2742
  return false;
2248
2743
  }
2249
2744
  typeToString(type) {
@@ -2258,6 +2753,12 @@ var require_typechecker = __commonJS({
2258
2753
  return type.name;
2259
2754
  case "function_type":
2260
2755
  return `(${type.params.map((param) => this.typeToString(param)).join(", ")}) -> ${this.typeToString(type.return)}`;
2756
+ case "entity":
2757
+ return type.entityType;
2758
+ case "selector":
2759
+ return "selector";
2760
+ default:
2761
+ return "unknown";
2261
2762
  }
2262
2763
  }
2263
2764
  normalizeType(type) {
@@ -2274,6 +2775,12 @@ var require_typechecker = __commonJS({
2274
2775
  if ((type.kind === "struct" || type.kind === "enum") && this.enums.has(type.name)) {
2275
2776
  return { kind: "enum", name: type.name };
2276
2777
  }
2778
+ if (type.kind === "struct" && type.name in ENTITY_HIERARCHY) {
2779
+ return { kind: "entity", entityType: type.name };
2780
+ }
2781
+ if (type.kind === "named" && type.name in ENTITY_HIERARCHY) {
2782
+ return { kind: "entity", entityType: type.name };
2783
+ }
2277
2784
  return type;
2278
2785
  }
2279
2786
  };
@@ -2385,10 +2892,49 @@ var require_builder = __commonJS({
2385
2892
  var require_lowering = __commonJS({
2386
2893
  "../../dist/lowering/index.js"(exports2) {
2387
2894
  "use strict";
2895
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
2896
+ if (k2 === void 0) k2 = k;
2897
+ var desc = Object.getOwnPropertyDescriptor(m, k);
2898
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
2899
+ desc = { enumerable: true, get: function() {
2900
+ return m[k];
2901
+ } };
2902
+ }
2903
+ Object.defineProperty(o, k2, desc);
2904
+ }) : (function(o, m, k, k2) {
2905
+ if (k2 === void 0) k2 = k;
2906
+ o[k2] = m[k];
2907
+ }));
2908
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
2909
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
2910
+ }) : function(o, v) {
2911
+ o["default"] = v;
2912
+ });
2913
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
2914
+ var ownKeys = function(o) {
2915
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
2916
+ var ar = [];
2917
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
2918
+ return ar;
2919
+ };
2920
+ return ownKeys(o);
2921
+ };
2922
+ return function(mod) {
2923
+ if (mod && mod.__esModule) return mod;
2924
+ var result = {};
2925
+ if (mod != null) {
2926
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
2927
+ }
2928
+ __setModuleDefault(result, mod);
2929
+ return result;
2930
+ };
2931
+ })();
2388
2932
  Object.defineProperty(exports2, "__esModule", { value: true });
2389
2933
  exports2.Lowering = void 0;
2390
2934
  var builder_1 = require_builder();
2391
2935
  var diagnostics_1 = require_diagnostics();
2936
+ var path = __importStar(require("path"));
2937
+ var types_1 = require_types();
2392
2938
  var BUILTINS2 = {
2393
2939
  say: ([msg]) => `say ${msg}`,
2394
2940
  tell: ([sel, msg]) => `tellraw ${sel} {"text":"${msg}"}`,
@@ -2397,9 +2943,10 @@ var require_lowering = __commonJS({
2397
2943
  subtitle: ([sel, msg]) => `title ${sel} subtitle {"text":"${msg}"}`,
2398
2944
  title_times: ([sel, fadeIn, stay, fadeOut]) => `title ${sel} times ${fadeIn} ${stay} ${fadeOut}`,
2399
2945
  announce: ([msg]) => `tellraw @a {"text":"${msg}"}`,
2400
- give: ([sel, item, count]) => `give ${sel} ${item} ${count ?? "1"}`,
2946
+ give: ([sel, item, count, nbt]) => nbt ? `give ${sel} ${item}${nbt} ${count ?? "1"}` : `give ${sel} ${item} ${count ?? "1"}`,
2401
2947
  kill: ([sel]) => `kill ${sel ?? "@s"}`,
2402
2948
  effect: ([sel, eff, dur, amp]) => `effect give ${sel} ${eff} ${dur ?? "30"} ${amp ?? "0"}`,
2949
+ effect_clear: ([sel, eff]) => eff ? `effect clear ${sel} ${eff}` : `effect clear ${sel}`,
2403
2950
  summon: ([type, x, y, z, nbt]) => {
2404
2951
  const pos = [x ?? "~", y ?? "~", z ?? "~"].join(" ");
2405
2952
  return nbt ? `summon ${type} ${pos} ${nbt}` : `summon ${type} ${pos}`;
@@ -2475,14 +3022,48 @@ var require_lowering = __commonJS({
2475
3022
  // Special handling
2476
3023
  team_option: () => null,
2477
3024
  // Special handling
2478
- data_get: () => null
3025
+ data_get: () => null,
2479
3026
  // Special handling (returns value from NBT)
3027
+ data_merge: () => null,
3028
+ // Special handling (merge NBT)
3029
+ set_new: () => null,
3030
+ // Special handling (returns set ID)
3031
+ set_add: () => null,
3032
+ // Special handling
3033
+ set_contains: () => null,
3034
+ // Special handling (returns 1/0)
3035
+ set_remove: () => null,
3036
+ // Special handling
3037
+ set_clear: () => null,
3038
+ // Special handling
3039
+ setTimeout: () => null,
3040
+ // Special handling
3041
+ setInterval: () => null,
3042
+ // Special handling
3043
+ clearInterval: () => null
3044
+ // Special handling
2480
3045
  };
2481
3046
  function getSpan(node) {
2482
3047
  return node?.span;
2483
3048
  }
2484
3049
  var NAMESPACED_ENTITY_TYPE_RE = /^[a-z0-9_.-]+:[a-z0-9_./-]+$/;
2485
3050
  var BARE_ENTITY_TYPE_RE = /^[a-z0-9_./-]+$/;
3051
+ var ENTITY_TO_MC_TYPE = {
3052
+ Player: "player",
3053
+ Zombie: "zombie",
3054
+ Skeleton: "skeleton",
3055
+ Creeper: "creeper",
3056
+ Spider: "spider",
3057
+ Enderman: "enderman",
3058
+ Pig: "pig",
3059
+ Cow: "cow",
3060
+ Sheep: "sheep",
3061
+ Chicken: "chicken",
3062
+ Villager: "villager",
3063
+ ArmorStand: "armor_stand",
3064
+ Item: "item",
3065
+ Arrow: "arrow"
3066
+ };
2486
3067
  function normalizeSelector(selector, warnings) {
2487
3068
  return selector.replace(/type=([^,\]]+)/g, (match, entityType) => {
2488
3069
  const trimmed = entityType.trim();
@@ -2516,17 +3097,23 @@ var require_lowering = __commonJS({
2516
3097
  return `${emitCoord(pos.x)} ${emitCoord(pos.y)} ${emitCoord(pos.z)}`;
2517
3098
  }
2518
3099
  var Lowering = class {
2519
- constructor(namespace) {
3100
+ constructor(namespace, sourceRanges = []) {
2520
3101
  this.functions = [];
2521
3102
  this.globals = [];
3103
+ this.globalNames = /* @__PURE__ */ new Map();
2522
3104
  this.fnDecls = /* @__PURE__ */ new Map();
3105
+ this.implMethods = /* @__PURE__ */ new Map();
2523
3106
  this.specializedFunctions = /* @__PURE__ */ new Map();
2524
3107
  this.currentFn = "";
2525
3108
  this.foreachCounter = 0;
2526
3109
  this.lambdaCounter = 0;
3110
+ this.timeoutCounter = 0;
3111
+ this.intervalCounter = 0;
2527
3112
  this.warnings = [];
2528
3113
  this.varMap = /* @__PURE__ */ new Map();
2529
3114
  this.lambdaBindings = /* @__PURE__ */ new Map();
3115
+ this.intervalBindings = /* @__PURE__ */ new Map();
3116
+ this.intervalFunctions = /* @__PURE__ */ new Map();
2530
3117
  this.currentCallbackBindings = /* @__PURE__ */ new Map();
2531
3118
  this.currentContext = {};
2532
3119
  this.blockPosVars = /* @__PURE__ */ new Map();
@@ -2539,6 +3126,8 @@ var require_lowering = __commonJS({
2539
3126
  this.floatVars = /* @__PURE__ */ new Set();
2540
3127
  this.worldObjCounter = 0;
2541
3128
  this.namespace = namespace;
3129
+ this.sourceRanges = sourceRanges;
3130
+ LoweringBuilder.resetTempCounter();
2542
3131
  }
2543
3132
  lower(program) {
2544
3133
  this.namespace = program.namespace;
@@ -2560,13 +3149,37 @@ var require_lowering = __commonJS({
2560
3149
  this.constValues.set(constDecl.name, constDecl.value);
2561
3150
  this.varTypes.set(constDecl.name, this.normalizeType(constDecl.type));
2562
3151
  }
3152
+ for (const g of program.globals ?? []) {
3153
+ this.globalNames.set(g.name, { mutable: g.mutable });
3154
+ this.varTypes.set(g.name, this.normalizeType(g.type));
3155
+ const initValue = g.init.kind === "int_lit" ? g.init.value : 0;
3156
+ this.globals.push({ name: `$${g.name}`, init: initValue });
3157
+ }
2563
3158
  for (const fn of program.declarations) {
2564
3159
  this.fnDecls.set(fn.name, fn);
2565
3160
  this.functionDefaults.set(fn.name, fn.params.map((param) => param.default));
2566
3161
  }
3162
+ for (const implBlock of program.implBlocks ?? []) {
3163
+ let methods = this.implMethods.get(implBlock.typeName);
3164
+ if (!methods) {
3165
+ methods = /* @__PURE__ */ new Map();
3166
+ this.implMethods.set(implBlock.typeName, methods);
3167
+ }
3168
+ for (const method of implBlock.methods) {
3169
+ const loweredName = `${implBlock.typeName}_${method.name}`;
3170
+ methods.set(method.name, { fn: method, loweredName });
3171
+ this.fnDecls.set(loweredName, method);
3172
+ this.functionDefaults.set(loweredName, method.params.map((param) => param.default));
3173
+ }
3174
+ }
2567
3175
  for (const fn of program.declarations) {
2568
3176
  this.lowerFn(fn);
2569
3177
  }
3178
+ for (const implBlock of program.implBlocks ?? []) {
3179
+ for (const method of implBlock.methods) {
3180
+ this.lowerFn(method, { name: `${implBlock.typeName}_${method.name}` });
3181
+ }
3182
+ }
2570
3183
  return (0, builder_1.buildModule)(this.namespace, this.functions, this.globals);
2571
3184
  }
2572
3185
  // -------------------------------------------------------------------------
@@ -2575,20 +3188,44 @@ var require_lowering = __commonJS({
2575
3188
  lowerFn(fn, options = {}) {
2576
3189
  const loweredName = options.name ?? fn.name;
2577
3190
  const callbackBindings = options.callbackBindings ?? /* @__PURE__ */ new Map();
2578
- const runtimeParams = fn.params.filter((param) => !callbackBindings.has(param.name));
3191
+ const stdlibCallSite = options.stdlibCallSite;
3192
+ const staticEventDec = fn.decorators.find((d) => d.name === "on");
3193
+ const eventType = staticEventDec?.args?.eventType;
3194
+ const eventParamSpecs = eventType && (0, types_1.isEventTypeName)(eventType) ? (0, types_1.getEventParamSpecs)(eventType) : [];
3195
+ const runtimeParams = staticEventDec ? [] : fn.params.filter((param) => !callbackBindings.has(param.name));
2579
3196
  this.currentFn = loweredName;
3197
+ this.currentStdlibCallSite = stdlibCallSite;
2580
3198
  this.foreachCounter = 0;
2581
3199
  this.varMap = /* @__PURE__ */ new Map();
2582
3200
  this.lambdaBindings = /* @__PURE__ */ new Map();
3201
+ this.intervalBindings = /* @__PURE__ */ new Map();
2583
3202
  this.currentCallbackBindings = new Map(callbackBindings);
2584
3203
  this.currentContext = {};
2585
3204
  this.blockPosVars = /* @__PURE__ */ new Map();
2586
3205
  this.stringValues = /* @__PURE__ */ new Map();
2587
3206
  this.builder = new LoweringBuilder();
2588
- for (const param of runtimeParams) {
2589
- const paramName = param.name;
2590
- this.varMap.set(paramName, `$${paramName}`);
2591
- this.varTypes.set(paramName, this.normalizeType(param.type));
3207
+ if (staticEventDec) {
3208
+ for (let i = 0; i < fn.params.length; i++) {
3209
+ const param = fn.params[i];
3210
+ const expected = eventParamSpecs[i];
3211
+ const normalizedType = this.normalizeType(param.type);
3212
+ this.varTypes.set(param.name, normalizedType);
3213
+ if (expected?.type.kind === "entity") {
3214
+ this.varMap.set(param.name, "@s");
3215
+ continue;
3216
+ }
3217
+ if (expected?.type.kind === "named" && expected.type.name === "string") {
3218
+ this.stringValues.set(param.name, "");
3219
+ continue;
3220
+ }
3221
+ this.varMap.set(param.name, `$${param.name}`);
3222
+ }
3223
+ } else {
3224
+ for (const param of runtimeParams) {
3225
+ const paramName = param.name;
3226
+ this.varMap.set(paramName, `$${paramName}`);
3227
+ this.varTypes.set(paramName, this.normalizeType(param.type));
3228
+ }
2592
3229
  }
2593
3230
  for (const param of fn.params) {
2594
3231
  if (callbackBindings.has(param.name)) {
@@ -2601,6 +3238,15 @@ var require_lowering = __commonJS({
2601
3238
  const varName = `$${paramName}`;
2602
3239
  this.builder.emitAssign(varName, { kind: "var", name: `$p${i}` });
2603
3240
  }
3241
+ if (staticEventDec) {
3242
+ for (let i = 0; i < fn.params.length; i++) {
3243
+ const param = fn.params[i];
3244
+ const expected = eventParamSpecs[i];
3245
+ if (expected?.type.kind === "named" && expected.type.name !== "string") {
3246
+ this.builder.emitAssign(`$${param.name}`, { kind: "const", value: 0 });
3247
+ }
3248
+ }
3249
+ }
2604
3250
  this.lowerBlock(fn.body);
2605
3251
  if (!this.builder.isBlockSealed()) {
2606
3252
  this.builder.emitReturn();
@@ -2635,6 +3281,15 @@ var require_lowering = __commonJS({
2635
3281
  break;
2636
3282
  }
2637
3283
  }
3284
+ if (eventType && (0, types_1.isEventTypeName)(eventType)) {
3285
+ irFn.eventHandler = {
3286
+ eventType,
3287
+ tag: types_1.EVENT_TYPES[eventType].tag
3288
+ };
3289
+ }
3290
+ if (fn.decorators.some((d) => d.name === "load")) {
3291
+ irFn.isLoadInit = true;
3292
+ }
2638
3293
  if (tickRate && tickRate > 1) {
2639
3294
  this.wrapWithTickRate(irFn, tickRate);
2640
3295
  }
@@ -2646,7 +3301,7 @@ var require_lowering = __commonJS({
2646
3301
  }
2647
3302
  wrapWithTickRate(fn, rate) {
2648
3303
  const counterVar = `$__tick_${fn.name}`;
2649
- this.globals.push(counterVar);
3304
+ this.globals.push({ name: counterVar, init: 0 });
2650
3305
  const entry = fn.blocks[0];
2651
3306
  const originalInstrs = [...entry.instrs];
2652
3307
  const originalTerm = entry.term;
@@ -2734,6 +3389,9 @@ var require_lowering = __commonJS({
2734
3389
  }
2735
3390
  }
2736
3391
  lowerLetStmt(stmt) {
3392
+ if (this.currentContext.binding === stmt.name) {
3393
+ throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot redeclare foreach binding '${stmt.name}'`, stmt.span ?? { line: 0, col: 0 });
3394
+ }
2737
3395
  const varName = `$${stmt.name}`;
2738
3396
  this.varMap.set(stmt.name, varName);
2739
3397
  const declaredType = stmt.type ? this.normalizeType(stmt.type) : this.inferExprType(stmt.init);
@@ -2748,15 +3406,24 @@ var require_lowering = __commonJS({
2748
3406
  this.lambdaBindings.set(stmt.name, lambdaName);
2749
3407
  return;
2750
3408
  }
3409
+ if (stmt.init.kind === "call" && stmt.init.fn === "setInterval") {
3410
+ const value2 = this.lowerExpr(stmt.init);
3411
+ const intervalFn = this.intervalFunctions.get(value2.kind === "const" ? value2.value : NaN);
3412
+ if (intervalFn) {
3413
+ this.intervalBindings.set(stmt.name, intervalFn);
3414
+ }
3415
+ this.builder.emitAssign(varName, value2);
3416
+ return;
3417
+ }
2751
3418
  if (stmt.init.kind === "struct_lit" && stmt.type?.kind === "struct") {
2752
3419
  const structName = stmt.type.name.toLowerCase();
2753
3420
  for (const field of stmt.init.fields) {
2754
- const path = `rs:heap ${structName}_${stmt.name}.${field.name}`;
3421
+ const path2 = `rs:heap ${structName}_${stmt.name}.${field.name}`;
2755
3422
  const fieldValue = this.lowerExpr(field.value);
2756
3423
  if (fieldValue.kind === "const") {
2757
- this.builder.emitRaw(`data modify storage ${path} set value ${fieldValue.value}`);
3424
+ this.builder.emitRaw(`data modify storage ${path2} set value ${fieldValue.value}`);
2758
3425
  } else if (fieldValue.kind === "var") {
2759
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${fieldValue.name} rs`);
3426
+ this.builder.emitRaw(`execute store result storage ${path2} int 1 run scoreboard players get ${fieldValue.name} rs`);
2760
3427
  }
2761
3428
  }
2762
3429
  return;
@@ -2774,6 +3441,12 @@ var require_lowering = __commonJS({
2774
3441
  }
2775
3442
  return;
2776
3443
  }
3444
+ if (stmt.init.kind === "call" && stmt.init.fn === "set_new") {
3445
+ const setId = `__set_${this.foreachCounter++}`;
3446
+ this.builder.emitRaw(`data modify storage rs:sets ${setId} set value []`);
3447
+ this.stringValues.set(stmt.name, setId);
3448
+ return;
3449
+ }
2777
3450
  if (stmt.init.kind === "call" && stmt.init.fn === "spawn_object") {
2778
3451
  const value2 = this.lowerExpr(stmt.init);
2779
3452
  if (value2.kind === "var" && value2.name.startsWith("@e[tag=__rs_obj_")) {
@@ -2803,6 +3476,10 @@ var require_lowering = __commonJS({
2803
3476
  }
2804
3477
  }
2805
3478
  lowerIfStmt(stmt) {
3479
+ if (stmt.cond.kind === "is_check") {
3480
+ this.lowerIsCheckIfStmt(stmt);
3481
+ return;
3482
+ }
2806
3483
  const condVar = this.lowerExpr(stmt.cond);
2807
3484
  const condName = this.operandToVar(condVar);
2808
3485
  const thenLabel = this.builder.freshLabel("then");
@@ -2823,6 +3500,40 @@ var require_lowering = __commonJS({
2823
3500
  }
2824
3501
  this.builder.startBlock(mergeLabel);
2825
3502
  }
3503
+ lowerIsCheckIfStmt(stmt) {
3504
+ const cond = stmt.cond;
3505
+ if (cond.kind !== "is_check") {
3506
+ throw new diagnostics_1.DiagnosticError("LoweringError", "Internal error: expected 'is' check condition", stmt.span ?? { line: 0, col: 0 });
3507
+ }
3508
+ if (stmt.else_) {
3509
+ throw new diagnostics_1.DiagnosticError("LoweringError", "'is' checks with else branches are not yet supported", cond.span ?? stmt.span ?? { line: 0, col: 0 });
3510
+ }
3511
+ const selector = this.exprToEntitySelector(cond.expr);
3512
+ if (!selector) {
3513
+ throw new diagnostics_1.DiagnosticError("LoweringError", "'is' checks require an entity selector or entity binding", cond.span ?? stmt.span ?? { line: 0, col: 0 });
3514
+ }
3515
+ const mcType = ENTITY_TO_MC_TYPE[cond.entityType];
3516
+ if (!mcType) {
3517
+ throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot lower entity type check for '${cond.entityType}'`, cond.span ?? stmt.span ?? { line: 0, col: 0 });
3518
+ }
3519
+ const thenFnName = `${this.currentFn}/then_${this.foreachCounter++}`;
3520
+ this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, mcType)} run function ${this.namespace}:${thenFnName}`);
3521
+ const savedBuilder = this.builder;
3522
+ const savedVarMap = new Map(this.varMap);
3523
+ const savedBlockPosVars = new Map(this.blockPosVars);
3524
+ this.builder = new LoweringBuilder();
3525
+ this.varMap = new Map(savedVarMap);
3526
+ this.blockPosVars = new Map(savedBlockPosVars);
3527
+ this.builder.startBlock("entry");
3528
+ this.lowerBlock(stmt.then);
3529
+ if (!this.builder.isBlockSealed()) {
3530
+ this.builder.emitReturn();
3531
+ }
3532
+ this.functions.push(this.builder.build(thenFnName, [], false));
3533
+ this.builder = savedBuilder;
3534
+ this.varMap = savedVarMap;
3535
+ this.blockPosVars = savedBlockPosVars;
3536
+ }
2826
3537
  lowerWhileStmt(stmt) {
2827
3538
  const checkLabel = this.builder.freshLabel("loop_check");
2828
3539
  const bodyLabel = this.builder.freshLabel("loop_body");
@@ -3093,10 +3804,20 @@ var require_lowering = __commonJS({
3093
3804
  parts.push(`at ${this.selectorToString(sub.selector)}`);
3094
3805
  break;
3095
3806
  case "if_entity":
3096
- parts.push(`if entity ${this.selectorToString(sub.selector)}`);
3807
+ if (sub.selector) {
3808
+ parts.push(`if entity ${this.selectorToString(sub.selector)}`);
3809
+ } else if (sub.varName) {
3810
+ const sel = { kind: "@s", filters: sub.filters };
3811
+ parts.push(`if entity ${this.selectorToString(sel)}`);
3812
+ }
3097
3813
  break;
3098
3814
  case "unless_entity":
3099
- parts.push(`unless entity ${this.selectorToString(sub.selector)}`);
3815
+ if (sub.selector) {
3816
+ parts.push(`unless entity ${this.selectorToString(sub.selector)}`);
3817
+ } else if (sub.varName) {
3818
+ const sel = { kind: "@s", filters: sub.filters };
3819
+ parts.push(`unless entity ${this.selectorToString(sel)}`);
3820
+ }
3100
3821
  break;
3101
3822
  case "in":
3102
3823
  parts.push(`in ${sub.dimension}`);
@@ -3181,12 +3902,16 @@ var require_lowering = __commonJS({
3181
3902
  return { kind: "var", name: this.selectorToString(expr.sel) };
3182
3903
  case "binary":
3183
3904
  return this.lowerBinaryExpr(expr);
3905
+ case "is_check":
3906
+ throw new diagnostics_1.DiagnosticError("LoweringError", "'is' checks are only supported as if conditions", expr.span ?? { line: 0, col: 0 });
3184
3907
  case "unary":
3185
3908
  return this.lowerUnaryExpr(expr);
3186
3909
  case "assign":
3187
3910
  return this.lowerAssignExpr(expr);
3188
3911
  case "call":
3189
3912
  return this.lowerCallExpr(expr);
3913
+ case "static_call":
3914
+ return this.lowerStaticCallExpr(expr);
3190
3915
  case "invoke":
3191
3916
  return this.lowerInvokeExpr(expr);
3192
3917
  case "member_assign":
@@ -3213,9 +3938,9 @@ var require_lowering = __commonJS({
3213
3938
  }
3214
3939
  if (varType?.kind === "struct") {
3215
3940
  const structName = varType.name.toLowerCase();
3216
- const path = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
3941
+ const path2 = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
3217
3942
  const dst = this.builder.freshTemp();
3218
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path}`);
3943
+ this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path2}`);
3219
3944
  return { kind: "var", name: dst };
3220
3945
  }
3221
3946
  if (varType?.kind === "array" && expr.field === "len") {
@@ -3253,20 +3978,20 @@ var require_lowering = __commonJS({
3253
3978
  }
3254
3979
  if (varType?.kind === "struct") {
3255
3980
  const structName = varType.name.toLowerCase();
3256
- const path = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
3981
+ const path2 = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
3257
3982
  const value2 = this.lowerExpr(expr.value);
3258
3983
  if (expr.op === "=") {
3259
3984
  if (value2.kind === "const") {
3260
- this.builder.emitRaw(`data modify storage ${path} set value ${value2.value}`);
3985
+ this.builder.emitRaw(`data modify storage ${path2} set value ${value2.value}`);
3261
3986
  } else if (value2.kind === "var") {
3262
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${value2.name} rs`);
3987
+ this.builder.emitRaw(`execute store result storage ${path2} int 1 run scoreboard players get ${value2.name} rs`);
3263
3988
  }
3264
3989
  } else {
3265
3990
  const dst = this.builder.freshTemp();
3266
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path}`);
3991
+ this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path2}`);
3267
3992
  const binOp = expr.op.slice(0, -1);
3268
3993
  this.builder.emitBinop(dst, { kind: "var", name: dst }, binOp, value2);
3269
- this.builder.emitRaw(`execute store result storage ${path} int 1 run scoreboard players get ${dst} rs`);
3994
+ this.builder.emitRaw(`execute store result storage ${path2} int 1 run scoreboard players get ${dst} rs`);
3270
3995
  }
3271
3996
  return { kind: "const", value: 0 };
3272
3997
  }
@@ -3345,6 +4070,13 @@ var require_lowering = __commonJS({
3345
4070
  return { kind: "var", name: dst };
3346
4071
  }
3347
4072
  lowerAssignExpr(expr) {
4073
+ if (this.constValues.has(expr.target)) {
4074
+ throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot assign to constant '${expr.target}'`, getSpan(expr) ?? { line: 1, col: 1 });
4075
+ }
4076
+ const globalInfo = this.globalNames.get(expr.target);
4077
+ if (globalInfo && !globalInfo.mutable) {
4078
+ throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot assign to constant '${expr.target}'`, getSpan(expr) ?? { line: 1, col: 1 });
4079
+ }
3348
4080
  const blockPosValue = this.resolveBlockPosExpr(expr.value);
3349
4081
  if (blockPosValue) {
3350
4082
  this.blockPosVars.set(expr.target, blockPosValue);
@@ -3452,6 +4184,10 @@ var require_lowering = __commonJS({
3452
4184
  if (callbackTarget) {
3453
4185
  return this.emitDirectFunctionCall(callbackTarget, expr.args);
3454
4186
  }
4187
+ const implMethod = this.resolveInstanceMethod(expr);
4188
+ if (implMethod) {
4189
+ return this.emitMethodCall(implMethod.loweredName, implMethod.fn, expr.args);
4190
+ }
3455
4191
  const fnDecl = this.fnDecls.get(expr.fn);
3456
4192
  const defaultArgs = this.functionDefaults.get(expr.fn) ?? [];
3457
4193
  const fullArgs = [...expr.args];
@@ -3477,11 +4213,17 @@ var require_lowering = __commonJS({
3477
4213
  }
3478
4214
  runtimeArgs.push(fullArgs[i]);
3479
4215
  }
3480
- const targetFn = callbackBindings.size > 0 ? this.ensureSpecializedFunction(fnDecl, callbackBindings) : expr.fn;
4216
+ const stdlibCallSite = this.getStdlibCallSiteContext(fnDecl, getSpan(expr));
4217
+ const targetFn = callbackBindings.size > 0 || stdlibCallSite ? this.ensureSpecializedFunctionWithContext(fnDecl, callbackBindings, stdlibCallSite) : expr.fn;
3481
4218
  return this.emitDirectFunctionCall(targetFn, runtimeArgs);
3482
4219
  }
3483
4220
  return this.emitDirectFunctionCall(expr.fn, fullArgs);
3484
4221
  }
4222
+ lowerStaticCallExpr(expr) {
4223
+ const method = this.implMethods.get(expr.type)?.get(expr.method);
4224
+ const targetFn = method?.loweredName ?? `${expr.type}_${expr.method}`;
4225
+ return this.emitMethodCall(targetFn, method?.fn, expr.args);
4226
+ }
3485
4227
  lowerInvokeExpr(expr) {
3486
4228
  if (expr.callee.kind === "lambda") {
3487
4229
  if (!Array.isArray(expr.callee.body)) {
@@ -3526,6 +4268,18 @@ var require_lowering = __commonJS({
3526
4268
  this.builder.emitCall(fn, loweredArgs, dst);
3527
4269
  return { kind: "var", name: dst };
3528
4270
  }
4271
+ emitMethodCall(fn, fnDecl, args) {
4272
+ const defaultArgs = this.functionDefaults.get(fn) ?? fnDecl?.params.map((param) => param.default) ?? [];
4273
+ const fullArgs = [...args];
4274
+ for (let i = fullArgs.length; i < defaultArgs.length; i++) {
4275
+ const defaultExpr = defaultArgs[i];
4276
+ if (!defaultExpr) {
4277
+ break;
4278
+ }
4279
+ fullArgs.push(defaultExpr);
4280
+ }
4281
+ return this.emitDirectFunctionCall(fn, fullArgs);
4282
+ }
3529
4283
  resolveFunctionRefExpr(expr) {
3530
4284
  if (expr.kind === "lambda") {
3531
4285
  return this.lowerLambdaExpr(expr);
@@ -3539,7 +4293,14 @@ var require_lowering = __commonJS({
3539
4293
  return this.lambdaBindings.get(name) ?? this.currentCallbackBindings.get(name) ?? null;
3540
4294
  }
3541
4295
  ensureSpecializedFunction(fn, callbackBindings) {
4296
+ return this.ensureSpecializedFunctionWithContext(fn, callbackBindings);
4297
+ }
4298
+ ensureSpecializedFunctionWithContext(fn, callbackBindings, stdlibCallSite) {
3542
4299
  const parts = [...callbackBindings.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([param, target]) => `${param}_${target.replace(/[^a-zA-Z0-9_]/g, "_")}`);
4300
+ const callSiteHash = stdlibCallSite ? this.shortHash(this.serializeCallSite(stdlibCallSite)) : null;
4301
+ if (callSiteHash) {
4302
+ parts.push(`callsite_${callSiteHash}`);
4303
+ }
3543
4304
  const key = `${fn.name}::${parts.join("::")}`;
3544
4305
  const cached = this.specializedFunctions.get(key);
3545
4306
  if (cached) {
@@ -3548,7 +4309,7 @@ var require_lowering = __commonJS({
3548
4309
  const specializedName = `${fn.name}__${parts.join("__")}`;
3549
4310
  this.specializedFunctions.set(key, specializedName);
3550
4311
  this.withSavedFunctionState(() => {
3551
- this.lowerFn(fn, { name: specializedName, callbackBindings });
4312
+ this.lowerFn(fn, { name: specializedName, callbackBindings, stdlibCallSite });
3552
4313
  });
3553
4314
  return specializedName;
3554
4315
  }
@@ -3571,10 +4332,12 @@ var require_lowering = __commonJS({
3571
4332
  }
3572
4333
  withSavedFunctionState(callback) {
3573
4334
  const savedCurrentFn = this.currentFn;
4335
+ const savedStdlibCallSite = this.currentStdlibCallSite;
3574
4336
  const savedForeachCounter = this.foreachCounter;
3575
4337
  const savedBuilder = this.builder;
3576
4338
  const savedVarMap = new Map(this.varMap);
3577
4339
  const savedLambdaBindings = new Map(this.lambdaBindings);
4340
+ const savedIntervalBindings = new Map(this.intervalBindings);
3578
4341
  const savedCallbackBindings = new Map(this.currentCallbackBindings);
3579
4342
  const savedContext = this.currentContext;
3580
4343
  const savedBlockPosVars = new Map(this.blockPosVars);
@@ -3584,10 +4347,12 @@ var require_lowering = __commonJS({
3584
4347
  return callback();
3585
4348
  } finally {
3586
4349
  this.currentFn = savedCurrentFn;
4350
+ this.currentStdlibCallSite = savedStdlibCallSite;
3587
4351
  this.foreachCounter = savedForeachCounter;
3588
4352
  this.builder = savedBuilder;
3589
4353
  this.varMap = savedVarMap;
3590
4354
  this.lambdaBindings = savedLambdaBindings;
4355
+ this.intervalBindings = savedIntervalBindings;
3591
4356
  this.currentCallbackBindings = savedCallbackBindings;
3592
4357
  this.currentContext = savedContext;
3593
4358
  this.blockPosVars = savedBlockPosVars;
@@ -3601,6 +4366,15 @@ var require_lowering = __commonJS({
3601
4366
  this.builder.emitRaw(richTextCommand);
3602
4367
  return { kind: "const", value: 0 };
3603
4368
  }
4369
+ if (name === "setTimeout") {
4370
+ return this.lowerSetTimeout(args);
4371
+ }
4372
+ if (name === "setInterval") {
4373
+ return this.lowerSetInterval(args);
4374
+ }
4375
+ if (name === "clearInterval") {
4376
+ return this.lowerClearInterval(args, callSpan);
4377
+ }
3604
4378
  if (name === "random") {
3605
4379
  const dst = this.builder.freshTemp();
3606
4380
  const min = args[0] ? this.exprToLiteral(args[0]) : "0";
@@ -3624,13 +4398,13 @@ var require_lowering = __commonJS({
3624
4398
  if (name === "scoreboard_get" || name === "score") {
3625
4399
  const dst = this.builder.freshTemp();
3626
4400
  const player = this.exprToTargetString(args[0]);
3627
- const objective = this.exprToString(args[1]);
4401
+ const objective = this.resolveScoreboardObjective(args[0], args[1], callSpan);
3628
4402
  this.builder.emitRaw(`execute store result score ${dst} rs run scoreboard players get ${player} ${objective}`);
3629
4403
  return { kind: "var", name: dst };
3630
4404
  }
3631
4405
  if (name === "scoreboard_set") {
3632
4406
  const player = this.exprToTargetString(args[0]);
3633
- const objective = this.exprToString(args[1]);
4407
+ const objective = this.resolveScoreboardObjective(args[0], args[1], callSpan);
3634
4408
  const value = this.lowerExpr(args[2]);
3635
4409
  if (value.kind === "const") {
3636
4410
  this.builder.emitRaw(`scoreboard players set ${player} ${objective} ${value.value}`);
@@ -3641,7 +4415,7 @@ var require_lowering = __commonJS({
3641
4415
  }
3642
4416
  if (name === "scoreboard_display") {
3643
4417
  const slot = this.exprToString(args[0]);
3644
- const objective = this.exprToString(args[1]);
4418
+ const objective = this.resolveScoreboardObjective(void 0, args[1], callSpan);
3645
4419
  this.builder.emitRaw(`scoreboard objectives setdisplay ${slot} ${objective}`);
3646
4420
  return { kind: "const", value: 0 };
3647
4421
  }
@@ -3651,14 +4425,14 @@ var require_lowering = __commonJS({
3651
4425
  return { kind: "const", value: 0 };
3652
4426
  }
3653
4427
  if (name === "scoreboard_add_objective") {
3654
- const objective = this.exprToString(args[0]);
4428
+ const objective = this.resolveScoreboardObjective(void 0, args[0], callSpan);
3655
4429
  const criteria = this.exprToString(args[1]);
3656
4430
  const displayName = args[2] ? ` ${this.exprToQuotedString(args[2])}` : "";
3657
4431
  this.builder.emitRaw(`scoreboard objectives add ${objective} ${criteria}${displayName}`);
3658
4432
  return { kind: "const", value: 0 };
3659
4433
  }
3660
4434
  if (name === "scoreboard_remove_objective") {
3661
- const objective = this.exprToString(args[0]);
4435
+ const objective = this.resolveScoreboardObjective(void 0, args[0], callSpan);
3662
4436
  this.builder.emitRaw(`scoreboard objectives remove ${objective}`);
3663
4437
  return { kind: "const", value: 0 };
3664
4438
  }
@@ -3730,11 +4504,57 @@ var require_lowering = __commonJS({
3730
4504
  const dst = this.builder.freshTemp();
3731
4505
  const targetType = this.exprToString(args[0]);
3732
4506
  const target = targetType === "entity" ? this.exprToTargetString(args[1]) : this.exprToString(args[1]);
3733
- const path = this.exprToString(args[2]);
4507
+ const path2 = this.exprToString(args[2]);
3734
4508
  const scale = args[3] ? this.exprToString(args[3]) : "1";
3735
- this.builder.emitRaw(`execute store result score ${dst} rs run data get ${targetType} ${target} ${path} ${scale}`);
4509
+ this.builder.emitRaw(`execute store result score ${dst} rs run data get ${targetType} ${target} ${path2} ${scale}`);
4510
+ return { kind: "var", name: dst };
4511
+ }
4512
+ if (name === "data_merge") {
4513
+ const target = args[0];
4514
+ const nbt = args[1];
4515
+ const nbtStr = this.exprToSnbt ? this.exprToSnbt(nbt) : this.exprToString(nbt);
4516
+ if (target.kind === "selector") {
4517
+ const sel = this.exprToTargetString(target);
4518
+ this.builder.emitRaw(`data merge entity ${sel} ${nbtStr}`);
4519
+ } else {
4520
+ const targetStr = this.exprToString(target);
4521
+ if (targetStr.match(/^~|^\d|^\^/)) {
4522
+ this.builder.emitRaw(`data merge block ${targetStr} ${nbtStr}`);
4523
+ } else {
4524
+ this.builder.emitRaw(`data merge storage ${targetStr} ${nbtStr}`);
4525
+ }
4526
+ }
4527
+ return { kind: "const", value: 0 };
4528
+ }
4529
+ if (name === "set_new") {
4530
+ const setId = `__set_${this.foreachCounter++}`;
4531
+ this.builder.emitRaw(`data modify storage rs:sets ${setId} set value []`);
4532
+ return { kind: "const", value: 0 };
4533
+ }
4534
+ if (name === "set_add") {
4535
+ const setId = this.exprToString(args[0]);
4536
+ const value = this.exprToString(args[1]);
4537
+ this.builder.emitRaw(`execute unless data storage rs:sets ${setId}[{value:${value}}] run data modify storage rs:sets ${setId} append value {value:${value}}`);
4538
+ return { kind: "const", value: 0 };
4539
+ }
4540
+ if (name === "set_contains") {
4541
+ const dst = this.builder.freshTemp();
4542
+ const setId = this.exprToString(args[0]);
4543
+ const value = this.exprToString(args[1]);
4544
+ this.builder.emitRaw(`execute store result score ${dst} rs if data storage rs:sets ${setId}[{value:${value}}]`);
3736
4545
  return { kind: "var", name: dst };
3737
4546
  }
4547
+ if (name === "set_remove") {
4548
+ const setId = this.exprToString(args[0]);
4549
+ const value = this.exprToString(args[1]);
4550
+ this.builder.emitRaw(`data remove storage rs:sets ${setId}[{value:${value}}]`);
4551
+ return { kind: "const", value: 0 };
4552
+ }
4553
+ if (name === "set_clear") {
4554
+ const setId = this.exprToString(args[0]);
4555
+ this.builder.emitRaw(`data modify storage rs:sets ${setId} set value []`);
4556
+ return { kind: "const", value: 0 };
4557
+ }
3738
4558
  const coordCommand = this.lowerCoordinateBuiltin(name, args);
3739
4559
  if (coordCommand) {
3740
4560
  this.builder.emitRaw(coordCommand);
@@ -3759,13 +4579,97 @@ var require_lowering = __commonJS({
3759
4579
  }
3760
4580
  return { kind: "const", value: 0 };
3761
4581
  }
3762
- const strArgs = args.map((arg) => this.exprToString(arg));
4582
+ const strArgs = args.map((arg) => arg.kind === "struct_lit" || arg.kind === "array_lit" ? this.exprToSnbt(arg) : this.exprToString(arg));
3763
4583
  const cmd = BUILTINS2[name](strArgs);
3764
4584
  if (cmd) {
3765
4585
  this.builder.emitRaw(cmd);
3766
4586
  }
3767
4587
  return { kind: "const", value: 0 };
3768
4588
  }
4589
+ lowerSetTimeout(args) {
4590
+ const delay = this.exprToLiteral(args[0]);
4591
+ const callback = args[1];
4592
+ if (!callback || callback.kind !== "lambda") {
4593
+ throw new diagnostics_1.DiagnosticError("LoweringError", "setTimeout requires a lambda callback", getSpan(callback) ?? { line: 1, col: 1 });
4594
+ }
4595
+ const fnName = `__timeout_${this.timeoutCounter++}`;
4596
+ this.lowerNamedLambdaFunction(fnName, callback);
4597
+ this.builder.emitRaw(`schedule function ${this.namespace}:${fnName} ${delay}t`);
4598
+ return { kind: "const", value: 0 };
4599
+ }
4600
+ lowerSetInterval(args) {
4601
+ const delay = this.exprToLiteral(args[0]);
4602
+ const callback = args[1];
4603
+ if (!callback || callback.kind !== "lambda") {
4604
+ throw new diagnostics_1.DiagnosticError("LoweringError", "setInterval requires a lambda callback", getSpan(callback) ?? { line: 1, col: 1 });
4605
+ }
4606
+ const id = this.intervalCounter++;
4607
+ const bodyName = `__interval_body_${id}`;
4608
+ const fnName = `__interval_${id}`;
4609
+ this.lowerNamedLambdaFunction(bodyName, callback);
4610
+ this.lowerIntervalWrapperFunction(fnName, bodyName, delay);
4611
+ this.intervalFunctions.set(id, fnName);
4612
+ this.builder.emitRaw(`schedule function ${this.namespace}:${fnName} ${delay}t`);
4613
+ return { kind: "const", value: id };
4614
+ }
4615
+ lowerClearInterval(args, callSpan) {
4616
+ const fnName = this.resolveIntervalFunctionName(args[0]);
4617
+ if (!fnName) {
4618
+ throw new diagnostics_1.DiagnosticError("LoweringError", "clearInterval requires an interval ID returned from setInterval", callSpan ?? getSpan(args[0]) ?? { line: 1, col: 1 });
4619
+ }
4620
+ this.builder.emitRaw(`schedule clear ${this.namespace}:${fnName}`);
4621
+ return { kind: "const", value: 0 };
4622
+ }
4623
+ lowerNamedLambdaFunction(name, expr) {
4624
+ const lambdaFn = {
4625
+ name,
4626
+ params: expr.params.map((param) => ({
4627
+ name: param.name,
4628
+ type: param.type ?? { kind: "named", name: "int" }
4629
+ })),
4630
+ returnType: expr.returnType ?? this.inferLambdaReturnType(expr),
4631
+ decorators: [],
4632
+ body: Array.isArray(expr.body) ? expr.body : [{ kind: "return", value: expr.body }]
4633
+ };
4634
+ this.withSavedFunctionState(() => {
4635
+ this.lowerFn(lambdaFn);
4636
+ });
4637
+ }
4638
+ lowerIntervalWrapperFunction(name, bodyName, delay) {
4639
+ const intervalFn = {
4640
+ name,
4641
+ params: [],
4642
+ returnType: { kind: "named", name: "void" },
4643
+ decorators: [],
4644
+ body: [
4645
+ { kind: "raw", cmd: `function ${this.namespace}:${bodyName}` },
4646
+ { kind: "raw", cmd: `schedule function ${this.namespace}:${name} ${delay}t` }
4647
+ ]
4648
+ };
4649
+ this.withSavedFunctionState(() => {
4650
+ this.lowerFn(intervalFn);
4651
+ });
4652
+ }
4653
+ resolveIntervalFunctionName(expr) {
4654
+ if (!expr) {
4655
+ return null;
4656
+ }
4657
+ if (expr.kind === "ident") {
4658
+ const boundInterval = this.intervalBindings.get(expr.name);
4659
+ if (boundInterval) {
4660
+ return boundInterval;
4661
+ }
4662
+ const constValue = this.constValues.get(expr.name);
4663
+ if (constValue?.kind === "int_lit") {
4664
+ return this.intervalFunctions.get(constValue.value) ?? null;
4665
+ }
4666
+ return null;
4667
+ }
4668
+ if (expr.kind === "int_lit") {
4669
+ return this.intervalFunctions.get(expr.value) ?? null;
4670
+ }
4671
+ return null;
4672
+ }
3769
4673
  lowerRichTextBuiltin(name, args) {
3770
4674
  const messageArgIndex = this.getRichTextArgIndex(name);
3771
4675
  if (messageArgIndex === null) {
@@ -3908,11 +4812,72 @@ var require_lowering = __commonJS({
3908
4812
  }
3909
4813
  case "selector":
3910
4814
  return this.selectorToString(expr.sel);
4815
+ case "unary":
4816
+ if (expr.op === "-" && expr.operand.kind === "int_lit") {
4817
+ return (-expr.operand.value).toString();
4818
+ }
4819
+ if (expr.op === "-" && expr.operand.kind === "float_lit") {
4820
+ return Math.trunc(-expr.operand.value).toString();
4821
+ }
4822
+ const unaryOp = this.lowerExpr(expr);
4823
+ return this.operandToVar(unaryOp);
3911
4824
  default:
3912
4825
  const op = this.lowerExpr(expr);
3913
4826
  return this.operandToVar(op);
3914
4827
  }
3915
4828
  }
4829
+ exprToEntitySelector(expr) {
4830
+ if (expr.kind === "selector") {
4831
+ return this.selectorToString(expr.sel);
4832
+ }
4833
+ if (expr.kind === "ident") {
4834
+ const constValue = this.constValues.get(expr.name);
4835
+ if (constValue) {
4836
+ return this.exprToEntitySelector(constValue);
4837
+ }
4838
+ const mapped = this.varMap.get(expr.name);
4839
+ if (mapped?.startsWith("@")) {
4840
+ return mapped;
4841
+ }
4842
+ }
4843
+ return null;
4844
+ }
4845
+ appendTypeFilter(selector, mcType) {
4846
+ if (selector.endsWith("]")) {
4847
+ return `${selector.slice(0, -1)},type=${mcType}]`;
4848
+ }
4849
+ return `${selector}[type=${mcType}]`;
4850
+ }
4851
+ exprToSnbt(expr) {
4852
+ switch (expr.kind) {
4853
+ case "struct_lit": {
4854
+ const entries = expr.fields.map((f) => `${f.name}:${this.exprToSnbt(f.value)}`);
4855
+ return `{${entries.join(",")}}`;
4856
+ }
4857
+ case "array_lit": {
4858
+ const items = expr.elements.map((e) => this.exprToSnbt(e));
4859
+ return `[${items.join(",")}]`;
4860
+ }
4861
+ case "str_lit":
4862
+ return `"${expr.value}"`;
4863
+ case "int_lit":
4864
+ return String(expr.value);
4865
+ case "float_lit":
4866
+ return String(expr.value);
4867
+ case "byte_lit":
4868
+ return `${expr.value}b`;
4869
+ case "short_lit":
4870
+ return `${expr.value}s`;
4871
+ case "long_lit":
4872
+ return `${expr.value}L`;
4873
+ case "double_lit":
4874
+ return `${expr.value}d`;
4875
+ case "bool_lit":
4876
+ return expr.value ? "1b" : "0b";
4877
+ default:
4878
+ return this.exprToString(expr);
4879
+ }
4880
+ }
3916
4881
  exprToTargetString(expr) {
3917
4882
  if (expr.kind === "selector") {
3918
4883
  return this.selectorToString(expr.sel);
@@ -3950,6 +4915,92 @@ var require_lowering = __commonJS({
3950
4915
  isTeamTextOption(option) {
3951
4916
  return option === "displayName" || option === "prefix" || option === "suffix";
3952
4917
  }
4918
+ exprToScoreboardObjective(expr, span) {
4919
+ if (expr.kind === "mc_name") {
4920
+ return expr.value;
4921
+ }
4922
+ const objective = this.exprToString(expr);
4923
+ if (objective.startsWith("#") || objective.includes(".")) {
4924
+ return objective.startsWith("#") ? objective.slice(1) : objective;
4925
+ }
4926
+ return `${this.getObjectiveNamespace(span)}.${objective}`;
4927
+ }
4928
+ resolveScoreboardObjective(playerExpr, objectiveExpr, span) {
4929
+ const stdlibInternalObjective = this.tryGetStdlibInternalObjective(playerExpr, objectiveExpr, span);
4930
+ if (stdlibInternalObjective) {
4931
+ return stdlibInternalObjective;
4932
+ }
4933
+ return this.exprToScoreboardObjective(objectiveExpr, span);
4934
+ }
4935
+ getObjectiveNamespace(span) {
4936
+ const filePath = this.filePathForSpan(span);
4937
+ if (!filePath) {
4938
+ return this.namespace;
4939
+ }
4940
+ return this.isStdlibFile(filePath) ? "rs" : this.namespace;
4941
+ }
4942
+ tryGetStdlibInternalObjective(playerExpr, objectiveExpr, span) {
4943
+ if (!span || !this.currentStdlibCallSite || objectiveExpr.kind !== "mc_name" || objectiveExpr.value !== "rs") {
4944
+ return null;
4945
+ }
4946
+ const filePath = this.filePathForSpan(span);
4947
+ if (!filePath || !this.isStdlibFile(filePath)) {
4948
+ return null;
4949
+ }
4950
+ const resourceBase = this.getStdlibInternalResourceBase(playerExpr);
4951
+ if (!resourceBase) {
4952
+ return null;
4953
+ }
4954
+ const hash = this.shortHash(this.serializeCallSite(this.currentStdlibCallSite));
4955
+ return `rs._${resourceBase}_${hash}`;
4956
+ }
4957
+ getStdlibInternalResourceBase(playerExpr) {
4958
+ if (!playerExpr || playerExpr.kind !== "str_lit") {
4959
+ return null;
4960
+ }
4961
+ const match = playerExpr.value.match(/^([a-z0-9]+)_/);
4962
+ return match?.[1] ?? null;
4963
+ }
4964
+ getStdlibCallSiteContext(fn, exprSpan) {
4965
+ const fnFilePath = this.filePathForSpan(getSpan(fn));
4966
+ if (!fnFilePath || !this.isStdlibFile(fnFilePath)) {
4967
+ return void 0;
4968
+ }
4969
+ if (this.currentStdlibCallSite) {
4970
+ return this.currentStdlibCallSite;
4971
+ }
4972
+ if (!exprSpan) {
4973
+ return void 0;
4974
+ }
4975
+ return {
4976
+ filePath: this.filePathForSpan(exprSpan),
4977
+ line: exprSpan.line,
4978
+ col: exprSpan.col
4979
+ };
4980
+ }
4981
+ serializeCallSite(callSite) {
4982
+ return `${callSite.filePath ?? "<memory>"}:${callSite.line}:${callSite.col}`;
4983
+ }
4984
+ shortHash(input) {
4985
+ let hash = 2166136261;
4986
+ for (let i = 0; i < input.length; i++) {
4987
+ hash ^= input.charCodeAt(i);
4988
+ hash = Math.imul(hash, 16777619);
4989
+ }
4990
+ return (hash >>> 0).toString(16).padStart(8, "0").slice(0, 4);
4991
+ }
4992
+ isStdlibFile(filePath) {
4993
+ const normalized = path.normalize(filePath);
4994
+ const stdlibSegment = `${path.sep}src${path.sep}stdlib${path.sep}`;
4995
+ return normalized.includes(stdlibSegment);
4996
+ }
4997
+ filePathForSpan(span) {
4998
+ if (!span) {
4999
+ return void 0;
5000
+ }
5001
+ const line = span.line;
5002
+ return this.sourceRanges.find((range) => line >= range.startLine && line <= range.endLine)?.filePath;
5003
+ }
3953
5004
  lowerCoordinateBuiltin(name, args) {
3954
5005
  const pos0 = args[0] ? this.resolveBlockPosExpr(args[0]) : null;
3955
5006
  const pos1 = args[1] ? this.resolveBlockPosExpr(args[1]) : null;
@@ -3972,6 +5023,13 @@ var require_lowering = __commonJS({
3972
5023
  }
3973
5024
  return null;
3974
5025
  }
5026
+ if (name === "summon") {
5027
+ if (args.length >= 2 && pos1) {
5028
+ const nbt = args[2] ? ` ${this.exprToString(args[2])}` : "";
5029
+ return `summon ${this.exprToString(args[0])} ${emitBlockPos(pos1)}${nbt}`;
5030
+ }
5031
+ return null;
5032
+ }
3975
5033
  return null;
3976
5034
  }
3977
5035
  lowerTpCommand(args) {
@@ -4050,7 +5108,11 @@ var require_lowering = __commonJS({
4050
5108
  };
4051
5109
  }
4052
5110
  if (expr.kind === "call") {
4053
- return this.fnDecls.get(this.resolveFunctionRefByName(expr.fn) ?? expr.fn)?.returnType;
5111
+ const resolved = this.resolveFunctionRefByName(expr.fn) ?? this.resolveInstanceMethod(expr)?.loweredName ?? expr.fn;
5112
+ return this.fnDecls.get(resolved)?.returnType;
5113
+ }
5114
+ if (expr.kind === "static_call") {
5115
+ return this.implMethods.get(expr.type)?.get(expr.method)?.fn.returnType;
4054
5116
  }
4055
5117
  if (expr.kind === "invoke") {
4056
5118
  const calleeType = this.inferExprType(expr.callee);
@@ -4078,6 +5140,21 @@ var require_lowering = __commonJS({
4078
5140
  }
4079
5141
  return void 0;
4080
5142
  }
5143
+ resolveInstanceMethod(expr) {
5144
+ const receiver = expr.args[0];
5145
+ if (!receiver) {
5146
+ return null;
5147
+ }
5148
+ const receiverType = this.inferExprType(receiver);
5149
+ if (receiverType?.kind !== "struct") {
5150
+ return null;
5151
+ }
5152
+ const method = this.implMethods.get(receiverType.name)?.get(expr.fn);
5153
+ if (!method || method.fn.params[0]?.name !== "self") {
5154
+ return null;
5155
+ }
5156
+ return method;
5157
+ }
4081
5158
  normalizeType(type) {
4082
5159
  if (type.kind === "array") {
4083
5160
  return { kind: "array", elem: this.normalizeType(type.elem) };
@@ -4199,6 +5276,16 @@ var require_lowering = __commonJS({
4199
5276
  parts.push(`nbt=${filters.nbt}`);
4200
5277
  if (filters.gamemode)
4201
5278
  parts.push(`gamemode=${filters.gamemode}`);
5279
+ if (filters.x)
5280
+ parts.push(`x=${this.rangeToString(filters.x)}`);
5281
+ if (filters.y)
5282
+ parts.push(`y=${this.rangeToString(filters.y)}`);
5283
+ if (filters.z)
5284
+ parts.push(`z=${this.rangeToString(filters.z)}`);
5285
+ if (filters.x_rotation)
5286
+ parts.push(`x_rotation=${this.rangeToString(filters.x_rotation)}`);
5287
+ if (filters.y_rotation)
5288
+ parts.push(`y_rotation=${this.rangeToString(filters.y_rotation)}`);
4202
5289
  return this.finalizeSelector(parts.length ? `${kind}[${parts.join(",")}]` : kind);
4203
5290
  }
4204
5291
  finalizeSelector(selector) {
@@ -4218,16 +5305,19 @@ var require_lowering = __commonJS({
4218
5305
  }
4219
5306
  };
4220
5307
  exports2.Lowering = Lowering;
4221
- var LoweringBuilder = class {
5308
+ var LoweringBuilder = class _LoweringBuilder {
4222
5309
  constructor() {
4223
- this.tempCount = 0;
4224
5310
  this.labelCount = 0;
4225
5311
  this.blocks = [];
4226
5312
  this.currentBlock = null;
4227
5313
  this.locals = /* @__PURE__ */ new Set();
4228
5314
  }
5315
+ /** Reset the global temp counter (call between compilations). */
5316
+ static resetTempCounter() {
5317
+ _LoweringBuilder.globalTempId = 0;
5318
+ }
4229
5319
  freshTemp() {
4230
- const name = `$t${this.tempCount++}`;
5320
+ const name = `$_${_LoweringBuilder.globalTempId++}`;
4231
5321
  this.locals.add(name);
4232
5322
  return name;
4233
5323
  }
@@ -4292,6 +5382,7 @@ var require_lowering = __commonJS({
4292
5382
  };
4293
5383
  }
4294
5384
  };
5385
+ LoweringBuilder.globalTempId = 0;
4295
5386
  }
4296
5387
  });
4297
5388
 
@@ -4859,6 +5950,7 @@ var require_mcfunction = __commonJS({
4859
5950
  exports2.generateDatapackWithStats = generateDatapackWithStats;
4860
5951
  exports2.generateDatapack = generateDatapack;
4861
5952
  var commands_1 = require_commands();
5953
+ var types_1 = require_types();
4862
5954
  var OBJ = "rs";
4863
5955
  function varRef(name) {
4864
5956
  return name.startsWith("$") ? name : `$${name}`;
@@ -5050,6 +6142,8 @@ var require_mcfunction = __commonJS({
5050
6142
  const ns = module3.namespace;
5051
6143
  const triggerHandlers = module3.functions.filter((fn) => fn.isTriggerHandler && fn.triggerName);
5052
6144
  const triggerNames = new Set(triggerHandlers.map((fn) => fn.triggerName));
6145
+ const eventHandlers = module3.functions.filter((fn) => !!fn.eventHandler && (0, types_1.isEventTypeName)(fn.eventHandler.eventType));
6146
+ const eventTypes = new Set(eventHandlers.map((fn) => fn.eventHandler.eventType));
5053
6147
  const tickFunctionNames = [];
5054
6148
  for (const fn of module3.functions) {
5055
6149
  if (fn.isTickLoop) {
@@ -5067,12 +6161,24 @@ var require_mcfunction = __commonJS({
5067
6161
  `scoreboard objectives add ${OBJ} dummy`
5068
6162
  ];
5069
6163
  for (const g of module3.globals) {
5070
- loadLines.push(`scoreboard players set ${varRef(g)} ${OBJ} 0`);
6164
+ loadLines.push(`scoreboard players set ${varRef(g.name)} ${OBJ} ${g.init}`);
5071
6165
  }
5072
6166
  for (const triggerName of triggerNames) {
5073
6167
  loadLines.push(`scoreboard objectives add ${triggerName} trigger`);
5074
6168
  loadLines.push(`scoreboard players enable @a ${triggerName}`);
5075
6169
  }
6170
+ for (const eventType of eventTypes) {
6171
+ const detection = types_1.EVENT_TYPES[eventType].detection;
6172
+ if (eventType === "PlayerDeath") {
6173
+ loadLines.push("scoreboard objectives add rs.deaths deathCount");
6174
+ } else if (eventType === "EntityKill") {
6175
+ loadLines.push("scoreboard objectives add rs.kills totalKillCount");
6176
+ } else if (eventType === "ItemUse") {
6177
+ loadLines.push("# ItemUse detection requires a project-specific objective/tag setup");
6178
+ } else if (detection === "tag" || detection === "advancement") {
6179
+ loadLines.push(`# ${eventType} detection expects tag ${types_1.EVENT_TYPES[eventType].tag} to be set externally`);
6180
+ }
6181
+ }
5076
6182
  for (const triggerName of triggerNames) {
5077
6183
  const handlers = triggerHandlers.filter((fn) => fn.triggerName === triggerName);
5078
6184
  const dispatchLines = [
@@ -5109,6 +6215,11 @@ var require_mcfunction = __commonJS({
5109
6215
  files.push({ path: filePath, content: lines.join("\n") });
5110
6216
  }
5111
6217
  }
6218
+ for (const fn of module3.functions) {
6219
+ if (fn.isLoadInit) {
6220
+ loadLines.push(`function ${ns}:${fn.name}`);
6221
+ }
6222
+ }
5112
6223
  files.push({
5113
6224
  path: `data/${ns}/function/__load.mcfunction`,
5114
6225
  content: loadLines.join("\n")
@@ -5127,7 +6238,18 @@ var require_mcfunction = __commonJS({
5127
6238
  tickLines.push(`execute as @a[scores={${triggerName}=1..}] run function ${ns}:__trigger_${triggerName}_dispatch`);
5128
6239
  }
5129
6240
  }
5130
- if (tickFunctionNames.length > 0 || triggerNames.size > 0) {
6241
+ if (eventHandlers.length > 0) {
6242
+ tickLines.push("# Event checks");
6243
+ for (const eventType of eventTypes) {
6244
+ const tag = types_1.EVENT_TYPES[eventType].tag;
6245
+ const handlers = eventHandlers.filter((fn) => fn.eventHandler?.eventType === eventType);
6246
+ for (const handler of handlers) {
6247
+ tickLines.push(`execute as @a[tag=${tag}] run function ${ns}:${handler.name}`);
6248
+ }
6249
+ tickLines.push(`tag @a[tag=${tag}] remove ${tag}`);
6250
+ }
6251
+ }
6252
+ if (tickFunctionNames.length > 0 || triggerNames.size > 0 || eventHandlers.length > 0) {
5131
6253
  files.push({
5132
6254
  path: `data/${ns}/function/__tick.mcfunction`,
5133
6255
  content: tickLines.join("\n")
@@ -5247,6 +6369,7 @@ var require_compile = __commonJS({
5247
6369
  };
5248
6370
  })();
5249
6371
  Object.defineProperty(exports2, "__esModule", { value: true });
6372
+ exports2.preprocessSourceWithMetadata = preprocessSourceWithMetadata;
5250
6373
  exports2.preprocessSource = preprocessSource;
5251
6374
  exports2.compile = compile;
5252
6375
  exports2.formatCompileError = formatCompileError;
@@ -5259,7 +6382,17 @@ var require_compile = __commonJS({
5259
6382
  var mcfunction_1 = require_mcfunction();
5260
6383
  var diagnostics_1 = require_diagnostics();
5261
6384
  var IMPORT_RE = /^\s*import\s+"([^"]+)"\s*;?\s*$/;
5262
- function preprocessSource(source, options = {}) {
6385
+ function countLines(source) {
6386
+ return source === "" ? 0 : source.split("\n").length;
6387
+ }
6388
+ function offsetRanges(ranges, lineOffset) {
6389
+ return ranges.map((range) => ({
6390
+ startLine: range.startLine + lineOffset,
6391
+ endLine: range.endLine + lineOffset,
6392
+ filePath: range.filePath
6393
+ }));
6394
+ }
6395
+ function preprocessSourceWithMetadata(source, options = {}) {
5263
6396
  const { filePath } = options;
5264
6397
  const seen = options.seen ?? /* @__PURE__ */ new Set();
5265
6398
  if (filePath) {
@@ -5286,7 +6419,7 @@ var require_compile = __commonJS({
5286
6419
  } catch {
5287
6420
  throw new diagnostics_1.DiagnosticError("ParseError", `Cannot import '${match[1]}'`, { file: filePath, line: i + 1, col: 1 }, lines);
5288
6421
  }
5289
- imports.push(preprocessSource(importedSource, { filePath: importPath, seen }));
6422
+ imports.push(preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen }));
5290
6423
  }
5291
6424
  continue;
5292
6425
  }
@@ -5297,17 +6430,37 @@ var require_compile = __commonJS({
5297
6430
  parsingHeader = false;
5298
6431
  bodyLines.push(line);
5299
6432
  }
5300
- return [...imports, bodyLines.join("\n")].filter(Boolean).join("\n");
6433
+ const body = bodyLines.join("\n");
6434
+ const parts = [...imports.map((entry) => entry.source), body].filter(Boolean);
6435
+ const combined = parts.join("\n");
6436
+ const ranges = [];
6437
+ let lineOffset = 0;
6438
+ for (const entry of imports) {
6439
+ ranges.push(...offsetRanges(entry.ranges, lineOffset));
6440
+ lineOffset += countLines(entry.source);
6441
+ }
6442
+ if (filePath && body) {
6443
+ ranges.push({
6444
+ startLine: lineOffset + 1,
6445
+ endLine: lineOffset + countLines(body),
6446
+ filePath: path.resolve(filePath)
6447
+ });
6448
+ }
6449
+ return { source: combined, ranges };
6450
+ }
6451
+ function preprocessSource(source, options = {}) {
6452
+ return preprocessSourceWithMetadata(source, options).source;
5301
6453
  }
5302
6454
  function compile(source, options = {}) {
5303
6455
  const { namespace = "redscript", filePath, optimize: shouldOptimize = true } = options;
5304
6456
  let sourceLines = source.split("\n");
5305
6457
  try {
5306
- const preprocessedSource = preprocessSource(source, { filePath });
6458
+ const preprocessed = preprocessSourceWithMetadata(source, { filePath });
6459
+ const preprocessedSource = preprocessed.source;
5307
6460
  sourceLines = preprocessedSource.split("\n");
5308
6461
  const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
5309
6462
  const ast = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
5310
- const ir = new lowering_1.Lowering(namespace).lower(ast);
6463
+ const ir = new lowering_1.Lowering(namespace, preprocessed.ranges).lower(ast);
5311
6464
  const optimized = shouldOptimize ? { ...ir, functions: ir.functions.map((fn) => (0, passes_1.optimize)(fn)) } : ir;
5312
6465
  const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized);
5313
6466
  return {
@@ -5691,7 +6844,8 @@ var require_dist = __commonJS({
5691
6844
  const shouldOptimize = options.optimize ?? true;
5692
6845
  const shouldTypeCheck = options.typeCheck ?? true;
5693
6846
  const filePath = options.filePath;
5694
- const preprocessedSource = (0, compile_1.preprocessSource)(source, { filePath });
6847
+ const preprocessed = (0, compile_1.preprocessSourceWithMetadata)(source, { filePath });
6848
+ const preprocessedSource = preprocessed.source;
5695
6849
  const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
5696
6850
  const ast = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
5697
6851
  let typeErrors;
@@ -5699,7 +6853,7 @@ var require_dist = __commonJS({
5699
6853
  const checker = new typechecker_1.TypeChecker(preprocessedSource, filePath);
5700
6854
  typeErrors = checker.check(ast);
5701
6855
  }
5702
- const lowering = new lowering_1.Lowering(namespace);
6856
+ const lowering = new lowering_1.Lowering(namespace, preprocessed.ranges);
5703
6857
  const ir = lowering.lower(ast);
5704
6858
  let optimizedIR = ir;
5705
6859
  let generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: shouldOptimize });
@@ -6495,16 +7649,52 @@ function findFnDeclLine(document, name) {
6495
7649
  }
6496
7650
  function findFnSignature(document, name) {
6497
7651
  const text = document.getText();
6498
- const re = new RegExp(`\\bfn\\s+${escapeRe(name)}\\s*\\(([^)]*)\\)(?:\\s*->\\s*([A-Za-z_][A-Za-z0-9_\\[\\]]*))?`, "m");
7652
+ const re = new RegExp(`\\bfn\\s+${escapeRe(name)}\\s*\\(([^)]*)\\)(?:\\s*->\\s*([A-Za-z_][A-Za-z0-9_\\[\\]]*))?\\s*\\{`, "m");
6499
7653
  const match = re.exec(text);
6500
7654
  if (!match) return null;
6501
7655
  const params = match[1].trim();
6502
- const returnType = match[2];
7656
+ let returnType = match[2];
7657
+ if (!returnType) {
7658
+ returnType = inferReturnType(text, match.index + match[0].length);
7659
+ }
6503
7660
  if (returnType) {
6504
7661
  return `fn ${name}(${params}) -> ${returnType}`;
6505
7662
  }
6506
7663
  return `fn ${name}(${params})`;
6507
7664
  }
7665
+ function inferReturnType(text, bodyStart) {
7666
+ let braceCount = 1;
7667
+ let pos = bodyStart;
7668
+ while (pos < text.length && braceCount > 0) {
7669
+ if (text[pos] === "{") braceCount++;
7670
+ else if (text[pos] === "}") braceCount--;
7671
+ pos++;
7672
+ }
7673
+ const body = text.slice(bodyStart, pos - 1);
7674
+ const returnMatch = body.match(/\breturn\s+(.+?);/);
7675
+ if (!returnMatch) return null;
7676
+ const returnExpr = returnMatch[1].trim();
7677
+ if (/^\d+$/.test(returnExpr)) return "int";
7678
+ if (/^\d+\.\d+$/.test(returnExpr)) return "float";
7679
+ if (/^\d+[bB]$/.test(returnExpr)) return "byte";
7680
+ if (/^\d+[sS]$/.test(returnExpr)) return "short";
7681
+ if (/^\d+[lL]$/.test(returnExpr)) return "long";
7682
+ if (/^\d+(\.\d+)?[dD]$/.test(returnExpr)) return "double";
7683
+ if (/^".*"$/.test(returnExpr)) return "string";
7684
+ if (/^(true|false)$/.test(returnExpr)) return "bool";
7685
+ if (/^@[aeprs]/.test(returnExpr)) return "selector";
7686
+ if (/^\{/.test(returnExpr)) return "struct";
7687
+ if (/^\[/.test(returnExpr)) return "array";
7688
+ const callMatch = returnExpr.match(/^(\w+)\s*\(/);
7689
+ if (callMatch) {
7690
+ const fnName = callMatch[1];
7691
+ if (["scoreboard_get", "score", "random", "random_native", "str_len", "len", "data_get", "bossbar_get_value", "set_contains"].includes(fnName)) {
7692
+ return "int";
7693
+ }
7694
+ if (fnName === "set_new") return "string";
7695
+ }
7696
+ return null;
7697
+ }
6508
7698
  function escapeRe(s) {
6509
7699
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6510
7700
  }
@@ -6978,6 +8168,23 @@ function isStructLiteralField(doc, position, word) {
6978
8168
  if (fnMatch) return fnMatch[1];
6979
8169
  return null;
6980
8170
  }
8171
+ function isMemberAccessField(doc, position, word) {
8172
+ const line = doc.lineAt(position.line).text;
8173
+ const wordStart = position.character;
8174
+ const beforeWord = line.slice(0, wordStart);
8175
+ if (!beforeWord.endsWith(".")) return null;
8176
+ const varMatch = beforeWord.match(/(\w+)\s*\.$/);
8177
+ if (!varMatch) return null;
8178
+ const varName = varMatch[1];
8179
+ const text = doc.getText();
8180
+ const typeRe = new RegExp(`\\b(?:let|const)\\s+${varName}\\s*:\\s*(\\w+)`, "m");
8181
+ const typeMatch = text.match(typeRe);
8182
+ if (typeMatch) return typeMatch[1];
8183
+ const paramRe = new RegExp(`\\((?:[^)]*,\\s*)?${varName}\\s*:\\s*(\\w+)`, "m");
8184
+ const paramMatch = text.match(paramRe);
8185
+ if (paramMatch) return paramMatch[1];
8186
+ return null;
8187
+ }
6981
8188
  function findAllOccurrences(doc, word) {
6982
8189
  const text = doc.getText();
6983
8190
  const re = new RegExp(`\\b${escapeRegex(word)}\\b`, "g");
@@ -7010,6 +8217,14 @@ function registerSymbolProviders(context) {
7010
8217
  return new vscode4.Location(doc.uri, field.fieldRange);
7011
8218
  }
7012
8219
  }
8220
+ const memberAccess = isMemberAccessField(doc, position, word);
8221
+ if (memberAccess) {
8222
+ const structFields = findStructFields(doc);
8223
+ const field = structFields.find((f) => f.structName === memberAccess && f.fieldName === word);
8224
+ if (field) {
8225
+ return new vscode4.Location(doc.uri, field.fieldRange);
8226
+ }
8227
+ }
7013
8228
  const decls = findDeclarations(doc);
7014
8229
  const decl = decls.find((d) => d.name === word);
7015
8230
  if (!decl) return null;