redscript-mc 1.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 (272) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
  3. package/.github/ISSUE_TEMPLATE/wrong_output.md +33 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
  5. package/.github/workflows/ci.yml +29 -0
  6. package/.github/workflows/publish-extension.yml +35 -0
  7. package/LICENSE +21 -0
  8. package/README.md +261 -0
  9. package/README.zh.md +261 -0
  10. package/dist/__tests__/cli.test.d.ts +1 -0
  11. package/dist/__tests__/cli.test.js +140 -0
  12. package/dist/__tests__/codegen.test.d.ts +1 -0
  13. package/dist/__tests__/codegen.test.js +121 -0
  14. package/dist/__tests__/diagnostics.test.d.ts +4 -0
  15. package/dist/__tests__/diagnostics.test.js +149 -0
  16. package/dist/__tests__/e2e.test.d.ts +6 -0
  17. package/dist/__tests__/e2e.test.js +1528 -0
  18. package/dist/__tests__/lexer.test.d.ts +1 -0
  19. package/dist/__tests__/lexer.test.js +316 -0
  20. package/dist/__tests__/lowering.test.d.ts +1 -0
  21. package/dist/__tests__/lowering.test.js +819 -0
  22. package/dist/__tests__/mc-integration.test.d.ts +12 -0
  23. package/dist/__tests__/mc-integration.test.js +395 -0
  24. package/dist/__tests__/mc-syntax.test.d.ts +1 -0
  25. package/dist/__tests__/mc-syntax.test.js +112 -0
  26. package/dist/__tests__/nbt.test.d.ts +1 -0
  27. package/dist/__tests__/nbt.test.js +82 -0
  28. package/dist/__tests__/optimizer-advanced.test.d.ts +1 -0
  29. package/dist/__tests__/optimizer-advanced.test.js +124 -0
  30. package/dist/__tests__/optimizer.test.d.ts +1 -0
  31. package/dist/__tests__/optimizer.test.js +118 -0
  32. package/dist/__tests__/parser.test.d.ts +1 -0
  33. package/dist/__tests__/parser.test.js +717 -0
  34. package/dist/__tests__/repl.test.d.ts +1 -0
  35. package/dist/__tests__/repl.test.js +27 -0
  36. package/dist/__tests__/runtime.test.d.ts +1 -0
  37. package/dist/__tests__/runtime.test.js +276 -0
  38. package/dist/__tests__/structure-optimizer.test.d.ts +1 -0
  39. package/dist/__tests__/structure-optimizer.test.js +33 -0
  40. package/dist/__tests__/typechecker.test.d.ts +1 -0
  41. package/dist/__tests__/typechecker.test.js +364 -0
  42. package/dist/ast/types.d.ts +357 -0
  43. package/dist/ast/types.js +9 -0
  44. package/dist/cli.d.ts +11 -0
  45. package/dist/cli.js +407 -0
  46. package/dist/codegen/cmdblock/index.d.ts +26 -0
  47. package/dist/codegen/cmdblock/index.js +45 -0
  48. package/dist/codegen/mcfunction/index.d.ts +34 -0
  49. package/dist/codegen/mcfunction/index.js +413 -0
  50. package/dist/codegen/structure/index.d.ts +18 -0
  51. package/dist/codegen/structure/index.js +249 -0
  52. package/dist/compile.d.ts +30 -0
  53. package/dist/compile.js +152 -0
  54. package/dist/data/arena/function/__load.mcfunction +6 -0
  55. package/dist/data/arena/function/__tick.mcfunction +2 -0
  56. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  57. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  58. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  59. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  60. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  61. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  62. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  63. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  64. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  65. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  66. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  67. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  68. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  69. package/dist/data/counter/function/__load.mcfunction +5 -0
  70. package/dist/data/counter/function/__tick.mcfunction +2 -0
  71. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  72. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  73. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  74. package/dist/data/minecraft/tags/function/load.json +5 -0
  75. package/dist/data/minecraft/tags/function/tick.json +5 -0
  76. package/dist/data/quiz/function/__load.mcfunction +16 -0
  77. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  78. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  79. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  80. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  81. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  82. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  83. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  84. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  85. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  86. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  87. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  88. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  89. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  90. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  91. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  92. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  93. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  94. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  95. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  96. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  97. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  98. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  99. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  100. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  101. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  102. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  103. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  104. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  105. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  106. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  107. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  108. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  109. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  110. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  111. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  112. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  113. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  114. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  115. package/dist/data/shop/function/__load.mcfunction +7 -0
  116. package/dist/data/shop/function/__tick.mcfunction +3 -0
  117. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  118. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  119. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  120. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  121. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  122. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  123. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  124. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  125. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  126. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  127. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  128. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  129. package/dist/data/turret/function/__load.mcfunction +5 -0
  130. package/dist/data/turret/function/__tick.mcfunction +4 -0
  131. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  132. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  133. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  134. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  135. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  136. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  137. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  138. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  139. package/dist/diagnostics/index.d.ts +44 -0
  140. package/dist/diagnostics/index.js +140 -0
  141. package/dist/index.d.ts +53 -0
  142. package/dist/index.js +126 -0
  143. package/dist/ir/builder.d.ts +32 -0
  144. package/dist/ir/builder.js +99 -0
  145. package/dist/ir/types.d.ts +117 -0
  146. package/dist/ir/types.js +15 -0
  147. package/dist/lexer/index.d.ts +36 -0
  148. package/dist/lexer/index.js +458 -0
  149. package/dist/lowering/index.d.ts +106 -0
  150. package/dist/lowering/index.js +2041 -0
  151. package/dist/mc-test/client.d.ts +128 -0
  152. package/dist/mc-test/client.js +174 -0
  153. package/dist/mc-test/runner.d.ts +28 -0
  154. package/dist/mc-test/runner.js +150 -0
  155. package/dist/mc-test/setup.d.ts +11 -0
  156. package/dist/mc-test/setup.js +98 -0
  157. package/dist/mc-validator/index.d.ts +17 -0
  158. package/dist/mc-validator/index.js +322 -0
  159. package/dist/nbt/index.d.ts +86 -0
  160. package/dist/nbt/index.js +250 -0
  161. package/dist/optimizer/commands.d.ts +36 -0
  162. package/dist/optimizer/commands.js +349 -0
  163. package/dist/optimizer/passes.d.ts +34 -0
  164. package/dist/optimizer/passes.js +227 -0
  165. package/dist/optimizer/structure.d.ts +8 -0
  166. package/dist/optimizer/structure.js +344 -0
  167. package/dist/pack.mcmeta +6 -0
  168. package/dist/parser/index.d.ts +76 -0
  169. package/dist/parser/index.js +1193 -0
  170. package/dist/repl.d.ts +16 -0
  171. package/dist/repl.js +165 -0
  172. package/dist/runtime/index.d.ts +101 -0
  173. package/dist/runtime/index.js +1288 -0
  174. package/dist/typechecker/index.d.ts +42 -0
  175. package/dist/typechecker/index.js +629 -0
  176. package/docs/COMPILATION_STATS.md +142 -0
  177. package/docs/IMPLEMENTATION_GUIDE.md +512 -0
  178. package/docs/LANGUAGE_REFERENCE.md +415 -0
  179. package/docs/MC_MAPPING.md +280 -0
  180. package/docs/STRUCTURE_TARGET.md +80 -0
  181. package/docs/mc-reference/commands.md +259 -0
  182. package/editors/vscode/.vscodeignore +10 -0
  183. package/editors/vscode/LICENSE +21 -0
  184. package/editors/vscode/README.md +78 -0
  185. package/editors/vscode/build.mjs +28 -0
  186. package/editors/vscode/icon.png +0 -0
  187. package/editors/vscode/mcfunction-language-configuration.json +28 -0
  188. package/editors/vscode/out/extension.js +7236 -0
  189. package/editors/vscode/package-lock.json +566 -0
  190. package/editors/vscode/package.json +137 -0
  191. package/editors/vscode/redscript-language-configuration.json +28 -0
  192. package/editors/vscode/snippets/redscript.json +114 -0
  193. package/editors/vscode/src/codeactions.ts +89 -0
  194. package/editors/vscode/src/completion.ts +130 -0
  195. package/editors/vscode/src/extension.ts +239 -0
  196. package/editors/vscode/src/hover.ts +1120 -0
  197. package/editors/vscode/src/symbols.ts +207 -0
  198. package/editors/vscode/syntaxes/mcfunction.tmLanguage.json +740 -0
  199. package/editors/vscode/syntaxes/redscript.tmLanguage.json +357 -0
  200. package/editors/vscode/tsconfig.json +13 -0
  201. package/jest.config.js +5 -0
  202. package/package.json +38 -0
  203. package/src/__tests__/cli.test.ts +130 -0
  204. package/src/__tests__/codegen.test.ts +128 -0
  205. package/src/__tests__/diagnostics.test.ts +195 -0
  206. package/src/__tests__/e2e.test.ts +1721 -0
  207. package/src/__tests__/fixtures/mc-commands-1.21.4.json +18734 -0
  208. package/src/__tests__/formatter.test.ts +46 -0
  209. package/src/__tests__/lexer.test.ts +356 -0
  210. package/src/__tests__/lowering.test.ts +962 -0
  211. package/src/__tests__/mc-integration.test.ts +409 -0
  212. package/src/__tests__/mc-syntax.test.ts +96 -0
  213. package/src/__tests__/nbt.test.ts +58 -0
  214. package/src/__tests__/optimizer-advanced.test.ts +144 -0
  215. package/src/__tests__/optimizer.test.ts +129 -0
  216. package/src/__tests__/parser.test.ts +800 -0
  217. package/src/__tests__/repl.test.ts +33 -0
  218. package/src/__tests__/runtime.test.ts +289 -0
  219. package/src/__tests__/structure-optimizer.test.ts +38 -0
  220. package/src/__tests__/typechecker.test.ts +395 -0
  221. package/src/ast/types.ts +248 -0
  222. package/src/cli.ts +445 -0
  223. package/src/codegen/cmdblock/index.ts +63 -0
  224. package/src/codegen/mcfunction/index.ts +471 -0
  225. package/src/codegen/structure/index.ts +305 -0
  226. package/src/compile.ts +188 -0
  227. package/src/diagnostics/index.ts +186 -0
  228. package/src/examples/README.md +77 -0
  229. package/src/examples/SHOWCASE_GAME.md +43 -0
  230. package/src/examples/arena.rs +44 -0
  231. package/src/examples/counter.rs +12 -0
  232. package/src/examples/pvp_arena.rs +131 -0
  233. package/src/examples/quiz.rs +90 -0
  234. package/src/examples/rpg.rs +13 -0
  235. package/src/examples/shop.rs +30 -0
  236. package/src/examples/showcase_game.rs +552 -0
  237. package/src/examples/stdlib_demo.rs +181 -0
  238. package/src/examples/turret.rs +27 -0
  239. package/src/examples/world_manager.rs +23 -0
  240. package/src/formatter/index.ts +22 -0
  241. package/src/index.ts +161 -0
  242. package/src/ir/builder.ts +114 -0
  243. package/src/ir/types.ts +119 -0
  244. package/src/lexer/index.ts +555 -0
  245. package/src/lowering/index.ts +2406 -0
  246. package/src/mc-test/client.ts +259 -0
  247. package/src/mc-test/runner.ts +140 -0
  248. package/src/mc-test/setup.ts +70 -0
  249. package/src/mc-validator/index.ts +367 -0
  250. package/src/nbt/index.ts +321 -0
  251. package/src/optimizer/commands.ts +416 -0
  252. package/src/optimizer/passes.ts +233 -0
  253. package/src/optimizer/structure.ts +441 -0
  254. package/src/parser/index.ts +1437 -0
  255. package/src/repl.ts +165 -0
  256. package/src/runtime/index.ts +1403 -0
  257. package/src/stdlib/README.md +156 -0
  258. package/src/stdlib/combat.rs +20 -0
  259. package/src/stdlib/cooldown.rs +45 -0
  260. package/src/stdlib/math.rs +49 -0
  261. package/src/stdlib/mobs.rs +99 -0
  262. package/src/stdlib/player.rs +29 -0
  263. package/src/stdlib/strings.rs +7 -0
  264. package/src/stdlib/timer.rs +51 -0
  265. package/src/templates/README.md +126 -0
  266. package/src/templates/combat.rs +96 -0
  267. package/src/templates/economy.rs +40 -0
  268. package/src/templates/mini-game-framework.rs +117 -0
  269. package/src/templates/quest.rs +78 -0
  270. package/src/test_programs/zombie_game.rs +25 -0
  271. package/src/typechecker/index.ts +737 -0
  272. package/tsconfig.json +16 -0
