redscript-mc 1.2.29 → 2.0.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 (274) hide show
  1. package/.claude/commands/build-test.md +10 -0
  2. package/.claude/commands/deploy-demo.md +12 -0
  3. package/.claude/commands/stage-status.md +13 -0
  4. package/.claude/settings.json +12 -0
  5. package/.github/workflows/ci.yml +1 -0
  6. package/CLAUDE.md +231 -0
  7. package/README.md +29 -28
  8. package/README.zh.md +28 -28
  9. package/demo.gif +0 -0
  10. package/dist/cli.js +2 -554
  11. package/dist/compile.js +2 -266
  12. package/dist/index.js +2 -159
  13. package/dist/lexer/index.js +9 -1
  14. package/dist/lowering/index.js +22 -5
  15. package/dist/src/__tests__/cli.test.d.ts +1 -0
  16. package/dist/src/__tests__/cli.test.js +104 -0
  17. package/dist/src/__tests__/codegen.test.d.ts +1 -0
  18. package/dist/src/__tests__/codegen.test.js +152 -0
  19. package/dist/src/__tests__/compile-all.test.d.ts +10 -0
  20. package/dist/src/__tests__/compile-all.test.js +108 -0
  21. package/dist/src/__tests__/dce.test.d.ts +1 -0
  22. package/dist/src/__tests__/dce.test.js +102 -0
  23. package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
  24. package/dist/src/__tests__/diagnostics.test.js +177 -0
  25. package/dist/src/__tests__/e2e.test.d.ts +6 -0
  26. package/dist/src/__tests__/e2e.test.js +1789 -0
  27. package/dist/src/__tests__/entity-types.test.d.ts +1 -0
  28. package/dist/src/__tests__/entity-types.test.js +203 -0
  29. package/dist/src/__tests__/formatter.test.d.ts +1 -0
  30. package/dist/src/__tests__/formatter.test.js +40 -0
  31. package/dist/src/__tests__/lexer.test.d.ts +1 -0
  32. package/dist/src/__tests__/lexer.test.js +343 -0
  33. package/dist/src/__tests__/lowering.test.d.ts +1 -0
  34. package/dist/src/__tests__/lowering.test.js +1015 -0
  35. package/dist/src/__tests__/macro.test.d.ts +8 -0
  36. package/dist/src/__tests__/macro.test.js +306 -0
  37. package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
  38. package/dist/src/__tests__/mc-integration.test.js +817 -0
  39. package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
  40. package/dist/src/__tests__/mc-syntax.test.js +124 -0
  41. package/dist/src/__tests__/nbt.test.d.ts +1 -0
  42. package/dist/src/__tests__/nbt.test.js +82 -0
  43. package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
  44. package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
  45. package/dist/src/__tests__/optimizer.test.d.ts +1 -0
  46. package/dist/src/__tests__/optimizer.test.js +149 -0
  47. package/dist/src/__tests__/parser.test.d.ts +1 -0
  48. package/dist/src/__tests__/parser.test.js +807 -0
  49. package/dist/src/__tests__/repl.test.d.ts +1 -0
  50. package/dist/src/__tests__/repl.test.js +27 -0
  51. package/dist/src/__tests__/runtime.test.d.ts +1 -0
  52. package/dist/src/__tests__/runtime.test.js +289 -0
  53. package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
  54. package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
  55. package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
  56. package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
  57. package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
  58. package/dist/src/__tests__/stdlib-math.test.js +351 -0
  59. package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
  60. package/dist/src/__tests__/stdlib-vec.test.js +263 -0
  61. package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
  62. package/dist/src/__tests__/structure-optimizer.test.js +33 -0
  63. package/dist/src/__tests__/typechecker.test.d.ts +1 -0
  64. package/dist/src/__tests__/typechecker.test.js +552 -0
  65. package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
  66. package/dist/src/__tests__/var-allocator.test.js +69 -0
  67. package/dist/src/ast/types.d.ts +515 -0
  68. package/dist/src/ast/types.js +9 -0
  69. package/dist/src/builtins/metadata.d.ts +36 -0
  70. package/dist/src/builtins/metadata.js +1014 -0
  71. package/dist/src/cli.d.ts +11 -0
  72. package/dist/src/cli.js +443 -0
  73. package/dist/src/codegen/cmdblock/index.d.ts +26 -0
  74. package/dist/src/codegen/cmdblock/index.js +45 -0
  75. package/dist/src/codegen/mcfunction/index.d.ts +40 -0
  76. package/dist/src/codegen/mcfunction/index.js +606 -0
  77. package/dist/src/codegen/structure/index.d.ts +24 -0
  78. package/dist/src/codegen/structure/index.js +279 -0
  79. package/dist/src/codegen/var-allocator.d.ts +45 -0
  80. package/dist/src/codegen/var-allocator.js +104 -0
  81. package/dist/src/compile.d.ts +37 -0
  82. package/dist/src/compile.js +165 -0
  83. package/dist/src/diagnostics/index.d.ts +44 -0
  84. package/dist/src/diagnostics/index.js +140 -0
  85. package/dist/src/events/types.d.ts +35 -0
  86. package/dist/src/events/types.js +59 -0
  87. package/dist/src/formatter/index.d.ts +1 -0
  88. package/dist/src/formatter/index.js +26 -0
  89. package/dist/src/index.d.ts +22 -0
  90. package/dist/src/index.js +45 -0
  91. package/dist/src/ir/builder.d.ts +33 -0
  92. package/dist/src/ir/builder.js +99 -0
  93. package/dist/src/ir/types.d.ts +132 -0
  94. package/dist/src/ir/types.js +15 -0
  95. package/dist/src/lexer/index.d.ts +37 -0
  96. package/dist/src/lexer/index.js +569 -0
  97. package/dist/src/lowering/index.d.ts +188 -0
  98. package/dist/src/lowering/index.js +3405 -0
  99. package/dist/src/mc-test/client.d.ts +128 -0
  100. package/dist/src/mc-test/client.js +174 -0
  101. package/dist/src/mc-test/runner.d.ts +28 -0
  102. package/dist/src/mc-test/runner.js +151 -0
  103. package/dist/src/mc-test/setup.d.ts +11 -0
  104. package/dist/src/mc-test/setup.js +98 -0
  105. package/dist/src/mc-validator/index.d.ts +17 -0
  106. package/dist/src/mc-validator/index.js +322 -0
  107. package/dist/src/nbt/index.d.ts +86 -0
  108. package/dist/src/nbt/index.js +250 -0
  109. package/dist/src/optimizer/commands.d.ts +38 -0
  110. package/dist/src/optimizer/commands.js +451 -0
  111. package/dist/src/optimizer/dce.d.ts +34 -0
  112. package/dist/src/optimizer/dce.js +639 -0
  113. package/dist/src/optimizer/passes.d.ts +34 -0
  114. package/dist/src/optimizer/passes.js +243 -0
  115. package/dist/src/optimizer/structure.d.ts +9 -0
  116. package/dist/src/optimizer/structure.js +356 -0
  117. package/dist/src/parser/index.d.ts +93 -0
  118. package/dist/src/parser/index.js +1687 -0
  119. package/dist/src/repl.d.ts +16 -0
  120. package/dist/src/repl.js +165 -0
  121. package/dist/src/runtime/index.d.ts +107 -0
  122. package/dist/src/runtime/index.js +1409 -0
  123. package/dist/src/typechecker/index.d.ts +61 -0
  124. package/dist/src/typechecker/index.js +1034 -0
  125. package/dist/src/types/entity-hierarchy.d.ts +29 -0
  126. package/dist/src/types/entity-hierarchy.js +107 -0
  127. package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
  128. package/dist/src2/__tests__/e2e/basic.test.js +140 -0
  129. package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
  130. package/dist/src2/__tests__/e2e/macros.test.js +182 -0
  131. package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
  132. package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
  133. package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
  134. package/dist/src2/__tests__/hir/desugar.test.js +234 -0
  135. package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
  136. package/dist/src2/__tests__/lir/lower.test.js +559 -0
  137. package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
  138. package/dist/src2/__tests__/lir/types.test.js +185 -0
  139. package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
  140. package/dist/src2/__tests__/lir/verify.test.js +221 -0
  141. package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
  142. package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
  143. package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
  144. package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
  145. package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
  146. package/dist/src2/__tests__/mir/verify.test.js +223 -0
  147. package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
  148. package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
  149. package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
  150. package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
  151. package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
  152. package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
  153. package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
  154. package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
  155. package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
  156. package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
  157. package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
  158. package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
  159. package/dist/src2/emit/compile.d.ts +19 -0
  160. package/dist/src2/emit/compile.js +80 -0
  161. package/dist/src2/emit/index.d.ts +17 -0
  162. package/dist/src2/emit/index.js +172 -0
  163. package/dist/src2/hir/lower.d.ts +15 -0
  164. package/dist/src2/hir/lower.js +378 -0
  165. package/dist/src2/hir/types.d.ts +373 -0
  166. package/dist/src2/hir/types.js +16 -0
  167. package/dist/src2/lir/lower.d.ts +15 -0
  168. package/dist/src2/lir/lower.js +453 -0
  169. package/dist/src2/lir/types.d.ts +136 -0
  170. package/dist/src2/lir/types.js +11 -0
  171. package/dist/src2/lir/verify.d.ts +14 -0
  172. package/dist/src2/lir/verify.js +113 -0
  173. package/dist/src2/mir/lower.d.ts +9 -0
  174. package/dist/src2/mir/lower.js +1030 -0
  175. package/dist/src2/mir/macro.d.ts +22 -0
  176. package/dist/src2/mir/macro.js +168 -0
  177. package/dist/src2/mir/types.d.ts +183 -0
  178. package/dist/src2/mir/types.js +11 -0
  179. package/dist/src2/mir/verify.d.ts +16 -0
  180. package/dist/src2/mir/verify.js +216 -0
  181. package/dist/src2/optimizer/block_merge.d.ts +12 -0
  182. package/dist/src2/optimizer/block_merge.js +84 -0
  183. package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
  184. package/dist/src2/optimizer/branch_simplify.js +28 -0
  185. package/dist/src2/optimizer/constant_fold.d.ts +10 -0
  186. package/dist/src2/optimizer/constant_fold.js +85 -0
  187. package/dist/src2/optimizer/copy_prop.d.ts +9 -0
  188. package/dist/src2/optimizer/copy_prop.js +113 -0
  189. package/dist/src2/optimizer/dce.d.ts +8 -0
  190. package/dist/src2/optimizer/dce.js +155 -0
  191. package/dist/src2/optimizer/pipeline.d.ts +10 -0
  192. package/dist/src2/optimizer/pipeline.js +42 -0
  193. package/dist/tsconfig.tsbuildinfo +1 -0
  194. package/docs/compiler-pipeline-redesign.md +2243 -0
  195. package/docs/optimization-ideas.md +1076 -0
  196. package/editors/vscode/package-lock.json +3 -3
  197. package/editors/vscode/package.json +1 -1
  198. package/examples/readme-demo.mcrs +44 -66
  199. package/jest.config.js +1 -1
  200. package/package.json +6 -5
  201. package/scripts/postbuild.js +15 -0
  202. package/src/__tests__/cli.test.ts +8 -220
  203. package/src/__tests__/dce.test.ts +11 -56
  204. package/src/__tests__/diagnostics.test.ts +59 -38
  205. package/src/__tests__/mc-integration.test.ts +1 -2
  206. package/src/ast/types.ts +6 -1
  207. package/src/cli.ts +29 -156
  208. package/src/compile.ts +6 -162
  209. package/src/index.ts +14 -178
  210. package/src/lexer/index.ts +9 -1
  211. package/src/mc-test/runner.ts +4 -3
  212. package/src/parser/index.ts +1 -1
  213. package/src/repl.ts +1 -1
  214. package/src/runtime/index.ts +1 -1
  215. package/src2/__tests__/e2e/basic.test.ts +154 -0
  216. package/src2/__tests__/e2e/macros.test.ts +199 -0
  217. package/src2/__tests__/e2e/migrate.test.ts +3008 -0
  218. package/src2/__tests__/hir/desugar.test.ts +263 -0
  219. package/src2/__tests__/lir/lower.test.ts +619 -0
  220. package/src2/__tests__/lir/types.test.ts +207 -0
  221. package/src2/__tests__/lir/verify.test.ts +249 -0
  222. package/src2/__tests__/mir/arithmetic.test.ts +156 -0
  223. package/src2/__tests__/mir/control-flow.test.ts +242 -0
  224. package/src2/__tests__/mir/verify.test.ts +254 -0
  225. package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
  226. package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
  227. package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
  228. package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
  229. package/src2/__tests__/optimizer/dce.test.ts +83 -0
  230. package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
  231. package/src2/emit/compile.ts +99 -0
  232. package/src2/emit/index.ts +222 -0
  233. package/src2/hir/lower.ts +428 -0
  234. package/src2/hir/types.ts +216 -0
  235. package/src2/lir/lower.ts +556 -0
  236. package/src2/lir/types.ts +109 -0
  237. package/src2/lir/verify.ts +129 -0
  238. package/src2/mir/lower.ts +1160 -0
  239. package/src2/mir/macro.ts +167 -0
  240. package/src2/mir/types.ts +106 -0
  241. package/src2/mir/verify.ts +218 -0
  242. package/src2/optimizer/block_merge.ts +93 -0
  243. package/src2/optimizer/branch_simplify.ts +27 -0
  244. package/src2/optimizer/constant_fold.ts +88 -0
  245. package/src2/optimizer/copy_prop.ts +106 -0
  246. package/src2/optimizer/dce.ts +133 -0
  247. package/src2/optimizer/pipeline.ts +44 -0
  248. package/tsconfig.json +2 -2
  249. package/src/__tests__/codegen.test.ts +0 -161
  250. package/src/__tests__/e2e.test.ts +0 -2039
  251. package/src/__tests__/entity-types.test.ts +0 -236
  252. package/src/__tests__/lowering.test.ts +0 -1185
  253. package/src/__tests__/macro.test.ts +0 -343
  254. package/src/__tests__/nbt.test.ts +0 -58
  255. package/src/__tests__/optimizer-advanced.test.ts +0 -144
  256. package/src/__tests__/optimizer.test.ts +0 -162
  257. package/src/__tests__/runtime.test.ts +0 -305
  258. package/src/__tests__/stdlib-advanced.test.ts +0 -379
  259. package/src/__tests__/stdlib-bigint.test.ts +0 -427
  260. package/src/__tests__/stdlib-math.test.ts +0 -374
  261. package/src/__tests__/stdlib-vec.test.ts +0 -259
  262. package/src/__tests__/structure-optimizer.test.ts +0 -38
  263. package/src/__tests__/var-allocator.test.ts +0 -75
  264. package/src/codegen/cmdblock/index.ts +0 -63
  265. package/src/codegen/mcfunction/index.ts +0 -662
  266. package/src/codegen/structure/index.ts +0 -346
  267. package/src/codegen/var-allocator.ts +0 -104
  268. package/src/ir/builder.ts +0 -116
  269. package/src/ir/types.ts +0 -134
  270. package/src/lowering/index.ts +0 -3860
  271. package/src/optimizer/commands.ts +0 -534
  272. package/src/optimizer/dce.ts +0 -679
  273. package/src/optimizer/passes.ts +0 -250
  274. package/src/optimizer/structure.ts +0 -450
