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 @@
1
+ export {};
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const lexer_1 = require("../lexer");
4
+ function tokenize(source) {
5
+ return new lexer_1.Lexer(source).tokenize();
6
+ }
7
+ function kinds(tokens) {
8
+ return tokens.map(t => t.kind);
9
+ }
10
+ describe('Lexer', () => {
11
+ describe('keywords', () => {
12
+ it('recognizes all keywords', () => {
13
+ const tokens = tokenize('fn let const if else while for foreach match return as at in struct enum trigger namespace');
14
+ expect(kinds(tokens)).toEqual([
15
+ 'fn', 'let', 'const', 'if', 'else', 'while', 'for', 'foreach', 'match',
16
+ 'return', 'as', 'at', 'in', 'struct', 'enum', 'trigger', 'namespace', 'eof'
17
+ ]);
18
+ });
19
+ it('recognizes type keywords', () => {
20
+ const tokens = tokenize('int bool float string void BlockPos');
21
+ expect(kinds(tokens)).toEqual(['int', 'bool', 'float', 'string', 'void', 'BlockPos', 'eof']);
22
+ });
23
+ it('recognizes boolean literals', () => {
24
+ const tokens = tokenize('true false');
25
+ expect(kinds(tokens)).toEqual(['true', 'false', 'eof']);
26
+ });
27
+ });
28
+ describe('identifiers', () => {
29
+ it('tokenizes simple identifiers', () => {
30
+ const tokens = tokenize('foo bar_baz _private x1');
31
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
32
+ ['ident', 'foo'],
33
+ ['ident', 'bar_baz'],
34
+ ['ident', '_private'],
35
+ ['ident', 'x1'],
36
+ ['eof', ''],
37
+ ]);
38
+ });
39
+ });
40
+ describe('literals', () => {
41
+ it('tokenizes integer literals', () => {
42
+ const tokens = tokenize('42 0 123');
43
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
44
+ ['int_lit', '42'],
45
+ ['int_lit', '0'],
46
+ ['int_lit', '123'],
47
+ ['eof', ''],
48
+ ]);
49
+ });
50
+ it('tokenizes float literals', () => {
51
+ const tokens = tokenize('3.14 0.5 10.0');
52
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
53
+ ['float_lit', '3.14'],
54
+ ['float_lit', '0.5'],
55
+ ['float_lit', '10.0'],
56
+ ['eof', ''],
57
+ ]);
58
+ });
59
+ it('tokenizes string literals', () => {
60
+ const tokens = tokenize('"hello" "world" "with \\"quotes\\""');
61
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
62
+ ['string_lit', 'hello'],
63
+ ['string_lit', 'world'],
64
+ ['string_lit', 'with "quotes"'],
65
+ ['eof', ''],
66
+ ]);
67
+ });
68
+ it('tokenizes interpolated strings as a single string token', () => {
69
+ const tokens = tokenize('"hello ${name + 1}"');
70
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
71
+ ['string_lit', 'hello ${name + 1}'],
72
+ ['eof', ''],
73
+ ]);
74
+ });
75
+ it('tokenizes byte literals (b suffix)', () => {
76
+ const tokens = tokenize('20b 0B 127b');
77
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
78
+ ['byte_lit', '20b'],
79
+ ['byte_lit', '0B'],
80
+ ['byte_lit', '127b'],
81
+ ['eof', ''],
82
+ ]);
83
+ });
84
+ it('tokenizes short literals (s suffix)', () => {
85
+ const tokens = tokenize('100s 0S 32767s');
86
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
87
+ ['short_lit', '100s'],
88
+ ['short_lit', '0S'],
89
+ ['short_lit', '32767s'],
90
+ ['eof', ''],
91
+ ]);
92
+ });
93
+ it('tokenizes long literals (L suffix)', () => {
94
+ const tokens = tokenize('1000L 0l 999999L');
95
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
96
+ ['long_lit', '1000L'],
97
+ ['long_lit', '0l'],
98
+ ['long_lit', '999999L'],
99
+ ['eof', ''],
100
+ ]);
101
+ });
102
+ it('tokenizes float literals with f suffix', () => {
103
+ const tokens = tokenize('3.14f 0.5F 10.0f');
104
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
105
+ ['float_lit', '3.14f'],
106
+ ['float_lit', '0.5F'],
107
+ ['float_lit', '10.0f'],
108
+ ['eof', ''],
109
+ ]);
110
+ });
111
+ it('tokenizes double literals (d suffix)', () => {
112
+ const tokens = tokenize('3.14d 0.5D 10.0d');
113
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
114
+ ['double_lit', '3.14d'],
115
+ ['double_lit', '0.5D'],
116
+ ['double_lit', '10.0d'],
117
+ ['eof', ''],
118
+ ]);
119
+ });
120
+ it('tokenizes integer with f/d suffix as float/double', () => {
121
+ const tokens = tokenize('5f 10d');
122
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
123
+ ['float_lit', '5f'],
124
+ ['double_lit', '10d'],
125
+ ['eof', ''],
126
+ ]);
127
+ });
128
+ it('does not treat suffix-like letters in identifiers as NBT suffixes', () => {
129
+ // 1b2 should not be byte_lit — the 'b' is followed by a digit
130
+ const tokens = tokenize('1b2');
131
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
132
+ ['int_lit', '1'],
133
+ ['ident', 'b2'],
134
+ ['eof', ''],
135
+ ]);
136
+ });
137
+ it('tokenizes range literals', () => {
138
+ const tokens = tokenize('..5 1.. 1..10');
139
+ expect(tokens.map(t => [t.kind, t.value])).toEqual([
140
+ ['range_lit', '..5'],
141
+ ['range_lit', '1..'],
142
+ ['range_lit', '1..10'],
143
+ ['eof', ''],
144
+ ]);
145
+ });
146
+ });
147
+ describe('operators', () => {
148
+ it('tokenizes arithmetic operators', () => {
149
+ const tokens = tokenize('+ - * / % ~ ^');
150
+ expect(kinds(tokens)).toEqual(['+', '-', '*', '/', '%', '~', '^', 'eof']);
151
+ });
152
+ it('tokenizes comparison operators', () => {
153
+ const tokens = tokenize('== != < <= > >=');
154
+ expect(kinds(tokens)).toEqual(['==', '!=', '<', '<=', '>', '>=', 'eof']);
155
+ });
156
+ it('tokenizes logical operators', () => {
157
+ const tokens = tokenize('&& || !');
158
+ expect(kinds(tokens)).toEqual(['&&', '||', '!', 'eof']);
159
+ });
160
+ it('tokenizes assignment operators', () => {
161
+ const tokens = tokenize('= += -= *= /= %=');
162
+ expect(kinds(tokens)).toEqual(['=', '+=', '-=', '*=', '/=', '%=', 'eof']);
163
+ });
164
+ it('tokenizes arrow operator', () => {
165
+ const tokens = tokenize('->');
166
+ expect(kinds(tokens)).toEqual(['->', 'eof']);
167
+ });
168
+ it('tokenizes fat arrow operator', () => {
169
+ const tokens = tokenize('=>');
170
+ expect(kinds(tokens)).toEqual(['=>', 'eof']);
171
+ });
172
+ });
173
+ describe('delimiters', () => {
174
+ it('tokenizes all delimiters', () => {
175
+ const tokens = tokenize('{ } ( ) [ ] , ; : .');
176
+ expect(kinds(tokens)).toEqual(['{', '}', '(', ')', '[', ']', ',', ';', ':', '.', 'eof']);
177
+ });
178
+ });
179
+ describe('selectors', () => {
180
+ it('tokenizes simple selectors', () => {
181
+ const tokens = tokenize('@a @e @s @p @r @n');
182
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
183
+ ['selector', '@a'],
184
+ ['selector', '@e'],
185
+ ['selector', '@s'],
186
+ ['selector', '@p'],
187
+ ['selector', '@r'],
188
+ ['selector', '@n'],
189
+ ]);
190
+ });
191
+ it('tokenizes selectors with parameters', () => {
192
+ const tokens = tokenize('@e[type=zombie] @a[distance=..5]');
193
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
194
+ ['selector', '@e[type=zombie]'],
195
+ ['selector', '@a[distance=..5]'],
196
+ ]);
197
+ });
198
+ it('tokenizes selectors with complex NBT', () => {
199
+ const tokens = tokenize('@e[type=zombie, nbt={NoAI:1b}]');
200
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
201
+ ['selector', '@e[type=zombie, nbt={NoAI:1b}]'],
202
+ ]);
203
+ });
204
+ it('handles nested braces in selector NBT', () => {
205
+ const tokens = tokenize('@e[nbt={Items:[{id:"stone"}]}]');
206
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
207
+ ['selector', '@e[nbt={Items:[{id:"stone"}]}]'],
208
+ ]);
209
+ });
210
+ });
211
+ describe('decorators', () => {
212
+ it('tokenizes simple decorators', () => {
213
+ const tokens = tokenize('@tick @on_trigger');
214
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
215
+ ['decorator', '@tick'],
216
+ ['decorator', '@on_trigger'],
217
+ ]);
218
+ });
219
+ it('tokenizes decorators with arguments', () => {
220
+ const tokens = tokenize('@tick(rate=20)');
221
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
222
+ ['decorator', '@tick(rate=20)'],
223
+ ]);
224
+ });
225
+ });
226
+ describe('raw commands', () => {
227
+ it('tokenizes raw command', () => {
228
+ const tokens = tokenize('raw("say hello")');
229
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
230
+ ['raw_cmd', 'say hello'],
231
+ ]);
232
+ });
233
+ });
234
+ describe('comments', () => {
235
+ it('skips line comments', () => {
236
+ const tokens = tokenize('let x = 5 // this is a comment\nlet y = 10');
237
+ expect(kinds(tokens)).toEqual(['let', 'ident', '=', 'int_lit', 'let', 'ident', '=', 'int_lit', 'eof']);
238
+ });
239
+ });
240
+ describe('complex expressions', () => {
241
+ it('tokenizes function declaration', () => {
242
+ const source = 'fn add(a: int, b: int) -> int { return a + b; }';
243
+ const tokens = tokenize(source);
244
+ expect(kinds(tokens)).toEqual([
245
+ 'fn', 'ident', '(', 'ident', ':', 'int', ',', 'ident', ':', 'int', ')',
246
+ '->', 'int', '{', 'return', 'ident', '+', 'ident', ';', '}', 'eof'
247
+ ]);
248
+ });
249
+ it('tokenizes foreach statement', () => {
250
+ const source = 'foreach (z in @e[type=zombie]) { kill(z); }';
251
+ const tokens = tokenize(source);
252
+ expect(kinds(tokens)).toEqual([
253
+ 'foreach', '(', 'ident', 'in', 'selector', ')', '{', 'ident', '(', 'ident', ')', ';', '}', 'eof'
254
+ ]);
255
+ });
256
+ it('tokenizes decorated function', () => {
257
+ const source = '@tick(rate=20)\nfn heartbeat() { say("alive"); }';
258
+ const tokens = tokenize(source);
259
+ expect(kinds(tokens)).toEqual([
260
+ 'decorator', 'fn', 'ident', '(', ')', '{', 'ident', '(', 'string_lit', ')', ';', '}', 'eof'
261
+ ]);
262
+ });
263
+ });
264
+ describe('line/column tracking', () => {
265
+ it('tracks line and column correctly', () => {
266
+ const source = 'let x\nlet y';
267
+ const tokens = tokenize(source);
268
+ expect(tokens[0]).toMatchObject({ kind: 'let', line: 1, col: 1 });
269
+ expect(tokens[1]).toMatchObject({ kind: 'ident', value: 'x', line: 1, col: 5 });
270
+ expect(tokens[2]).toMatchObject({ kind: 'let', line: 2, col: 1 });
271
+ expect(tokens[3]).toMatchObject({ kind: 'ident', value: 'y', line: 2, col: 5 });
272
+ });
273
+ });
274
+ describe('edge cases', () => {
275
+ it('handles empty input', () => {
276
+ const tokens = tokenize('');
277
+ expect(kinds(tokens)).toEqual(['eof']);
278
+ });
279
+ it('handles whitespace only', () => {
280
+ const tokens = tokenize(' \n\t ');
281
+ expect(kinds(tokens)).toEqual(['eof']);
282
+ });
283
+ it('distinguishes selector from decorator', () => {
284
+ // @a is selector (single char followed by non-letter)
285
+ // @aa would be decorator
286
+ const tokens = tokenize('@a @aa');
287
+ expect(tokens.filter(t => t.kind !== 'eof').map(t => [t.kind, t.value])).toEqual([
288
+ ['selector', '@a'],
289
+ ['decorator', '@aa'],
290
+ ]);
291
+ });
292
+ });
293
+ });
294
+ describe('Block comments', () => {
295
+ it('skips single-line block comment', () => {
296
+ const src = `/* comment */ fn test() {}`;
297
+ const tokens = tokenize(src);
298
+ expect(tokens.map(t => t.kind)).not.toContain('/');
299
+ expect(tokens.find(t => t.kind === 'fn')).toBeDefined();
300
+ });
301
+ it('skips multi-line block comment', () => {
302
+ const src = `/**
303
+ * JSDoc comment
304
+ */
305
+ fn test() {}`;
306
+ const tokens = tokenize(src);
307
+ expect(tokens.map(t => t.kind)).not.toContain('/');
308
+ expect(tokens.find(t => t.kind === 'fn')).toBeDefined();
309
+ });
310
+ it('handles block comment with asterisks', () => {
311
+ const src = `/*** stars ***/fn x(){}`;
312
+ const tokens = tokenize(src);
313
+ expect(tokens.find(t => t.kind === 'fn')).toBeDefined();
314
+ });
315
+ });
316
+ //# sourceMappingURL=lexer.test.js.map
@@ -0,0 +1 @@
1
+ export {};