redscript-mc 2.6.2 → 3.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 (636) hide show
  1. package/.github/workflows/ci.yml +11 -0
  2. package/CHANGELOG.md +18 -9
  3. package/README-benchmarks.md +48 -0
  4. package/README-vscode-test.md +251 -0
  5. package/RELEASE_NOTES.md +74 -0
  6. package/ROADMAP.md +131 -167
  7. package/benchmarks/_shared.ts +468 -0
  8. package/benchmarks/baseline.json +2816 -0
  9. package/benchmarks/baseline.md +13 -0
  10. package/benchmarks/compiler-perf.report.json +207 -0
  11. package/benchmarks/compiler-perf.ts +76 -0
  12. package/benchmarks/results.md +13 -0
  13. package/benchmarks/stdlib-complexity.report.json +2606 -0
  14. package/benchmarks/stdlib-complexity.ts +54 -0
  15. package/benchmarks/stdlib-size.md +57 -0
  16. package/benchmarks/stdlib-size.ts +91 -0
  17. package/coverage-report.md +177 -0
  18. package/dist/src/__tests__/budget.test.js +4 -0
  19. package/dist/src/__tests__/cache/cache-behavior.test.d.ts +10 -0
  20. package/dist/src/__tests__/cache/cache-behavior.test.js +425 -0
  21. package/dist/src/__tests__/cache-extra.test.d.ts +5 -0
  22. package/dist/src/__tests__/cache-extra.test.js +211 -0
  23. package/dist/src/__tests__/cli-init.test.d.ts +1 -0
  24. package/dist/src/__tests__/cli-init.test.js +97 -0
  25. package/dist/src/__tests__/cli-publish.test.d.ts +9 -0
  26. package/dist/src/__tests__/cli-publish.test.js +189 -0
  27. package/dist/src/__tests__/cli.test.js +76 -0
  28. package/dist/src/__tests__/compile-preprocess.test.d.ts +11 -0
  29. package/dist/src/__tests__/compile-preprocess.test.js +328 -0
  30. package/dist/src/__tests__/compiler/break-stmt.test.d.ts +1 -0
  31. package/dist/src/__tests__/compiler/break-stmt.test.js +58 -0
  32. package/dist/src/__tests__/compiler/const-decl.test.d.ts +1 -0
  33. package/dist/src/__tests__/compiler/const-decl.test.js +123 -0
  34. package/dist/src/__tests__/compiler/continue-stmt.test.d.ts +1 -0
  35. package/dist/src/__tests__/compiler/continue-stmt.test.js +67 -0
  36. package/dist/src/__tests__/compiler/coroutine-extended.test.d.ts +17 -0
  37. package/dist/src/__tests__/compiler/coroutine-extended.test.js +565 -0
  38. package/dist/src/__tests__/compiler/deprecated.test.d.ts +4 -0
  39. package/dist/src/__tests__/compiler/deprecated.test.js +285 -0
  40. package/dist/src/__tests__/compiler/do-while.test.d.ts +1 -0
  41. package/dist/src/__tests__/compiler/do-while.test.js +120 -0
  42. package/dist/src/__tests__/compiler/enum-payload.test.d.ts +9 -0
  43. package/dist/src/__tests__/compiler/enum-payload.test.js +272 -0
  44. package/dist/src/__tests__/compiler/interface.test.d.ts +10 -0
  45. package/dist/src/__tests__/compiler/interface.test.js +258 -0
  46. package/dist/src/__tests__/compiler/labeled-loops.test.d.ts +1 -0
  47. package/dist/src/__tests__/compiler/labeled-loops.test.js +263 -0
  48. package/dist/src/__tests__/compiler/match-string.test.d.ts +1 -0
  49. package/dist/src/__tests__/compiler/match-string.test.js +43 -0
  50. package/dist/src/__tests__/compiler/memoize.test.d.ts +1 -0
  51. package/dist/src/__tests__/compiler/memoize.test.js +113 -0
  52. package/dist/src/__tests__/compiler/method-chain.test.d.ts +5 -0
  53. package/dist/src/__tests__/compiler/method-chain.test.js +115 -0
  54. package/dist/src/__tests__/compiler/module-import.test.d.ts +12 -0
  55. package/dist/src/__tests__/compiler/module-import.test.js +261 -0
  56. package/dist/src/__tests__/compiler/option-extensions.test.d.ts +6 -0
  57. package/dist/src/__tests__/compiler/option-extensions.test.js +191 -0
  58. package/dist/src/__tests__/compiler/profile-decorator.test.d.ts +1 -0
  59. package/dist/src/__tests__/compiler/profile-decorator.test.js +69 -0
  60. package/dist/src/__tests__/compiler/string-advanced.test.d.ts +7 -0
  61. package/dist/src/__tests__/compiler/string-advanced.test.js +281 -0
  62. package/dist/src/__tests__/compiler/struct-extends.test.d.ts +1 -0
  63. package/dist/src/__tests__/compiler/struct-extends.test.js +95 -0
  64. package/dist/src/__tests__/compiler/throttle-retry.test.d.ts +1 -0
  65. package/dist/src/__tests__/compiler/throttle-retry.test.js +166 -0
  66. package/dist/src/__tests__/compiler/tuple-type.test.d.ts +10 -0
  67. package/dist/src/__tests__/compiler/tuple-type.test.js +229 -0
  68. package/dist/src/__tests__/compiler/watch-decorator.test.d.ts +1 -0
  69. package/dist/src/__tests__/compiler/watch-decorator.test.js +65 -0
  70. package/dist/src/__tests__/config/project-config.test.d.ts +1 -0
  71. package/dist/src/__tests__/config/project-config.test.js +199 -0
  72. package/dist/src/__tests__/config-decorator.test.d.ts +8 -0
  73. package/dist/src/__tests__/config-decorator.test.js +142 -0
  74. package/dist/src/__tests__/diagnostics-extra.test.d.ts +6 -0
  75. package/dist/src/__tests__/diagnostics-extra.test.js +132 -0
  76. package/dist/src/__tests__/emit/compile-branches.test.d.ts +1 -0
  77. package/dist/src/__tests__/emit/compile-branches.test.js +123 -0
  78. package/dist/src/__tests__/emit/compile-coverage.test.d.ts +25 -0
  79. package/dist/src/__tests__/emit/compile-coverage.test.js +617 -0
  80. package/dist/src/__tests__/emit/compile-extra-branches.test.d.ts +12 -0
  81. package/dist/src/__tests__/emit/compile-extra-branches.test.js +225 -0
  82. package/dist/src/__tests__/emit/compile-mocked-branches.test.d.ts +0 -0
  83. package/dist/src/__tests__/emit/compile-mocked-branches.test.js +238 -0
  84. package/dist/src/__tests__/emit/execute-chain.test.d.ts +10 -0
  85. package/dist/src/__tests__/emit/execute-chain.test.js +94 -0
  86. package/dist/src/__tests__/emit/index.test.js +2 -1
  87. package/dist/src/__tests__/emit/modules-branches.test.d.ts +1 -0
  88. package/dist/src/__tests__/emit/modules-branches.test.js +88 -0
  89. package/dist/src/__tests__/emit/modules-coverage.test.d.ts +15 -0
  90. package/dist/src/__tests__/emit/modules-coverage.test.js +221 -0
  91. package/dist/src/__tests__/emit/modules-errors.test.d.ts +12 -0
  92. package/dist/src/__tests__/emit/modules-errors.test.js +169 -0
  93. package/dist/src/__tests__/emit/modules-rewrite.test.d.ts +17 -0
  94. package/dist/src/__tests__/emit/modules-rewrite.test.js +204 -0
  95. package/dist/src/__tests__/emit/source-map.test.d.ts +1 -0
  96. package/dist/src/__tests__/emit/source-map.test.js +167 -0
  97. package/dist/src/__tests__/enum.test.js +9 -4
  98. package/dist/src/__tests__/error-recovery.test.d.ts +7 -0
  99. package/dist/src/__tests__/error-recovery.test.js +217 -0
  100. package/dist/src/__tests__/events-types-extra.test.d.ts +10 -0
  101. package/dist/src/__tests__/events-types-extra.test.js +91 -0
  102. package/dist/src/__tests__/events-types.test.d.ts +4 -0
  103. package/dist/src/__tests__/events-types.test.js +56 -0
  104. package/dist/src/__tests__/formatter.test.js +13 -5
  105. package/dist/src/__tests__/hir/lower-extra.test.d.ts +9 -0
  106. package/dist/src/__tests__/hir/lower-extra.test.js +140 -0
  107. package/dist/src/__tests__/hir/monomorphize-extra.test.d.ts +15 -0
  108. package/dist/src/__tests__/hir/monomorphize-extra.test.js +200 -0
  109. package/dist/src/__tests__/hir/monomorphize-extra2.test.d.ts +16 -0
  110. package/dist/src/__tests__/hir/monomorphize-extra2.test.js +316 -0
  111. package/dist/src/__tests__/incremental.test.js +10 -2
  112. package/dist/src/__tests__/index-extra.test.d.ts +10 -0
  113. package/dist/src/__tests__/index-extra.test.js +71 -0
  114. package/dist/src/__tests__/lexer.test.js +2 -2
  115. package/dist/src/__tests__/lint/rules.test.d.ts +5 -0
  116. package/dist/src/__tests__/lint/rules.test.js +208 -0
  117. package/dist/src/__tests__/lir/lower.test.js +29 -0
  118. package/dist/src/__tests__/lir/verify.test.js +30 -0
  119. package/dist/src/__tests__/lsp/completion.test.d.ts +7 -0
  120. package/dist/src/__tests__/lsp/completion.test.js +583 -0
  121. package/dist/src/__tests__/lsp/definition.test.d.ts +7 -0
  122. package/dist/src/__tests__/lsp/definition.test.js +454 -0
  123. package/dist/src/__tests__/lsp/diagnostics.test.d.ts +10 -0
  124. package/dist/src/__tests__/lsp/diagnostics.test.js +98 -0
  125. package/dist/src/__tests__/lsp/hover-docs.test.d.ts +10 -0
  126. package/dist/src/__tests__/lsp/hover-docs.test.js +210 -0
  127. package/dist/src/__tests__/lsp.test.js +4 -1
  128. package/dist/src/__tests__/mc-integration/item-entity-events.test.js +4 -0
  129. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +4 -0
  130. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.d.ts +13 -0
  131. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1227 -0
  132. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.d.ts +13 -0
  133. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1509 -0
  134. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.d.ts +14 -0
  135. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1374 -0
  136. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.d.ts +10 -0
  137. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +759 -0
  138. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.d.ts +13 -0
  139. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +855 -0
  140. package/dist/src/__tests__/mc-integration/stdlib-coverage.test.js +4 -0
  141. package/dist/src/__tests__/mc-integration/syntax-coverage.test.js +4 -0
  142. package/dist/src/__tests__/mc-validator-coverage.test.d.ts +13 -0
  143. package/dist/src/__tests__/mc-validator-coverage.test.js +296 -0
  144. package/dist/src/__tests__/mc-validator-extra.test.d.ts +13 -0
  145. package/dist/src/__tests__/mc-validator-extra.test.js +245 -0
  146. package/dist/src/__tests__/mir/lower-extra.test.d.ts +20 -0
  147. package/dist/src/__tests__/mir/lower-extra.test.js +361 -0
  148. package/dist/src/__tests__/mir/lower-extra2.test.d.ts +17 -0
  149. package/dist/src/__tests__/mir/lower-extra2.test.js +317 -0
  150. package/dist/src/__tests__/mir/lower-extra3.test.d.ts +19 -0
  151. package/dist/src/__tests__/mir/lower-extra3.test.js +249 -0
  152. package/dist/src/__tests__/mir/lower-extra4.test.d.ts +23 -0
  153. package/dist/src/__tests__/mir/lower-extra4.test.js +606 -0
  154. package/dist/src/__tests__/mir/lower-extra5.test.d.ts +25 -0
  155. package/dist/src/__tests__/mir/lower-extra5.test.js +543 -0
  156. package/dist/src/__tests__/mir/lower-extra6.test.d.ts +16 -0
  157. package/dist/src/__tests__/mir/lower-extra6.test.js +471 -0
  158. package/dist/src/__tests__/mir/lower-extra7.test.d.ts +35 -0
  159. package/dist/src/__tests__/mir/lower-extra7.test.js +921 -0
  160. package/dist/src/__tests__/mir/lower-extra8.test.d.ts +19 -0
  161. package/dist/src/__tests__/mir/lower-extra8.test.js +626 -0
  162. package/dist/src/__tests__/mir/lower-extra9.test.d.ts +14 -0
  163. package/dist/src/__tests__/mir/lower-extra9.test.js +717 -0
  164. package/dist/src/__tests__/optimizer/auto-inline.test.d.ts +1 -0
  165. package/dist/src/__tests__/optimizer/auto-inline.test.js +176 -0
  166. package/dist/src/__tests__/optimizer/cse.test.d.ts +4 -0
  167. package/dist/src/__tests__/optimizer/cse.test.js +178 -0
  168. package/dist/src/__tests__/optimizer/inline_fn.test.d.ts +1 -0
  169. package/dist/src/__tests__/optimizer/inline_fn.test.js +221 -0
  170. package/dist/src/__tests__/optimizer/licm.test.d.ts +1 -0
  171. package/dist/src/__tests__/optimizer/licm.test.js +244 -0
  172. package/dist/src/__tests__/optimizer/optimizer-extended.test.d.ts +12 -0
  173. package/dist/src/__tests__/optimizer/optimizer-extended.test.js +993 -0
  174. package/dist/src/__tests__/optimizer/strength-reduction.test.d.ts +1 -0
  175. package/dist/src/__tests__/optimizer/strength-reduction.test.js +86 -0
  176. package/dist/src/__tests__/optimizer/tco.test.d.ts +14 -0
  177. package/dist/src/__tests__/optimizer/tco.test.js +203 -0
  178. package/dist/src/__tests__/parser-coverage.test.d.ts +25 -0
  179. package/dist/src/__tests__/parser-coverage.test.js +491 -0
  180. package/dist/src/__tests__/parser-extra.test.d.ts +6 -0
  181. package/dist/src/__tests__/parser-extra.test.js +451 -0
  182. package/dist/src/__tests__/parser.test.js +12 -0
  183. package/dist/src/__tests__/repl-extra.test.d.ts +13 -0
  184. package/dist/src/__tests__/repl-extra.test.js +174 -0
  185. package/dist/src/__tests__/repl-server-extra.test.d.ts +10 -0
  186. package/dist/src/__tests__/repl-server-extra.test.js +161 -0
  187. package/dist/src/__tests__/repl-server.test.d.ts +6 -0
  188. package/dist/src/__tests__/repl-server.test.js +146 -0
  189. package/dist/src/__tests__/runtime-extra.test.d.ts +15 -0
  190. package/dist/src/__tests__/runtime-extra.test.js +732 -0
  191. package/dist/src/__tests__/singleton-decorator.test.d.ts +11 -0
  192. package/dist/src/__tests__/singleton-decorator.test.js +260 -0
  193. package/dist/src/__tests__/sourcemap.test.js +1 -1
  194. package/dist/src/__tests__/stdlib/advanced.test.d.ts +5 -0
  195. package/dist/src/__tests__/stdlib/advanced.test.js +301 -0
  196. package/dist/src/__tests__/stdlib/bigint.test.d.ts +4 -0
  197. package/dist/src/__tests__/stdlib/bigint.test.js +83 -0
  198. package/dist/src/__tests__/stdlib/bits.test.d.ts +4 -0
  199. package/dist/src/__tests__/stdlib/bits.test.js +96 -0
  200. package/dist/src/__tests__/stdlib/bossbar.test.d.ts +4 -0
  201. package/dist/src/__tests__/stdlib/bossbar.test.js +72 -0
  202. package/dist/src/__tests__/stdlib/color.test.d.ts +4 -0
  203. package/dist/src/__tests__/stdlib/color.test.js +84 -0
  204. package/dist/src/__tests__/stdlib/combat.test.d.ts +4 -0
  205. package/dist/src/__tests__/stdlib/combat.test.js +64 -0
  206. package/dist/src/__tests__/stdlib/cooldown.test.d.ts +4 -0
  207. package/dist/src/__tests__/stdlib/cooldown.test.js +64 -0
  208. package/dist/src/__tests__/stdlib/dialog.test.js +15 -7
  209. package/dist/src/__tests__/stdlib/ecs.test.d.ts +4 -0
  210. package/dist/src/__tests__/stdlib/ecs.test.js +81 -0
  211. package/dist/src/__tests__/stdlib/effects.test.d.ts +4 -0
  212. package/dist/src/__tests__/stdlib/effects.test.js +72 -0
  213. package/dist/src/__tests__/stdlib/events.test.d.ts +4 -0
  214. package/dist/src/__tests__/stdlib/events.test.js +55 -0
  215. package/dist/src/__tests__/stdlib/expr.test.d.ts +4 -0
  216. package/dist/src/__tests__/stdlib/expr.test.js +77 -0
  217. package/dist/src/__tests__/stdlib/fft.test.d.ts +4 -0
  218. package/dist/src/__tests__/stdlib/fft.test.js +82 -0
  219. package/dist/src/__tests__/stdlib/graph.test.d.ts +4 -0
  220. package/dist/src/__tests__/stdlib/graph.test.js +102 -0
  221. package/dist/src/__tests__/stdlib/interactions.test.d.ts +4 -0
  222. package/dist/src/__tests__/stdlib/interactions.test.js +60 -0
  223. package/dist/src/__tests__/stdlib/inventory.test.d.ts +4 -0
  224. package/dist/src/__tests__/stdlib/inventory.test.js +68 -0
  225. package/dist/src/__tests__/stdlib/linalg.test.d.ts +5 -0
  226. package/dist/src/__tests__/stdlib/linalg.test.js +78 -0
  227. package/dist/src/__tests__/stdlib/map.test.d.ts +1 -0
  228. package/dist/src/__tests__/stdlib/map.test.js +84 -0
  229. package/dist/src/__tests__/stdlib/math.test.js +19 -6
  230. package/dist/src/__tests__/stdlib/math_hp.test.d.ts +4 -0
  231. package/dist/src/__tests__/stdlib/math_hp.test.js +80 -0
  232. package/dist/src/__tests__/stdlib/mobs.test.d.ts +4 -0
  233. package/dist/src/__tests__/stdlib/mobs.test.js +61 -0
  234. package/dist/src/__tests__/stdlib/noise.test.d.ts +4 -0
  235. package/dist/src/__tests__/stdlib/noise.test.js +73 -0
  236. package/dist/src/__tests__/stdlib/ode.test.d.ts +4 -0
  237. package/dist/src/__tests__/stdlib/ode.test.js +68 -0
  238. package/dist/src/__tests__/stdlib/parabola.test.d.ts +4 -0
  239. package/dist/src/__tests__/stdlib/parabola.test.js +77 -0
  240. package/dist/src/__tests__/stdlib/particles.test.d.ts +4 -0
  241. package/dist/src/__tests__/stdlib/particles.test.js +68 -0
  242. package/dist/src/__tests__/stdlib/physics.test.d.ts +4 -0
  243. package/dist/src/__tests__/stdlib/physics.test.js +76 -0
  244. package/dist/src/__tests__/stdlib/player.test.d.ts +4 -0
  245. package/dist/src/__tests__/stdlib/player.test.js +64 -0
  246. package/dist/src/__tests__/stdlib/quaternion.test.d.ts +4 -0
  247. package/dist/src/__tests__/stdlib/quaternion.test.js +73 -0
  248. package/dist/src/__tests__/stdlib/queue.test.d.ts +1 -0
  249. package/dist/src/__tests__/stdlib/queue.test.js +97 -0
  250. package/dist/src/__tests__/stdlib/random.test.d.ts +4 -0
  251. package/dist/src/__tests__/stdlib/random.test.js +76 -0
  252. package/dist/src/__tests__/stdlib/result.test.d.ts +12 -0
  253. package/dist/src/__tests__/stdlib/result.test.js +329 -0
  254. package/dist/src/__tests__/stdlib/scheduler.test.js +19 -8
  255. package/dist/src/__tests__/stdlib/set_int.test.d.ts +1 -0
  256. package/dist/src/__tests__/stdlib/set_int.test.js +88 -0
  257. package/dist/src/__tests__/stdlib/sets.test.d.ts +6 -0
  258. package/dist/src/__tests__/stdlib/sets.test.js +60 -0
  259. package/dist/src/__tests__/stdlib/signal.test.d.ts +4 -0
  260. package/dist/src/__tests__/stdlib/signal.test.js +84 -0
  261. package/dist/src/__tests__/stdlib/spawn.test.d.ts +4 -0
  262. package/dist/src/__tests__/stdlib/spawn.test.js +68 -0
  263. package/dist/src/__tests__/stdlib/string.test.d.ts +12 -0
  264. package/dist/src/__tests__/stdlib/string.test.js +231 -0
  265. package/dist/src/__tests__/stdlib/strings.test.d.ts +4 -0
  266. package/dist/src/__tests__/stdlib/strings.test.js +83 -0
  267. package/dist/src/__tests__/stdlib/tags.test.d.ts +4 -0
  268. package/dist/src/__tests__/stdlib/tags.test.js +57 -0
  269. package/dist/src/__tests__/stdlib/teams.test.d.ts +4 -0
  270. package/dist/src/__tests__/stdlib/teams.test.js +72 -0
  271. package/dist/src/__tests__/stdlib/timer.test.d.ts +4 -0
  272. package/dist/src/__tests__/stdlib/timer.test.js +79 -0
  273. package/dist/src/__tests__/stdlib/vec.test.d.ts +5 -0
  274. package/dist/src/__tests__/stdlib/vec.test.js +94 -0
  275. package/dist/src/__tests__/stdlib/world.test.d.ts +4 -0
  276. package/dist/src/__tests__/stdlib/world.test.js +72 -0
  277. package/dist/src/__tests__/struct-display.test.d.ts +1 -0
  278. package/dist/src/__tests__/struct-display.test.js +64 -0
  279. package/dist/src/__tests__/test-framework/runner.test.d.ts +10 -0
  280. package/dist/src/__tests__/test-framework/runner.test.js +193 -0
  281. package/dist/src/__tests__/tuner/adapters.test.d.ts +14 -0
  282. package/dist/src/__tests__/tuner/adapters.test.js +194 -0
  283. package/dist/src/__tests__/tuner/simulator-extra.test.d.ts +4 -0
  284. package/dist/src/__tests__/tuner/simulator-extra.test.js +193 -0
  285. package/dist/src/__tests__/typechecker-coverage.test.d.ts +30 -0
  286. package/dist/src/__tests__/typechecker-coverage.test.js +627 -0
  287. package/dist/src/__tests__/typechecker.test.js +3 -3
  288. package/dist/src/__tests__/watch-decorator.test.d.ts +1 -0
  289. package/dist/src/__tests__/watch-decorator.test.js +54 -0
  290. package/dist/src/ast/types.d.ts +102 -3
  291. package/dist/src/cache/incremental.d.ts +13 -14
  292. package/dist/src/cache/incremental.js +106 -89
  293. package/dist/src/cache/index.d.ts +8 -2
  294. package/dist/src/cache/index.js +18 -6
  295. package/dist/src/cli.d.ts +1 -0
  296. package/dist/src/cli.js +466 -17
  297. package/dist/src/config/project-config.d.ts +29 -0
  298. package/dist/src/config/project-config.js +180 -0
  299. package/dist/src/diagnostics/index.d.ts +9 -0
  300. package/dist/src/diagnostics/index.js +18 -1
  301. package/dist/src/emit/compile.d.ts +10 -0
  302. package/dist/src/emit/compile.js +395 -50
  303. package/dist/src/emit/index.d.ts +40 -0
  304. package/dist/src/emit/index.js +307 -14
  305. package/dist/src/emit/modules.js +21 -3
  306. package/dist/src/emit/sourcemap.d.ts +23 -27
  307. package/dist/src/emit/sourcemap.js +52 -30
  308. package/dist/src/formatter/index.js +33 -8
  309. package/dist/src/hir/deprecated.d.ts +13 -0
  310. package/dist/src/hir/deprecated.js +218 -0
  311. package/dist/src/hir/lower.js +114 -8
  312. package/dist/src/hir/monomorphize.js +22 -2
  313. package/dist/src/hir/types.d.ts +65 -1
  314. package/dist/src/index.d.ts +6 -0
  315. package/dist/src/index.js +18 -3
  316. package/dist/src/lexer/index.d.ts +2 -1
  317. package/dist/src/lexer/index.js +39 -3
  318. package/dist/src/lint/index.d.ts +45 -0
  319. package/dist/src/lint/index.js +930 -0
  320. package/dist/src/lir/lower.js +29 -2
  321. package/dist/src/lir/types.d.ts +2 -0
  322. package/dist/src/lsp/server.js +92 -5
  323. package/dist/src/mir/lower.js +775 -34
  324. package/dist/src/mir/macro.js +36 -2
  325. package/dist/src/mir/types.d.ts +12 -0
  326. package/dist/src/mir/verify.js +9 -0
  327. package/dist/src/optimizer/auto-inline.d.ts +2 -0
  328. package/dist/src/optimizer/auto-inline.js +67 -0
  329. package/dist/src/optimizer/cse.d.ts +20 -0
  330. package/dist/src/optimizer/cse.js +234 -0
  331. package/dist/src/optimizer/inline.d.ts +26 -0
  332. package/dist/src/optimizer/inline.js +286 -0
  333. package/dist/src/optimizer/interprocedural.js +4 -0
  334. package/dist/src/optimizer/licm.d.ts +32 -0
  335. package/dist/src/optimizer/licm.js +371 -0
  336. package/dist/src/optimizer/pipeline.js +12 -2
  337. package/dist/src/optimizer/strength_reduction.d.ts +15 -0
  338. package/dist/src/optimizer/strength_reduction.js +90 -0
  339. package/dist/src/optimizer/tco.d.ts +53 -0
  340. package/dist/src/optimizer/tco.js +238 -0
  341. package/dist/src/parser/index.d.ts +32 -0
  342. package/dist/src/parser/index.js +421 -59
  343. package/dist/src/repl-server.d.ts +13 -0
  344. package/dist/src/repl-server.js +127 -0
  345. package/dist/src/structs/expand.d.ts +15 -0
  346. package/dist/src/structs/expand.js +46 -0
  347. package/dist/src/testing/runner.d.ts +40 -0
  348. package/dist/src/testing/runner.js +237 -0
  349. package/dist/src/typechecker/index.d.ts +3 -0
  350. package/dist/src/typechecker/index.js +254 -9
  351. package/dist/tsconfig.tsbuildinfo +1 -1
  352. package/doc-drafts/redscript-docs/docs/en/stdlib/graph.md +104 -0
  353. package/doc-drafts/redscript-docs/docs/en/stdlib/parabola.md +113 -0
  354. package/doc-drafts/redscript-docs/docs/en/stdlib/pathfind.md +104 -0
  355. package/doc-drafts/redscript-docs/docs/en/stdlib/physics.md +134 -0
  356. package/doc-drafts/redscript-docs/docs/en/stdlib/quaternion.md +135 -0
  357. package/doc-drafts/redscript-docs/docs/zh/stdlib/graph.md +104 -0
  358. package/doc-drafts/redscript-docs/docs/zh/stdlib/parabola.md +113 -0
  359. package/doc-drafts/redscript-docs/docs/zh/stdlib/pathfind.md +104 -0
  360. package/doc-drafts/redscript-docs/docs/zh/stdlib/physics.md +134 -0
  361. package/doc-drafts/redscript-docs/docs/zh/stdlib/quaternion.md +135 -0
  362. package/docs/stdlib/result.md +156 -0
  363. package/docs/stdlib/result.zh.md +156 -0
  364. package/editors/vscode/fixtures/test.mcrs +7 -0
  365. package/editors/vscode/out/extension.js +2095 -225
  366. package/editors/vscode/out/lsp-server.js +519 -51
  367. package/editors/vscode/package-lock.json +9 -4
  368. package/editors/vscode/package.json +1 -1
  369. package/examples/display-demo.mcrs +64 -0
  370. package/examples/game/racing.mcrs +301 -0
  371. package/examples/game/tower_defense.mcrs +311 -0
  372. package/examples/math/physics_sim.mcrs +322 -0
  373. package/examples/rpg/boss_fight.mcrs +313 -0
  374. package/examples/rpg/health_system.mcrs +237 -0
  375. package/examples/rpg/inventory.mcrs +265 -0
  376. package/examples/util/debug_hud.mcrs +279 -0
  377. package/jest.config.js +10 -0
  378. package/package.json +12 -3
  379. package/playground/index.html +823 -0
  380. package/scripts/gen-docs.ts +533 -0
  381. package/scripts/update-redscript-docs-stdlib.sh +770 -0
  382. package/src/__tests__/budget.test.ts +5 -0
  383. package/src/__tests__/cache/cache-behavior.test.ts +480 -0
  384. package/src/__tests__/cache-extra.test.ts +199 -0
  385. package/src/__tests__/cli-docs.test.ts +77 -0
  386. package/src/__tests__/cli-init.test.ts +91 -0
  387. package/src/__tests__/cli-publish.test.ts +190 -0
  388. package/src/__tests__/cli.test.ts +117 -1
  389. package/src/__tests__/compile-preprocess.test.ts +366 -0
  390. package/src/__tests__/compiler/break-stmt.test.ts +66 -0
  391. package/src/__tests__/compiler/const-decl.test.ts +141 -0
  392. package/src/__tests__/compiler/continue-stmt.test.ts +81 -0
  393. package/src/__tests__/compiler/coroutine-extended.test.ts +723 -0
  394. package/src/__tests__/compiler/deprecated.test.ts +305 -0
  395. package/src/__tests__/compiler/do-while.test.ts +130 -0
  396. package/src/__tests__/compiler/enum-payload.test.ts +299 -0
  397. package/src/__tests__/compiler/interface.test.ts +287 -0
  398. package/src/__tests__/compiler/labeled-loops.test.ts +279 -0
  399. package/src/__tests__/compiler/match-string.test.ts +45 -0
  400. package/src/__tests__/compiler/memoize.test.ts +126 -0
  401. package/src/__tests__/compiler/method-chain.test.ts +121 -0
  402. package/src/__tests__/compiler/module-import.test.ts +240 -0
  403. package/src/__tests__/compiler/option-extensions.test.ts +207 -0
  404. package/src/__tests__/compiler/profile-decorator.test.ts +79 -0
  405. package/src/__tests__/compiler/string-advanced.test.ts +310 -0
  406. package/src/__tests__/compiler/struct-extends.test.ts +109 -0
  407. package/src/__tests__/compiler/throttle-retry.test.ts +191 -0
  408. package/src/__tests__/compiler/tuple-type.test.ts +263 -0
  409. package/src/__tests__/compiler/watch-decorator.test.ts +72 -0
  410. package/src/__tests__/config/project-config.test.ts +181 -0
  411. package/src/__tests__/config-decorator.test.ts +157 -0
  412. package/src/__tests__/diagnostics-extra.test.ts +155 -0
  413. package/src/__tests__/emit/compile-branches.test.ts +135 -0
  414. package/src/__tests__/emit/compile-coverage.test.ts +696 -0
  415. package/src/__tests__/emit/compile-extra-branches.test.ts +228 -0
  416. package/src/__tests__/emit/compile-mocked-branches.test.ts +249 -0
  417. package/src/__tests__/emit/compile.test.ts +6 -1
  418. package/src/__tests__/emit/execute-chain.test.ts +114 -0
  419. package/src/__tests__/emit/index.test.ts +2 -1
  420. package/src/__tests__/emit/modules-branches.test.ts +90 -0
  421. package/src/__tests__/emit/modules-coverage.test.ts +241 -0
  422. package/src/__tests__/emit/modules-errors.test.ts +192 -0
  423. package/src/__tests__/emit/modules-rewrite.test.ts +232 -0
  424. package/src/__tests__/emit/source-map.test.ts +152 -0
  425. package/src/__tests__/enum.test.ts +9 -4
  426. package/src/__tests__/error-recovery.test.ts +226 -0
  427. package/src/__tests__/events-types-extra.test.ts +110 -0
  428. package/src/__tests__/events-types.test.ts +66 -0
  429. package/src/__tests__/formatter.test.ts +15 -5
  430. package/src/__tests__/generics.test.ts +16 -9
  431. package/src/__tests__/hir/lower-extra.test.ts +151 -0
  432. package/src/__tests__/hir/monomorphize-coverage.test.ts +432 -0
  433. package/src/__tests__/hir/monomorphize-extra.test.ts +220 -0
  434. package/src/__tests__/hir/monomorphize-extra2.test.ts +350 -0
  435. package/src/__tests__/impl.test.ts +12 -8
  436. package/src/__tests__/incremental.test.ts +10 -2
  437. package/src/__tests__/index-extra.test.ts +79 -0
  438. package/src/__tests__/lexer.test.ts +2 -2
  439. package/src/__tests__/lint/hir-coverage.test.ts +1716 -0
  440. package/src/__tests__/lint/rules-coverage.test.ts +598 -0
  441. package/src/__tests__/lint/rules.test.ts +230 -0
  442. package/src/__tests__/lir/lower.test.ts +33 -0
  443. package/src/__tests__/lir/verify.test.ts +33 -0
  444. package/src/__tests__/lsp/completion.test.ts +687 -0
  445. package/src/__tests__/lsp/definition.test.ts +499 -0
  446. package/src/__tests__/lsp/diagnostics.test.ts +108 -0
  447. package/src/__tests__/lsp/hover-docs.test.ts +222 -0
  448. package/src/__tests__/lsp.test.ts +4 -1
  449. package/src/__tests__/mc-integration/item-entity-events.test.ts +5 -0
  450. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +5 -0
  451. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1105 -0
  452. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1366 -0
  453. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1245 -0
  454. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +755 -0
  455. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +771 -0
  456. package/src/__tests__/mc-integration/stdlib-coverage.test.ts +5 -0
  457. package/src/__tests__/mc-integration/syntax-coverage.test.ts +5 -0
  458. package/src/__tests__/mc-validator-coverage.test.ts +325 -0
  459. package/src/__tests__/mc-validator-extra.test.ts +252 -0
  460. package/src/__tests__/mir/lower-extra.test.ts +402 -0
  461. package/src/__tests__/mir/lower-extra2.test.ts +348 -0
  462. package/src/__tests__/mir/lower-extra3.test.ts +277 -0
  463. package/src/__tests__/mir/lower-extra4.test.ts +636 -0
  464. package/src/__tests__/mir/lower-extra5.test.ts +612 -0
  465. package/src/__tests__/mir/lower-extra6.test.ts +520 -0
  466. package/src/__tests__/mir/lower-extra7.test.ts +1045 -0
  467. package/src/__tests__/mir/lower-extra8.test.ts +704 -0
  468. package/src/__tests__/mir/lower-extra9.test.ts +821 -0
  469. package/src/__tests__/optimizer/auto-inline.test.ts +206 -0
  470. package/src/__tests__/optimizer/cse.test.ts +195 -0
  471. package/src/__tests__/optimizer/inline_fn.test.ts +263 -0
  472. package/src/__tests__/optimizer/licm.test.ts +358 -0
  473. package/src/__tests__/optimizer/nbt-coalesce.test.ts +147 -0
  474. package/src/__tests__/optimizer/optimizer-extended.test.ts +1081 -0
  475. package/src/__tests__/optimizer/scoreboard-batch.test.ts +141 -0
  476. package/src/__tests__/optimizer/strength-reduction.test.ts +111 -0
  477. package/src/__tests__/optimizer/tco-coverage.test.ts +309 -0
  478. package/src/__tests__/optimizer/tco.test.ts +238 -0
  479. package/src/__tests__/option.test.ts +14 -7
  480. package/src/__tests__/parser-coverage.test.ts +576 -0
  481. package/src/__tests__/parser-extra.test.ts +531 -0
  482. package/src/__tests__/parser.test.ts +14 -0
  483. package/src/__tests__/repl-extra.test.ts +195 -0
  484. package/src/__tests__/repl-server-extra.test.ts +150 -0
  485. package/src/__tests__/repl-server.test.ts +122 -0
  486. package/src/__tests__/runtime-extra.test.ts +862 -0
  487. package/src/__tests__/singleton-decorator.test.ts +285 -0
  488. package/src/__tests__/sourcemap.test.ts +1 -1
  489. package/src/__tests__/stdlib/advanced.test.ts +312 -0
  490. package/src/__tests__/stdlib/bigint.test.ts +57 -0
  491. package/src/__tests__/stdlib/bits.test.ts +75 -0
  492. package/src/__tests__/stdlib/bossbar.test.ts +45 -0
  493. package/src/__tests__/stdlib/color.test.ts +60 -0
  494. package/src/__tests__/stdlib/combat.test.ts +35 -0
  495. package/src/__tests__/stdlib/cooldown.test.ts +35 -0
  496. package/src/__tests__/stdlib/dialog.test.ts +14 -6
  497. package/src/__tests__/stdlib/ecs.test.ts +54 -0
  498. package/src/__tests__/stdlib/effects.test.ts +45 -0
  499. package/src/__tests__/stdlib/events.test.ts +23 -0
  500. package/src/__tests__/stdlib/expr.test.ts +48 -0
  501. package/src/__tests__/stdlib/fft.test.ts +54 -0
  502. package/src/__tests__/stdlib/graph.test.ts +77 -0
  503. package/src/__tests__/stdlib/interactions.test.ts +30 -0
  504. package/src/__tests__/stdlib/inventory.test.ts +40 -0
  505. package/src/__tests__/stdlib/linalg.test.ts +52 -0
  506. package/src/__tests__/stdlib/map.test.ts +55 -0
  507. package/src/__tests__/stdlib/math.test.ts +19 -5
  508. package/src/__tests__/stdlib/math_hp.test.ts +55 -0
  509. package/src/__tests__/stdlib/mobs.test.ts +40 -0
  510. package/src/__tests__/stdlib/noise.test.ts +46 -0
  511. package/src/__tests__/stdlib/ode.test.ts +40 -0
  512. package/src/__tests__/stdlib/parabola.test.ts +51 -0
  513. package/src/__tests__/stdlib/particles.test.ts +40 -0
  514. package/src/__tests__/stdlib/physics.test.ts +50 -0
  515. package/src/__tests__/stdlib/player.test.ts +35 -0
  516. package/src/__tests__/stdlib/quaternion.test.ts +46 -0
  517. package/src/__tests__/stdlib/queue.test.ts +73 -0
  518. package/src/__tests__/stdlib/random.test.ts +50 -0
  519. package/src/__tests__/stdlib/result.test.ts +326 -0
  520. package/src/__tests__/stdlib/scheduler.test.ts +18 -7
  521. package/src/__tests__/stdlib/set_int.test.ts +62 -0
  522. package/src/__tests__/stdlib/sets.test.ts +28 -0
  523. package/src/__tests__/stdlib/signal.test.ts +60 -0
  524. package/src/__tests__/stdlib/spawn.test.ts +40 -0
  525. package/src/__tests__/stdlib/string.test.ts +224 -0
  526. package/src/__tests__/stdlib/strings.test.ts +55 -0
  527. package/src/__tests__/stdlib/tags.test.ts +32 -0
  528. package/src/__tests__/stdlib/teams.test.ts +45 -0
  529. package/src/__tests__/stdlib/timer.test.ts +53 -0
  530. package/src/__tests__/stdlib/vec.test.ts +72 -0
  531. package/src/__tests__/stdlib/world.test.ts +45 -0
  532. package/src/__tests__/struct-display.test.ts +69 -0
  533. package/src/__tests__/test-framework/runner.test.ts +208 -0
  534. package/src/__tests__/tuner/adapters.test.ts +232 -0
  535. package/src/__tests__/tuner/simulator-extra.test.ts +222 -0
  536. package/src/__tests__/tuple.test.ts +11 -4
  537. package/src/__tests__/typechecker-coverage.test.ts +671 -0
  538. package/src/__tests__/typechecker.test.ts +4 -3
  539. package/src/__tests__/watch-decorator.test.ts +59 -0
  540. package/src/ast/types.ts +65 -3
  541. package/src/cache/incremental.ts +128 -99
  542. package/src/cache/index.ts +35 -8
  543. package/src/cli.ts +538 -29
  544. package/src/config/project-config.ts +176 -0
  545. package/src/diagnostics/index.ts +22 -0
  546. package/src/docs.ts +98 -0
  547. package/src/emit/compile.ts +408 -51
  548. package/src/emit/index.ts +366 -18
  549. package/src/emit/modules.ts +19 -3
  550. package/src/emit/sourcemap.ts +64 -43
  551. package/src/formatter/index.ts +35 -8
  552. package/src/hir/deprecated.ts +212 -0
  553. package/src/hir/lower.ts +128 -8
  554. package/src/hir/monomorphize.ts +24 -2
  555. package/src/hir/types.ts +26 -1
  556. package/src/index.ts +23 -3
  557. package/src/lexer/index.ts +45 -6
  558. package/src/lint/index.ts +922 -0
  559. package/src/lir/lower.ts +30 -2
  560. package/src/lir/types.ts +4 -0
  561. package/src/lsp/server.ts +100 -1
  562. package/src/mir/lower.ts +785 -40
  563. package/src/mir/macro.ts +30 -2
  564. package/src/mir/types.ts +13 -0
  565. package/src/mir/verify.ts +10 -2
  566. package/src/optimizer/auto-inline.ts +86 -0
  567. package/src/optimizer/copy_prop.ts +2 -2
  568. package/src/optimizer/coroutine.ts +3 -3
  569. package/src/optimizer/cse.ts +205 -0
  570. package/src/optimizer/dce.ts +2 -2
  571. package/src/optimizer/inline.ts +335 -0
  572. package/src/optimizer/interprocedural.ts +5 -1
  573. package/src/optimizer/licm.ts +454 -0
  574. package/src/optimizer/nbt-coalesce.ts +109 -0
  575. package/src/optimizer/pipeline.ts +16 -2
  576. package/src/optimizer/scoreboard-batch.ts +52 -0
  577. package/src/optimizer/strength_reduction.ts +95 -0
  578. package/src/optimizer/tco.ts +267 -0
  579. package/src/optimizer/unroll.ts +2 -2
  580. package/src/parser/index.ts +426 -53
  581. package/src/repl-server.ts +102 -0
  582. package/src/stdlib/advanced.mcrs +271 -101
  583. package/src/stdlib/bigint.mcrs +97 -11
  584. package/src/stdlib/bits.mcrs +75 -12
  585. package/src/stdlib/bossbar.mcrs +37 -8
  586. package/src/stdlib/calculus.mcrs +82 -26
  587. package/src/stdlib/color.mcrs +98 -16
  588. package/src/stdlib/combat.mcrs +23 -5
  589. package/src/stdlib/cooldown.mcrs +19 -0
  590. package/src/stdlib/dialog.mcrs +45 -7
  591. package/src/stdlib/easing.mcrs +132 -12
  592. package/src/stdlib/ecs.mcrs +142 -25
  593. package/src/stdlib/effects.mcrs +88 -12
  594. package/src/stdlib/events.mcrs +21 -2
  595. package/src/stdlib/expr.mcrs +18 -3
  596. package/src/stdlib/fft.mcrs +66 -56
  597. package/src/stdlib/geometry.mcrs +137 -39
  598. package/src/stdlib/graph.mcrs +73 -0
  599. package/src/stdlib/heap.mcrs +49 -8
  600. package/src/stdlib/i18n/zh.yaml +2891 -0
  601. package/src/stdlib/interactions.mcrs +43 -20
  602. package/src/stdlib/inventory.mcrs +14 -3
  603. package/src/stdlib/linalg.mcrs +185 -30
  604. package/src/stdlib/list.mcrs +168 -18
  605. package/src/stdlib/map.mcrs +112 -0
  606. package/src/stdlib/math.mcrs +68 -18
  607. package/src/stdlib/math_hp.mcrs +124 -33
  608. package/src/stdlib/matrix.mcrs +133 -20
  609. package/src/stdlib/mobs.mcrs +87 -0
  610. package/src/stdlib/noise.mcrs +65 -21
  611. package/src/stdlib/ode.mcrs +96 -0
  612. package/src/stdlib/parabola.mcrs +104 -29
  613. package/src/stdlib/particles.mcrs +78 -21
  614. package/src/stdlib/pathfind.mcrs +89 -35
  615. package/src/stdlib/physics.mcrs +134 -26
  616. package/src/stdlib/player.mcrs +18 -0
  617. package/src/stdlib/quaternion.mcrs +213 -9
  618. package/src/stdlib/queue.mcrs +123 -0
  619. package/src/stdlib/random.mcrs +63 -18
  620. package/src/stdlib/result.mcrs +111 -0
  621. package/src/stdlib/scheduler.mcrs +59 -10
  622. package/src/stdlib/set_int.mcrs +240 -0
  623. package/src/stdlib/sets.mcrs +49 -19
  624. package/src/stdlib/signal.mcrs +151 -79
  625. package/src/stdlib/sort.mcrs +44 -24
  626. package/src/stdlib/spawn.mcrs +30 -7
  627. package/src/stdlib/state.mcrs +40 -5
  628. package/src/stdlib/strings.mcrs +131 -3
  629. package/src/stdlib/tags.mcrs +2 -2
  630. package/src/stdlib/teams.mcrs +22 -10
  631. package/src/stdlib/timer.mcrs +36 -6
  632. package/src/stdlib/vec.mcrs +44 -9
  633. package/src/stdlib/world.mcrs +57 -25
  634. package/src/structs/expand.ts +64 -0
  635. package/src/testing/runner.ts +271 -0
  636. package/src/typechecker/index.ts +273 -9
