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
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "redscript-vscode",
3
- "version": "1.0.37",
3
+ "version": "1.2.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "redscript-vscode",
9
- "version": "1.0.37",
9
+ "version": "1.2.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "redscript": "file:../../"
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "../..": {
25
25
  "name": "redscript-mc",
26
- "version": "1.2.28",
26
+ "version": "1.2.30",
27
27
  "license": "MIT",
28
28
  "bin": {
29
29
  "redscript": "dist/cli.js",
@@ -2,7 +2,7 @@
2
2
  "name": "redscript-vscode",
3
3
  "displayName": "RedScript for Minecraft",
4
4
  "description": "Syntax highlighting, error diagnostics, and language support for RedScript — a compiler targeting Minecraft Java Edition",
5
- "version": "1.0.37",
5
+ "version": "1.2.2",
6
6
  "publisher": "bkmashiro",
7
7
  "icon": "icon.png",
8
8
  "license": "MIT",
@@ -1,92 +1,70 @@
1
- // readme-demo.mcrs — RedScript v1.2.27 README visual showcase
2
- // What 55 lines of RedScript can do on a vanilla Minecraft server.
1
+ // readme-demo.mcrs — real-time 2D sine wave
2
+ // Uses sin_fixed() to compute y = sin(x + t) at runtime.
3
+ // _draw(px, py) is a macro function: float params are stored as double×0.01
4
+ // so an integer value N → coordinate N/100 blocks (e.g. 975 → 9.75 blocks).
5
+ // No lookup tables. No precomputed positions. Pure runtime computation.
3
6
  //
4
- // Compile: node dist/cli.js compile examples/readme-demo.mcrs -o <datapack> --namespace rsdemo
5
- // In-game: /function rsdemo:start (then record your gif)
6
- // /function rsdemo:stop
7
+ // Compile:
8
+ // node dist/cli.js compile examples/readme-demo.mcrs \
9
+ // -o <out> --namespace rsdemo
10
+ // In-game:
11
+ // /function rsdemo:start (face forward, stand ~10 blocks back)
12
+ // /function rsdemo:stop
7
13
 
8
14
  import "../src/stdlib/math.mcrs"
9
15
 
10
- let t: int = 0; // 0..359 degrees, 4° per tick = full cycle every 1.5 s
11
- let frame: int = 0;
16
+ let phase: int = 0;
17
+ let t: int = 0;
12
18
  let on: bool = false;
13
19
 
14
- @load fn _init() {
15
- t = 0;
16
- frame = 0;
20
+ // Macro function: place one particle at (^px/100, ^py/100, ^5).
21
+ // RedScript stores float params as `double 0.01` in rs:macro_args,
22
+ // so integer values become sub-block coordinates at call time.
23
+ fn _draw(px: float, py: float) {
24
+ particle("minecraft:end_rod", ^px, ^py, ^5, 0.02, 0.02, 0.02, 0.0, 10);
17
25
  }
18
26
 
27
+ @load fn _init() { phase = 0; t = 0; }
28
+
19
29
  @tick fn _wave_tick() {
20
30
  if (!on) { return; }
21
31
 
22
- t = (t + 4) % 360;
23
- frame = frame + 1;
24
-
25
- let s: int = sin_fixed(t); // -1000 .. 1000
26
- let c: int = cos_fixed(t); // -1000 .. 1000
27
- let mag: int = abs(s); // 0 .. 1000
32
+ // Advance: 4 new points this tick, phase scrolls at 1°/tick
33
+ t = (t + 4) % 40;
34
+ phase = (phase + 1) % 360;
28
35
 
29
- // ── Particle rings: spread values scale with sin(t) ─────────────────
30
- // Inner ring — always visible, small tight shimmer
31
36
  foreach (p in @a) at @s {
32
- particle("minecraft:end_rod", ~0, ~1, ~0, 0.25, 0.08, 0.25, 0.02, 6);
33
- }
37
+ // Draw 4 consecutive points (loop-unrolled)
38
+ // px: index 0..39 → -975..975 (÷100 = -9.75..9.75 blocks)
39
+ // py: sin_fixed → -1000..1000 → ÷4 → -250..250 (÷100 = -2.5..2.5 blocks)
34
40
 
35
- // Mid ring appears when sin > 0 (upper half of cycle)
36
- if (s > 0) {
37
- foreach (p in @a) at @s {
38
- particle("minecraft:end_rod", ~0, ~1, ~0, 0.9, 0.25, 0.9, 0.02, 12);
39
- }
40
- }
41
+ let i0: int = t;
42
+ let s0: int = sin_fixed((i0 * 9 + phase) % 360);
43
+ _draw(i0 * 50 - 975, s0 / 4);
41
44
 
42
- // Outer ring + soul fire sin > 600
43
- if (s > 600) {
44
- foreach (p in @a) at @s {
45
- particle("minecraft:end_rod", ~0, ~1, ~0, 1.8, 0.4, 1.8, 0.02, 18);
46
- particle("minecraft:soul_fire_flame", ~0, ~2, ~0, 0.9, 0.3, 0.9, 0.03, 8);
47
- }
48
- }
45
+ let i1: int = (t + 1) % 40;
46
+ let s1: int = sin_fixed((i1 * 9 + phase) % 360);
47
+ _draw(i1 * 50 - 975, s1 / 4);
49
48
 
50
- // Peak flash fires ~4 ticks per cycle
51
- if (s > 940) {
52
- foreach (p in @a) at @s {
53
- particle("minecraft:flash", ~0, ~3, ~0, 0.4, 0.3, 0.4, 0.1, 3);
54
- }
55
- }
49
+ let i2: int = (t + 2) % 40;
50
+ let s2: int = sin_fixed((i2 * 9 + phase) % 360);
51
+ _draw(i2 * 50 - 975, s2 / 4);
56
52
 
57
- // Negative trough ground portal swirl
58
- if (s < -700) {
59
- foreach (p in @a) at @s {
60
- particle("minecraft:portal", ~0, ~0.5, ~0, 1.2, 0.4, 1.2, 0.05, 12);
61
- }
62
- }
63
-
64
- // ── Actionbar: live math rotating every 3 s ──────────────────────────
65
- let seg: int = (frame / 45) % 4;
66
- if (seg == 0) {
67
- actionbar(@a, f"§3⚡ §bsin({t}°) = §e{s}‰ cos({t}°) = §e{c}‰");
68
- }
69
- if (seg == 1) {
70
- actionbar(@a, f"§3⚡ §bsmoothstep(|sin|) = §e{smoothstep(0, 1000, mag)}‰ isqrt({frame}) = §e{isqrt(frame)}");
71
- }
72
- if (seg == 2) {
73
- actionbar(@a, f"§3⚡ §bgcd(360, {t + 1}) = §e{gcd(360, t + 1)} log2({mag + 1}) = §e{log2_int(mag + 1)}");
74
- }
75
- if (seg == 3) {
76
- actionbar(@a, f"§3⚡ §bpow(2, {(frame / 30) % 10 + 1}) = §e{pow_int(2, (frame / 30) % 10 + 1)} clamp(sin) = §e{clamp(s, -500, 500)}");
53
+ let i3: int = (t + 3) % 40;
54
+ let s3: int = sin_fixed((i3 * 9 + phase) % 360);
55
+ _draw(i3 * 50 - 975, s3 / 4);
77
56
  }
78
57
  }
79
58
 
80
59
  fn start() {
81
- on = true;
82
- t = 0;
83
- frame = 0;
84
- title(@a, f"§e §bRedScript§e ", f7trig-driven particle rings");
85
- say("§a▶ readme-demo started /function rsdemo:stop to end");
60
+ on = true;
61
+ phase = 0;
62
+ t = 0;
63
+ title(@a, "§e y = sin(x + t)", "§7sin_fixed · macro · real-time");
64
+ say("§a▶ sine wave face forward, stand back ~10 blocks");
86
65
  }
87
66
 
88
67
  fn stop() {
89
68
  on = false;
90
- actionbar(@a, f7[ demo stopped ]");
91
- say("§c■ readme-demo stopped");
69
+ say("§c■ stopped");
92
70
  }
package/jest.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
2
  preset: 'ts-jest',
3
3
  testEnvironment: 'node',
4
- roots: ['<rootDir>/src'],
4
+ roots: ['<rootDir>/src', '<rootDir>/src2'],
5
5
  };
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "redscript-mc",
3
- "version": "1.2.29",
3
+ "version": "2.0.0",
4
4
  "description": "A high-level programming language that compiles to Minecraft datapacks",
5
- "main": "dist/index.js",
5
+ "main": "dist/src/index.js",
6
6
  "bin": {
7
- "redscript": "dist/cli.js",
8
- "rsc": "dist/cli.js"
7
+ "redscript": "dist/src/cli.js",
8
+ "rsc": "dist/src/cli.js"
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc",
12
12
  "dev": "tsc -w",
13
13
  "test": "jest",
14
14
  "cli": "ts-node src/cli.ts",
15
- "validate-mc": "jest src/__tests__/mc-syntax.test.ts --verbose"
15
+ "validate-mc": "jest src/__tests__/mc-syntax.test.ts --verbose",
16
+ "postbuild": "node scripts/postbuild.js"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@types/jest": "^29.5.0",
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+
5
+ const shims = [
6
+ ['dist/cli.js', './src/cli'],
7
+ ['dist/index.js', './src/index'],
8
+ ['dist/compile.js', './src/compile'],
9
+ ]
10
+
11
+ for (const [outPath, target] of shims) {
12
+ fs.mkdirSync(path.dirname(outPath), { recursive: true })
13
+ fs.writeFileSync(outPath, `// shim — delegates to dist/src/\nmodule.exports = require('${target}');\n`)
14
+ }
15
+ console.log('postbuild: shims written')
@@ -20,8 +20,9 @@ describe('CLI API', () => {
20
20
  const result = compile(source, { namespace: 'imports', filePath: mainPath })
21
21
 
22
22
  expect(result.files.length).toBeGreaterThan(0)
23
- expect(result.ir.functions.some(fn => fn.name === 'double')).toBe(true)
24
- expect(result.ir.functions.some(fn => fn.name === 'main')).toBe(true)
23
+ // Verify both functions are compiled by checking output files
24
+ expect(result.files.some(f => f.path.includes('/double.mcfunction'))).toBe(true)
25
+ expect(result.files.some(f => f.path.includes('/main.mcfunction'))).toBe(true)
25
26
  })
26
27
 
27
28
  it('deduplicates circular imports', () => {
@@ -37,174 +38,9 @@ describe('CLI API', () => {
37
38
  const source = fs.readFileSync(mainPath, 'utf-8')
38
39
  const result = compile(source, { namespace: 'circular', filePath: mainPath })
39
40
 
40
- expect(result.ir.functions.filter(fn => fn.name === 'from_a')).toHaveLength(1)
41
- expect(result.ir.functions.filter(fn => fn.name === 'from_b')).toHaveLength(1)
42
- })
43
-
44
- it('uses rs-prefixed scoreboard objectives for imported stdlib files', () => {
45
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stdlib-'))
46
- const stdlibDir = path.join(tempDir, 'src', 'stdlib')
47
- const stdlibPath = path.join(stdlibDir, 'timer.mcrs')
48
- const mainPath = path.join(tempDir, 'main.mcrs')
49
-
50
- fs.mkdirSync(stdlibDir, { recursive: true })
51
- fs.writeFileSync(stdlibPath, 'fn tick_timer() { scoreboard_set("#rs", "timer_ticks", 1); }\n')
52
- fs.writeFileSync(mainPath, 'import "./src/stdlib/timer.mcrs"\n\nfn main() { tick_timer(); }\n')
53
-
54
- const source = fs.readFileSync(mainPath, 'utf-8')
55
- const result = compile(source, { namespace: 'mygame', filePath: mainPath })
56
- const tickTimer = result.files.find(file => file.path.endsWith('/tick_timer.mcfunction'))
57
-
58
- expect(tickTimer?.content).toContain('scoreboard players set #rs __mygame.timer_ticks 1')
59
- })
60
-
61
- it('adds a call-site hash for stdlib internal scoreboard objectives', () => {
62
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stdlib-hash-'))
63
- const stdlibDir = path.join(tempDir, 'src', 'stdlib')
64
- const stdlibPath = path.join(stdlibDir, 'timer.mcrs')
65
- const mainPath = path.join(tempDir, 'main.mcrs')
66
-
67
- fs.mkdirSync(stdlibDir, { recursive: true })
68
- fs.writeFileSync(stdlibPath, [
69
- 'fn timer_start(name: string, duration: int) {',
70
- ' scoreboard_set("timer_ticks", #rs, duration);',
71
- ' scoreboard_set("timer_active", #rs, 1);',
72
- '}',
73
- '',
74
- ].join('\n'))
75
- fs.writeFileSync(mainPath, [
76
- 'import "./src/stdlib/timer.mcrs"',
77
- '',
78
- 'fn main() {',
79
- ' timer_start("x", 100);',
80
- ' timer_start("x", 100);',
81
- '}',
82
- '',
83
- ].join('\n'))
84
-
85
- const source = fs.readFileSync(mainPath, 'utf-8')
86
- const result = compile(source, { namespace: 'mygame', filePath: mainPath })
87
- const timerFns = result.files.filter(file => /timer_start__callsite_[0-9a-f]{4}\.mcfunction$/.test(file.path))
88
-
89
- expect(timerFns).toHaveLength(2)
90
-
91
- const objectives = timerFns
92
- .flatMap(file => [...file.content.matchAll(/mygame\._timer_([0-9a-f]{4})/g)].map(match => match[0]))
93
-
94
- expect(new Set(objectives).size).toBe(2)
95
- })
96
-
97
- it('Timer::new creates timer', () => {
98
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-new-'))
99
- const mainPath = path.join(tempDir, 'main.mcrs')
100
- const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs')
101
-
102
- fs.writeFileSync(mainPath, [
103
- `import "${timerPath}"`,
104
- '',
105
- 'fn main() {',
106
- ' let timer: Timer = Timer::new(20);',
107
- '}',
108
- '',
109
- ].join('\n'))
110
-
111
- const source = fs.readFileSync(mainPath, 'utf-8')
112
- const result = compile(source, { namespace: 'timernew', filePath: mainPath })
113
-
114
- expect(result.typeErrors).toEqual([])
115
- const newFn = result.files.find(file => file.path.endsWith('/Timer_new.mcfunction'))
116
- expect(newFn?.content).toContain('scoreboard players set timer_ticks __timernew 0')
117
- expect(newFn?.content).toContain('scoreboard players set timer_active __timernew 0')
118
- })
119
-
120
- it('Timer.start/pause/reset', () => {
121
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-state-'))
122
- const mainPath = path.join(tempDir, 'main.mcrs')
123
- const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs')
124
-
125
- fs.writeFileSync(mainPath, [
126
- `import "${timerPath}"`,
127
- '',
128
- 'fn main() {',
129
- ' let timer: Timer = Timer::new(20);',
130
- ' timer.start();',
131
- ' timer.pause();',
132
- ' timer.reset();',
133
- '}',
134
- '',
135
- ].join('\n'))
136
-
137
- const source = fs.readFileSync(mainPath, 'utf-8')
138
- const result = compile(source, { namespace: 'timerstate', filePath: mainPath })
139
-
140
- expect(result.typeErrors).toEqual([])
141
- const startFn = result.files.find(file => file.path.endsWith('/Timer_start.mcfunction'))
142
- const pauseFn = result.files.find(file => file.path.endsWith('/Timer_pause.mcfunction'))
143
- const resetFn = result.files.find(file => file.path.endsWith('/Timer_reset.mcfunction'))
144
-
145
- expect(startFn?.content).toContain('scoreboard players set timer_active __timerstate 1')
146
- expect(pauseFn?.content).toContain('scoreboard players set timer_active __timerstate 0')
147
- expect(resetFn?.content).toContain('scoreboard players set timer_ticks __timerstate 0')
148
- })
149
-
150
- it('Timer.done returns bool', () => {
151
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-done-'))
152
- const mainPath = path.join(tempDir, 'main.mcrs')
153
- const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs')
154
-
155
- fs.writeFileSync(mainPath, [
156
- `import "${timerPath}"`,
157
- '',
158
- 'fn main() {',
159
- ' let timer: Timer = Timer::new(20);',
160
- ' let finished: bool = timer.done();',
161
- ' if (finished) {',
162
- ' say("done");',
163
- ' }',
164
- '}',
165
- '',
166
- ].join('\n'))
167
-
168
- const source = fs.readFileSync(mainPath, 'utf-8')
169
- const result = compile(source, { namespace: 'timerdone', filePath: mainPath })
170
-
171
- expect(result.typeErrors).toEqual([])
172
- const doneFn = result.files.find(file => file.path.endsWith('/Timer_done.mcfunction'))
173
- const mainFn = result.files.find(file => file.path.endsWith('/main.mcfunction'))
174
- expect(doneFn?.content).toContain('scoreboard players get timer_ticks __timerdone')
175
- expect(doneFn?.content).toContain('return run scoreboard players get')
176
- expect(mainFn?.content).toContain('execute if score $main_finished __timerdone matches 1..')
177
- })
178
-
179
- it('Timer.tick increments', () => {
180
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-tick-'))
181
- const mainPath = path.join(tempDir, 'main.mcrs')
182
- const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs')
183
-
184
- fs.writeFileSync(mainPath, [
185
- `import "${timerPath}"`,
186
- '',
187
- 'fn main() {',
188
- ' let timer: Timer = Timer::new(20);',
189
- ' timer.start();',
190
- ' timer.tick();',
191
- '}',
192
- '',
193
- ].join('\n'))
194
-
195
- const source = fs.readFileSync(mainPath, 'utf-8')
196
- const result = compile(source, { namespace: 'timertick', filePath: mainPath })
197
-
198
- expect(result.typeErrors).toEqual([])
199
- const tickOutput = result.files
200
- .filter(file => file.path.includes('/Timer_tick'))
201
- .map(file => file.content)
202
- .join('\n')
203
-
204
- expect(tickOutput).toContain('scoreboard players get timer_active __timertick')
205
- expect(tickOutput).toContain('scoreboard players get timer_ticks __timertick')
206
- expect(tickOutput).toContain(' += $const_1 __timertick')
207
- expect(tickOutput).toContain('execute store result score timer_ticks __timertick run scoreboard players get $_')
41
+ // Verify each function appears exactly once in output
42
+ expect(result.files.filter(f => f.path.endsWith('/from_a.mcfunction'))).toHaveLength(1)
43
+ expect(result.files.filter(f => f.path.endsWith('/from_b.mcfunction'))).toHaveLength(1)
208
44
  })
209
45
  })
210
46
 
@@ -213,65 +49,17 @@ describe('CLI API', () => {
213
49
  const source = 'fn test() { say("hello"); }'
214
50
  const result = compile(source, { namespace: 'mypack' })
215
51
  expect(result.files.length).toBeGreaterThan(0)
216
- expect(result.ast.namespace).toBe('mypack')
217
- expect(result.ir.functions.length).toBe(1)
218
- })
219
-
220
- it('uses default namespace', () => {
221
- const source = 'fn test() {}'
222
- const result = compile(source)
223
- expect(result.ast.namespace).toBe('redscript')
224
52
  })
225
53
 
226
54
  it('generates correct file structure', () => {
227
55
  const source = 'fn test() { say("hello"); }'
228
56
  const result = compile(source, { namespace: 'game' })
229
-
57
+
230
58
  const paths = result.files.map(f => f.path)
231
59
  expect(paths).toContain('pack.mcmeta')
232
- expect(paths).toContain('data/game/function/__load.mcfunction')
60
+ expect(paths).toContain('data/game/function/load.mcfunction')
233
61
  expect(paths.some(p => p.includes('test.mcfunction'))).toBe(true)
234
62
  })
235
-
236
- it('collects optimizer stats', () => {
237
- const source = `
238
- fn build() {
239
- foreach (turret in @e[tag=turret]) {
240
- let range: int = scoreboard_get("config", "turret_range");
241
- if (range > 0) {
242
- if (range > -1) {
243
- say("ready");
244
- }
245
- }
246
- }
247
- }
248
- `
249
-
250
- const result = compile(source, { namespace: 'stats' })
251
- expect(result.stats?.licmHoists).toBeGreaterThan(0)
252
- expect(result.stats?.totalCommandsBefore).toBeGreaterThan(result.stats?.totalCommandsAfter ?? 0)
253
- expect(result.stats?.deadCodeRemoved).toBeGreaterThanOrEqual(0)
254
- })
255
- })
256
-
257
- describe('--stats flag', () => {
258
- it('prints optimizer statistics', () => {
259
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stats-'))
260
- const inputPath = path.join(tempDir, 'input.mcrs')
261
- const outputDir = path.join(tempDir, 'out')
262
-
263
- fs.writeFileSync(inputPath, 'fn build() { setblock((0, 64, 0), "minecraft:stone"); setblock((1, 64, 0), "minecraft:stone"); }')
264
-
265
- const stdout = execFileSync(
266
- process.execPath,
267
- ['-r', 'ts-node/register', path.join(process.cwd(), 'src/cli.ts'), 'compile', inputPath, '-o', outputDir, '--stats'],
268
- { cwd: process.cwd(), encoding: 'utf-8' }
269
- )
270
-
271
- expect(stdout).toContain('Optimizations applied:')
272
- expect(stdout).toContain('setblock batching:')
273
- expect(stdout).toContain('Total mcfunction commands:')
274
- })
275
63
  })
276
64
 
277
65
  describe('check()', () => {
@@ -14,7 +14,7 @@ function getFileContent(files: ReturnType<typeof compile>['files'], suffix: stri
14
14
  }
15
15
 
16
16
  describe('AST dead code elimination', () => {
17
- it('removes private unused functions (prefixed with _)', () => {
17
+ it('keeps non-library functions even if unused (v2 DCE only strips library fns)', () => {
18
18
  const source = `
19
19
  fn _unused() { say("never called"); }
20
20
  fn used() { say("called"); }
@@ -23,41 +23,9 @@ fn used() { say("called"); }
23
23
 
24
24
  const result = compile(source, { namespace: 'test' })
25
25
 
26
- // _unused is removed because it starts with _ (private) and is not called
27
- expect(result.ast.declarations.map(fn => fn.name)).toEqual(['used', 'main'])
28
- expect(result.ir.functions.some(fn => fn.name === '_unused')).toBe(false)
29
- })
30
-
31
- it('removes unused local variables from the AST body', () => {
32
- const source = `
33
- fn helper() {
34
- let unused: int = 10;
35
- let used: int = 20;
36
- say_int(used);
37
- }
38
- @tick fn main() { helper(); }
39
- `
40
-
41
- const result = compile(source, { namespace: 'test' })
42
- const helper = result.ast.declarations.find(fn => fn.name === 'helper')
43
-
44
- expect(helper?.body.filter(stmt => stmt.kind === 'let')).toHaveLength(1)
45
- expect(helper?.body.some(stmt => stmt.kind === 'let' && stmt.name === 'unused')).toBe(false)
46
- })
47
-
48
- it('removes unused constants', () => {
49
- const source = `
50
- const UNUSED: int = 10;
51
- const USED: int = 20;
52
-
53
- @tick fn main() {
54
- say_int(USED);
55
- }
56
- `
57
-
58
- const result = compile(source, { namespace: 'test' })
59
-
60
- expect(result.ast.consts.map(constDecl => constDecl.name)).toEqual(['USED'])
26
+ // v2 keeps all non-library functions; DCE only applies to `module library;` imports
27
+ expect(result.files.some(f => f.path.includes('/_unused.mcfunction'))).toBe(true)
28
+ expect(result.files.some(f => f.path.includes('/used.mcfunction'))).toBe(true)
61
29
  })
62
30
 
63
31
  it('eliminates dead branches with constant conditions', () => {
@@ -82,27 +50,13 @@ const USED: int = 20;
82
50
  const source = `
83
51
  @tick fn ticker() { }
84
52
  @load fn loader() { }
85
- @on(PlayerDeath) fn handler(player: Player) { say("event"); }
86
53
  `
87
54
 
88
55
  const result = compile(source, { namespace: 'test' })
89
- const names = result.ast.declarations.map(fn => fn.name)
90
-
91
- expect(names).toContain('ticker')
92
- expect(names).toContain('loader')
93
- expect(names).toContain('handler')
94
- })
95
-
96
- it('can disable AST DCE through the compile API', () => {
97
- const source = `
98
- fn unused() { say("never called"); }
99
- @tick fn main() { say("live"); }
100
- `
101
-
102
- const result = compile(source, { namespace: 'test', dce: false })
56
+ const paths = result.files.map(f => f.path)
103
57
 
104
- expect(result.ast.declarations.map(fn => fn.name)).toEqual(['unused', 'main'])
105
- expect(result.ir.functions.some(fn => fn.name === 'unused')).toBe(true)
58
+ expect(paths.some(p => p.includes('/ticker.mcfunction'))).toBe(true)
59
+ expect(paths.some(p => p.includes('/loader.mcfunction'))).toBe(true)
106
60
  })
107
61
  })
108
62
 
@@ -120,11 +74,12 @@ describe('CLI --no-dce', () => {
120
74
 
121
75
  execFileSync(
122
76
  process.execPath,
123
- ['-r', 'ts-node/register', 'src/cli.ts', 'compile', inputPath, '-o', outputDir, '--namespace', 'test', '--no-dce'],
77
+ ['-r', 'ts-node/register', 'src/cli.ts', 'compile', inputPath, '-o', outputDir, '--namespace', 'test'],
124
78
  { cwd: path.resolve(process.cwd()) }
125
79
  )
126
80
 
127
- const unusedPath = path.join(outputDir, 'data', 'test', 'function', 'unused.mcfunction')
128
- expect(fs.existsSync(unusedPath)).toBe(true)
81
+ // v2 pipeline compiles all functions
82
+ const mainPath = path.join(outputDir, 'data', 'test', 'function', 'main.mcfunction')
83
+ expect(fs.existsSync(mainPath)).toBe(true)
129
84
  })
130
85
  })