redscript-mc 1.2.0 → 1.2.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 (55) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +53 -10
  3. package/README.zh.md +53 -10
  4. package/dist/__tests__/dce.test.d.ts +1 -0
  5. package/dist/__tests__/dce.test.js +137 -0
  6. package/dist/__tests__/lexer.test.js +19 -2
  7. package/dist/__tests__/lowering.test.js +8 -0
  8. package/dist/__tests__/mc-syntax.test.js +12 -0
  9. package/dist/__tests__/parser.test.js +10 -0
  10. package/dist/__tests__/runtime.test.js +13 -0
  11. package/dist/__tests__/typechecker.test.js +30 -0
  12. package/dist/ast/types.d.ts +22 -2
  13. package/dist/cli.js +15 -10
  14. package/dist/codegen/structure/index.d.ts +4 -1
  15. package/dist/codegen/structure/index.js +4 -2
  16. package/dist/compile.d.ts +1 -0
  17. package/dist/compile.js +4 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +4 -1
  20. package/dist/lexer/index.d.ts +2 -1
  21. package/dist/lexer/index.js +89 -1
  22. package/dist/lowering/index.js +37 -1
  23. package/dist/optimizer/dce.d.ts +23 -0
  24. package/dist/optimizer/dce.js +592 -0
  25. package/dist/parser/index.d.ts +2 -0
  26. package/dist/parser/index.js +81 -16
  27. package/dist/typechecker/index.d.ts +2 -0
  28. package/dist/typechecker/index.js +49 -0
  29. package/docs/ARCHITECTURE.zh.md +1088 -0
  30. package/editors/vscode/.vscodeignore +3 -0
  31. package/editors/vscode/icon.png +0 -0
  32. package/editors/vscode/out/extension.js +834 -19
  33. package/editors/vscode/package-lock.json +2 -2
  34. package/editors/vscode/package.json +1 -1
  35. package/editors/vscode/syntaxes/redscript.tmLanguage.json +6 -2
  36. package/examples/spiral.mcrs +41 -0
  37. package/logo.png +0 -0
  38. package/package.json +1 -1
  39. package/src/__tests__/dce.test.ts +129 -0
  40. package/src/__tests__/lexer.test.ts +21 -2
  41. package/src/__tests__/lowering.test.ts +9 -0
  42. package/src/__tests__/mc-syntax.test.ts +14 -0
  43. package/src/__tests__/parser.test.ts +11 -0
  44. package/src/__tests__/runtime.test.ts +16 -0
  45. package/src/__tests__/typechecker.test.ts +33 -0
  46. package/src/ast/types.ts +14 -1
  47. package/src/cli.ts +24 -10
  48. package/src/codegen/structure/index.ts +13 -2
  49. package/src/compile.ts +5 -1
  50. package/src/index.ts +5 -1
  51. package/src/lexer/index.ts +102 -1
  52. package/src/lowering/index.ts +38 -2
  53. package/src/optimizer/dce.ts +619 -0
  54. package/src/parser/index.ts +97 -17
  55. package/src/typechecker/index.ts +65 -0