@@ -0,0 +1,1034 @@
1
+ "use strict";
2
+ /**
3
+ * RedScript Type Checker
4
+ *
5
+ * Performs basic type checking between Parser and Lowering phases.
6
+ * Collects errors but doesn't block compilation (warn mode).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.TypeChecker = void 0;
10
+ const diagnostics_1 = require("../diagnostics");
11
+ const types_1 = require("../events/types");
12
+ // Entity type hierarchy for subtype checking
13
+ const ENTITY_HIERARCHY = {
14
+ 'entity': null,
15
+ 'Player': 'entity',
16
+ 'Mob': 'entity',
17
+ 'HostileMob': 'Mob',
18
+ 'PassiveMob': 'Mob',
19
+ 'Zombie': 'HostileMob',
20
+ 'Skeleton': 'HostileMob',
21
+ 'Creeper': 'HostileMob',
22
+ 'Spider': 'HostileMob',
23
+ 'Enderman': 'HostileMob',
24
+ 'Blaze': 'HostileMob',
25
+ 'Witch': 'HostileMob',
26
+ 'Slime': 'HostileMob',
27
+ 'ZombieVillager': 'HostileMob',
28
+ 'Husk': 'HostileMob',
29
+ 'Drowned': 'HostileMob',
30
+ 'Stray': 'HostileMob',
31
+ 'WitherSkeleton': 'HostileMob',
32
+ 'CaveSpider': 'HostileMob',
33
+ 'Pig': 'PassiveMob',
34
+ 'Cow': 'PassiveMob',
35
+ 'Sheep': 'PassiveMob',
36
+ 'Chicken': 'PassiveMob',
37
+ 'Villager': 'PassiveMob',
38
+ 'WanderingTrader': 'PassiveMob',
39
+ 'ArmorStand': 'entity',
40
+ 'Item': 'entity',
41
+ 'Arrow': 'entity',
42
+ };
43
+ // Map Minecraft type names to entity types
44
+ const MC_TYPE_TO_ENTITY = {
45
+ 'zombie': 'Zombie',
46
+ 'minecraft:zombie': 'Zombie',
47
+ 'skeleton': 'Skeleton',
48
+ 'minecraft:skeleton': 'Skeleton',
49
+ 'creeper': 'Creeper',
50
+ 'minecraft:creeper': 'Creeper',
51
+ 'spider': 'Spider',
52
+ 'minecraft:spider': 'Spider',
53
+ 'enderman': 'Enderman',
54
+ 'minecraft:enderman': 'Enderman',
55
+ 'blaze': 'Blaze',
56
+ 'minecraft:blaze': 'Blaze',
57
+ 'witch': 'Witch',
58
+ 'minecraft:witch': 'Witch',
59
+ 'slime': 'Slime',
60
+ 'minecraft:slime': 'Slime',
61
+ 'zombie_villager': 'ZombieVillager',
62
+ 'minecraft:zombie_villager': 'ZombieVillager',
63
+ 'husk': 'Husk',
64
+ 'minecraft:husk': 'Husk',
65
+ 'drowned': 'Drowned',
66
+ 'minecraft:drowned': 'Drowned',
67
+ 'stray': 'Stray',
68
+ 'minecraft:stray': 'Stray',
69
+ 'wither_skeleton': 'WitherSkeleton',
70
+ 'minecraft:wither_skeleton': 'WitherSkeleton',
71
+ 'cave_spider': 'CaveSpider',
72
+ 'minecraft:cave_spider': 'CaveSpider',
73
+ 'pig': 'Pig',
74
+ 'minecraft:pig': 'Pig',
75
+ 'cow': 'Cow',
76
+ 'minecraft:cow': 'Cow',
77
+ 'sheep': 'Sheep',
78
+ 'minecraft:sheep': 'Sheep',
79
+ 'chicken': 'Chicken',
80
+ 'minecraft:chicken': 'Chicken',
81
+ 'villager': 'Villager',
82
+ 'minecraft:villager': 'Villager',
83
+ 'wandering_trader': 'WanderingTrader',
84
+ 'minecraft:wandering_trader': 'WanderingTrader',
85
+ 'armor_stand': 'ArmorStand',
86
+ 'minecraft:armor_stand': 'ArmorStand',
87
+ 'item': 'Item',
88
+ 'minecraft:item': 'Item',
89
+ 'arrow': 'Arrow',
90
+ 'minecraft:arrow': 'Arrow',
91
+ };
92
+ const VOID_TYPE = { kind: 'named', name: 'void' };
93
+ const INT_TYPE = { kind: 'named', name: 'int' };
94
+ const STRING_TYPE = { kind: 'named', name: 'string' };
95
+ const FORMAT_STRING_TYPE = { kind: 'named', name: 'format_string' };
96
+ const BUILTIN_SIGNATURES = {
97
+ setTimeout: {
98
+ params: [INT_TYPE, { kind: 'function_type', params: [], return: VOID_TYPE }],
99
+ return: VOID_TYPE,
100
+ },
101
+ setInterval: {
102
+ params: [INT_TYPE, { kind: 'function_type', params: [], return: VOID_TYPE }],
103
+ return: INT_TYPE,
104
+ },
105
+ clearInterval: {
106
+ params: [INT_TYPE],
107
+ return: VOID_TYPE,
108
+ },
109
+ };
110
+ // ---------------------------------------------------------------------------
111
+ // Type Checker
112
+ // ---------------------------------------------------------------------------
113
+ class TypeChecker {
114
+ constructor(source, filePath) {
115
+ this.functions = new Map();
116
+ this.implMethods = new Map();
117
+ this.structs = new Map();
118
+ this.enums = new Map();
119
+ this.consts = new Map();
120
+ this.currentFn = null;
121
+ this.currentReturnType = null;
122
+ this.scope = new Map();
123
+ // Stack for tracking @s type in different contexts
124
+ this.selfTypeStack = ['entity'];
125
+ this.richTextBuiltins = new Map([
126
+ ['say', { messageIndex: 0 }],
127
+ ['announce', { messageIndex: 0 }],
128
+ ['tell', { messageIndex: 1 }],
129
+ ['tellraw', { messageIndex: 1 }],
130
+ ['title', { messageIndex: 1 }],
131
+ ['actionbar', { messageIndex: 1 }],
132
+ ['subtitle', { messageIndex: 1 }],
133
+ ]);
134
+ this.collector = new diagnostics_1.DiagnosticCollector(source, filePath);
135
+ }
136
+ getNodeLocation(node) {
137
+ const span = node?.span;
138
+ return {
139
+ line: span?.line ?? 1,
140
+ col: span?.col ?? 1,
141
+ };
142
+ }
143
+ report(message, node) {
144
+ const { line, col } = this.getNodeLocation(node);
145
+ this.collector.error('TypeError', message, line, col);
146
+ }
147
+ /**
148
+ * Type check a program. Returns collected errors.
149
+ */
150
+ check(program) {
151
+ // First pass: collect function and struct declarations
152
+ for (const fn of program.declarations) {
153
+ this.functions.set(fn.name, fn);
154
+ }
155
+ for (const implBlock of program.implBlocks ?? []) {
156
+ let methods = this.implMethods.get(implBlock.typeName);
157
+ if (!methods) {
158
+ methods = new Map();
159
+ this.implMethods.set(implBlock.typeName, methods);
160
+ }
161
+ for (const method of implBlock.methods) {
162
+ const selfIndex = method.params.findIndex(param => param.name === 'self');
163
+ if (selfIndex > 0) {
164
+ this.report(`Method '${method.name}' must declare 'self' as the first parameter`, method.params[selfIndex]);
165
+ }
166
+ if (selfIndex === 0) {
167
+ const selfType = this.normalizeType(method.params[0].type);
168
+ if (selfType.kind !== 'struct' || selfType.name !== implBlock.typeName) {
169
+ this.report(`Method '${method.name}' has invalid 'self' type`, method.params[0]);
170
+ }
171
+ }
172
+ methods.set(method.name, method);
173
+ }
174
+ }
175
+ for (const struct of program.structs ?? []) {
176
+ const fields = new Map();
177
+ for (const field of struct.fields) {
178
+ fields.set(field.name, field.type);
179
+ }
180
+ this.structs.set(struct.name, fields);
181
+ }
182
+ for (const enumDecl of program.enums ?? []) {
183
+ const variants = new Map();
184
+ for (const variant of enumDecl.variants) {
185
+ variants.set(variant.name, variant.value ?? 0);
186
+ }
187
+ this.enums.set(enumDecl.name, variants);
188
+ }
189
+ for (const constDecl of program.consts ?? []) {
190
+ const constType = this.normalizeType(constDecl.type);
191
+ const actualType = this.inferType(constDecl.value);
192
+ if (!this.typesMatch(constType, actualType)) {
193
+ this.report(`Type mismatch: expected ${this.typeToString(constType)}, got ${this.typeToString(actualType)}`, constDecl.value);
194
+ }
195
+ this.consts.set(constDecl.name, constType);
196
+ }
197
+ // Second pass: type check function bodies
198
+ for (const fn of program.declarations) {
199
+ this.checkFunction(fn);
200
+ }
201
+ for (const implBlock of program.implBlocks ?? []) {
202
+ for (const method of implBlock.methods) {
203
+ this.checkFunction(method);
204
+ }
205
+ }
206
+ return this.collector.getErrors();
207
+ }
208
+ checkFunction(fn) {
209
+ this.currentFn = fn;
210
+ this.currentReturnType = this.normalizeType(fn.returnType);
211
+ this.scope = new Map();
212
+ let seenDefault = false;
213
+ this.checkFunctionDecorators(fn);
214
+ for (const [name, type] of this.consts.entries()) {
215
+ this.scope.set(name, { type, mutable: false });
216
+ }
217
+ // Add parameters to scope
218
+ for (const param of fn.params) {
219
+ this.scope.set(param.name, { type: this.normalizeType(param.type), mutable: true });
220
+ if (param.default) {
221
+ seenDefault = true;
222
+ this.checkExpr(param.default);
223
+ const defaultType = this.inferType(param.default);
224
+ const paramType = this.normalizeType(param.type);
225
+ if (!this.typesMatch(paramType, defaultType)) {
226
+ this.report(`Default value for '${param.name}' must be ${this.typeToString(paramType)}, got ${this.typeToString(defaultType)}`, param.default);
227
+ }
228
+ }
229
+ else if (seenDefault) {
230
+ this.report(`Parameter '${param.name}' cannot follow a default parameter`, param);
231
+ }
232
+ }
233
+ // Check body
234
+ this.checkBlock(fn.body);
235
+ this.currentFn = null;
236
+ this.currentReturnType = null;
237
+ }
238
+ checkFunctionDecorators(fn) {
239
+ const eventDecorators = fn.decorators.filter(decorator => decorator.name === 'on');
240
+ if (eventDecorators.length === 0) {
241
+ return;
242
+ }
243
+ if (eventDecorators.length > 1) {
244
+ this.report(`Function '${fn.name}' cannot have multiple @on decorators`, fn);
245
+ return;
246
+ }
247
+ const eventType = eventDecorators[0].args?.eventType;
248
+ if (!eventType) {
249
+ this.report(`Function '${fn.name}' is missing an event type in @on(...)`, fn);
250
+ return;
251
+ }
252
+ if (!(0, types_1.isEventTypeName)(eventType)) {
253
+ this.report(`Unknown event type '${eventType}'`, fn);
254
+ return;
255
+ }
256
+ const expectedParams = (0, types_1.getEventParamSpecs)(eventType);
257
+ if (fn.params.length !== expectedParams.length) {
258
+ this.report(`Event handler '${fn.name}' for ${eventType} must declare ${expectedParams.length} parameter(s), got ${fn.params.length}`, fn);
259
+ return;
260
+ }
261
+ for (let i = 0; i < expectedParams.length; i++) {
262
+ const actual = this.normalizeType(fn.params[i].type);
263
+ const expected = this.normalizeType(expectedParams[i].type);
264
+ if (!this.typesMatch(expected, actual)) {
265
+ this.report(`Event handler '${fn.name}' parameter ${i + 1} must be ${this.typeToString(expected)}, got ${this.typeToString(actual)}`, fn.params[i]);
266
+ }
267
+ }
268
+ }
269
+ checkBlock(stmts) {
270
+ for (const stmt of stmts) {
271
+ this.checkStmt(stmt);
272
+ }
273
+ }
274
+ checkStmt(stmt) {
275
+ switch (stmt.kind) {
276
+ case 'let':
277
+ this.checkLetStmt(stmt);
278
+ break;
279
+ case 'return':
280
+ this.checkReturnStmt(stmt);
281
+ break;
282
+ case 'if':
283
+ this.checkExpr(stmt.cond);
284
+ this.checkIfBranches(stmt);
285
+ break;
286
+ case 'while':
287
+ this.checkExpr(stmt.cond);
288
+ this.checkBlock(stmt.body);
289
+ break;
290
+ case 'for':
291
+ if (stmt.init)
292
+ this.checkStmt(stmt.init);
293
+ this.checkExpr(stmt.cond);
294
+ this.checkExpr(stmt.step);
295
+ this.checkBlock(stmt.body);
296
+ break;
297
+ case 'foreach':
298
+ this.checkExpr(stmt.iterable);
299
+ if (stmt.iterable.kind === 'selector') {
300
+ // Infer entity type from selector (access .sel for the EntitySelector)
301
+ const entityType = this.inferEntityTypeFromSelector(stmt.iterable.sel);
302
+ this.scope.set(stmt.binding, {
303
+ type: { kind: 'entity', entityType },
304
+ mutable: false // Entity bindings are not reassignable
305
+ });
306
+ // Push self type context for @s inside the loop
307
+ this.pushSelfType(entityType);
308
+ this.checkBlock(stmt.body);
309
+ this.popSelfType();
310
+ }
311
+ else {
312
+ const iterableType = this.inferType(stmt.iterable);
313
+ if (iterableType.kind === 'array') {
314
+ this.scope.set(stmt.binding, { type: iterableType.elem, mutable: true });
315
+ }
316
+ else {
317
+ this.scope.set(stmt.binding, { type: { kind: 'named', name: 'void' }, mutable: true });
318
+ }
319
+ this.checkBlock(stmt.body);
320
+ }
321
+ break;
322
+ case 'match':
323
+ this.checkExpr(stmt.expr);
324
+ for (const arm of stmt.arms) {
325
+ if (arm.pattern) {
326
+ this.checkExpr(arm.pattern);
327
+ if (!this.typesMatch(this.inferType(stmt.expr), this.inferType(arm.pattern))) {
328
+ this.report('Match arm pattern type must match subject type', arm.pattern);
329
+ }
330
+ }
331
+ this.checkBlock(arm.body);
332
+ }
333
+ break;
334
+ case 'as_block': {
335
+ // as block changes @s to the selector's entity type
336
+ const entityType = this.inferEntityTypeFromSelector(stmt.selector);
337
+ this.pushSelfType(entityType);
338
+ this.checkBlock(stmt.body);
339
+ this.popSelfType();
340
+ break;
341
+ }
342
+ case 'at_block':
343
+ // at block doesn't change @s type, only position
344
+ this.checkBlock(stmt.body);
345
+ break;
346
+ case 'as_at': {
347
+ // as @x at @y - @s becomes the as selector's type
348
+ const entityType = this.inferEntityTypeFromSelector(stmt.as_sel);
349
+ this.pushSelfType(entityType);
350
+ this.checkBlock(stmt.body);
351
+ this.popSelfType();
352
+ break;
353
+ }
354
+ case 'execute':
355
+ // execute with subcommands - check for 'as' subcommands
356
+ for (const sub of stmt.subcommands) {
357
+ if (sub.kind === 'as' && sub.selector) {
358
+ const entityType = this.inferEntityTypeFromSelector(sub.selector);
359
+ this.pushSelfType(entityType);
360
+ }
361
+ }
362
+ this.checkBlock(stmt.body);
363
+ // Pop for each 'as' subcommand
364
+ for (const sub of stmt.subcommands) {
365
+ if (sub.kind === 'as') {
366
+ this.popSelfType();
367
+ }
368
+ }
369
+ break;
370
+ case 'expr':
371
+ this.checkExpr(stmt.expr);
372
+ break;
373
+ case 'raw':
374
+ // Raw commands are not type checked
375
+ break;
376
+ }
377
+ }
378
+ checkLetStmt(stmt) {
379
+ // Check initializer
380
+ const expectedType = stmt.type ? this.normalizeType(stmt.type) : undefined;
381
+ this.checkExpr(stmt.init, expectedType);
382
+ // Add variable to scope
383
+ const type = expectedType ?? this.inferType(stmt.init);
384
+ this.scope.set(stmt.name, { type, mutable: true });
385
+ const actualType = this.inferType(stmt.init, expectedType);
386
+ if (expectedType &&
387
+ stmt.init.kind !== 'struct_lit' &&
388
+ stmt.init.kind !== 'array_lit' &&
389
+ !(actualType.kind === 'named' && actualType.name === 'void') &&
390
+ !this.typesMatch(expectedType, actualType)) {
391
+ this.report(`Type mismatch: expected ${this.typeToString(expectedType)}, got ${this.typeToString(actualType)}`, stmt);
392
+ }
393
+ }
394
+ checkReturnStmt(stmt) {
395
+ if (!this.currentReturnType)
396
+ return;
397
+ const expectedType = this.currentReturnType;
398
+ if (stmt.value) {
399
+ const actualType = this.inferType(stmt.value, expectedType);
400
+ this.checkExpr(stmt.value, expectedType);
401
+ if (!this.typesMatch(expectedType, actualType)) {
402
+ this.report(`Return type mismatch: expected ${this.typeToString(expectedType)}, got ${this.typeToString(actualType)}`, stmt);
403
+ }
404
+ }
405
+ else {
406
+ // No return value
407
+ if (expectedType.kind !== 'named' || expectedType.name !== 'void') {
408
+ this.report(`Missing return value: expected ${this.typeToString(expectedType)}`, stmt);
409
+ }
410
+ }
411
+ }
412
+ checkExpr(expr, expectedType) {
413
+ switch (expr.kind) {
414
+ case 'ident':
415
+ if (!this.scope.has(expr.name)) {
416
+ this.report(`Variable '${expr.name}' used before declaration`, expr);
417
+ }
418
+ break;
419
+ case 'call':
420
+ this.checkCallExpr(expr);
421
+ break;
422
+ case 'invoke':
423
+ this.checkInvokeExpr(expr);
424
+ break;
425
+ case 'member':
426
+ this.checkMemberExpr(expr);
427
+ break;
428
+ case 'static_call':
429
+ this.checkStaticCallExpr(expr);
430
+ break;
431
+ case 'binary':
432
+ this.checkExpr(expr.left);
433
+ this.checkExpr(expr.right);
434
+ break;
435
+ case 'is_check': {
436
+ this.checkExpr(expr.expr);
437
+ const checkedType = this.inferType(expr.expr);
438
+ if (checkedType.kind !== 'entity') {
439
+ this.report(`'is' checks require an entity expression, got ${this.typeToString(checkedType)}`, expr.expr);
440
+ }
441
+ break;
442
+ }
443
+ case 'unary':
444
+ this.checkExpr(expr.operand);
445
+ break;
446
+ case 'assign':
447
+ if (!this.scope.has(expr.target)) {
448
+ this.report(`Variable '${expr.target}' used before declaration`, expr);
449
+ }
450
+ else if (!this.scope.get(expr.target)?.mutable) {
451
+ this.report(`Cannot assign to const '${expr.target}'`, expr);
452
+ }
453
+ this.checkExpr(expr.value, this.scope.get(expr.target)?.type);
454
+ break;
455
+ case 'member_assign':
456
+ this.checkExpr(expr.obj);
457
+ this.checkExpr(expr.value);
458
+ break;
459
+ case 'index':
460
+ this.checkExpr(expr.obj);
461
+ this.checkExpr(expr.index);
462
+ const indexType = this.inferType(expr.index);
463
+ if (indexType.kind !== 'named' || indexType.name !== 'int') {
464
+ this.report('Array index must be int', expr.index);
465
+ }
466
+ break;
467
+ case 'struct_lit':
468
+ for (const field of expr.fields) {
469
+ this.checkExpr(field.value);
470
+ }
471
+ break;
472
+ case 'str_interp':
473
+ for (const part of expr.parts) {
474
+ if (typeof part !== 'string') {
475
+ this.checkExpr(part);
476
+ }
477
+ }
478
+ break;
479
+ case 'f_string':
480
+ for (const part of expr.parts) {
481
+ if (part.kind !== 'expr') {
482
+ continue;
483
+ }
484
+ this.checkExpr(part.expr);
485
+ const partType = this.inferType(part.expr);
486
+ if (!(partType.kind === 'named' && (partType.name === 'int' || partType.name === 'string' || partType.name === 'format_string'))) {
487
+ this.report(`f-string placeholder must be int or string, got ${this.typeToString(partType)}`, part.expr);
488
+ }
489
+ }
490
+ break;
491
+ case 'array_lit':
492
+ for (const elem of expr.elements) {
493
+ this.checkExpr(elem);
494
+ }
495
+ break;
496
+ case 'lambda':
497
+ this.checkLambdaExpr(expr, expectedType);
498
+ break;
499
+ case 'blockpos':
500
+ break;
501
+ // Literals don't need checking
502
+ case 'int_lit':
503
+ case 'float_lit':
504
+ case 'bool_lit':
505
+ case 'str_lit':
506
+ case 'mc_name':
507
+ case 'range_lit':
508
+ case 'selector':
509
+ case 'byte_lit':
510
+ case 'short_lit':
511
+ case 'long_lit':
512
+ case 'double_lit':
513
+ break;
514
+ }
515
+ }
516
+ checkCallExpr(expr) {
517
+ if (expr.fn === 'tp' || expr.fn === 'tp_to') {
518
+ this.checkTpCall(expr);
519
+ }
520
+ const richTextBuiltin = this.richTextBuiltins.get(expr.fn);
521
+ if (richTextBuiltin) {
522
+ this.checkRichTextBuiltinCall(expr, richTextBuiltin.messageIndex);
523
+ return;
524
+ }
525
+ const builtin = BUILTIN_SIGNATURES[expr.fn];
526
+ if (builtin) {
527
+ this.checkFunctionCallArgs(expr.args, builtin.params, expr.fn, expr);
528
+ return;
529
+ }
530
+ // Check if function exists and arg count matches
531
+ const fn = this.functions.get(expr.fn);
532
+ if (fn) {
533
+ const requiredParams = fn.params.filter(param => !param.default).length;
534
+ if (expr.args.length < requiredParams || expr.args.length > fn.params.length) {
535
+ const expectedRange = requiredParams === fn.params.length
536
+ ? `${fn.params.length}`
537
+ : `${requiredParams}-${fn.params.length}`;
538
+ this.report(`Function '${expr.fn}' expects ${expectedRange} arguments, got ${expr.args.length}`, expr);
539
+ }
540
+ for (let i = 0; i < expr.args.length; i++) {
541
+ const paramType = fn.params[i] ? this.normalizeType(fn.params[i].type) : undefined;
542
+ if (paramType) {
543
+ this.checkExpr(expr.args[i], paramType);
544
+ }
545
+ const argType = this.inferType(expr.args[i], paramType);
546
+ if (paramType && !this.typesMatch(paramType, argType)) {
547
+ this.report(`Argument ${i + 1} of '${expr.fn}' expects ${this.typeToString(paramType)}, got ${this.typeToString(argType)}`, expr.args[i]);
548
+ }
549
+ }
550
+ return;
551
+ }
552
+ const varType = this.scope.get(expr.fn)?.type;
553
+ if (varType?.kind === 'function_type') {
554
+ this.checkFunctionCallArgs(expr.args, varType.params, expr.fn, expr);
555
+ return;
556
+ }
557
+ const implMethod = this.resolveInstanceMethod(expr);
558
+ if (implMethod) {
559
+ this.checkFunctionCallArgs(expr.args, implMethod.params.map(param => this.normalizeType(param.type)), implMethod.name, expr);
560
+ return;
561
+ }
562
+ for (const arg of expr.args) {
563
+ this.checkExpr(arg);
564
+ }
565
+ // Built-in functions are not checked for arg count
566
+ }
567
+ checkRichTextBuiltinCall(expr, messageIndex) {
568
+ for (let i = 0; i < expr.args.length; i++) {
569
+ this.checkExpr(expr.args[i], i === messageIndex ? undefined : STRING_TYPE);
570
+ }
571
+ const message = expr.args[messageIndex];
572
+ if (!message) {
573
+ return;
574
+ }
575
+ const messageType = this.inferType(message);
576
+ if (messageType.kind !== 'named' ||
577
+ (messageType.name !== 'string' && messageType.name !== 'format_string')) {
578
+ this.report(`Argument ${messageIndex + 1} of '${expr.fn}' expects string or format_string, got ${this.typeToString(messageType)}`, message);
579
+ }
580
+ }
581
+ checkInvokeExpr(expr) {
582
+ this.checkExpr(expr.callee);
583
+ const calleeType = this.inferType(expr.callee);
584
+ if (calleeType.kind !== 'function_type') {
585
+ this.report('Attempted to call a non-function value', expr.callee);
586
+ for (const arg of expr.args) {
587
+ this.checkExpr(arg);
588
+ }
589
+ return;
590
+ }
591
+ this.checkFunctionCallArgs(expr.args, calleeType.params, 'lambda', expr);
592
+ }
593
+ checkFunctionCallArgs(args, params, calleeName, node) {
594
+ if (args.length !== params.length) {
595
+ this.report(`Function '${calleeName}' expects ${params.length} arguments, got ${args.length}`, node);
596
+ }
597
+ for (let i = 0; i < args.length; i++) {
598
+ const paramType = params[i];
599
+ if (!paramType) {
600
+ this.checkExpr(args[i]);
601
+ continue;
602
+ }
603
+ this.checkExpr(args[i], paramType);
604
+ const argType = this.inferType(args[i], paramType);
605
+ if (!this.typesMatch(paramType, argType)) {
606
+ this.report(`Argument ${i + 1} of '${calleeName}' expects ${this.typeToString(paramType)}, got ${this.typeToString(argType)}`, args[i]);
607
+ }
608
+ }
609
+ }
610
+ checkTpCall(expr) {
611
+ const dest = expr.args[1];
612
+ if (!dest) {
613
+ return;
614
+ }
615
+ const destType = this.inferType(dest);
616
+ if (destType.kind === 'named' && destType.name === 'BlockPos') {
617
+ return;
618
+ }
619
+ if (dest.kind === 'selector' && !dest.isSingle) {
620
+ this.report('tp destination must be a single-entity selector (@s, @p, @r, or limit=1)', dest);
621
+ }
622
+ }
623
+ checkMemberExpr(expr) {
624
+ if (!(expr.obj.kind === 'ident' && this.enums.has(expr.obj.name))) {
625
+ this.checkExpr(expr.obj);
626
+ }
627
+ // Check if accessing member on appropriate type
628
+ if (expr.obj.kind === 'ident') {
629
+ if (this.enums.has(expr.obj.name)) {
630
+ const enumVariants = this.enums.get(expr.obj.name);
631
+ if (!enumVariants.has(expr.field)) {
632
+ this.report(`Enum '${expr.obj.name}' has no variant '${expr.field}'`, expr);
633
+ }
634
+ return;
635
+ }
636
+ const varSymbol = this.scope.get(expr.obj.name);
637
+ const varType = varSymbol?.type;
638
+ if (varType) {
639
+ // Allow member access on struct types
640
+ if (varType.kind === 'struct') {
641
+ const structFields = this.structs.get(varType.name);
642
+ if (structFields && !structFields.has(expr.field)) {
643
+ this.report(`Struct '${varType.name}' has no field '${expr.field}'`, expr);
644
+ }
645
+ }
646
+ else if (varType.kind === 'array') {
647
+ if (expr.field !== 'len' && expr.field !== 'push' && expr.field !== 'pop') {
648
+ this.report(`Array has no field '${expr.field}'`, expr);
649
+ }
650
+ }
651
+ else if (varType.kind === 'named') {
652
+ // Entity marker (void) - allow all members
653
+ if (varType.name !== 'void') {
654
+ // Only warn for primitive types
655
+ if (['int', 'bool', 'float', 'string', 'byte', 'short', 'long', 'double'].includes(varType.name)) {
656
+ this.report(`Cannot access member '${expr.field}' on ${this.typeToString(varType)}`, expr);
657
+ }
658
+ }
659
+ }
660
+ }
661
+ }
662
+ }
663
+ checkStaticCallExpr(expr) {
664
+ const method = this.implMethods.get(expr.type)?.get(expr.method);
665
+ if (!method) {
666
+ this.report(`Type '${expr.type}' has no static method '${expr.method}'`, expr);
667
+ for (const arg of expr.args) {
668
+ this.checkExpr(arg);
669
+ }
670
+ return;
671
+ }
672
+ if (method.params[0]?.name === 'self') {
673
+ this.report(`Method '${expr.type}::${expr.method}' is an instance method`, expr);
674
+ return;
675
+ }
676
+ this.checkFunctionCallArgs(expr.args, method.params.map(param => this.normalizeType(param.type)), `${expr.type}::${expr.method}`, expr);
677
+ }
678
+ checkLambdaExpr(expr, expectedType) {
679
+ const normalizedExpected = expectedType ? this.normalizeType(expectedType) : undefined;
680
+ const expectedFnType = normalizedExpected?.kind === 'function_type' ? normalizedExpected : undefined;
681
+ const lambdaType = this.inferLambdaType(expr, expectedFnType);
682
+ if (expectedFnType && !this.typesMatch(expectedFnType, lambdaType)) {
683
+ this.report(`Type mismatch: expected ${this.typeToString(expectedFnType)}, got ${this.typeToString(lambdaType)}`, expr);
684
+ return;
685
+ }
686
+ const outerScope = this.scope;
687
+ const outerReturnType = this.currentReturnType;
688
+ const lambdaScope = new Map(this.scope);
689
+ const paramTypes = expectedFnType?.params ?? lambdaType.params;
690
+ for (let i = 0; i < expr.params.length; i++) {
691
+ lambdaScope.set(expr.params[i].name, {
692
+ type: paramTypes[i] ?? { kind: 'named', name: 'void' },
693
+ mutable: true,
694
+ });
695
+ }
696
+ this.scope = lambdaScope;
697
+ this.currentReturnType = expr.returnType
698
+ ? this.normalizeType(expr.returnType)
699
+ : (expectedFnType?.return ?? lambdaType.return);
700
+ if (Array.isArray(expr.body)) {
701
+ this.checkBlock(expr.body);
702
+ }
703
+ else {
704
+ this.checkExpr(expr.body, this.currentReturnType);
705
+ const actualType = this.inferType(expr.body, this.currentReturnType);
706
+ if (!this.typesMatch(this.currentReturnType, actualType)) {
707
+ this.report(`Return type mismatch: expected ${this.typeToString(this.currentReturnType)}, got ${this.typeToString(actualType)}`, expr.body);
708
+ }
709
+ }
710
+ this.scope = outerScope;
711
+ this.currentReturnType = outerReturnType;
712
+ }
713
+ checkIfBranches(stmt) {
714
+ const narrowed = this.getThenBranchNarrowing(stmt.cond);
715
+ if (narrowed) {
716
+ const thenScope = new Map(this.scope);
717
+ thenScope.set(narrowed.name, { type: narrowed.type, mutable: narrowed.mutable });
718
+ const outerScope = this.scope;
719
+ this.scope = thenScope;
720
+ this.checkBlock(stmt.then);
721
+ this.scope = outerScope;
722
+ }
723
+ else {
724
+ this.checkBlock(stmt.then);
725
+ }
726
+ if (stmt.else_) {
727
+ this.checkBlock(stmt.else_);
728
+ }
729
+ }
730
+ getThenBranchNarrowing(cond) {
731
+ if (cond.kind !== 'is_check' || cond.expr.kind !== 'ident') {
732
+ return null;
733
+ }
734
+ const symbol = this.scope.get(cond.expr.name);
735
+ if (!symbol || symbol.type.kind !== 'entity') {
736
+ return null;
737
+ }
738
+ return {
739
+ name: cond.expr.name,
740
+ type: { kind: 'entity', entityType: cond.entityType },
741
+ mutable: symbol.mutable,
742
+ };
743
+ }
744
+ inferType(expr, expectedType) {
745
+ switch (expr.kind) {
746
+ case 'int_lit':
747
+ return { kind: 'named', name: 'int' };
748
+ case 'float_lit':
749
+ return { kind: 'named', name: 'float' };
750
+ case 'byte_lit':
751
+ return { kind: 'named', name: 'byte' };
752
+ case 'short_lit':
753
+ return { kind: 'named', name: 'short' };
754
+ case 'long_lit':
755
+ return { kind: 'named', name: 'long' };
756
+ case 'double_lit':
757
+ return { kind: 'named', name: 'double' };
758
+ case 'bool_lit':
759
+ return { kind: 'named', name: 'bool' };
760
+ case 'str_lit':
761
+ case 'mc_name':
762
+ return { kind: 'named', name: 'string' };
763
+ case 'str_interp':
764
+ for (const part of expr.parts) {
765
+ if (typeof part !== 'string') {
766
+ this.checkExpr(part);
767
+ }
768
+ }
769
+ return { kind: 'named', name: 'string' };
770
+ case 'f_string':
771
+ for (const part of expr.parts) {
772
+ if (part.kind === 'expr') {
773
+ this.checkExpr(part.expr);
774
+ }
775
+ }
776
+ return FORMAT_STRING_TYPE;
777
+ case 'blockpos':
778
+ return { kind: 'named', name: 'BlockPos' };
779
+ case 'ident':
780
+ return this.scope.get(expr.name)?.type ?? { kind: 'named', name: 'void' };
781
+ case 'call': {
782
+ const builtin = BUILTIN_SIGNATURES[expr.fn];
783
+ if (builtin) {
784
+ return builtin.return;
785
+ }
786
+ if (expr.fn === '__array_push') {
787
+ return VOID_TYPE;
788
+ }
789
+ if (expr.fn === '__array_pop') {
790
+ const target = expr.args[0];
791
+ if (target && target.kind === 'ident') {
792
+ const targetType = this.scope.get(target.name)?.type;
793
+ if (targetType?.kind === 'array')
794
+ return targetType.elem;
795
+ }
796
+ return INT_TYPE;
797
+ }
798
+ if (expr.fn === 'bossbar_get_value') {
799
+ return INT_TYPE;
800
+ }
801
+ if (expr.fn === 'random_sequence') {
802
+ return VOID_TYPE;
803
+ }
804
+ const varType = this.scope.get(expr.fn)?.type;
805
+ if (varType?.kind === 'function_type') {
806
+ return varType.return;
807
+ }
808
+ const implMethod = this.resolveInstanceMethod(expr);
809
+ if (implMethod) {
810
+ return this.normalizeType(implMethod.returnType);
811
+ }
812
+ const fn = this.functions.get(expr.fn);
813
+ return fn?.returnType ?? INT_TYPE;
814
+ }
815
+ case 'static_call': {
816
+ const method = this.implMethods.get(expr.type)?.get(expr.method);
817
+ return method ? this.normalizeType(method.returnType) : { kind: 'named', name: 'void' };
818
+ }
819
+ case 'invoke': {
820
+ const calleeType = this.inferType(expr.callee);
821
+ if (calleeType.kind === 'function_type') {
822
+ return calleeType.return;
823
+ }
824
+ return { kind: 'named', name: 'void' };
825
+ }
826
+ case 'member':
827
+ if (expr.obj.kind === 'ident' && this.enums.has(expr.obj.name)) {
828
+ return { kind: 'enum', name: expr.obj.name };
829
+ }
830
+ if (expr.obj.kind === 'ident') {
831
+ const objTypeNode = this.scope.get(expr.obj.name)?.type;
832
+ if (objTypeNode?.kind === 'array' && expr.field === 'len') {
833
+ return { kind: 'named', name: 'int' };
834
+ }
835
+ }
836
+ return { kind: 'named', name: 'void' };
837
+ case 'index': {
838
+ const objType = this.inferType(expr.obj);
839
+ if (objType.kind === 'array')
840
+ return objType.elem;
841
+ return { kind: 'named', name: 'void' };
842
+ }
843
+ case 'binary':
844
+ if (['==', '!=', '<', '<=', '>', '>=', '&&', '||'].includes(expr.op)) {
845
+ return { kind: 'named', name: 'bool' };
846
+ }
847
+ return this.inferType(expr.left);
848
+ case 'is_check':
849
+ return { kind: 'named', name: 'bool' };
850
+ case 'unary':
851
+ if (expr.op === '!')
852
+ return { kind: 'named', name: 'bool' };
853
+ return this.inferType(expr.operand);
854
+ case 'array_lit':
855
+ if (expr.elements.length > 0) {
856
+ return { kind: 'array', elem: this.inferType(expr.elements[0]) };
857
+ }
858
+ return { kind: 'array', elem: { kind: 'named', name: 'int' } };
859
+ case 'struct_lit':
860
+ if (expectedType) {
861
+ const normalized = this.normalizeType(expectedType);
862
+ if (normalized.kind === 'struct') {
863
+ return normalized;
864
+ }
865
+ }
866
+ return { kind: 'named', name: 'void' };
867
+ case 'lambda':
868
+ return this.inferLambdaType(expr, expectedType && this.normalizeType(expectedType).kind === 'function_type'
869
+ ? this.normalizeType(expectedType)
870
+ : undefined);
871
+ default:
872
+ return { kind: 'named', name: 'void' };
873
+ }
874
+ }
875
+ inferLambdaType(expr, expectedType) {
876
+ const params = expr.params.map((param, index) => {
877
+ if (param.type) {
878
+ return this.normalizeType(param.type);
879
+ }
880
+ const inferred = expectedType?.params[index];
881
+ if (inferred) {
882
+ return inferred;
883
+ }
884
+ this.report(`Lambda parameter '${param.name}' requires a type annotation`, expr);
885
+ return { kind: 'named', name: 'void' };
886
+ });
887
+ let returnType = expr.returnType
888
+ ? this.normalizeType(expr.returnType)
889
+ : expectedType?.return;
890
+ if (!returnType) {
891
+ returnType = Array.isArray(expr.body) ? { kind: 'named', name: 'void' } : this.inferType(expr.body);
892
+ }
893
+ return { kind: 'function_type', params, return: returnType };
894
+ }
895
+ // ---------------------------------------------------------------------------
896
+ // Entity Type Helpers
897
+ // ---------------------------------------------------------------------------
898
+ /** Infer entity type from a selector */
899
+ inferEntityTypeFromSelector(selector) {
900
+ // @a, @p, @r always return Player
901
+ if (selector.kind === '@a' || selector.kind === '@p' || selector.kind === '@r') {
902
+ return 'Player';
903
+ }
904
+ // @e or @s with type= filter
905
+ if (selector.filters?.type) {
906
+ const mcType = selector.filters.type.toLowerCase();
907
+ return MC_TYPE_TO_ENTITY[mcType] ?? 'entity';
908
+ }
909
+ // @s uses current context
910
+ if (selector.kind === '@s') {
911
+ return this.selfTypeStack[this.selfTypeStack.length - 1];
912
+ }
913
+ // Default to entity
914
+ return 'entity';
915
+ }
916
+ resolveInstanceMethod(expr) {
917
+ const receiver = expr.args[0];
918
+ if (!receiver) {
919
+ return null;
920
+ }
921
+ const receiverType = this.inferType(receiver);
922
+ if (receiverType.kind !== 'struct') {
923
+ return null;
924
+ }
925
+ const method = this.implMethods.get(receiverType.name)?.get(expr.fn);
926
+ if (!method || method.params[0]?.name !== 'self') {
927
+ return null;
928
+ }
929
+ return method;
930
+ }
931
+ /** Check if childType is a subtype of parentType */
932
+ isEntitySubtype(childType, parentType) {
933
+ if (childType === parentType)
934
+ return true;
935
+ let current = childType;
936
+ while (current !== null) {
937
+ if (current === parentType)
938
+ return true;
939
+ current = ENTITY_HIERARCHY[current];
940
+ }
941
+ return false;
942
+ }
943
+ /** Push a new self type context */
944
+ pushSelfType(entityType) {
945
+ this.selfTypeStack.push(entityType);
946
+ }
947
+ /** Pop self type context */
948
+ popSelfType() {
949
+ if (this.selfTypeStack.length > 1) {
950
+ this.selfTypeStack.pop();
951
+ }
952
+ }
953
+ /** Get current @s type */
954
+ getCurrentSelfType() {
955
+ return this.selfTypeStack[this.selfTypeStack.length - 1];
956
+ }
957
+ typesMatch(expected, actual) {
958
+ if (expected.kind !== actual.kind)
959
+ return false;
960
+ if (expected.kind === 'named' && actual.kind === 'named') {
961
+ // void matches anything (for inferred types)
962
+ if (actual.name === 'void')
963
+ return true;
964
+ return expected.name === actual.name;
965
+ }
966
+ if (expected.kind === 'array' && actual.kind === 'array') {
967
+ return this.typesMatch(expected.elem, actual.elem);
968
+ }
969
+ if (expected.kind === 'struct' && actual.kind === 'struct') {
970
+ return expected.name === actual.name;
971
+ }
972
+ if (expected.kind === 'enum' && actual.kind === 'enum') {
973
+ return expected.name === actual.name;
974
+ }
975
+ if (expected.kind === 'function_type' && actual.kind === 'function_type') {
976
+ return expected.params.length === actual.params.length &&
977
+ expected.params.every((param, index) => this.typesMatch(param, actual.params[index])) &&
978
+ this.typesMatch(expected.return, actual.return);
979
+ }
980
+ // Entity type matching with subtype support
981
+ if (expected.kind === 'entity' && actual.kind === 'entity') {
982
+ return this.isEntitySubtype(actual.entityType, expected.entityType);
983
+ }
984
+ // Selector matches any entity type
985
+ if (expected.kind === 'selector' && actual.kind === 'entity') {
986
+ return true;
987
+ }
988
+ return false;
989
+ }
990
+ typeToString(type) {
991
+ switch (type.kind) {
992
+ case 'named':
993
+ return type.name;
994
+ case 'array':
995
+ return `${this.typeToString(type.elem)}[]`;
996
+ case 'struct':
997
+ return type.name;
998
+ case 'enum':
999
+ return type.name;
1000
+ case 'function_type':
1001
+ return `(${type.params.map(param => this.typeToString(param)).join(', ')}) -> ${this.typeToString(type.return)}`;
1002
+ case 'entity':
1003
+ return type.entityType;
1004
+ case 'selector':
1005
+ return 'selector';
1006
+ default:
1007
+ return 'unknown';
1008
+ }
1009
+ }
1010
+ normalizeType(type) {
1011
+ if (type.kind === 'array') {
1012
+ return { kind: 'array', elem: this.normalizeType(type.elem) };
1013
+ }
1014
+ if (type.kind === 'function_type') {
1015
+ return {
1016
+ kind: 'function_type',
1017
+ params: type.params.map(param => this.normalizeType(param)),
1018
+ return: this.normalizeType(type.return),
1019
+ };
1020
+ }
1021
+ if ((type.kind === 'struct' || type.kind === 'enum') && this.enums.has(type.name)) {
1022
+ return { kind: 'enum', name: type.name };
1023
+ }
1024
+ if (type.kind === 'struct' && type.name in ENTITY_HIERARCHY) {
1025
+ return { kind: 'entity', entityType: type.name };
1026
+ }
1027
+ if (type.kind === 'named' && type.name in ENTITY_HIERARCHY) {
1028
+ return { kind: 'entity', entityType: type.name };
1029
+ }
1030
+ return type;
1031
+ }
1032
+ }
1033
+ exports.TypeChecker = TypeChecker;
1034
+ //# sourceMappingURL=index.js.map