@@ -10,7 +10,8 @@ import type {
10
10
  Block, ConstDecl, Decorator, EntitySelector, Expr, FnDecl, GlobalDecl, LiteralExpr, Param,
11
11
  Program, RangeExpr, SelectorFilter, SelectorKind, Span, Stmt, TypeNode, AssignOp,
12
12
  StructDecl, StructField, ExecuteSubcommand, EnumDecl, EnumVariant, BlockPosExpr, ImplBlock,
13
- CoordComponent, LambdaParam, EntityTypeName, ImportDecl, MatchPattern
13
+ CoordComponent, LambdaParam, EntityTypeName, ImportDecl, MatchPattern,
14
+ InterfaceDecl, InterfaceMethod
14
15
  } from '../ast/types'
15
16
  import type { BinOp, CmpOp } from '../ast/types'
16
17
  import { DiagnosticError } from '../diagnostics'
@@ -83,6 +84,8 @@ export class Parser {
83
84
  private inLibraryMode: boolean = false
84
85
  /** Warnings accumulated during parsing (e.g. deprecated keyword usage). */
85
86
  readonly warnings: string[] = []
87
+ /** Parse errors collected during error-recovery mode. */
88
+ readonly parseErrors: DiagnosticError[] = []
86
89
 
87
90
  constructor(tokens: Token[], source?: string, filePath?: string) {
88
91
  this.tokens = tokens
@@ -164,6 +167,54 @@ export class Parser {
164
167
  return { kind: 'eof', value: '', line: span.line, col: span.col }
165
168
  }
166
169
 
170
+ // -------------------------------------------------------------------------
171
+ // Error Recovery
172
+ // -------------------------------------------------------------------------
173
+
174
+ /**
175
+ * Synchronize to the next top-level declaration boundary after a parse error.
176
+ * Skips tokens until we find a keyword that starts a top-level declaration,
177
+ * or a `}` (end of a block), or EOF.
178
+ */
179
+ private syncToNextDecl(): void {
180
+ const TOP_LEVEL_KEYWORDS = new Set([
181
+ 'fn', 'struct', 'impl', 'enum', 'const', 'let', 'export', 'declare', 'import', 'namespace', 'module'
182
+ ])
183
+ while (!this.check('eof')) {
184
+ const kind = this.peek().kind
185
+ if (kind === '}') {
186
+ this.advance() // consume the stray `}`
187
+ return
188
+ }
189
+ if (TOP_LEVEL_KEYWORDS.has(kind)) {
190
+ return
191
+ }
192
+ // Also recover on a plain ident that could be 'import' keyword used as ident
193
+ if (kind === 'ident' && this.peek().value === 'import') {
194
+ return
195
+ }
196
+ this.advance()
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Synchronize to the next statement boundary inside a block after a parse error.
202
+ * Skips tokens until we reach `;`, `}`, or EOF.
203
+ */
204
+ private syncToNextStmt(): void {
205
+ while (!this.check('eof')) {
206
+ const kind = this.peek().kind
207
+ if (kind === ';') {
208
+ this.advance() // consume the `;`
209
+ return
210
+ }
211
+ if (kind === '}') {
212
+ return // leave `}` for parseBlock to consume
213
+ }
214
+ this.advance()
215
+ }
216
+ }
217
+
167
218
  // -------------------------------------------------------------------------
168
219
  // Program
169
220
  // -------------------------------------------------------------------------
@@ -177,6 +228,7 @@ export class Parser {
177
228
  const enums: EnumDecl[] = []
178
229
  const consts: ConstDecl[] = []
179
230
  const imports: ImportDecl[] = []
231
+ const interfaces: InterfaceDecl[] = []
180
232
  let isLibrary = false
181
233
  let moduleName: string | undefined
182
234
 
@@ -207,43 +259,81 @@ export class Parser {
207
259
 
208
260
  // Parse struct, function, and import declarations
209
261
  while (!this.check('eof')) {
210
- if (this.check('let')) {
211
- globals.push(this.parseGlobalDecl(true))
212
- } else if (this.check('struct')) {
213
- structs.push(this.parseStructDecl())
214
- } else if (this.check('impl')) {
215
- implBlocks.push(this.parseImplBlock())
216
- } else if (this.check('enum')) {
217
- enums.push(this.parseEnumDecl())
218
- } else if (this.check('const')) {
219
- consts.push(this.parseConstDecl())
220
- } else if (this.check('declare')) {
221
- // Declaration-only stub (e.g. from builtins.d.mcrs) — parse and discard
222
- this.advance() // consume 'declare'
223
- this.parseDeclareStub()
224
- } else if (this.check('export')) {
225
- declarations.push(this.parseExportedFnDecl())
226
- } else if (this.check('ident') && this.peek().value === 'import') {
227
- // `import math::sin;` or `import math::*;`
228
- this.advance() // consume 'import'
229
- const importToken = this.peek()
230
- const modName = this.expect('ident').value
231
- this.expect('::')
232
- let symbol: string
233
- if (this.check('*')) {
234
- this.advance()
235
- symbol = '*'
262
+ try {
263
+ if (this.check('decorator') && this.peek().value.startsWith('@config')) {
264
+ // @config decorator on a global let
265
+ const decorToken = this.advance()
266
+ const decorator = this.parseDecoratorValue(decorToken.value)
267
+ if (!this.check('let')) {
268
+ this.error('@config decorator must be followed by a let declaration')
269
+ }
270
+ const g = this.parseGlobalDecl(true)
271
+ g.configKey = decorator.args?.configKey
272
+ g.configDefault = decorator.args?.configDefault
273
+ globals.push(g)
274
+ } else if (this.check('let')) {
275
+ globals.push(this.parseGlobalDecl(true))
276
+ } else if (this.check('decorator') && this.peek().value === '@singleton') {
277
+ // @singleton decorator on a struct
278
+ this.advance() // consume '@singleton'
279
+ if (!this.check('struct')) {
280
+ this.error('@singleton decorator must be followed by a struct declaration')
281
+ }
282
+ const s = this.parseStructDecl()
283
+ s.isSingleton = true
284
+ structs.push(s)
285
+ } else if (this.check('struct')) {
286
+ structs.push(this.parseStructDecl())
287
+ } else if (this.check('impl')) {
288
+ implBlocks.push(this.parseImplBlock())
289
+ } else if (this.check('interface')) {
290
+ interfaces.push(this.parseInterfaceDecl())
291
+ } else if (this.check('enum')) {
292
+ enums.push(this.parseEnumDecl())
293
+ } else if (this.check('const')) {
294
+ consts.push(this.parseConstDecl())
295
+ } else if (this.check('declare')) {
296
+ // Declaration-only stub (e.g. from builtins.d.mcrs) — parse and discard
297
+ this.advance() // consume 'declare'
298
+ this.parseDeclareStub()
299
+ } else if (this.check('export')) {
300
+ declarations.push(this.parseExportedFnDecl())
301
+ } else if (this.check('import') || (this.check('ident') && this.peek().value === 'import')) {
302
+ // `import math::sin;` or `import math::*;` or `import player_utils;` (whole-module file import)
303
+ this.advance() // consume 'import' (keyword or ident)
304
+ const importToken = this.peek()
305
+ const modName = this.expect('ident').value
306
+ // Check for `::` — if present, this is a symbol import; otherwise, whole-module import
307
+ if (this.check('::')) {
308
+ this.advance() // consume '::'
309
+ let symbol: string
310
+ if (this.check('*')) {
311
+ this.advance()
312
+ symbol = '*'
313
+ } else {
314
+ symbol = this.expect('ident').value
315
+ }
316
+ this.match(';')
317
+ imports.push(this.withLoc({ moduleName: modName, symbol }, importToken))
318
+ } else {
319
+ // Whole-module import: `import player_utils;`
320
+ this.match(';')
321
+ imports.push(this.withLoc({ moduleName: modName, symbol: undefined }, importToken))
322
+ }
236
323
  } else {
237
- symbol = this.expect('ident').value
324
+ declarations.push(this.parseFnDecl())
325
+ }
326
+ } catch (err) {
327
+ if (err instanceof DiagnosticError) {
328
+ this.parseErrors.push(err)
329
+ this.syncToNextDecl()
330
+ } else {
331
+ throw err
238
332
  }
239
- this.match(';')
240
- imports.push(this.withLoc({ moduleName: modName, symbol }, importToken))
241
- } else {
242
- declarations.push(this.parseFnDecl())
243
333
  }
244
334
  }
245
335
 
246
- return { namespace, moduleName, globals, declarations, structs, implBlocks, enums, consts, imports, isLibrary }
336
+ return { namespace, moduleName, globals, declarations, structs, implBlocks, enums, consts, imports, interfaces, isLibrary }
247
337
  }
248
338
 
249
339
  // -------------------------------------------------------------------------
@@ -253,6 +343,7 @@ export class Parser {
253
343
  private parseStructDecl(): StructDecl {
254
344
  const structToken = this.expect('struct')
255
345
  const name = this.expect('ident').value
346
+ const extendsName = this.match('extends') ? this.expect('ident').value : undefined
256
347
  this.expect('{')
257
348
 
258
349
  const fields: StructField[] = []
@@ -267,7 +358,7 @@ export class Parser {
267
358
  }
268
359
 
269
360
  this.expect('}')
270
- return this.withLoc({ name, fields }, structToken)
361
+ return this.withLoc({ name, extends: extendsName, fields }, structToken)
271
362
  }
272
363
 
273
364
  private parseEnumDecl(): EnumDecl {
@@ -282,6 +373,21 @@ export class Parser {
282
373
  const variantToken = this.expect('ident')
283
374
  const variant: EnumVariant = { name: variantToken.value }
284
375
 
376
+ // Payload fields: Variant(field: Type, ...)
377
+ if (this.check('(')) {
378
+ this.advance() // consume '('
379
+ const fields: { name: string; type: TypeNode }[] = []
380
+ while (!this.check(')') && !this.check('eof')) {
381
+ const fieldName = this.expect('ident').value
382
+ this.expect(':')
383
+ const fieldType = this.parseType()
384
+ fields.push({ name: fieldName, type: fieldType })
385
+ if (!this.match(',')) break
386
+ }
387
+ this.expect(')')
388
+ variant.fields = fields
389
+ }
390
+
285
391
  if (this.match('=')) {
286
392
  const valueToken = this.expect('int_lit')
287
393
  variant.value = parseInt(valueToken.value, 10)
@@ -303,7 +409,15 @@ export class Parser {
303
409
 
304
410
  private parseImplBlock(): ImplBlock {
305
411
  const implToken = this.expect('impl')
306
- const typeName = this.expect('ident').value
412
+ let traitName: string | undefined
413
+ let typeName: string
414
+ const firstName = this.expect('ident').value
415
+ if (this.match('for')) {
416
+ traitName = firstName
417
+ typeName = this.expect('ident').value
418
+ } else {
419
+ typeName = firstName
420
+ }
307
421
  this.expect('{')
308
422
 
309
423
  const methods: FnDecl[] = []
@@ -312,7 +426,63 @@ export class Parser {
312
426
  }
313
427
 
314
428
  this.expect('}')
315
- return this.withLoc({ kind: 'impl_block', typeName, methods }, implToken)
429
+ return this.withLoc({ kind: 'impl_block', traitName, typeName, methods }, implToken)
430
+ }
431
+
432
+ /**
433
+ * Parse an interface declaration:
434
+ * interface <Name> {
435
+ * fn <method>(<params>): <retType>
436
+ * ...
437
+ * }
438
+ * Method signatures have no body — they are prototype-only.
439
+ */
440
+ private parseInterfaceDecl(): InterfaceDecl {
441
+ const ifaceToken = this.expect('interface')
442
+ const name = this.expect('ident').value
443
+ this.expect('{')
444
+
445
+ const methods: InterfaceMethod[] = []
446
+ while (!this.check('}') && !this.check('eof')) {
447
+ const fnToken = this.expect('fn')
448
+ const methodName = this.expect('ident').value
449
+ this.expect('(')
450
+ const params = this.parseInterfaceParams()
451
+ this.expect(')')
452
+ let returnType: TypeNode | undefined
453
+ if (this.match(':')) {
454
+ returnType = this.parseType()
455
+ }
456
+ // No body — interface methods are signature-only
457
+ methods.push(this.withLoc({ name: methodName, params, returnType }, fnToken) as InterfaceMethod)
458
+ }
459
+
460
+ this.expect('}')
461
+ return this.withLoc({ name, methods }, ifaceToken) as InterfaceDecl
462
+ }
463
+
464
+ /**
465
+ * Parse interface method params — like parseParams but allows bare `self`
466
+ * (no `:` required for the first param named 'self').
467
+ */
468
+ private parseInterfaceParams(): Param[] {
469
+ const params: Param[] = []
470
+ if (!this.check(')')) {
471
+ do {
472
+ const paramToken = this.expect('ident')
473
+ const paramName = paramToken.value
474
+ let type: TypeNode
475
+ if (params.length === 0 && paramName === 'self' && !this.check(':')) {
476
+ // self without type annotation — use a sentinel struct type
477
+ type = { kind: 'named', name: 'void' }
478
+ } else {
479
+ this.expect(':')
480
+ type = this.parseType()
481
+ }
482
+ params.push(this.withLoc({ name: paramName, type }, paramToken))
483
+ } while (this.match(','))
484
+ }
485
+ return params
316
486
  }
317
487
 
318
488
  private parseConstDecl(): ConstDecl {
@@ -340,8 +510,14 @@ export class Parser {
340
510
  const name = this.expect('ident').value
341
511
  this.expect(':')
342
512
  const type = this.parseType()
343
- this.expect('=')
344
- const init = this.parseExpr()
513
+ let init: Expr
514
+ if (this.match('=')) {
515
+ init = this.parseExpr()
516
+ } else {
517
+ // No init — valid only for @config-decorated globals (resolved later)
518
+ // Use a placeholder zero literal; will be replaced in compile step
519
+ init = { kind: 'int_lit', value: 0 }
520
+ }
345
521
  this.match(';')
346
522
  return this.withLoc({ kind: 'global', name, type, init, mutable }, token)
347
523
  }
@@ -360,6 +536,7 @@ export class Parser {
360
536
 
361
537
  private parseFnDecl(implTypeName?: string): FnDecl {
362
538
  const decorators = this.parseDecorators()
539
+ const watchObjective = decorators.find(decorator => decorator.name === 'watch')?.args?.objective
363
540
 
364
541
  // Map @keep decorator to isExported flag (backward compat)
365
542
  let isExported: boolean | undefined
@@ -400,7 +577,7 @@ export class Parser {
400
577
 
401
578
  const fn: import('../ast/types').FnDecl = this.withLoc(
402
579
  { name, typeParams, params, returnType, decorators: filteredDecorators, body,
403
- isLibraryFn: this.inLibraryMode || undefined, isExported },
580
+ isLibraryFn: this.inLibraryMode || undefined, isExported, watchObjective },
404
581
  fnToken,
405
582
  )
406
583
  if (fn.span && closingBraceLine) fn.span.endLine = closingBraceLine
@@ -439,8 +616,9 @@ export class Parser {
439
616
  }
440
617
 
441
618
  private parseDecoratorValue(value: string): Decorator {
442
- // Parse @tick, @on(PlayerDeath), or @on_trigger("name")
443
- const match = value.match(/^@(\w+)(?:\(([^)]*)\))?$/)
619
+ // Parse @tick, @on(PlayerDeath), @on_trigger("name"), or @deprecated("msg with ) parens")
620
+ // Use a greedy match for args that allows any content inside the outermost parens.
621
+ const match = value.match(/^@([A-Za-z_][A-Za-z0-9_-]*)(?:\((.*)\))?$/s)
444
622
  if (!match) {
445
623
  this.error(`Invalid decorator: ${value}`)
446
624
  }
@@ -452,6 +630,10 @@ export class Parser {
452
630
  return { name }
453
631
  }
454
632
 
633
+ if (name === 'profile' || name === 'memoize') {
634
+ this.error(`@${name} decorator does not accept arguments`)
635
+ }
636
+
455
637
  const args: Decorator['args'] = {}
456
638
 
457
639
  if (name === 'on') {
@@ -462,11 +644,13 @@ export class Parser {
462
644
  }
463
645
  }
464
646
 
465
- // Handle @on_trigger("name"), @on_advancement("id"), @on_craft("item"), @on_join_team("team")
466
- if (name === 'on_trigger' || name === 'on_advancement' || name === 'on_craft' || name === 'on_join_team') {
647
+ // Handle @watch("objective"), @on_trigger("name"), @on_advancement("id"), @on_craft("item"), @on_join_team("team")
648
+ if (name === 'watch' || name === 'on_trigger' || name === 'on_advancement' || name === 'on_craft' || name === 'on_join_team') {
467
649
  const strMatch = argsStr.match(/^"([^"]*)"$/)
468
650
  if (strMatch) {
469
- if (name === 'on_trigger') {
651
+ if (name === 'watch') {
652
+ args.objective = strMatch[1]
653
+ } else if (name === 'on_trigger') {
470
654
  args.trigger = strMatch[1]
471
655
  } else if (name === 'on_advancement') {
472
656
  args.advancement = strMatch[1]
@@ -479,6 +663,41 @@ export class Parser {
479
663
  }
480
664
  }
481
665
 
666
+ // Handle @config("key", default: value)
667
+ if (name === 'config') {
668
+ // Format: @config("key_name", default: 42)
669
+ const configMatch = argsStr.match(/^"([^"]+)"\s*,\s*default\s*:\s*(-?\d+(?:\.\d+)?)$/)
670
+ if (configMatch) {
671
+ return { name, args: { configKey: configMatch[1], configDefault: parseFloat(configMatch[2]) } }
672
+ }
673
+ // Format: @config("key_name") — no default
674
+ const keyOnlyMatch = argsStr.match(/^"([^"]+)"$/)
675
+ if (keyOnlyMatch) {
676
+ return { name, args: { configKey: keyOnlyMatch[1] } }
677
+ }
678
+ this.error(`Invalid @config syntax. Expected: @config("key", default: value) or @config("key")`)
679
+ }
680
+
681
+ // Handle @deprecated("message")
682
+ if (name === 'deprecated') {
683
+ const strMatch = argsStr.match(/^"([^"]*)"$/)
684
+ if (strMatch) {
685
+ return { name, args: { message: strMatch[1] } }
686
+ }
687
+ // @deprecated with no message string
688
+ return { name, args: {} }
689
+ }
690
+
691
+ // @test("label") — marks a test function with a human-readable label
692
+ if (name === 'test') {
693
+ const strMatch = argsStr.match(/^"([^"]*)"$/)
694
+ if (strMatch) {
695
+ return { name, args: { testLabel: strMatch[1] } }
696
+ }
697
+ // @test with no label — use empty string
698
+ return { name, args: { testLabel: '' } }
699
+ }
700
+
482
701
  // @require_on_load(fn_name) — when this fn is used, fn_name is called from __load.
483
702
  // Accepts bare identifiers (with optional leading _) or quoted strings.
484
703
  if (name === 'require_on_load') {
@@ -519,6 +738,8 @@ export class Parser {
519
738
  args.item = val
520
739
  } else if (key === 'team') {
521
740
  args.team = val
741
+ } else if (key === 'max') {
742
+ args.max = parseInt(val, 10)
522
743
  }
523
744
  }
524
745
 
@@ -645,7 +866,16 @@ export class Parser {
645
866
  const stmts: Stmt[] = []
646
867
 
647
868
  while (!this.check('}') && !this.check('eof')) {
648
- stmts.push(this.parseStmt())
869
+ try {
870
+ stmts.push(this.parseStmt())
871
+ } catch (err) {
872
+ if (err instanceof DiagnosticError) {
873
+ this.parseErrors.push(err)
874
+ this.syncToNextStmt()
875
+ } else {
876
+ throw err
877
+ }
878
+ }
649
879
  }
650
880
 
651
881
  this.expect('}')
@@ -658,21 +888,38 @@ export class Parser {
658
888
  return this.parseLetStmt()
659
889
  }
660
890
 
891
+ // Const declaration (local)
892
+ if (this.check('const')) {
893
+ return this.parseLocalConstDecl()
894
+ }
895
+
661
896
  // Return statement
662
897
  if (this.check('return')) {
663
898
  return this.parseReturnStmt()
664
899
  }
665
900
 
666
- // Break statement
901
+ // Break statement (with optional label: break outer)
667
902
  if (this.check('break')) {
668
903
  const token = this.advance()
904
+ // Check if next token is an identifier (label name)
905
+ if (this.check('ident')) {
906
+ const labelToken = this.advance()
907
+ this.match(';')
908
+ return this.withLoc({ kind: 'break_label', label: labelToken.value }, token)
909
+ }
669
910
  this.match(';')
670
911
  return this.withLoc({ kind: 'break' }, token)
671
912
  }
672
913
 
673
- // Continue statement
914
+ // Continue statement (with optional label: continue outer)
674
915
  if (this.check('continue')) {
675
916
  const token = this.advance()
917
+ // Check if next token is an identifier (label name)
918
+ if (this.check('ident')) {
919
+ const labelToken = this.advance()
920
+ this.match(';')
921
+ return this.withLoc({ kind: 'continue_label', label: labelToken.value }, token)
922
+ }
676
923
  this.match(';')
677
924
  return this.withLoc({ kind: 'continue' }, token)
678
925
  }
@@ -682,11 +929,45 @@ export class Parser {
682
929
  return this.parseIfStmt()
683
930
  }
684
931
 
932
+ // Labeled loop: ident ':' (while|for|foreach|repeat)
933
+ if (this.check('ident') && this.peek(1).kind === ':') {
934
+ const labelToken = this.advance() // consume ident
935
+ const colonToken = this.advance() // consume ':'
936
+ // Now parse the loop body
937
+ let loopStmt: Stmt
938
+ if (this.check('while')) {
939
+ loopStmt = this.parseWhileStmt()
940
+ } else if (this.check('for')) {
941
+ loopStmt = this.parseForStmt()
942
+ } else if (this.check('foreach')) {
943
+ loopStmt = this.parseForeachStmt()
944
+ } else if (this.check('repeat')) {
945
+ loopStmt = this.parseRepeatStmt()
946
+ } else {
947
+ throw new DiagnosticError(
948
+ 'ParseError',
949
+ `Expected loop statement after label '${labelToken.value}:', found '${this.peek().kind}'`,
950
+ { line: labelToken.line, col: labelToken.col },
951
+ )
952
+ }
953
+ return this.withLoc({ kind: 'labeled_loop', label: labelToken.value, body: loopStmt }, labelToken)
954
+ }
955
+
685
956
  // While statement
686
957
  if (this.check('while')) {
687
958
  return this.parseWhileStmt()
688
959
  }
689
960
 
961
+ // Do-while statement
962
+ if (this.check('do')) {
963
+ return this.parseDoWhileStmt()
964
+ }
965
+
966
+ // Repeat N statement
967
+ if (this.check('repeat')) {
968
+ return this.parseRepeatStmt()
969
+ }
970
+
690
971
  // For statement
691
972
  if (this.check('for')) {
692
973
  return this.parseForStmt()
@@ -763,6 +1044,17 @@ export class Parser {
763
1044
  return this.withLoc({ kind: 'let', name, type, init }, letToken)
764
1045
  }
765
1046
 
1047
+ private parseLocalConstDecl(): Stmt {
1048
+ const constToken = this.expect('const')
1049
+ const name = this.expect('ident').value
1050
+ this.expect(':')
1051
+ const type = this.parseType()
1052
+ this.expect('=')
1053
+ const value = this.parseExpr()
1054
+ this.match(';')
1055
+ return this.withLoc({ kind: 'const_decl', name, type, value }, constToken)
1056
+ }
1057
+
766
1058
  private parseReturnStmt(): Stmt {
767
1059
  const returnToken = this.expect('return')
768
1060
 
@@ -801,9 +1093,7 @@ export class Parser {
801
1093
  return this.withLoc({ kind: 'if_let_some', binding, init, then, else_ }, ifToken)
802
1094
  }
803
1095
 
804
- this.expect('(')
805
- const cond = this.parseExpr()
806
- this.expect(')')
1096
+ const cond = this.parseParenOptionalCond()
807
1097
  const then = this.parseBlock()
808
1098
 
809
1099
  let else_: Block | undefined
@@ -821,14 +1111,52 @@ export class Parser {
821
1111
 
822
1112
  private parseWhileStmt(): Stmt {
823
1113
  const whileToken = this.expect('while')
824
- this.expect('(')
825
- const cond = this.parseExpr()
826
- this.expect(')')
1114
+
1115
+ // while let Some(x) = expr { ... }
1116
+ if (this.check('let') && this.peek(1).kind === 'ident' && this.peek(1).value === 'Some') {
1117
+ this.advance() // consume 'let'
1118
+ this.advance() // consume 'Some'
1119
+ this.expect('(')
1120
+ const binding = this.expect('ident').value
1121
+ this.expect(')')
1122
+ this.expect('=')
1123
+ const init = this.parseExpr()
1124
+ const body = this.parseBlock()
1125
+ return this.withLoc({ kind: 'while_let_some', binding, init, body }, whileToken)
1126
+ }
1127
+
1128
+ const cond = this.parseParenOptionalCond()
827
1129
  const body = this.parseBlock()
828
1130
 
829
1131
  return this.withLoc({ kind: 'while', cond, body }, whileToken)
830
1132
  }
831
1133
 
1134
+ private parseDoWhileStmt(): Stmt {
1135
+ const doToken = this.expect('do')
1136
+ const body = this.parseBlock()
1137
+ this.expect('while')
1138
+ const cond = this.parseParenOptionalCond()
1139
+ this.match(';')
1140
+ return this.withLoc({ kind: 'do_while', cond, body }, doToken)
1141
+ }
1142
+
1143
+ private parseRepeatStmt(): Stmt {
1144
+ const repeatToken = this.expect('repeat')
1145
+ const countToken = this.expect('int_lit')
1146
+ const count = parseInt(countToken.value, 10)
1147
+ const body = this.parseBlock()
1148
+ return this.withLoc({ kind: 'repeat', count, body }, repeatToken)
1149
+ }
1150
+
1151
+ private parseParenOptionalCond(): Expr {
1152
+ if (this.match('(')) {
1153
+ const cond = this.parseExpr()
1154
+ this.expect(')')
1155
+ return cond
1156
+ }
1157
+ return this.parseExpr()
1158
+ }
1159
+
832
1160
  private parseForStmt(): Stmt {
833
1161
  const forToken = this.expect('for')
834
1162
 
@@ -986,6 +1314,22 @@ export class Parser {
986
1314
  this.expect(')')
987
1315
  return { kind: 'PatSome', binding }
988
1316
  }
1317
+ // Enum pattern: EnumName::Variant or EnumName::Variant(b1, b2, ...)
1318
+ if (this.check('ident') && this.peek(1).kind === '::') {
1319
+ const enumName = this.advance().value
1320
+ this.expect('::')
1321
+ const variant = this.expect('ident').value
1322
+ const bindings: string[] = []
1323
+ if (this.check('(')) {
1324
+ this.advance() // consume '('
1325
+ while (!this.check(')') && !this.check('eof')) {
1326
+ bindings.push(this.expect('ident').value)
1327
+ if (!this.match(',')) break
1328
+ }
1329
+ this.expect(')')
1330
+ }
1331
+ return { kind: 'PatEnum', enumName, variant, bindings }
1332
+ }
989
1333
  // Integer literal
990
1334
  if (this.check('int_lit')) {
991
1335
  const tok = this.advance()
@@ -1390,6 +1734,17 @@ export class Parser {
1390
1734
  // Member call: entity.tag("name") → __entity_tag(entity, "name")
1391
1735
  // Also handle arr.push(val) and arr.length
1392
1736
  if (expr.kind === 'member') {
1737
+ // Option.unwrap_or(default) → unwrap_or AST node
1738
+ if (expr.field === 'unwrap_or') {
1739
+ const defaultExpr = this.parseExpr()
1740
+ this.expect(')')
1741
+ expr = this.withLoc(
1742
+ { kind: 'unwrap_or', opt: expr.obj, default_: defaultExpr },
1743
+ this.getLocToken(expr) ?? openParenToken
1744
+ )
1745
+ continue
1746
+ }
1747
+
1393
1748
  const methodMap: Record<string, string> = {
1394
1749
  'tag': '__entity_tag',
1395
1750
  'untag': '__entity_untag',
@@ -1502,6 +1857,24 @@ export class Parser {
1502
1857
  this.expect('::')
1503
1858
  const memberToken = this.expect('ident')
1504
1859
  if (this.check('(')) {
1860
+ // Peek inside: if first non-'(' token is `ident :` it's enum construction with named args.
1861
+ // We only treat it as enum_construct when there are actual named args (not empty parens),
1862
+ // because empty `()` is ambiguous and most commonly means a static method call.
1863
+ const isNamedArgs = this.peek(1).kind === 'ident' && this.peek(2).kind === ':'
1864
+ if (isNamedArgs) {
1865
+ // Enum variant construction: EnumName::Variant(field: expr, ...)
1866
+ this.advance() // consume '('
1867
+ const args: { name: string; value: Expr }[] = []
1868
+ while (!this.check(')') && !this.check('eof')) {
1869
+ const fieldName = this.expect('ident').value
1870
+ this.expect(':')
1871
+ const value = this.parseExpr()
1872
+ args.push({ name: fieldName, value })
1873
+ if (!this.match(',')) break
1874
+ }
1875
+ this.expect(')')
1876
+ return this.withLoc({ kind: 'enum_construct', enumName: typeToken.value, variant: memberToken.value, args }, typeToken)
1877
+ }
1505
1878
  // Static method call: Type::method(args)
1506
1879
  this.advance() // consume '('
1507
1880
  const args = this.parseArgs()