@@ -0,0 +1,395 @@
1
+ import { Lexer } from '../lexer'
2
+ import { Parser } from '../parser'
3
+ import { TypeChecker } from '../typechecker'
4
+ import type { DiagnosticError } from '../diagnostics'
5
+
6
+ function typeCheck(source: string): DiagnosticError[] {
7
+ const tokens = new Lexer(source).tokenize()
8
+ const ast = new Parser(tokens).parse('test')
9
+ const checker = new TypeChecker(source)
10
+ return checker.check(ast)
11
+ }
12
+
13
+ describe('TypeChecker', () => {
14
+ describe('variable declaration', () => {
15
+ it('allows using top-level consts inside functions', () => {
16
+ const errors = typeCheck(`
17
+ const MAX_HP: int = 100
18
+
19
+ fn test() {
20
+ let hp: int = MAX_HP;
21
+ }
22
+ `)
23
+ expect(errors).toHaveLength(0)
24
+ })
25
+
26
+ it('allows using declared variables', () => {
27
+ const errors = typeCheck(`
28
+ fn test() {
29
+ let x: int = 5;
30
+ let y: int = x;
31
+ }
32
+ `)
33
+ expect(errors).toHaveLength(0)
34
+ })
35
+
36
+ it('detects undeclared variable usage', () => {
37
+ const errors = typeCheck(`
38
+ fn test() {
39
+ let x: int = y;
40
+ }
41
+ `)
42
+ expect(errors.length).toBeGreaterThan(0)
43
+ expect(errors[0].message).toContain("'y' used before declaration")
44
+ expect(errors[0].location.line).toBe(3)
45
+ expect(errors[0].location.col).toBe(18)
46
+ })
47
+
48
+ it('detects undeclared variable in expression', () => {
49
+ const errors = typeCheck(`
50
+ fn test() {
51
+ let x: int = 5 + undeclared;
52
+ }
53
+ `)
54
+ expect(errors.length).toBeGreaterThan(0)
55
+ expect(errors[0].message).toContain("'undeclared' used before declaration")
56
+ })
57
+
58
+ it('accepts BlockPos literals for BlockPos variables', () => {
59
+ const errors = typeCheck(`
60
+ fn test() {
61
+ let spawn: BlockPos = (~0, 64, ~0);
62
+ }
63
+ `)
64
+ expect(errors).toHaveLength(0)
65
+ })
66
+
67
+ it('rejects assigning to const', () => {
68
+ const errors = typeCheck(`
69
+ const MAX_HP: int = 100
70
+
71
+ fn test() {
72
+ MAX_HP = 50;
73
+ }
74
+ `)
75
+ expect(errors.length).toBeGreaterThan(0)
76
+ expect(errors[0].message).toContain("Cannot assign to const 'MAX_HP'")
77
+ })
78
+ })
79
+
80
+ describe('function calls', () => {
81
+ it('allows correct number of arguments', () => {
82
+ const errors = typeCheck(`
83
+ fn add(a: int, b: int) -> int {
84
+ return a + b;
85
+ }
86
+
87
+ fn test() {
88
+ let x: int = add(1, 2);
89
+ }
90
+ `)
91
+ expect(errors).toHaveLength(0)
92
+ })
93
+
94
+ it('detects wrong number of arguments', () => {
95
+ const errors = typeCheck(`
96
+ fn add(a: int, b: int) -> int {
97
+ return a + b;
98
+ }
99
+
100
+ fn test() {
101
+ let x: int = add(1);
102
+ }
103
+ `)
104
+ expect(errors.length).toBeGreaterThan(0)
105
+ expect(errors[0].message).toContain("expects 2 arguments, got 1")
106
+ expect(errors[0].location.line).toBe(7)
107
+ })
108
+
109
+ it('allows omitting trailing default arguments', () => {
110
+ const errors = typeCheck(`
111
+ fn greet(name: string, formal: bool = false) {
112
+ say(name);
113
+ }
114
+
115
+ fn test() {
116
+ greet("Player");
117
+ greet("Player", true);
118
+ }
119
+ `)
120
+ expect(errors).toHaveLength(0)
121
+ })
122
+
123
+ it('detects too many arguments', () => {
124
+ const errors = typeCheck(`
125
+ fn greet() {
126
+ say("hello");
127
+ }
128
+
129
+ fn test() {
130
+ greet(1, 2, 3);
131
+ }
132
+ `)
133
+ expect(errors.length).toBeGreaterThan(0)
134
+ expect(errors[0].message).toContain("expects 0 arguments, got 3")
135
+ })
136
+
137
+ it('rejects a required parameter after a default parameter', () => {
138
+ const errors = typeCheck(`
139
+ fn bad(a: int = 1, b: int) {}
140
+ `)
141
+ expect(errors.length).toBeGreaterThan(0)
142
+ expect(errors[0].message).toContain("cannot follow a default parameter")
143
+ })
144
+
145
+ it('rejects non-single selector tp destinations', () => {
146
+ const errors = typeCheck(`
147
+ fn test() {
148
+ tp(@s, @a);
149
+ }
150
+ `)
151
+ expect(errors.length).toBeGreaterThan(0)
152
+ expect(errors[0].message).toContain('tp destination must be a single-entity selector')
153
+ })
154
+
155
+ it('allows single-selector and BlockPos tp destinations', () => {
156
+ const singleErrors = typeCheck(`
157
+ fn test() {
158
+ tp(@s, @p);
159
+ tp(@s, @e[limit=1, tag=target]);
160
+ }
161
+ `)
162
+ expect(singleErrors).toHaveLength(0)
163
+
164
+ const posErrors = typeCheck(`
165
+ fn test() {
166
+ tp(@a, (1, 64, 1));
167
+ tp(@s, (~0, ~10, ~0));
168
+ }
169
+ `)
170
+ expect(posErrors).toHaveLength(0)
171
+ })
172
+
173
+ it('treats bossbar_get_value() as int', () => {
174
+ const errors = typeCheck(`
175
+ fn test() {
176
+ let current: int = bossbar_get_value("ns:health");
177
+ }
178
+ `)
179
+ expect(errors).toHaveLength(0)
180
+ })
181
+
182
+ it('allows lambda variables to be called via function types', () => {
183
+ const errors = typeCheck(`
184
+ fn test() {
185
+ let double: (int) -> int = (x: int) => x * 2;
186
+ let result: int = double(5);
187
+ }
188
+ `)
189
+ expect(errors).toHaveLength(0)
190
+ })
191
+
192
+ it('allows lambdas as callback arguments', () => {
193
+ const errors = typeCheck(`
194
+ fn apply(val: int, cb: (int) -> int) -> int {
195
+ return cb(val);
196
+ }
197
+
198
+ fn test() {
199
+ let result: int = apply(5, (x: int) => x * 3);
200
+ }
201
+ `)
202
+ expect(errors).toHaveLength(0)
203
+ })
204
+
205
+ it('infers single-parameter lambdas from the expected function type', () => {
206
+ const errors = typeCheck(`
207
+ fn test() {
208
+ let double: (int) -> int = x => x * 2;
209
+ let result: int = double(5);
210
+ }
211
+ `)
212
+ expect(errors).toHaveLength(0)
213
+ })
214
+ })
215
+
216
+ describe('return type checking', () => {
217
+ it('allows matching return type', () => {
218
+ const errors = typeCheck(`
219
+ fn get_five() -> int {
220
+ return 5;
221
+ }
222
+ `)
223
+ expect(errors).toHaveLength(0)
224
+ })
225
+
226
+ it('detects return type mismatch', () => {
227
+ const errors = typeCheck(`
228
+ fn get_bool() -> bool {
229
+ return 5;
230
+ }
231
+ `)
232
+ expect(errors.length).toBeGreaterThan(0)
233
+ expect(errors[0].message).toContain("Return type mismatch")
234
+ })
235
+
236
+ it('detects missing return value', () => {
237
+ const errors = typeCheck(`
238
+ fn get_int() -> int {
239
+ return;
240
+ }
241
+ `)
242
+ expect(errors.length).toBeGreaterThan(0)
243
+ expect(errors[0].message).toContain("Missing return value")
244
+ })
245
+
246
+ it('allows void return with no value', () => {
247
+ const errors = typeCheck(`
248
+ fn do_nothing() {
249
+ return;
250
+ }
251
+ `)
252
+ expect(errors).toHaveLength(0)
253
+ })
254
+ })
255
+
256
+ describe('member access', () => {
257
+ it('allows struct field access', () => {
258
+ const errors = typeCheck(`
259
+ struct Point { x: int, y: int }
260
+
261
+ fn test() {
262
+ let p: Point = { x: 10, y: 20 };
263
+ let val: int = p.x;
264
+ }
265
+ `)
266
+ expect(errors).toHaveLength(0)
267
+ })
268
+
269
+ it('detects invalid struct field', () => {
270
+ const errors = typeCheck(`
271
+ struct Point { x: int, y: int }
272
+
273
+ fn test() {
274
+ let p: Point = { x: 10, y: 20 };
275
+ let val: int = p.z;
276
+ }
277
+ `)
278
+ expect(errors.length).toBeGreaterThan(0)
279
+ expect(errors[0].message).toContain("has no field 'z'")
280
+ expect(errors[0].location.line).toBe(6)
281
+ })
282
+
283
+ it('allows array.len access', () => {
284
+ const errors = typeCheck(`
285
+ fn test() {
286
+ let arr: int[] = [1, 2, 3];
287
+ let len: int = arr.len;
288
+ }
289
+ `)
290
+ expect(errors).toHaveLength(0)
291
+ })
292
+
293
+ it('detects member access on primitive', () => {
294
+ const errors = typeCheck(`
295
+ fn test() {
296
+ let x: int = 5;
297
+ let y: int = x.value;
298
+ }
299
+ `)
300
+ expect(errors.length).toBeGreaterThan(0)
301
+ expect(errors[0].message).toContain("Cannot access member")
302
+ })
303
+
304
+ it('allows enum variants and enum-typed variables', () => {
305
+ const errors = typeCheck(`
306
+ enum Direction { North, South, East, West }
307
+
308
+ fn test() {
309
+ let dir: Direction = Direction.North;
310
+ if (dir == Direction.South) {
311
+ say("south");
312
+ }
313
+ match (dir) {
314
+ Direction.East => { say("east"); }
315
+ _ => { say("other"); }
316
+ }
317
+ }
318
+ `)
319
+ expect(errors).toHaveLength(0)
320
+ })
321
+ })
322
+
323
+ describe('control flow', () => {
324
+ it('checks conditions in if statements', () => {
325
+ const errors = typeCheck(`
326
+ fn test() {
327
+ if (undeclared > 0) {
328
+ say("yes");
329
+ }
330
+ }
331
+ `)
332
+ expect(errors.length).toBeGreaterThan(0)
333
+ expect(errors[0].message).toContain("'undeclared' used before declaration")
334
+ })
335
+
336
+ it('checks conditions in while loops', () => {
337
+ const errors = typeCheck(`
338
+ fn test() {
339
+ while (missing) {
340
+ say("loop");
341
+ }
342
+ }
343
+ `)
344
+ expect(errors.length).toBeGreaterThan(0)
345
+ expect(errors[0].message).toContain("'missing' used before declaration")
346
+ })
347
+
348
+ it('checks for loop parts', () => {
349
+ const errors = typeCheck(`
350
+ fn test() {
351
+ for (let i: int = 0; i < count; i = i + 1) {
352
+ say("loop");
353
+ }
354
+ }
355
+ `)
356
+ expect(errors.length).toBeGreaterThan(0)
357
+ expect(errors[0].message).toContain("'count' used before declaration")
358
+ })
359
+ })
360
+
361
+ describe('complex programs', () => {
362
+ it('handles valid complex program', () => {
363
+ const errors = typeCheck(`
364
+ struct Stats { health: int, mana: int }
365
+
366
+ fn heal(amount: int) -> int {
367
+ let bonus: int = amount * 2;
368
+ return bonus;
369
+ }
370
+
371
+ @tick
372
+ fn game_loop() {
373
+ let stats: Stats = { health: 100, mana: 50 };
374
+ let healed: int = heal(10);
375
+ stats.health = stats.health + healed;
376
+ }
377
+ `)
378
+ expect(errors).toHaveLength(0)
379
+ })
380
+
381
+ it('collects multiple errors', () => {
382
+ const errors = typeCheck(`
383
+ fn broken() -> int {
384
+ let x: int = undefined_var;
385
+ let y: int = another_undefined;
386
+ missing_func();
387
+ return false;
388
+ }
389
+ `)
390
+ // Should have multiple errors: 2 undefined vars, return type mismatch
391
+ // (missing_func is not checked since it's not defined as a user function)
392
+ expect(errors.length).toBeGreaterThanOrEqual(3)
393
+ })
394
+ })
395
+ })
@@ -0,0 +1,248 @@
1
+ /**
2
+ * RedScript AST Types
3
+ *
4
+ * This module defines the Abstract Syntax Tree structure for RedScript.
5
+ * The AST is produced by the parser and consumed by the lowering pass.
6
+ */
7
+
8
+ import type { BinOp, CmpOp } from '../ir/types'
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Source Span
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export interface Span {
15
+ line: number // 1-indexed
16
+ col: number // 1-indexed
17
+ endLine?: number
18
+ endCol?: number
19
+ }
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Type Nodes
23
+ // ---------------------------------------------------------------------------
24
+
25
+ export type PrimitiveType = 'int' | 'bool' | 'float' | 'string' | 'void' | 'BlockPos' | 'byte' | 'short' | 'long' | 'double'
26
+
27
+ export type TypeNode =
28
+ | { kind: 'named'; name: PrimitiveType }
29
+ | { kind: 'array'; elem: TypeNode }
30
+ | { kind: 'struct'; name: string }
31
+ | { kind: 'enum'; name: string }
32
+ | { kind: 'function_type'; params: TypeNode[]; return: TypeNode }
33
+
34
+ export interface LambdaParam {
35
+ name: string
36
+ type?: TypeNode
37
+ }
38
+
39
+ export interface LambdaExpr {
40
+ kind: 'lambda'
41
+ params: LambdaParam[]
42
+ returnType?: TypeNode
43
+ body: Expr | Block
44
+ }
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Range Expression
48
+ // ---------------------------------------------------------------------------
49
+
50
+ export interface RangeExpr {
51
+ min?: number // undefined = no lower bound
52
+ max?: number // undefined = no upper bound
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Entity Selector
57
+ // ---------------------------------------------------------------------------
58
+
59
+ export type SelectorKind = '@a' | '@e' | '@s' | '@p' | '@r' | '@n'
60
+
61
+ export interface SelectorFilter {
62
+ type?: string
63
+ distance?: RangeExpr
64
+ tag?: string[]
65
+ notTag?: string[]
66
+ scores?: Record<string, RangeExpr>
67
+ limit?: number
68
+ sort?: 'nearest' | 'furthest' | 'random' | 'arbitrary'
69
+ nbt?: string
70
+ gamemode?: string
71
+ }
72
+
73
+ export interface EntitySelector {
74
+ kind: SelectorKind
75
+ filters?: SelectorFilter
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Block Positions
80
+ // ---------------------------------------------------------------------------
81
+
82
+ export type CoordComponent =
83
+ | { kind: 'absolute'; value: number }
84
+ | { kind: 'relative'; offset: number }
85
+ | { kind: 'local'; offset: number }
86
+
87
+ export interface BlockPosExpr {
88
+ kind: 'blockpos'
89
+ x: CoordComponent
90
+ y: CoordComponent
91
+ z: CoordComponent
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Assignment Operators
96
+ // ---------------------------------------------------------------------------
97
+
98
+ export type AssignOp = '=' | '+=' | '-=' | '*=' | '/=' | '%='
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Expressions
102
+ // ---------------------------------------------------------------------------
103
+
104
+ export type Expr =
105
+ | { kind: 'int_lit'; value: number; span?: Span }
106
+ | { kind: 'float_lit'; value: number; span?: Span }
107
+ | { kind: 'byte_lit'; value: number; span?: Span }
108
+ | { kind: 'short_lit'; value: number; span?: Span }
109
+ | { kind: 'long_lit'; value: number; span?: Span }
110
+ | { kind: 'double_lit'; value: number; span?: Span }
111
+ | { kind: 'bool_lit'; value: boolean; span?: Span }
112
+ | { kind: 'str_lit'; value: string; span?: Span }
113
+ | { kind: 'mc_name'; value: string; span?: Span } // #health → "health" (MC identifier)
114
+ | { kind: 'str_interp'; parts: Array<string | Expr>; span?: Span }
115
+ | { kind: 'range_lit'; range: RangeExpr; span?: Span }
116
+ | (BlockPosExpr & { span?: Span })
117
+ | { kind: 'ident'; name: string; span?: Span }
118
+ | { kind: 'selector'; raw: string; isSingle: boolean; sel: EntitySelector; span?: Span }
119
+ | { kind: 'binary'; op: BinOp | CmpOp | '&&' | '||'; left: Expr; right: Expr; span?: Span }
120
+ | { kind: 'unary'; op: '!' | '-'; operand: Expr; span?: Span }
121
+ | { kind: 'assign'; target: string; op: AssignOp; value: Expr; span?: Span }
122
+ | { kind: 'call'; fn: string; args: Expr[]; span?: Span }
123
+ | { kind: 'invoke'; callee: Expr; args: Expr[]; span?: Span }
124
+ | { kind: 'member'; obj: Expr; field: string; span?: Span }
125
+ | { kind: 'struct_lit'; fields: { name: string; value: Expr }[]; span?: Span }
126
+ | { kind: 'member_assign'; obj: Expr; field: string; op: AssignOp; value: Expr; span?: Span }
127
+ | { kind: 'index'; obj: Expr; index: Expr; span?: Span }
128
+ | { kind: 'array_lit'; elements: Expr[]; span?: Span }
129
+ | { kind: 'static_call'; type: string; method: string; args: Expr[]; span?: Span }
130
+ | (LambdaExpr & { span?: Span })
131
+
132
+ export type LiteralExpr =
133
+ | Extract<Expr, { kind: 'int_lit' }>
134
+ | Extract<Expr, { kind: 'float_lit' }>
135
+ | Extract<Expr, { kind: 'bool_lit' }>
136
+ | Extract<Expr, { kind: 'str_lit' }>
137
+
138
+ // ---------------------------------------------------------------------------
139
+ // Statements
140
+ // ---------------------------------------------------------------------------
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // Execute Subcommand Types
144
+ // ---------------------------------------------------------------------------
145
+
146
+ export type ExecuteSubcommand =
147
+ | { kind: 'as'; selector: EntitySelector }
148
+ | { kind: 'at'; selector: EntitySelector }
149
+ | { kind: 'if_entity'; selector: EntitySelector }
150
+ | { kind: 'unless_entity'; selector: EntitySelector }
151
+ | { kind: 'in'; dimension: string }
152
+
153
+ export type Stmt =
154
+ | { kind: 'let'; name: string; type?: TypeNode; init: Expr; span?: Span }
155
+ | { kind: 'expr'; expr: Expr; span?: Span }
156
+ | { kind: 'return'; value?: Expr; span?: Span }
157
+ | { kind: 'if'; cond: Expr; then: Block; else_?: Block; span?: Span }
158
+ | { kind: 'while'; cond: Expr; body: Block; span?: Span }
159
+ | { kind: 'for'; init?: Stmt; cond: Expr; step: Expr; body: Block; span?: Span }
160
+ | { kind: 'foreach'; binding: string; iterable: Expr; body: Block; span?: Span }
161
+ | { kind: 'for_range'; varName: string; start: Expr; end: Expr; body: Block; span?: Span }
162
+ | { kind: 'match'; expr: Expr; arms: { pattern: Expr | null; body: Block }[]; span?: Span }
163
+ | { kind: 'as_block'; selector: EntitySelector; body: Block; span?: Span }
164
+ | { kind: 'at_block'; selector: EntitySelector; body: Block; span?: Span }
165
+ | { kind: 'as_at'; as_sel: EntitySelector; at_sel: EntitySelector; body: Block; span?: Span }
166
+ | { kind: 'execute'; subcommands: ExecuteSubcommand[]; body: Block; span?: Span }
167
+ | { kind: 'raw'; cmd: string; span?: Span }
168
+
169
+ export type Block = Stmt[]
170
+
171
+ // ---------------------------------------------------------------------------
172
+ // Decorators
173
+ // ---------------------------------------------------------------------------
174
+
175
+ export interface Decorator {
176
+ name: 'tick' | 'on_trigger' | 'on_advancement' | 'on_craft' | 'on_death' | 'on_login' | 'on_join_team'
177
+ args?: {
178
+ rate?: number
179
+ trigger?: string
180
+ advancement?: string
181
+ item?: string
182
+ team?: string
183
+ }
184
+ }
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // Function Declaration
188
+ // ---------------------------------------------------------------------------
189
+
190
+ export interface Param {
191
+ name: string
192
+ type: TypeNode
193
+ default?: Expr
194
+ }
195
+
196
+ export interface FnDecl {
197
+ name: string
198
+ params: Param[]
199
+ returnType: TypeNode
200
+ decorators: Decorator[]
201
+ body: Block
202
+ span?: Span
203
+ }
204
+
205
+ // ---------------------------------------------------------------------------
206
+ // Struct Declaration
207
+ // ---------------------------------------------------------------------------
208
+
209
+ export interface StructField {
210
+ name: string
211
+ type: TypeNode
212
+ }
213
+
214
+ export interface StructDecl {
215
+ name: string
216
+ fields: StructField[]
217
+ span?: Span
218
+ }
219
+
220
+ export interface EnumVariant {
221
+ name: string
222
+ value?: number
223
+ }
224
+
225
+ export interface EnumDecl {
226
+ name: string
227
+ variants: EnumVariant[]
228
+ span?: Span
229
+ }
230
+
231
+ export interface ConstDecl {
232
+ name: string
233
+ type: TypeNode
234
+ value: LiteralExpr
235
+ span?: Span
236
+ }
237
+
238
+ // ---------------------------------------------------------------------------
239
+ // Program (Top-Level)
240
+ // ---------------------------------------------------------------------------
241
+
242
+ export interface Program {
243
+ namespace: string // Inferred from filename or `namespace mypack;`
244
+ declarations: FnDecl[]
245
+ structs: StructDecl[]
246
+ enums: EnumDecl[]
247
+ consts: ConstDecl[]
248
+ }