redscript-mc 3.0.1 → 3.0.2

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 (225) hide show
  1. package/.github/workflows/ci.yml +1 -0
  2. package/README.md +119 -313
  3. package/README.zh.md +118 -314
  4. package/ROADMAP.md +5 -5
  5. package/dist/data/impl_test/function/counter/get.mcfunction +5 -0
  6. package/dist/data/impl_test/function/counter/inc.mcfunction +7 -0
  7. package/dist/data/impl_test/function/counter/new.mcfunction +4 -0
  8. package/dist/data/impl_test/function/load.mcfunction +1 -0
  9. package/dist/data/impl_test/function/test_impl.mcfunction +10 -0
  10. package/dist/data/minecraft/tags/function/load.json +5 -0
  11. package/dist/data/playground/function/load.mcfunction +1 -0
  12. package/dist/data/playground/function/start.mcfunction +4 -0
  13. package/dist/data/playground/function/start__say_macro_t1.mcfunction +1 -0
  14. package/dist/data/playground/function/stop.mcfunction +5 -0
  15. package/dist/data/playground/function/stop__say_macro_t0.mcfunction +1 -0
  16. package/dist/data/stdlib_queue8_test/function/__queue_append_apply.mcfunction +4 -0
  17. package/dist/data/stdlib_queue8_test/function/__queue_peek_apply.mcfunction +4 -0
  18. package/dist/data/stdlib_queue8_test/function/__queue_size_raw_apply.mcfunction +4 -0
  19. package/dist/data/stdlib_queue8_test/function/load.mcfunction +1 -0
  20. package/dist/data/stdlib_queue8_test/function/queue_clear.mcfunction +6 -0
  21. package/dist/data/stdlib_queue8_test/function/queue_empty__merge_1.mcfunction +5 -0
  22. package/dist/data/stdlib_queue8_test/function/queue_empty__then_0.mcfunction +5 -0
  23. package/dist/data/stdlib_queue8_test/function/queue_peek__merge_1.mcfunction +13 -0
  24. package/dist/data/stdlib_queue8_test/function/queue_peek__then_0.mcfunction +5 -0
  25. package/dist/data/stdlib_queue8_test/function/queue_pop__merge_1.mcfunction +15 -0
  26. package/dist/data/stdlib_queue8_test/function/queue_pop__then_0.mcfunction +5 -0
  27. package/dist/data/stdlib_queue8_test/function/queue_push__const_11.mcfunction +6 -0
  28. package/dist/data/stdlib_queue8_test/function/queue_push__const_22.mcfunction +6 -0
  29. package/dist/data/stdlib_queue8_test/function/queue_size.mcfunction +13 -0
  30. package/dist/data/stdlib_queue8_test/function/test_queue_push_and_size.mcfunction +13 -0
  31. package/dist/data/test/function/load.mcfunction +1 -0
  32. package/dist/data/test/function/say_at.mcfunction +6 -0
  33. package/dist/data/test/function/test.mcfunction +4 -0
  34. package/dist/pack.mcmeta +6 -0
  35. package/dist/package.json +1 -1
  36. package/dist/src/__tests__/formatter-extra.test.d.ts +7 -0
  37. package/dist/src/__tests__/formatter-extra.test.js +123 -0
  38. package/dist/src/__tests__/global-vars.test.d.ts +13 -0
  39. package/dist/src/__tests__/global-vars.test.js +156 -0
  40. package/dist/src/__tests__/lint/new-rules.test.d.ts +9 -0
  41. package/dist/src/__tests__/lint/new-rules.test.js +402 -0
  42. package/dist/src/__tests__/lsp-rename.test.d.ts +8 -0
  43. package/dist/src/__tests__/lsp-rename.test.js +157 -0
  44. package/dist/src/__tests__/mc-integration/say-fstring.test.d.ts +11 -0
  45. package/dist/src/__tests__/mc-integration/say-fstring.test.js +220 -0
  46. package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +1 -1
  47. package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1 -1
  48. package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1 -1
  49. package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1 -1
  50. package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +1 -1
  51. package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +1 -1
  52. package/dist/src/__tests__/mc-integration/stdlib-coverage-8.test.js +1 -1
  53. package/dist/src/__tests__/mc-syntax.test.js +4 -1
  54. package/dist/src/__tests__/monomorphize-coverage.test.d.ts +9 -0
  55. package/dist/src/__tests__/monomorphize-coverage.test.js +204 -0
  56. package/dist/src/__tests__/optimizer-cse.test.d.ts +7 -0
  57. package/dist/src/__tests__/optimizer-cse.test.js +226 -0
  58. package/dist/src/__tests__/parser.test.js +4 -13
  59. package/dist/src/__tests__/repl-server-extra.test.js +6 -7
  60. package/dist/src/__tests__/repl-server.test.js +5 -7
  61. package/dist/src/__tests__/stdlib/queue.test.js +6 -6
  62. package/dist/src/cli.js +0 -0
  63. package/dist/src/lexer/index.js +2 -1
  64. package/dist/src/lint/index.d.ts +12 -5
  65. package/dist/src/lint/index.js +730 -5
  66. package/dist/src/lsp/main.js +0 -0
  67. package/dist/src/mc-test/client.d.ts +21 -0
  68. package/dist/src/mc-test/client.js +34 -0
  69. package/dist/src/mir/lower.js +108 -6
  70. package/dist/src/optimizer/interprocedural.js +37 -2
  71. package/dist/src/parser/decl-parser.d.ts +19 -0
  72. package/dist/src/parser/decl-parser.js +323 -0
  73. package/dist/src/parser/expr-parser.d.ts +46 -0
  74. package/dist/src/parser/expr-parser.js +759 -0
  75. package/dist/src/parser/index.d.ts +8 -129
  76. package/dist/src/parser/index.js +13 -2262
  77. package/dist/src/parser/stmt-parser.d.ts +28 -0
  78. package/dist/src/parser/stmt-parser.js +577 -0
  79. package/dist/src/parser/type-parser.d.ts +20 -0
  80. package/dist/src/parser/type-parser.js +257 -0
  81. package/dist/src/parser/utils.d.ts +34 -0
  82. package/dist/src/parser/utils.js +141 -0
  83. package/docs/dev/README-mc-integration-tests.md +141 -0
  84. package/docs/lint-rules.md +162 -0
  85. package/docs/stdlib/bigint.md +2 -0
  86. package/editors/vscode/README.md +63 -41
  87. package/editors/vscode/out/extension.js +1881 -1776
  88. package/editors/vscode/out/lsp-server.js +4257 -3651
  89. package/editors/vscode/package-lock.json +3 -3
  90. package/editors/vscode/package.json +1 -1
  91. package/examples/loops-demo.mcrs +87 -0
  92. package/package.json +1 -1
  93. package/redscript-docs/docs/en/stdlib/advanced.md +629 -0
  94. package/redscript-docs/docs/en/stdlib/bigint.md +316 -0
  95. package/redscript-docs/docs/en/stdlib/bits.md +292 -0
  96. package/redscript-docs/docs/en/stdlib/bossbar.md +177 -0
  97. package/redscript-docs/docs/en/stdlib/calculus.md +289 -0
  98. package/redscript-docs/docs/en/stdlib/color.md +353 -0
  99. package/redscript-docs/docs/en/stdlib/combat.md +88 -0
  100. package/redscript-docs/docs/en/stdlib/cooldown.md +82 -0
  101. package/redscript-docs/docs/en/stdlib/dialog.md +155 -0
  102. package/redscript-docs/docs/en/stdlib/easing.md +558 -0
  103. package/redscript-docs/docs/en/stdlib/ecs.md +475 -0
  104. package/redscript-docs/docs/en/stdlib/effects.md +324 -0
  105. package/redscript-docs/docs/en/stdlib/events.md +3 -0
  106. package/redscript-docs/docs/en/stdlib/expr.md +45 -0
  107. package/redscript-docs/docs/en/stdlib/fft.md +141 -0
  108. package/redscript-docs/docs/en/stdlib/geometry.md +430 -0
  109. package/redscript-docs/docs/en/stdlib/graph.md +259 -0
  110. package/redscript-docs/docs/en/stdlib/heap.md +185 -0
  111. package/redscript-docs/docs/en/stdlib/interactions.md +179 -0
  112. package/redscript-docs/docs/en/stdlib/inventory.md +97 -0
  113. package/redscript-docs/docs/en/stdlib/linalg.md +557 -0
  114. package/redscript-docs/docs/en/stdlib/list.md +559 -0
  115. package/redscript-docs/docs/en/stdlib/map.md +140 -0
  116. package/redscript-docs/docs/en/stdlib/math.md +193 -0
  117. package/redscript-docs/docs/en/stdlib/math_hp.md +149 -0
  118. package/redscript-docs/docs/en/stdlib/matrix.md +403 -0
  119. package/redscript-docs/docs/en/stdlib/mobs.md +965 -0
  120. package/redscript-docs/docs/en/stdlib/noise.md +244 -0
  121. package/redscript-docs/docs/en/stdlib/ode.md +253 -0
  122. package/redscript-docs/docs/en/stdlib/parabola.md +342 -0
  123. package/redscript-docs/docs/en/stdlib/particles.md +311 -0
  124. package/redscript-docs/docs/en/stdlib/pathfind.md +255 -0
  125. package/redscript-docs/docs/en/stdlib/physics.md +493 -0
  126. package/redscript-docs/docs/en/stdlib/player.md +78 -0
  127. package/redscript-docs/docs/en/stdlib/quaternion.md +673 -0
  128. package/redscript-docs/docs/en/stdlib/queue.md +134 -0
  129. package/redscript-docs/docs/en/stdlib/random.md +223 -0
  130. package/redscript-docs/docs/en/stdlib/result.md +143 -0
  131. package/redscript-docs/docs/en/stdlib/scheduler.md +183 -0
  132. package/redscript-docs/docs/en/stdlib/set_int.md +190 -0
  133. package/redscript-docs/docs/en/stdlib/sets.md +101 -0
  134. package/redscript-docs/docs/en/stdlib/signal.md +400 -0
  135. package/redscript-docs/docs/en/stdlib/sort.md +104 -0
  136. package/redscript-docs/docs/en/stdlib/spawn.md +147 -0
  137. package/redscript-docs/docs/en/stdlib/state.md +142 -0
  138. package/redscript-docs/docs/en/stdlib/strings.md +154 -0
  139. package/redscript-docs/docs/en/stdlib/tags.md +3451 -0
  140. package/redscript-docs/docs/en/stdlib/teams.md +153 -0
  141. package/redscript-docs/docs/en/stdlib/timer.md +246 -0
  142. package/redscript-docs/docs/en/stdlib/vec.md +158 -0
  143. package/redscript-docs/docs/en/stdlib/world.md +298 -0
  144. package/redscript-docs/docs/zh/stdlib/advanced.md +615 -0
  145. package/redscript-docs/docs/zh/stdlib/bigint.md +316 -0
  146. package/redscript-docs/docs/zh/stdlib/bits.md +292 -0
  147. package/redscript-docs/docs/zh/stdlib/bossbar.md +170 -0
  148. package/redscript-docs/docs/zh/stdlib/calculus.md +287 -0
  149. package/redscript-docs/docs/zh/stdlib/color.md +353 -0
  150. package/redscript-docs/docs/zh/stdlib/combat.md +88 -0
  151. package/redscript-docs/docs/zh/stdlib/cooldown.md +84 -0
  152. package/redscript-docs/docs/zh/stdlib/dialog.md +152 -0
  153. package/redscript-docs/docs/zh/stdlib/easing.md +558 -0
  154. package/redscript-docs/docs/zh/stdlib/ecs.md +472 -0
  155. package/redscript-docs/docs/zh/stdlib/effects.md +324 -0
  156. package/redscript-docs/docs/zh/stdlib/events.md +3 -0
  157. package/redscript-docs/docs/zh/stdlib/expr.md +37 -0
  158. package/redscript-docs/docs/zh/stdlib/fft.md +128 -0
  159. package/redscript-docs/docs/zh/stdlib/geometry.md +430 -0
  160. package/redscript-docs/docs/zh/stdlib/graph.md +259 -0
  161. package/redscript-docs/docs/zh/stdlib/heap.md +185 -0
  162. package/redscript-docs/docs/zh/stdlib/interactions.md +160 -0
  163. package/redscript-docs/docs/zh/stdlib/inventory.md +94 -0
  164. package/redscript-docs/docs/zh/stdlib/linalg.md +543 -0
  165. package/redscript-docs/docs/zh/stdlib/list.md +561 -0
  166. package/redscript-docs/docs/zh/stdlib/map.md +132 -0
  167. package/redscript-docs/docs/zh/stdlib/math.md +193 -0
  168. package/redscript-docs/docs/zh/stdlib/math_hp.md +143 -0
  169. package/redscript-docs/docs/zh/stdlib/matrix.md +396 -0
  170. package/redscript-docs/docs/zh/stdlib/mobs.md +965 -0
  171. package/redscript-docs/docs/zh/stdlib/noise.md +244 -0
  172. package/redscript-docs/docs/zh/stdlib/ode.md +243 -0
  173. package/redscript-docs/docs/zh/stdlib/parabola.md +337 -0
  174. package/redscript-docs/docs/zh/stdlib/particles.md +307 -0
  175. package/redscript-docs/docs/zh/stdlib/pathfind.md +255 -0
  176. package/redscript-docs/docs/zh/stdlib/physics.md +493 -0
  177. package/redscript-docs/docs/zh/stdlib/player.md +78 -0
  178. package/redscript-docs/docs/zh/stdlib/quaternion.md +669 -0
  179. package/redscript-docs/docs/zh/stdlib/queue.md +124 -0
  180. package/redscript-docs/docs/zh/stdlib/random.md +222 -0
  181. package/redscript-docs/docs/zh/stdlib/result.md +147 -0
  182. package/redscript-docs/docs/zh/stdlib/scheduler.md +173 -0
  183. package/redscript-docs/docs/zh/stdlib/set_int.md +180 -0
  184. package/redscript-docs/docs/zh/stdlib/sets.md +107 -0
  185. package/redscript-docs/docs/zh/stdlib/signal.md +373 -0
  186. package/redscript-docs/docs/zh/stdlib/sort.md +104 -0
  187. package/redscript-docs/docs/zh/stdlib/spawn.md +142 -0
  188. package/redscript-docs/docs/zh/stdlib/state.md +134 -0
  189. package/redscript-docs/docs/zh/stdlib/strings.md +107 -0
  190. package/redscript-docs/docs/zh/stdlib/tags.md +3451 -0
  191. package/redscript-docs/docs/zh/stdlib/teams.md +150 -0
  192. package/redscript-docs/docs/zh/stdlib/timer.md +254 -0
  193. package/redscript-docs/docs/zh/stdlib/vec.md +158 -0
  194. package/redscript-docs/docs/zh/stdlib/world.md +289 -0
  195. package/src/__tests__/formatter-extra.test.ts +139 -0
  196. package/src/__tests__/global-vars.test.ts +171 -0
  197. package/src/__tests__/lint/new-rules.test.ts +437 -0
  198. package/src/__tests__/lsp-rename.test.ts +171 -0
  199. package/src/__tests__/mc-integration/say-fstring.test.ts +211 -0
  200. package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +1 -1
  201. package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1 -1
  202. package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1 -1
  203. package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1 -1
  204. package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +1 -1
  205. package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +1 -1
  206. package/src/__tests__/mc-integration/stdlib-coverage-8.test.ts +1 -1
  207. package/src/__tests__/mc-syntax.test.ts +3 -0
  208. package/src/__tests__/monomorphize-coverage.test.ts +220 -0
  209. package/src/__tests__/optimizer-cse.test.ts +250 -0
  210. package/src/__tests__/parser.test.ts +4 -13
  211. package/src/__tests__/repl-server-extra.test.ts +6 -6
  212. package/src/__tests__/repl-server.test.ts +5 -6
  213. package/src/__tests__/stdlib/queue.test.ts +6 -6
  214. package/src/lexer/index.ts +2 -1
  215. package/src/lint/index.ts +713 -5
  216. package/src/mc-test/client.ts +40 -0
  217. package/src/mir/lower.ts +111 -2
  218. package/src/optimizer/interprocedural.ts +40 -2
  219. package/src/parser/decl-parser.ts +349 -0
  220. package/src/parser/expr-parser.ts +838 -0
  221. package/src/parser/index.ts +17 -2558
  222. package/src/parser/stmt-parser.ts +585 -0
  223. package/src/parser/type-parser.ts +276 -0
  224. package/src/parser/utils.ts +173 -0
  225. package/src/stdlib/queue.mcrs +19 -6
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ /**
3
+ * TypeParser — parses type annotations and generic type arguments.
4
+ * Extends ParserBase to gain token navigation.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.TypeParser = void 0;
8
+ const utils_1 = require("./utils");
9
+ class TypeParser extends utils_1.ParserBase {
10
+ // -------------------------------------------------------------------------
11
+ // Type Parsing
12
+ // -------------------------------------------------------------------------
13
+ parseType() {
14
+ const token = this.peek();
15
+ let type;
16
+ if (token.kind === '(') {
17
+ // Disambiguate: tuple type `(T, T)` vs function type `(T) -> R`
18
+ const saved = this.pos;
19
+ this.advance(); // consume '('
20
+ const elements = [];
21
+ if (!this.check(')')) {
22
+ do {
23
+ elements.push(this.parseType());
24
+ } while (this.match(','));
25
+ }
26
+ this.expect(')');
27
+ if (this.check('->')) {
28
+ this.pos = saved;
29
+ return this.parseFunctionType();
30
+ }
31
+ return { kind: 'tuple', elements };
32
+ }
33
+ if (token.kind === 'float') {
34
+ this.advance();
35
+ const filePart = this.filePath ? `${this.filePath}:` : '';
36
+ this.warnings.push(`[DeprecatedType] ${filePart}line ${token.line}, col ${token.col}: 'float' is deprecated, use 'fixed' instead (×10000 fixed-point)`);
37
+ type = { kind: 'named', name: 'float' };
38
+ }
39
+ else if (token.kind === 'int' || token.kind === 'bool' ||
40
+ token.kind === 'fixed' || token.kind === 'string' || token.kind === 'void' ||
41
+ token.kind === 'BlockPos') {
42
+ this.advance();
43
+ type = { kind: 'named', name: token.kind };
44
+ }
45
+ else if (token.kind === 'ident') {
46
+ this.advance();
47
+ if (token.value === 'selector' && this.check('<')) {
48
+ this.advance(); // consume <
49
+ const entityType = this.expect('ident').value;
50
+ this.expect('>');
51
+ type = { kind: 'selector', entityType };
52
+ }
53
+ else if (token.value === 'selector') {
54
+ type = { kind: 'selector' };
55
+ }
56
+ else if (token.value === 'Option' && this.check('<')) {
57
+ this.advance(); // consume <
58
+ const inner = this.parseType();
59
+ this.expect('>');
60
+ type = { kind: 'option', inner };
61
+ }
62
+ else if (token.value === 'double' || token.value === 'byte' ||
63
+ token.value === 'short' || token.value === 'long' ||
64
+ token.value === 'format_string') {
65
+ type = { kind: 'named', name: token.value };
66
+ }
67
+ else {
68
+ type = { kind: 'struct', name: token.value };
69
+ }
70
+ }
71
+ else {
72
+ this.error(`Expected type, got '${token.value || token.kind}'. Valid types: int, float, bool, string, void, or a struct/enum name`);
73
+ }
74
+ while (this.match('[')) {
75
+ this.expect(']');
76
+ type = { kind: 'array', elem: type };
77
+ }
78
+ return type;
79
+ }
80
+ parseFunctionType() {
81
+ this.expect('(');
82
+ const params = [];
83
+ if (!this.check(')')) {
84
+ do {
85
+ params.push(this.parseType());
86
+ } while (this.match(','));
87
+ }
88
+ this.expect(')');
89
+ this.expect('->');
90
+ const returnType = this.parseType();
91
+ return { kind: 'function_type', params, return: returnType };
92
+ }
93
+ /**
94
+ * Try to parse `<Type, ...>` as explicit generic type arguments.
95
+ * Returns the parsed type list if successful, null if this looks like a comparison.
96
+ * Does NOT consume any tokens if it returns null.
97
+ */
98
+ tryParseTypeArgs() {
99
+ const saved = this.pos;
100
+ this.advance(); // consume '<'
101
+ const typeArgs = [];
102
+ try {
103
+ do {
104
+ typeArgs.push(this.parseType());
105
+ } while (this.match(','));
106
+ if (!this.check('>')) {
107
+ this.pos = saved;
108
+ return null;
109
+ }
110
+ this.advance(); // consume '>'
111
+ return typeArgs;
112
+ }
113
+ catch {
114
+ this.pos = saved;
115
+ return null;
116
+ }
117
+ }
118
+ // -------------------------------------------------------------------------
119
+ // Lambda lookahead helpers (needed by expr-parser)
120
+ // -------------------------------------------------------------------------
121
+ isLambdaStart() {
122
+ if (!this.check('('))
123
+ return false;
124
+ let offset = 1;
125
+ if (this.peek(offset).kind !== ')') {
126
+ while (true) {
127
+ if (this.peek(offset).kind !== 'ident') {
128
+ return false;
129
+ }
130
+ offset += 1;
131
+ if (this.peek(offset).kind === ':') {
132
+ offset += 1;
133
+ const consumed = this.typeTokenLength(offset);
134
+ if (consumed === 0) {
135
+ return false;
136
+ }
137
+ offset += consumed;
138
+ }
139
+ if (this.peek(offset).kind === ',') {
140
+ offset += 1;
141
+ continue;
142
+ }
143
+ break;
144
+ }
145
+ }
146
+ if (this.peek(offset).kind !== ')') {
147
+ return false;
148
+ }
149
+ offset += 1;
150
+ if (this.peek(offset).kind === '=>') {
151
+ return true;
152
+ }
153
+ if (this.peek(offset).kind === '->') {
154
+ offset += 1;
155
+ const consumed = this.typeTokenLength(offset);
156
+ if (consumed === 0) {
157
+ return false;
158
+ }
159
+ offset += consumed;
160
+ return this.peek(offset).kind === '=>';
161
+ }
162
+ return false;
163
+ }
164
+ typeTokenLength(offset) {
165
+ const token = this.peek(offset);
166
+ if (token.kind === '(') {
167
+ let inner = offset + 1;
168
+ if (this.peek(inner).kind !== ')') {
169
+ while (true) {
170
+ const consumed = this.typeTokenLength(inner);
171
+ if (consumed === 0) {
172
+ return 0;
173
+ }
174
+ inner += consumed;
175
+ if (this.peek(inner).kind === ',') {
176
+ inner += 1;
177
+ continue;
178
+ }
179
+ break;
180
+ }
181
+ }
182
+ if (this.peek(inner).kind !== ')') {
183
+ return 0;
184
+ }
185
+ inner += 1;
186
+ if (this.peek(inner).kind !== '->') {
187
+ return 0;
188
+ }
189
+ inner += 1;
190
+ const returnLen = this.typeTokenLength(inner);
191
+ return returnLen === 0 ? 0 : inner + returnLen - offset;
192
+ }
193
+ const isNamedType = token.kind === 'int' ||
194
+ token.kind === 'bool' ||
195
+ token.kind === 'float' ||
196
+ token.kind === 'fixed' ||
197
+ token.kind === 'string' ||
198
+ token.kind === 'void' ||
199
+ token.kind === 'BlockPos' ||
200
+ token.kind === 'ident';
201
+ if (!isNamedType) {
202
+ return 0;
203
+ }
204
+ let length = 1;
205
+ while (this.peek(offset + length).kind === '[' && this.peek(offset + length + 1).kind === ']') {
206
+ length += 2;
207
+ }
208
+ return length;
209
+ }
210
+ // -------------------------------------------------------------------------
211
+ // Params parsing (used by decl-parser)
212
+ // -------------------------------------------------------------------------
213
+ parseParams(implTypeName) {
214
+ const params = [];
215
+ if (!this.check(')')) {
216
+ do {
217
+ const paramToken = this.expect('ident');
218
+ const name = paramToken.value;
219
+ let type;
220
+ if (implTypeName && params.length === 0 && name === 'self' && !this.check(':')) {
221
+ type = { kind: 'struct', name: implTypeName };
222
+ }
223
+ else {
224
+ this.expect(':');
225
+ type = this.parseType();
226
+ }
227
+ let defaultValue;
228
+ if (this.match('=')) {
229
+ defaultValue = this.parseExpr();
230
+ }
231
+ params.push(this.withLoc({ name, type, default: defaultValue }, paramToken));
232
+ } while (this.match(','));
233
+ }
234
+ return params;
235
+ }
236
+ parseInterfaceParams() {
237
+ const params = [];
238
+ if (!this.check(')')) {
239
+ do {
240
+ const paramToken = this.expect('ident');
241
+ const paramName = paramToken.value;
242
+ let type;
243
+ if (params.length === 0 && paramName === 'self' && !this.check(':')) {
244
+ type = { kind: 'named', name: 'void' };
245
+ }
246
+ else {
247
+ this.expect(':');
248
+ type = this.parseType();
249
+ }
250
+ params.push(this.withLoc({ name: paramName, type }, paramToken));
251
+ } while (this.match(','));
252
+ }
253
+ return params;
254
+ }
255
+ }
256
+ exports.TypeParser = TypeParser;
257
+ //# sourceMappingURL=type-parser.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Parser utilities — base class with token navigation, error handling,
3
+ * and shared constants used by all sub-parsers.
4
+ */
5
+ import { Lexer, type Token, type TokenKind } from '../lexer';
6
+ import { DiagnosticError } from '../diagnostics';
7
+ export declare const PRECEDENCE: Record<string, number>;
8
+ export declare const BINARY_OPS: Set<string>;
9
+ export type { Lexer };
10
+ export declare class ParserBase {
11
+ protected tokens: Token[];
12
+ protected pos: number;
13
+ protected sourceLines: string[];
14
+ protected filePath?: string;
15
+ /** Set to true once `module library;` is seen. */
16
+ protected inLibraryMode: boolean;
17
+ /** Warnings accumulated during parsing (e.g. deprecated keyword usage). */
18
+ readonly warnings: string[];
19
+ /** Parse errors collected during error-recovery mode. */
20
+ readonly parseErrors: DiagnosticError[];
21
+ constructor(tokens: Token[], source?: string, filePath?: string);
22
+ peek(offset?: number): Token;
23
+ advance(): Token;
24
+ check(kind: TokenKind): boolean;
25
+ match(...kinds: TokenKind[]): boolean;
26
+ expect(kind: TokenKind): Token;
27
+ error(message: string): never;
28
+ withLoc<T extends object>(node: T, token: Token): T;
29
+ getLocToken(node: object): Token | null;
30
+ checkIdent(value: string): boolean;
31
+ syncToNextDecl(): void;
32
+ syncToNextStmt(): void;
33
+ protected makeSubParser(source: string): ParserBase;
34
+ }
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * Parser utilities — base class with token navigation, error handling,
4
+ * and shared constants used by all sub-parsers.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ParserBase = exports.BINARY_OPS = exports.PRECEDENCE = void 0;
8
+ const lexer_1 = require("../lexer");
9
+ const diagnostics_1 = require("../diagnostics");
10
+ // ---------------------------------------------------------------------------
11
+ // Operator Precedence (higher = binds tighter)
12
+ // ---------------------------------------------------------------------------
13
+ exports.PRECEDENCE = {
14
+ '||': 1,
15
+ '&&': 2,
16
+ '==': 3, '!=': 3,
17
+ '<': 4, '<=': 4, '>': 4, '>=': 4, 'is': 4,
18
+ '+': 5, '-': 5,
19
+ '*': 6, '/': 6, '%': 6,
20
+ };
21
+ exports.BINARY_OPS = new Set(['||', '&&', '==', '!=', '<', '<=', '>', '>=', 'is', '+', '-', '*', '/', '%']);
22
+ // ---------------------------------------------------------------------------
23
+ // ParserBase — token navigation, error reporting, span attachment
24
+ // ---------------------------------------------------------------------------
25
+ class ParserBase {
26
+ constructor(tokens, source, filePath) {
27
+ this.pos = 0;
28
+ /** Set to true once `module library;` is seen. */
29
+ this.inLibraryMode = false;
30
+ /** Warnings accumulated during parsing (e.g. deprecated keyword usage). */
31
+ this.warnings = [];
32
+ /** Parse errors collected during error-recovery mode. */
33
+ this.parseErrors = [];
34
+ this.tokens = tokens;
35
+ this.sourceLines = source?.split('\n') ?? [];
36
+ this.filePath = filePath;
37
+ }
38
+ // -------------------------------------------------------------------------
39
+ // Token navigation
40
+ // -------------------------------------------------------------------------
41
+ peek(offset = 0) {
42
+ const idx = this.pos + offset;
43
+ if (idx >= this.tokens.length) {
44
+ return this.tokens[this.tokens.length - 1]; // eof
45
+ }
46
+ return this.tokens[idx];
47
+ }
48
+ advance() {
49
+ const token = this.tokens[this.pos];
50
+ if (token.kind !== 'eof')
51
+ this.pos++;
52
+ return token;
53
+ }
54
+ check(kind) {
55
+ return this.peek().kind === kind;
56
+ }
57
+ match(...kinds) {
58
+ for (const kind of kinds) {
59
+ if (this.check(kind)) {
60
+ this.advance();
61
+ return true;
62
+ }
63
+ }
64
+ return false;
65
+ }
66
+ expect(kind) {
67
+ const token = this.peek();
68
+ if (token.kind !== kind) {
69
+ throw new diagnostics_1.DiagnosticError('ParseError', `Expected '${kind}' but got '${token.kind}'`, { file: this.filePath, line: token.line, col: token.col }, this.sourceLines);
70
+ }
71
+ return this.advance();
72
+ }
73
+ error(message) {
74
+ const token = this.peek();
75
+ throw new diagnostics_1.DiagnosticError('ParseError', message, { file: this.filePath, line: token.line, col: token.col }, this.sourceLines);
76
+ }
77
+ withLoc(node, token) {
78
+ const span = { line: token.line, col: token.col };
79
+ Object.defineProperty(node, 'span', {
80
+ value: span,
81
+ enumerable: false,
82
+ configurable: true,
83
+ writable: true,
84
+ });
85
+ return node;
86
+ }
87
+ getLocToken(node) {
88
+ const span = node.span;
89
+ if (!span) {
90
+ return null;
91
+ }
92
+ return { kind: 'eof', value: '', line: span.line, col: span.col };
93
+ }
94
+ checkIdent(value) {
95
+ return this.check('ident') && this.peek().value === value;
96
+ }
97
+ // -------------------------------------------------------------------------
98
+ // Error Recovery
99
+ // -------------------------------------------------------------------------
100
+ syncToNextDecl() {
101
+ const TOP_LEVEL_KEYWORDS = new Set([
102
+ 'fn', 'struct', 'impl', 'enum', 'const', 'let', 'export', 'declare', 'import', 'namespace', 'module'
103
+ ]);
104
+ while (!this.check('eof')) {
105
+ const kind = this.peek().kind;
106
+ if (kind === '}') {
107
+ this.advance();
108
+ return;
109
+ }
110
+ if (TOP_LEVEL_KEYWORDS.has(kind)) {
111
+ return;
112
+ }
113
+ if (kind === 'ident' && this.peek().value === 'import') {
114
+ return;
115
+ }
116
+ this.advance();
117
+ }
118
+ }
119
+ syncToNextStmt() {
120
+ while (!this.check('eof')) {
121
+ const kind = this.peek().kind;
122
+ if (kind === ';') {
123
+ this.advance();
124
+ return;
125
+ }
126
+ if (kind === '}') {
127
+ return;
128
+ }
129
+ this.advance();
130
+ }
131
+ }
132
+ // -------------------------------------------------------------------------
133
+ // Sub-parser helper (used by string interpolation)
134
+ // -------------------------------------------------------------------------
135
+ makeSubParser(source) {
136
+ const tokens = new lexer_1.Lexer(source, this.filePath).tokenize();
137
+ return new ParserBase(tokens, source, this.filePath);
138
+ }
139
+ }
140
+ exports.ParserBase = ParserBase;
141
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,141 @@
1
+ # MC Integration Tests — 操作手册
2
+
3
+ RedScript 有两层测试:
4
+
5
+ 1. **单元/e2e 测试**:纯编译器,不需要 MC 服务器,直接 `npm test`
6
+ 2. **MC 联动测试**:需要 Paper 服务器 + TestHarness 插件,测试生成的 mcfunction 在真实 MC 里是否正确执行
7
+
8
+ ---
9
+
10
+ ## 快速跑法
11
+
12
+ ```bash
13
+ # 1. 确认服务器在跑
14
+ curl http://localhost:25561/status
15
+
16
+ # 2. 跑所有 MC 联动测试
17
+ cd ~/projects/redscript
18
+ MC_SERVER_DIR=~/mc-test-server MC_PORT=25561 npx jest src/__tests__/mc-integration/ --testTimeout=120000 --no-coverage --forceExit
19
+
20
+ # 3. 跑单个测试文件
21
+ MC_SERVER_DIR=~/mc-test-server MC_PORT=25561 npx jest say-fstring --testTimeout=60000 --forceExit
22
+ ```
23
+
24
+ 服务器不在线时,测试会自动跳过(不报错),只有编译时测试会跑。
25
+
26
+ ---
27
+
28
+ ## 启动 Paper 服务器
29
+
30
+ Java 装在 Homebrew(不在系统 PATH):
31
+
32
+ ```bash
33
+ export JAVA_HOME="/opt/homebrew/opt/openjdk@21"
34
+ export PATH="$JAVA_HOME/bin:$PATH"
35
+
36
+ cd ~/mc-test-server
37
+ java -Xmx2G -Xms512M -jar paper.jar --nogui
38
+ ```
39
+
40
+ 服务器起来后 TestHarness 插件自动在 **端口 25561** 上开 HTTP API。
41
+
42
+ 验证:
43
+ ```bash
44
+ curl http://localhost:25561/status
45
+ # → {"online":true,"tps_1m":20.0,...}
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 关键注意事项
51
+
52
+ ### 1. 只能有一个 redscript-test datapack
53
+
54
+ `~/mc-test-server/world/datapacks/` 里**只留 `redscript-test/`**,其他旧 datapack 全删掉。
55
+ 多个 datapack 有相同 namespace 会导致 MC 用旧版本,测试结果不对。
56
+
57
+ ```bash
58
+ # 清理旧 datapack(只留 redscript-test)
59
+ ls ~/mc-test-server/world/datapacks/
60
+ trash ~/mc-test-server/world/datapacks/<旧的>
61
+ ```
62
+
63
+ ### 2. Reload 用 TestHarness API
64
+
65
+ 不要用 `/reload` 命令(需要 `/reload confirm`,Paper 1.21+ 要求)。
66
+ 测试代码里用 `mc.reload()`,它调用的是 `POST /reload`。
67
+
68
+ ### 3. Java 路径
69
+
70
+ 系统 `java` 命令指向一个壳,实际没装。用:
71
+ ```bash
72
+ /opt/homebrew/opt/openjdk@21/bin/java -version
73
+ # openjdk version "21.0.10"
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 测试文件结构
79
+
80
+ ```
81
+ src/__tests__/mc-integration/
82
+ ├── syntax-coverage.test.ts # 语法特性:for-each、match、Option、impl、struct、array
83
+ ├── say-fstring.test.ts # say() + f-string macro 编译和运行时测试
84
+ ├── stdlib-coverage.test.ts # stdlib 覆盖(数学、物理等)
85
+ ├── stdlib-coverage-2~8.test.ts # stdlib 覆盖续集
86
+ └── item-entity-events.test.ts # 实体/物品/事件
87
+ ```
88
+
89
+ ### 写新 test case 的模式
90
+
91
+ ```typescript
92
+ import { compile } from '../../compile'
93
+ import { MCTestClient } from '../../mc-test/client'
94
+
95
+ const NS = 'my_test_ns' // 每个文件用唯一 namespace
96
+ let serverOnline = false
97
+ let mc: MCTestClient
98
+
99
+ beforeAll(async () => {
100
+ mc = new MCTestClient(process.env.MC_HOST ?? 'localhost', parseInt(process.env.MC_PORT ?? '25561'))
101
+ try {
102
+ serverOnline = await mc.isOnline()
103
+ } catch { serverOnline = false }
104
+ if (!serverOnline) return
105
+
106
+ // 编译 + 写入 datapack
107
+ writeFixture(`...redscript code...`, NS)
108
+ await mc.reload()
109
+ await mc.ticks(5)
110
+ }, 30_000)
111
+
112
+ test('runtime: xxx', async () => {
113
+ if (!serverOnline) return // 服务器不在线就跳过
114
+
115
+ await mc.command(`/function ${NS}:fn_name`)
116
+ await mc.ticks(5)
117
+ const score = await mc.scoreboard('#result', 'objective')
118
+ expect(score).toBe(42)
119
+ }, 20_000)
120
+ ```
121
+
122
+ ### 可用的 TestHarness API(via MCTestClient)
123
+
124
+ | 方法 | 说明 |
125
+ |------|------|
126
+ | `mc.isOnline()` | 检查服务器是否在线 |
127
+ | `mc.reload()` | Reload datapacks(正确方式) |
128
+ | `mc.ticks(n)` | 等待 n 个游戏 tick(50ms 各) |
129
+ | `mc.command('/xxx')` | 执行 MC 命令 |
130
+ | `mc.scoreboard(player, obj)` | 读取 scoreboard 值 |
131
+ | `mc.chat(since)` | 读取聊天日志 |
132
+ | `mc.assertChatContains(str)` | 断言聊天包含字符串 |
133
+ | `mc.reset()` | 清空聊天和事件日志 |
134
+ | `mc.fullReset(...)` | 完整重置(清空区域、实体、scoreboard) |
135
+
136
+ ---
137
+
138
+ ## 已知问题
139
+
140
+ - `impl Counter` 测试(syntax-coverage):runtime 失败,`#impl_out` 读到 0 而不是 3。这是预存在的 impl 方法运行时 bug,与 f-string/say macro 无关。
141
+ - `repl-server-extra.test.ts`:端口 3001 占用导致 10 个测试失败,与代码无关。