@@ -0,0 +1,592 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeadCodeEliminator = void 0;
4
+ exports.eliminateDeadCode = eliminateDeadCode;
5
+ function copySpan(target, source) {
6
+ const descriptor = Object.getOwnPropertyDescriptor(source, 'span');
7
+ if (descriptor) {
8
+ Object.defineProperty(target, 'span', descriptor);
9
+ }
10
+ return target;
11
+ }
12
+ function isConstantBoolean(expr) {
13
+ if (expr.kind === 'bool_lit') {
14
+ return expr.value;
15
+ }
16
+ return null;
17
+ }
18
+ function isPureExpr(expr) {
19
+ switch (expr.kind) {
20
+ case 'int_lit':
21
+ case 'float_lit':
22
+ case 'byte_lit':
23
+ case 'short_lit':
24
+ case 'long_lit':
25
+ case 'double_lit':
26
+ case 'rel_coord':
27
+ case 'local_coord':
28
+ case 'bool_lit':
29
+ case 'str_lit':
30
+ case 'mc_name':
31
+ case 'range_lit':
32
+ case 'selector':
33
+ case 'ident':
34
+ case 'blockpos':
35
+ return true;
36
+ case 'str_interp':
37
+ return expr.parts.every(part => typeof part === 'string' || isPureExpr(part));
38
+ case 'f_string':
39
+ return expr.parts.every(part => part.kind === 'text' || isPureExpr(part.expr));
40
+ case 'binary':
41
+ return isPureExpr(expr.left) && isPureExpr(expr.right);
42
+ case 'is_check':
43
+ return isPureExpr(expr.expr);
44
+ case 'unary':
45
+ return isPureExpr(expr.operand);
46
+ case 'member':
47
+ return isPureExpr(expr.obj);
48
+ case 'index':
49
+ return isPureExpr(expr.obj) && isPureExpr(expr.index);
50
+ case 'array_lit':
51
+ return expr.elements.every(isPureExpr);
52
+ case 'struct_lit':
53
+ return expr.fields.every(field => isPureExpr(field.value));
54
+ case 'lambda':
55
+ return true;
56
+ case 'assign':
57
+ case 'member_assign':
58
+ case 'call':
59
+ case 'invoke':
60
+ case 'static_call':
61
+ return false;
62
+ }
63
+ }
64
+ class DeadCodeEliminator {
65
+ constructor() {
66
+ this.functionMap = new Map();
67
+ this.reachableFunctions = new Set();
68
+ this.usedConstants = new Set();
69
+ this.localReads = new Set();
70
+ this.localDeclIds = new WeakMap();
71
+ this.localIdCounter = 0;
72
+ }
73
+ eliminate(program) {
74
+ this.functionMap.clear();
75
+ this.reachableFunctions.clear();
76
+ this.usedConstants.clear();
77
+ this.localReads.clear();
78
+ this.localIdCounter = 0;
79
+ for (const fn of program.declarations) {
80
+ this.functionMap.set(fn.name, fn);
81
+ }
82
+ const entryPoints = this.findEntryPoints(program);
83
+ if (entryPoints.length === 0) {
84
+ for (const fn of program.declarations) {
85
+ this.markReachable(fn.name);
86
+ }
87
+ }
88
+ else {
89
+ for (const fnName of entryPoints) {
90
+ this.markReachable(fnName);
91
+ }
92
+ }
93
+ for (const global of program.globals) {
94
+ this.collectExprRefs(global.init, []);
95
+ }
96
+ for (const implBlock of program.implBlocks) {
97
+ for (const method of implBlock.methods) {
98
+ this.collectFunctionRefs(method);
99
+ }
100
+ }
101
+ return {
102
+ ...program,
103
+ declarations: program.declarations
104
+ .filter(fn => this.reachableFunctions.has(fn.name))
105
+ .map(fn => this.transformFunction(fn)),
106
+ consts: program.consts.filter(constDecl => this.usedConstants.has(constDecl.name)),
107
+ implBlocks: program.implBlocks.map(implBlock => ({
108
+ ...implBlock,
109
+ methods: implBlock.methods.map(method => this.transformFunction(method)),
110
+ })),
111
+ };
112
+ }
113
+ findEntryPoints(program) {
114
+ const entries = new Set();
115
+ for (const fn of program.declarations) {
116
+ if (fn.name === 'main') {
117
+ entries.add(fn.name);
118
+ }
119
+ if (fn.decorators.some(decorator => [
120
+ 'tick',
121
+ 'load',
122
+ 'on',
123
+ 'on_trigger',
124
+ 'on_advancement',
125
+ 'on_craft',
126
+ 'on_death',
127
+ 'on_login',
128
+ 'on_join_team',
129
+ 'keep', // Prevent DCE from removing this function
130
+ ].includes(decorator.name))) {
131
+ entries.add(fn.name);
132
+ }
133
+ }
134
+ return [...entries];
135
+ }
136
+ markReachable(fnName) {
137
+ if (this.reachableFunctions.has(fnName)) {
138
+ return;
139
+ }
140
+ const fn = this.functionMap.get(fnName);
141
+ if (!fn) {
142
+ return;
143
+ }
144
+ this.reachableFunctions.add(fnName);
145
+ this.collectFunctionRefs(fn);
146
+ }
147
+ collectFunctionRefs(fn) {
148
+ const scope = [fn.params.map(param => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
149
+ for (const param of fn.params) {
150
+ if (param.default) {
151
+ this.collectExprRefs(param.default, scope);
152
+ }
153
+ }
154
+ this.collectStmtRefs(fn.body, scope);
155
+ }
156
+ collectStmtRefs(block, scope) {
157
+ scope.push([]);
158
+ for (const stmt of block) {
159
+ this.collectStmtRef(stmt, scope);
160
+ }
161
+ scope.pop();
162
+ }
163
+ collectStmtRef(stmt, scope) {
164
+ switch (stmt.kind) {
165
+ case 'let': {
166
+ this.collectExprRefs(stmt.init, scope);
167
+ const id = `local:${stmt.name}:${this.localIdCounter++}:${(stmt.span?.line ?? 0)}:${(stmt.span?.col ?? 0)}`;
168
+ this.localDeclIds.set(stmt, id);
169
+ scope[scope.length - 1].push({ id, name: stmt.name });
170
+ break;
171
+ }
172
+ case 'expr':
173
+ this.collectExprRefs(stmt.expr, scope);
174
+ break;
175
+ case 'return':
176
+ if (stmt.value) {
177
+ this.collectExprRefs(stmt.value, scope);
178
+ }
179
+ break;
180
+ case 'if': {
181
+ this.collectExprRefs(stmt.cond, scope);
182
+ const constant = isConstantBoolean(stmt.cond);
183
+ if (constant === true) {
184
+ this.collectStmtRefs(stmt.then, scope);
185
+ }
186
+ else if (constant === false) {
187
+ if (stmt.else_) {
188
+ this.collectStmtRefs(stmt.else_, scope);
189
+ }
190
+ }
191
+ else {
192
+ this.collectStmtRefs(stmt.then, scope);
193
+ if (stmt.else_) {
194
+ this.collectStmtRefs(stmt.else_, scope);
195
+ }
196
+ }
197
+ break;
198
+ }
199
+ case 'while':
200
+ this.collectExprRefs(stmt.cond, scope);
201
+ this.collectStmtRefs(stmt.body, scope);
202
+ break;
203
+ case 'for':
204
+ scope.push([]);
205
+ if (stmt.init) {
206
+ this.collectStmtRef(stmt.init, scope);
207
+ }
208
+ this.collectExprRefs(stmt.cond, scope);
209
+ this.collectExprRefs(stmt.step, scope);
210
+ this.collectStmtRefs(stmt.body, scope);
211
+ scope.pop();
212
+ break;
213
+ case 'foreach':
214
+ this.collectExprRefs(stmt.iterable, scope);
215
+ scope.push([{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]);
216
+ this.collectStmtRefs(stmt.body, scope);
217
+ scope.pop();
218
+ break;
219
+ case 'for_range':
220
+ this.collectExprRefs(stmt.start, scope);
221
+ this.collectExprRefs(stmt.end, scope);
222
+ scope.push([{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]);
223
+ this.collectStmtRefs(stmt.body, scope);
224
+ scope.pop();
225
+ break;
226
+ case 'match':
227
+ this.collectExprRefs(stmt.expr, scope);
228
+ for (const arm of stmt.arms) {
229
+ if (arm.pattern) {
230
+ this.collectExprRefs(arm.pattern, scope);
231
+ }
232
+ this.collectStmtRefs(arm.body, scope);
233
+ }
234
+ break;
235
+ case 'as_block':
236
+ case 'at_block':
237
+ case 'as_at':
238
+ case 'execute':
239
+ this.collectNestedStmtRefs(stmt, scope);
240
+ break;
241
+ case 'raw':
242
+ break;
243
+ }
244
+ }
245
+ collectNestedStmtRefs(stmt, scope) {
246
+ if (stmt.kind === 'execute') {
247
+ for (const sub of stmt.subcommands) {
248
+ if ('varName' in sub && sub.varName) {
249
+ const resolved = this.resolveLocal(sub.varName, scope);
250
+ if (resolved) {
251
+ this.localReads.add(resolved.id);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ this.collectStmtRefs(stmt.body, scope);
257
+ }
258
+ collectExprRefs(expr, scope) {
259
+ switch (expr.kind) {
260
+ case 'ident': {
261
+ const resolved = this.resolveLocal(expr.name, scope);
262
+ if (resolved) {
263
+ this.localReads.add(resolved.id);
264
+ }
265
+ else {
266
+ this.usedConstants.add(expr.name);
267
+ }
268
+ break;
269
+ }
270
+ case 'call':
271
+ {
272
+ const resolved = this.resolveLocal(expr.fn, scope);
273
+ if (resolved) {
274
+ this.localReads.add(resolved.id);
275
+ }
276
+ else if (this.functionMap.has(expr.fn)) {
277
+ this.markReachable(expr.fn);
278
+ }
279
+ }
280
+ for (const arg of expr.args) {
281
+ this.collectExprRefs(arg, scope);
282
+ }
283
+ break;
284
+ case 'static_call':
285
+ for (const arg of expr.args) {
286
+ this.collectExprRefs(arg, scope);
287
+ }
288
+ break;
289
+ case 'invoke':
290
+ this.collectExprRefs(expr.callee, scope);
291
+ for (const arg of expr.args) {
292
+ this.collectExprRefs(arg, scope);
293
+ }
294
+ break;
295
+ case 'member':
296
+ this.collectExprRefs(expr.obj, scope);
297
+ break;
298
+ case 'member_assign':
299
+ this.collectExprRefs(expr.obj, scope);
300
+ this.collectExprRefs(expr.value, scope);
301
+ break;
302
+ case 'index':
303
+ this.collectExprRefs(expr.obj, scope);
304
+ this.collectExprRefs(expr.index, scope);
305
+ break;
306
+ case 'array_lit':
307
+ expr.elements.forEach(element => this.collectExprRefs(element, scope));
308
+ break;
309
+ case 'struct_lit':
310
+ expr.fields.forEach(field => this.collectExprRefs(field.value, scope));
311
+ break;
312
+ case 'binary':
313
+ this.collectExprRefs(expr.left, scope);
314
+ this.collectExprRefs(expr.right, scope);
315
+ break;
316
+ case 'is_check':
317
+ this.collectExprRefs(expr.expr, scope);
318
+ break;
319
+ case 'unary':
320
+ this.collectExprRefs(expr.operand, scope);
321
+ break;
322
+ case 'assign': {
323
+ this.collectExprRefs(expr.value, scope);
324
+ break;
325
+ }
326
+ case 'str_interp':
327
+ expr.parts.forEach(part => {
328
+ if (typeof part !== 'string') {
329
+ this.collectExprRefs(part, scope);
330
+ }
331
+ });
332
+ break;
333
+ case 'f_string':
334
+ expr.parts.forEach(part => {
335
+ if (part.kind === 'expr') {
336
+ this.collectExprRefs(part.expr, scope);
337
+ }
338
+ });
339
+ break;
340
+ case 'lambda': {
341
+ const lambdaScope = [
342
+ ...scope.map(entries => [...entries]),
343
+ expr.params.map(param => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name })),
344
+ ];
345
+ if (Array.isArray(expr.body)) {
346
+ this.collectStmtRefs(expr.body, lambdaScope);
347
+ }
348
+ else {
349
+ this.collectExprRefs(expr.body, lambdaScope);
350
+ }
351
+ break;
352
+ }
353
+ case 'blockpos':
354
+ case 'bool_lit':
355
+ case 'byte_lit':
356
+ case 'double_lit':
357
+ case 'float_lit':
358
+ case 'int_lit':
359
+ case 'long_lit':
360
+ case 'mc_name':
361
+ case 'range_lit':
362
+ case 'selector':
363
+ case 'short_lit':
364
+ case 'str_lit':
365
+ break;
366
+ }
367
+ }
368
+ resolveLocal(name, scope) {
369
+ for (let i = scope.length - 1; i >= 0; i--) {
370
+ for (let j = scope[i].length - 1; j >= 0; j--) {
371
+ if (scope[i][j].name === name) {
372
+ return scope[i][j];
373
+ }
374
+ }
375
+ }
376
+ return null;
377
+ }
378
+ transformFunction(fn) {
379
+ const scope = [fn.params.map(param => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
380
+ const body = this.transformBlock(fn.body, scope);
381
+ return body === fn.body ? fn : copySpan({ ...fn, body }, fn);
382
+ }
383
+ transformBlock(block, scope) {
384
+ scope.push([]);
385
+ const transformed = [];
386
+ for (const stmt of block) {
387
+ const next = this.transformStmt(stmt, scope);
388
+ transformed.push(...next);
389
+ }
390
+ scope.pop();
391
+ return transformed;
392
+ }
393
+ transformStmt(stmt, scope) {
394
+ switch (stmt.kind) {
395
+ case 'let': {
396
+ const init = this.transformExpr(stmt.init, scope);
397
+ const id = this.localDeclIds.get(stmt) ?? `local:${stmt.name}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`;
398
+ scope[scope.length - 1].push({ id, name: stmt.name });
399
+ if (this.localReads.has(id)) {
400
+ if (init === stmt.init) {
401
+ return [stmt];
402
+ }
403
+ return [copySpan({ ...stmt, init }, stmt)];
404
+ }
405
+ if (isPureExpr(init)) {
406
+ return [];
407
+ }
408
+ return [copySpan({ kind: 'expr', expr: init }, stmt)];
409
+ }
410
+ case 'expr': {
411
+ const expr = this.transformExpr(stmt.expr, scope);
412
+ if (expr.kind === 'assign') {
413
+ const resolved = this.resolveLocal(expr.target, scope);
414
+ if (resolved && !this.localReads.has(resolved.id)) {
415
+ if (isPureExpr(expr.value)) {
416
+ return [];
417
+ }
418
+ return [copySpan({ kind: 'expr', expr: expr.value }, stmt)];
419
+ }
420
+ }
421
+ if (expr === stmt.expr) {
422
+ return [stmt];
423
+ }
424
+ return [copySpan({ ...stmt, expr }, stmt)];
425
+ }
426
+ case 'return': {
427
+ if (!stmt.value) {
428
+ return [stmt];
429
+ }
430
+ const value = this.transformExpr(stmt.value, scope);
431
+ if (value === stmt.value) {
432
+ return [stmt];
433
+ }
434
+ return [copySpan({ ...stmt, value }, stmt)];
435
+ }
436
+ case 'if': {
437
+ const cond = this.transformExpr(stmt.cond, scope);
438
+ const constant = isConstantBoolean(cond);
439
+ if (constant === true) {
440
+ return this.transformBlock(stmt.then, scope);
441
+ }
442
+ if (constant === false) {
443
+ return stmt.else_ ? this.transformBlock(stmt.else_, scope) : [];
444
+ }
445
+ const thenBlock = this.transformBlock(stmt.then, scope);
446
+ const elseBlock = stmt.else_ ? this.transformBlock(stmt.else_, scope) : undefined;
447
+ if (cond === stmt.cond && thenBlock === stmt.then && elseBlock === stmt.else_) {
448
+ return [stmt];
449
+ }
450
+ return [copySpan({ ...stmt, cond, then: thenBlock, else_: elseBlock }, stmt)];
451
+ }
452
+ case 'while': {
453
+ const cond = this.transformExpr(stmt.cond, scope);
454
+ if (isConstantBoolean(cond) === false) {
455
+ return [];
456
+ }
457
+ const body = this.transformBlock(stmt.body, scope);
458
+ return [copySpan({ ...stmt, cond, body }, stmt)];
459
+ }
460
+ case 'for': {
461
+ const forScope = [...scope, []];
462
+ const init = stmt.init ? this.transformStmt(stmt.init, forScope)[0] : undefined;
463
+ const cond = this.transformExpr(stmt.cond, forScope);
464
+ if (isConstantBoolean(cond) === false) {
465
+ return init ? [init] : [];
466
+ }
467
+ const step = this.transformExpr(stmt.step, forScope);
468
+ const body = this.transformBlock(stmt.body, forScope);
469
+ return [copySpan({ ...stmt, init, cond, step, body }, stmt)];
470
+ }
471
+ case 'foreach': {
472
+ const iterable = this.transformExpr(stmt.iterable, scope);
473
+ const foreachScope = [...scope, [{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]];
474
+ const body = this.transformBlock(stmt.body, foreachScope);
475
+ return [copySpan({ ...stmt, iterable, body }, stmt)];
476
+ }
477
+ case 'for_range': {
478
+ const start = this.transformExpr(stmt.start, scope);
479
+ const end = this.transformExpr(stmt.end, scope);
480
+ const rangeScope = [...scope, [{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]];
481
+ const body = this.transformBlock(stmt.body, rangeScope);
482
+ return [copySpan({ ...stmt, start, end, body }, stmt)];
483
+ }
484
+ case 'match': {
485
+ const expr = this.transformExpr(stmt.expr, scope);
486
+ const arms = stmt.arms.map(arm => ({
487
+ pattern: arm.pattern ? this.transformExpr(arm.pattern, scope) : null,
488
+ body: this.transformBlock(arm.body, scope),
489
+ }));
490
+ return [copySpan({ ...stmt, expr, arms }, stmt)];
491
+ }
492
+ case 'as_block':
493
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
494
+ case 'at_block':
495
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
496
+ case 'as_at':
497
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
498
+ case 'execute':
499
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
500
+ case 'raw':
501
+ return [stmt];
502
+ }
503
+ }
504
+ transformExpr(expr, scope) {
505
+ switch (expr.kind) {
506
+ case 'call':
507
+ return copySpan({ ...expr, args: expr.args.map(arg => this.transformExpr(arg, scope)) }, expr);
508
+ case 'static_call':
509
+ return copySpan({ ...expr, args: expr.args.map(arg => this.transformExpr(arg, scope)) }, expr);
510
+ case 'invoke':
511
+ return copySpan({
512
+ ...expr,
513
+ callee: this.transformExpr(expr.callee, scope),
514
+ args: expr.args.map(arg => this.transformExpr(arg, scope)),
515
+ }, expr);
516
+ case 'binary':
517
+ return copySpan({
518
+ ...expr,
519
+ left: this.transformExpr(expr.left, scope),
520
+ right: this.transformExpr(expr.right, scope),
521
+ }, expr);
522
+ case 'is_check':
523
+ return copySpan({ ...expr, expr: this.transformExpr(expr.expr, scope) }, expr);
524
+ case 'unary':
525
+ return copySpan({ ...expr, operand: this.transformExpr(expr.operand, scope) }, expr);
526
+ case 'assign':
527
+ return copySpan({ ...expr, value: this.transformExpr(expr.value, scope) }, expr);
528
+ case 'member':
529
+ return copySpan({ ...expr, obj: this.transformExpr(expr.obj, scope) }, expr);
530
+ case 'member_assign':
531
+ return copySpan({
532
+ ...expr,
533
+ obj: this.transformExpr(expr.obj, scope),
534
+ value: this.transformExpr(expr.value, scope),
535
+ }, expr);
536
+ case 'index':
537
+ return copySpan({
538
+ ...expr,
539
+ obj: this.transformExpr(expr.obj, scope),
540
+ index: this.transformExpr(expr.index, scope),
541
+ }, expr);
542
+ case 'array_lit':
543
+ return copySpan({ ...expr, elements: expr.elements.map(element => this.transformExpr(element, scope)) }, expr);
544
+ case 'struct_lit':
545
+ return copySpan({
546
+ ...expr,
547
+ fields: expr.fields.map(field => ({ ...field, value: this.transformExpr(field.value, scope) })),
548
+ }, expr);
549
+ case 'str_interp':
550
+ return copySpan({
551
+ ...expr,
552
+ parts: expr.parts.map(part => typeof part === 'string' ? part : this.transformExpr(part, scope)),
553
+ }, expr);
554
+ case 'f_string':
555
+ return copySpan({
556
+ ...expr,
557
+ parts: expr.parts.map(part => part.kind === 'text' ? part : { kind: 'expr', expr: this.transformExpr(part.expr, scope) }),
558
+ }, expr);
559
+ case 'lambda': {
560
+ const lambdaScope = [
561
+ ...scope.map(entries => [...entries]),
562
+ expr.params.map(param => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name })),
563
+ ];
564
+ const body = Array.isArray(expr.body)
565
+ ? this.transformBlock(expr.body, lambdaScope)
566
+ : this.transformExpr(expr.body, lambdaScope);
567
+ return copySpan({ ...expr, body }, expr);
568
+ }
569
+ case 'blockpos':
570
+ case 'bool_lit':
571
+ case 'byte_lit':
572
+ case 'double_lit':
573
+ case 'float_lit':
574
+ case 'ident':
575
+ case 'int_lit':
576
+ case 'long_lit':
577
+ case 'mc_name':
578
+ case 'range_lit':
579
+ case 'rel_coord':
580
+ case 'local_coord':
581
+ case 'selector':
582
+ case 'short_lit':
583
+ case 'str_lit':
584
+ return expr;
585
+ }
586
+ }
587
+ }
588
+ exports.DeadCodeEliminator = DeadCodeEliminator;
589
+ function eliminateDeadCode(program) {
590
+ return new DeadCodeEliminator().eliminate(program);
591
+ }
592
+ //# sourceMappingURL=dce.js.map
@@ -60,6 +60,7 @@ export declare class Parser {
60
60
  private parseLambdaExpr;
61
61
  private finishLambdaExpr;
62
62
  private parseStringExpr;
63
+ private parseFStringExpr;
63
64
  private parseEmbeddedExpr;
64
65
  private parseStructLit;
65
66
  private parseArrayLit;
@@ -69,6 +70,7 @@ export declare class Parser {
69
70
  private coordComponentTokenLength;
70
71
  private parseBlockPos;
71
72
  private parseCoordComponent;
73
+ private parseCoordOffsetFromValue;
72
74
  private parseSignedCoordOffset;
73
75
  private parseSelector;
74
76
  private parseSelectorOrVarSelector;