redscript-mc 2.2.1 → 2.4.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.
- package/CHANGELOG.md +31 -0
- package/README.md +18 -2
- package/dist/src/__tests__/array-dynamic.test.d.ts +12 -0
- package/dist/src/__tests__/array-dynamic.test.js +131 -0
- package/dist/src/__tests__/array-write.test.d.ts +11 -0
- package/dist/src/__tests__/array-write.test.js +149 -0
- package/dist/src/__tests__/tuner/engine.test.d.ts +4 -0
- package/dist/src/__tests__/tuner/engine.test.js +232 -0
- package/dist/src/ast/types.d.ts +7 -0
- package/dist/src/emit/modules.js +5 -0
- package/dist/src/hir/lower.js +29 -0
- package/dist/src/hir/monomorphize.js +2 -0
- package/dist/src/hir/types.d.ts +9 -2
- package/dist/src/lir/lower.js +131 -0
- package/dist/src/mir/lower.js +73 -3
- package/dist/src/mir/macro.js +5 -0
- package/dist/src/mir/types.d.ts +12 -0
- package/dist/src/mir/verify.js +7 -0
- package/dist/src/optimizer/copy_prop.js +5 -0
- package/dist/src/optimizer/coroutine.js +12 -0
- package/dist/src/optimizer/dce.js +9 -0
- package/dist/src/optimizer/unroll.js +3 -0
- package/dist/src/parser/index.js +5 -0
- package/dist/src/tuner/adapters/ln-polynomial.d.ts +23 -0
- package/dist/src/tuner/adapters/ln-polynomial.js +142 -0
- package/dist/src/tuner/adapters/sqrt-newton.d.ts +28 -0
- package/dist/src/tuner/adapters/sqrt-newton.js +125 -0
- package/dist/src/tuner/cli.d.ts +5 -0
- package/dist/src/tuner/cli.js +168 -0
- package/dist/src/tuner/engine.d.ts +17 -0
- package/dist/src/tuner/engine.js +215 -0
- package/dist/src/tuner/metrics.d.ts +15 -0
- package/dist/src/tuner/metrics.js +51 -0
- package/dist/src/tuner/simulator.d.ts +35 -0
- package/dist/src/tuner/simulator.js +78 -0
- package/dist/src/tuner/types.d.ts +32 -0
- package/dist/src/tuner/types.js +6 -0
- package/dist/src/typechecker/index.js +5 -0
- package/docs/STDLIB_ROADMAP.md +142 -0
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/package.json +1 -1
- package/src/__tests__/array-dynamic.test.ts +147 -0
- package/src/__tests__/array-write.test.ts +169 -0
- package/src/__tests__/tuner/engine.test.ts +260 -0
- package/src/ast/types.ts +1 -0
- package/src/emit/modules.ts +5 -0
- package/src/hir/lower.ts +30 -0
- package/src/hir/monomorphize.ts +2 -0
- package/src/hir/types.ts +3 -1
- package/src/lir/lower.ts +151 -0
- package/src/mir/lower.ts +75 -3
- package/src/mir/macro.ts +5 -0
- package/src/mir/types.ts +2 -0
- package/src/mir/verify.ts +7 -0
- package/src/optimizer/copy_prop.ts +5 -0
- package/src/optimizer/coroutine.ts +9 -0
- package/src/optimizer/dce.ts +6 -0
- package/src/optimizer/unroll.ts +3 -0
- package/src/parser/index.ts +9 -0
- package/src/stdlib/bigint.mcrs +155 -192
- package/src/stdlib/bits.mcrs +158 -0
- package/src/stdlib/color.mcrs +160 -0
- package/src/stdlib/geometry.mcrs +124 -0
- package/src/stdlib/list.mcrs +96 -0
- package/src/stdlib/math.mcrs +227 -0
- package/src/stdlib/math_hp.mcrs +65 -0
- package/src/stdlib/random.mcrs +67 -0
- package/src/stdlib/signal.mcrs +112 -0
- package/src/stdlib/timer.mcrs +32 -0
- package/src/stdlib/vec.mcrs +27 -0
- package/src/tuner/adapters/ln-polynomial.ts +147 -0
- package/src/tuner/adapters/sqrt-newton.ts +135 -0
- package/src/tuner/cli.ts +158 -0
- package/src/tuner/engine.ts +272 -0
- package/src/tuner/metrics.ts +66 -0
- package/src/tuner/simulator.ts +69 -0
- package/src/tuner/types.ts +44 -0
- package/src/typechecker/index.ts +6 -0
- package/docs/ARCHITECTURE.zh.md +0 -1088
- package/docs/COMPILATION_STATS.md +0 -142
- package/docs/IMPLEMENTATION_GUIDE.md +0 -512
package/dist/src/hir/lower.js
CHANGED
|
@@ -360,6 +360,35 @@ function lowerExpr(expr) {
|
|
|
360
360
|
return { kind: 'member', obj: lowerExpr(expr.obj), field: expr.field, span: expr.span };
|
|
361
361
|
case 'index':
|
|
362
362
|
return { kind: 'index', obj: lowerExpr(expr.obj), index: lowerExpr(expr.index), span: expr.span };
|
|
363
|
+
// --- Desugaring: compound index_assign → plain index_assign ---
|
|
364
|
+
case 'index_assign':
|
|
365
|
+
if (expr.op !== '=') {
|
|
366
|
+
const binOp = COMPOUND_TO_BINOP[expr.op];
|
|
367
|
+
const obj = lowerExpr(expr.obj);
|
|
368
|
+
const index = lowerExpr(expr.index);
|
|
369
|
+
return {
|
|
370
|
+
kind: 'index_assign',
|
|
371
|
+
obj,
|
|
372
|
+
index,
|
|
373
|
+
op: '=',
|
|
374
|
+
value: {
|
|
375
|
+
kind: 'binary',
|
|
376
|
+
op: binOp,
|
|
377
|
+
left: { kind: 'index', obj, index },
|
|
378
|
+
right: lowerExpr(expr.value),
|
|
379
|
+
span: expr.span,
|
|
380
|
+
},
|
|
381
|
+
span: expr.span,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
kind: 'index_assign',
|
|
386
|
+
obj: lowerExpr(expr.obj),
|
|
387
|
+
index: lowerExpr(expr.index),
|
|
388
|
+
op: expr.op,
|
|
389
|
+
value: lowerExpr(expr.value),
|
|
390
|
+
span: expr.span,
|
|
391
|
+
};
|
|
363
392
|
case 'call':
|
|
364
393
|
return { kind: 'call', fn: expr.fn, args: expr.args.map(lowerExpr), typeArgs: expr.typeArgs, span: expr.span };
|
|
365
394
|
case 'invoke':
|
|
@@ -278,6 +278,8 @@ class Monomorphizer {
|
|
|
278
278
|
return { ...expr, value: this.rewriteExpr(expr.value, ctx) };
|
|
279
279
|
case 'member_assign':
|
|
280
280
|
return { ...expr, obj: this.rewriteExpr(expr.obj, ctx), value: this.rewriteExpr(expr.value, ctx) };
|
|
281
|
+
case 'index_assign':
|
|
282
|
+
return { ...expr, obj: this.rewriteExpr(expr.obj, ctx), index: this.rewriteExpr(expr.index, ctx), value: this.rewriteExpr(expr.value, ctx) };
|
|
281
283
|
case 'member':
|
|
282
284
|
return { ...expr, obj: this.rewriteExpr(expr.obj, ctx) };
|
|
283
285
|
case 'index':
|
package/dist/src/hir/types.d.ts
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* All types and names are preserved from the AST.
|
|
13
13
|
*/
|
|
14
14
|
import type { Span, TypeNode, EntitySelector, CoordComponent, Decorator, RangeExpr, FStringPart, SelectorFilter, EntityTypeName, LambdaParam } from '../ast/types';
|
|
15
|
-
import type { BinOp, CmpOp } from '../ast/types';
|
|
16
|
-
export type { Span, TypeNode, EntitySelector, CoordComponent, Decorator, RangeExpr, FStringPart, SelectorFilter, EntityTypeName, LambdaParam, BinOp, CmpOp, };
|
|
15
|
+
import type { BinOp, CmpOp, AssignOp } from '../ast/types';
|
|
16
|
+
export type { Span, TypeNode, EntitySelector, CoordComponent, Decorator, RangeExpr, FStringPart, SelectorFilter, EntityTypeName, LambdaParam, BinOp, CmpOp, AssignOp, };
|
|
17
17
|
export type HIRExpr = {
|
|
18
18
|
kind: 'int_lit';
|
|
19
19
|
value: number;
|
|
@@ -124,6 +124,13 @@ export type HIRExpr = {
|
|
|
124
124
|
field: string;
|
|
125
125
|
value: HIRExpr;
|
|
126
126
|
span?: Span;
|
|
127
|
+
} | {
|
|
128
|
+
kind: 'index_assign';
|
|
129
|
+
obj: HIRExpr;
|
|
130
|
+
index: HIRExpr;
|
|
131
|
+
op: AssignOp;
|
|
132
|
+
value: HIRExpr;
|
|
133
|
+
span?: Span;
|
|
127
134
|
} | {
|
|
128
135
|
kind: 'member';
|
|
129
136
|
obj: HIRExpr;
|
package/dist/src/lir/lower.js
CHANGED
|
@@ -41,6 +41,10 @@ class LoweringContext {
|
|
|
41
41
|
this.currentMIRFn = null;
|
|
42
42
|
/** Block map for quick lookup */
|
|
43
43
|
this.blockMap = new Map();
|
|
44
|
+
/** Track generated dynamic array macro helper functions to avoid duplicates: key → fn name */
|
|
45
|
+
this.dynIdxHelpers = new Map();
|
|
46
|
+
/** Track generated dynamic array write helper functions: key → fn name */
|
|
47
|
+
this.dynWrtHelpers = new Map();
|
|
44
48
|
this.namespace = namespace;
|
|
45
49
|
this.objective = objective;
|
|
46
50
|
}
|
|
@@ -62,6 +66,68 @@ class LoweringContext {
|
|
|
62
66
|
addFunction(fn) {
|
|
63
67
|
this.functions.push(fn);
|
|
64
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Get or create a macro helper function for dynamic array index reads.
|
|
71
|
+
* The helper function is: $return run data get storage <ns> <pathPrefix>[$(arr_idx)] 1
|
|
72
|
+
* Returns the qualified MC function name (namespace:fnName).
|
|
73
|
+
*/
|
|
74
|
+
getDynIdxHelper(ns, pathPrefix) {
|
|
75
|
+
const key = `${ns}\0${pathPrefix}`;
|
|
76
|
+
const existing = this.dynIdxHelpers.get(key);
|
|
77
|
+
if (existing)
|
|
78
|
+
return existing;
|
|
79
|
+
// Generate deterministic name from ns and pathPrefix
|
|
80
|
+
const sanitize = (s) => s.replace(/[^a-z0-9_]/gi, '_').toLowerCase();
|
|
81
|
+
// Extract just the storage name part from ns (e.g. "myns:arrays" → "myns_arrays")
|
|
82
|
+
const nsStr = sanitize(ns);
|
|
83
|
+
const prefixStr = sanitize(pathPrefix);
|
|
84
|
+
const helperName = `__dyn_idx_${nsStr}_${prefixStr}`;
|
|
85
|
+
// The helper is placed in the current namespace
|
|
86
|
+
const qualifiedName = `${this.namespace}:${helperName}`;
|
|
87
|
+
// Generate the macro function content:
|
|
88
|
+
// $return run data get storage <ns> <pathPrefix>[$(arr_idx)] 1
|
|
89
|
+
const macroLine = {
|
|
90
|
+
kind: 'macro_line',
|
|
91
|
+
template: `return run data get storage ${ns} ${pathPrefix}[$(arr_idx)] 1`,
|
|
92
|
+
};
|
|
93
|
+
this.addFunction({
|
|
94
|
+
name: helperName,
|
|
95
|
+
instructions: [macroLine],
|
|
96
|
+
isMacro: true,
|
|
97
|
+
macroParams: ['arr_idx'],
|
|
98
|
+
});
|
|
99
|
+
this.dynIdxHelpers.set(key, qualifiedName);
|
|
100
|
+
return qualifiedName;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get or create a macro helper function for dynamic array index writes.
|
|
104
|
+
* The helper function: $data modify storage <ns> <pathPrefix>[$(arr_idx)] set value $(arr_val)
|
|
105
|
+
* Returns the qualified MC function name.
|
|
106
|
+
*/
|
|
107
|
+
getDynWrtHelper(ns, pathPrefix) {
|
|
108
|
+
const key = `${ns}\0${pathPrefix}`;
|
|
109
|
+
const existing = this.dynWrtHelpers.get(key);
|
|
110
|
+
if (existing)
|
|
111
|
+
return existing;
|
|
112
|
+
const sanitize = (s) => s.replace(/[^a-z0-9_]/gi, '_').toLowerCase();
|
|
113
|
+
const nsStr = sanitize(ns);
|
|
114
|
+
const prefixStr = sanitize(pathPrefix);
|
|
115
|
+
const helperName = `__dyn_wrt_${nsStr}_${prefixStr}`;
|
|
116
|
+
const qualifiedName = `${this.namespace}:${helperName}`;
|
|
117
|
+
// Macro line: $data modify storage <ns> <pathPrefix>[$(arr_idx)] set value $(arr_val)
|
|
118
|
+
const macroLine = {
|
|
119
|
+
kind: 'macro_line',
|
|
120
|
+
template: `data modify storage ${ns} ${pathPrefix}[$(arr_idx)] set value $(arr_val)`,
|
|
121
|
+
};
|
|
122
|
+
this.addFunction({
|
|
123
|
+
name: helperName,
|
|
124
|
+
instructions: [macroLine],
|
|
125
|
+
isMacro: true,
|
|
126
|
+
macroParams: ['arr_idx', 'arr_val'],
|
|
127
|
+
});
|
|
128
|
+
this.dynWrtHelpers.set(key, qualifiedName);
|
|
129
|
+
return qualifiedName;
|
|
130
|
+
}
|
|
65
131
|
/** Attach sourceLoc to newly added instructions (from the given start index onward) */
|
|
66
132
|
tagSourceLoc(instrs, fromIndex, sourceLoc) {
|
|
67
133
|
if (!sourceLoc)
|
|
@@ -263,6 +329,41 @@ function lowerInstrInner(instr, fn, ctx, instrs) {
|
|
|
263
329
|
});
|
|
264
330
|
break;
|
|
265
331
|
}
|
|
332
|
+
case 'nbt_read_dynamic': {
|
|
333
|
+
// Strategy:
|
|
334
|
+
// 1. Store the index value into rs:macro_args __arr_idx (int)
|
|
335
|
+
// 2. Call the per-array macro helper function with 'with storage rs:macro_args'
|
|
336
|
+
// 3. Result comes back via $ret scoreboard slot (the macro uses $return)
|
|
337
|
+
const dst = ctx.slot(instr.dst);
|
|
338
|
+
const idxSlot = operandToSlot(instr.indexSrc, ctx, instrs);
|
|
339
|
+
// Step 1: store index score → rs:macro_args arr_idx (int, scale 1)
|
|
340
|
+
instrs.push({
|
|
341
|
+
kind: 'store_score_to_nbt',
|
|
342
|
+
ns: 'rs:macro_args',
|
|
343
|
+
path: 'arr_idx',
|
|
344
|
+
type: 'int',
|
|
345
|
+
scale: 1,
|
|
346
|
+
src: idxSlot,
|
|
347
|
+
});
|
|
348
|
+
// Step 2: get or create the macro helper function, then call it
|
|
349
|
+
const helperFn = ctx.getDynIdxHelper(instr.ns, instr.pathPrefix);
|
|
350
|
+
instrs.push({ kind: 'call_macro', fn: helperFn, storage: 'rs:macro_args' });
|
|
351
|
+
// Step 3: the macro uses $return which sets the MC return value.
|
|
352
|
+
// We need to capture that. In MC, $return run ... returns the result
|
|
353
|
+
// to the caller via the execute store mechanism.
|
|
354
|
+
// Use store_cmd_to_score to capture the return value of the macro call.
|
|
355
|
+
// Actually, the call_macro instruction above already ran the function.
|
|
356
|
+
// The $return run data get ... sets the scoreboard return value for the
|
|
357
|
+
// *calling* function context. We need to use execute store result score.
|
|
358
|
+
// Rewrite: use raw command instead:
|
|
359
|
+
instrs.pop(); // remove the call_macro we just added
|
|
360
|
+
instrs.push({
|
|
361
|
+
kind: 'store_cmd_to_score',
|
|
362
|
+
dst,
|
|
363
|
+
cmd: { kind: 'call_macro', fn: helperFn, storage: 'rs:macro_args' },
|
|
364
|
+
});
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
266
367
|
case 'nbt_write': {
|
|
267
368
|
const srcSlot = operandToSlot(instr.src, ctx, instrs);
|
|
268
369
|
instrs.push({
|
|
@@ -275,6 +376,36 @@ function lowerInstrInner(instr, fn, ctx, instrs) {
|
|
|
275
376
|
});
|
|
276
377
|
break;
|
|
277
378
|
}
|
|
379
|
+
case 'nbt_write_dynamic': {
|
|
380
|
+
// Strategy:
|
|
381
|
+
// 1. Store index score → rs:macro_args arr_idx (int)
|
|
382
|
+
// 2. Store value score → rs:macro_args arr_val (int)
|
|
383
|
+
// 3. Call macro helper: $data modify storage <ns> <pathPrefix>[$(arr_idx)] set value $(arr_val)
|
|
384
|
+
const idxSlot = operandToSlot(instr.indexSrc, ctx, instrs);
|
|
385
|
+
const valSlot = operandToSlot(instr.valueSrc, ctx, instrs);
|
|
386
|
+
// Store index
|
|
387
|
+
instrs.push({
|
|
388
|
+
kind: 'store_score_to_nbt',
|
|
389
|
+
ns: 'rs:macro_args',
|
|
390
|
+
path: 'arr_idx',
|
|
391
|
+
type: 'int',
|
|
392
|
+
scale: 1,
|
|
393
|
+
src: idxSlot,
|
|
394
|
+
});
|
|
395
|
+
// Store value
|
|
396
|
+
instrs.push({
|
|
397
|
+
kind: 'store_score_to_nbt',
|
|
398
|
+
ns: 'rs:macro_args',
|
|
399
|
+
path: 'arr_val',
|
|
400
|
+
type: 'int',
|
|
401
|
+
scale: 1,
|
|
402
|
+
src: valSlot,
|
|
403
|
+
});
|
|
404
|
+
// Call macro helper function
|
|
405
|
+
const helperFn = ctx.getDynWrtHelper(instr.ns, instr.pathPrefix);
|
|
406
|
+
instrs.push({ kind: 'call_macro', fn: helperFn, storage: 'rs:macro_args' });
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
278
409
|
case 'score_read': {
|
|
279
410
|
// execute store result score $dst __obj run scoreboard players get <player> <obj>
|
|
280
411
|
const dst = ctx.slot(instr.dst);
|
package/dist/src/mir/lower.js
CHANGED
|
@@ -929,12 +929,20 @@ function lowerExpr(expr, ctx, scope) {
|
|
|
929
929
|
return { kind: 'temp', name: t };
|
|
930
930
|
}
|
|
931
931
|
case 'index': {
|
|
932
|
-
// Check if obj is a tracked array variable
|
|
932
|
+
// Check if obj is a tracked array variable
|
|
933
933
|
if (expr.obj.kind === 'ident') {
|
|
934
934
|
const arrInfo = ctx.arrayVars.get(expr.obj.name);
|
|
935
|
-
if (arrInfo
|
|
935
|
+
if (arrInfo) {
|
|
936
936
|
const t = ctx.freshTemp();
|
|
937
|
-
|
|
937
|
+
if (expr.index.kind === 'int_lit') {
|
|
938
|
+
// Constant index: direct NBT read
|
|
939
|
+
ctx.emit({ kind: 'nbt_read', dst: t, ns: arrInfo.ns, path: `${arrInfo.pathPrefix}[${expr.index.value}]`, scale: 1 });
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
// Dynamic index: emit nbt_read_dynamic
|
|
943
|
+
const idxOp = lowerExpr(expr.index, ctx, scope);
|
|
944
|
+
ctx.emit({ kind: 'nbt_read_dynamic', dst: t, ns: arrInfo.ns, pathPrefix: arrInfo.pathPrefix, indexSrc: idxOp });
|
|
945
|
+
}
|
|
938
946
|
return { kind: 'temp', name: t };
|
|
939
947
|
}
|
|
940
948
|
}
|
|
@@ -944,6 +952,25 @@ function lowerExpr(expr, ctx, scope) {
|
|
|
944
952
|
ctx.emit({ kind: 'copy', dst: t, src: obj });
|
|
945
953
|
return { kind: 'temp', name: t };
|
|
946
954
|
}
|
|
955
|
+
case 'index_assign': {
|
|
956
|
+
const valOp = lowerExpr(expr.value, ctx, scope);
|
|
957
|
+
if (expr.obj.kind === 'ident') {
|
|
958
|
+
const arrInfo = ctx.arrayVars.get(expr.obj.name);
|
|
959
|
+
if (arrInfo) {
|
|
960
|
+
if (expr.index.kind === 'int_lit') {
|
|
961
|
+
// constant index → direct nbt_write
|
|
962
|
+
ctx.emit({ kind: 'nbt_write', ns: arrInfo.ns, path: `${arrInfo.pathPrefix}[${expr.index.value}]`, type: 'int', scale: 1, src: valOp });
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
// dynamic index → nbt_write_dynamic
|
|
966
|
+
const idxOp = lowerExpr(expr.index, ctx, scope);
|
|
967
|
+
ctx.emit({ kind: 'nbt_write_dynamic', ns: arrInfo.ns, pathPrefix: arrInfo.pathPrefix, indexSrc: idxOp, valueSrc: valOp });
|
|
968
|
+
}
|
|
969
|
+
return valOp;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return valOp;
|
|
973
|
+
}
|
|
947
974
|
case 'call': {
|
|
948
975
|
// Handle scoreboard_get / score — read from vanilla MC scoreboard
|
|
949
976
|
if (expr.fn === 'scoreboard_get' || expr.fn === 'score') {
|
|
@@ -963,6 +990,49 @@ function lowerExpr(expr, ctx, scope) {
|
|
|
963
990
|
ctx.emit({ kind: 'const', dst: t, value: 0 });
|
|
964
991
|
return { kind: 'temp', name: t };
|
|
965
992
|
}
|
|
993
|
+
// Handle list_push(arr_name, val) — append an int to an NBT int array
|
|
994
|
+
// list_push("rs:lists", "mylist", val) or simpler: uses the array's storage path
|
|
995
|
+
if (expr.fn === 'list_push') {
|
|
996
|
+
// list_push(array_var, value)
|
|
997
|
+
// 1. Append a placeholder 0
|
|
998
|
+
// 2. Overwrite [-1] with the actual value
|
|
999
|
+
if (expr.args[0].kind === 'ident') {
|
|
1000
|
+
const arrInfo = ctx.arrayVars.get(expr.args[0].name);
|
|
1001
|
+
if (arrInfo) {
|
|
1002
|
+
const valOp = lowerExpr(expr.args[1], ctx, scope);
|
|
1003
|
+
// Step 1: append placeholder
|
|
1004
|
+
ctx.emit({ kind: 'call', dst: null, fn: `__raw:data modify storage ${arrInfo.ns} ${arrInfo.pathPrefix} append value 0`, args: [] });
|
|
1005
|
+
// Step 2: overwrite last element with actual value
|
|
1006
|
+
ctx.emit({ kind: 'nbt_write', ns: arrInfo.ns, path: `${arrInfo.pathPrefix}[-1]`, type: 'int', scale: 1, src: valOp });
|
|
1007
|
+
const t = ctx.freshTemp();
|
|
1008
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 });
|
|
1009
|
+
return { kind: 'temp', name: t };
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
// Handle list_pop(arr_var) — remove last element from NBT int array
|
|
1014
|
+
if (expr.fn === 'list_pop') {
|
|
1015
|
+
if (expr.args[0].kind === 'ident') {
|
|
1016
|
+
const arrInfo = ctx.arrayVars.get(expr.args[0].name);
|
|
1017
|
+
if (arrInfo) {
|
|
1018
|
+
ctx.emit({ kind: 'call', dst: null, fn: `__raw:data remove storage ${arrInfo.ns} ${arrInfo.pathPrefix}[-1]`, args: [] });
|
|
1019
|
+
const t = ctx.freshTemp();
|
|
1020
|
+
ctx.emit({ kind: 'const', dst: t, value: 0 });
|
|
1021
|
+
return { kind: 'temp', name: t };
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
// Handle list_len(arr_var) — get length of NBT int array
|
|
1026
|
+
if (expr.fn === 'list_len') {
|
|
1027
|
+
if (expr.args[0].kind === 'ident') {
|
|
1028
|
+
const arrInfo = ctx.arrayVars.get(expr.args[0].name);
|
|
1029
|
+
if (arrInfo) {
|
|
1030
|
+
const t = ctx.freshTemp();
|
|
1031
|
+
ctx.emit({ kind: 'nbt_read', dst: t, ns: arrInfo.ns, path: `${arrInfo.pathPrefix}`, scale: 1 });
|
|
1032
|
+
return { kind: 'temp', name: t };
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
966
1036
|
// Handle setTimeout/setInterval: lift lambda arg to a named helper function
|
|
967
1037
|
if ((expr.fn === 'setTimeout' || expr.fn === 'setInterval') && expr.args.length === 2) {
|
|
968
1038
|
const ticksArg = expr.args[0];
|
package/dist/src/mir/macro.js
CHANGED
|
@@ -139,6 +139,11 @@ function scanExpr(expr, paramNames, macroParams) {
|
|
|
139
139
|
scanExpr(expr.obj, paramNames, macroParams);
|
|
140
140
|
scanExpr(expr.value, paramNames, macroParams);
|
|
141
141
|
break;
|
|
142
|
+
case 'index_assign':
|
|
143
|
+
scanExpr(expr.obj, paramNames, macroParams);
|
|
144
|
+
scanExpr(expr.index, paramNames, macroParams);
|
|
145
|
+
scanExpr(expr.value, paramNames, macroParams);
|
|
146
|
+
break;
|
|
142
147
|
case 'member':
|
|
143
148
|
scanExpr(expr.obj, paramNames, macroParams);
|
|
144
149
|
break;
|
package/dist/src/mir/types.d.ts
CHANGED
|
@@ -130,6 +130,12 @@ export type MIRInstr = MIRInstrBase & ({
|
|
|
130
130
|
ns: string;
|
|
131
131
|
path: string;
|
|
132
132
|
scale: number;
|
|
133
|
+
} | {
|
|
134
|
+
kind: 'nbt_read_dynamic';
|
|
135
|
+
dst: Temp;
|
|
136
|
+
ns: string;
|
|
137
|
+
pathPrefix: string;
|
|
138
|
+
indexSrc: Operand;
|
|
133
139
|
} | {
|
|
134
140
|
kind: 'nbt_write';
|
|
135
141
|
ns: string;
|
|
@@ -137,6 +143,12 @@ export type MIRInstr = MIRInstrBase & ({
|
|
|
137
143
|
type: NBTType;
|
|
138
144
|
scale: number;
|
|
139
145
|
src: Operand;
|
|
146
|
+
} | {
|
|
147
|
+
kind: 'nbt_write_dynamic';
|
|
148
|
+
ns: string;
|
|
149
|
+
pathPrefix: string;
|
|
150
|
+
indexSrc: Operand;
|
|
151
|
+
valueSrc: Operand;
|
|
140
152
|
} | {
|
|
141
153
|
kind: 'score_read';
|
|
142
154
|
dst: Temp;
|
package/dist/src/mir/verify.js
CHANGED
|
@@ -155,6 +155,7 @@ function getDst(instr) {
|
|
|
155
155
|
case 'or':
|
|
156
156
|
case 'not':
|
|
157
157
|
case 'nbt_read':
|
|
158
|
+
case 'nbt_read_dynamic':
|
|
158
159
|
return instr.dst;
|
|
159
160
|
case 'call':
|
|
160
161
|
case 'call_macro':
|
|
@@ -188,9 +189,15 @@ function getUsedTemps(instr) {
|
|
|
188
189
|
break;
|
|
189
190
|
case 'nbt_read':
|
|
190
191
|
break;
|
|
192
|
+
case 'nbt_read_dynamic':
|
|
193
|
+
temps.push(...getOperandTemps(instr.indexSrc));
|
|
194
|
+
break;
|
|
191
195
|
case 'nbt_write':
|
|
192
196
|
temps.push(...getOperandTemps(instr.src));
|
|
193
197
|
break;
|
|
198
|
+
case 'nbt_write_dynamic':
|
|
199
|
+
temps.push(...getOperandTemps(instr.indexSrc), ...getOperandTemps(instr.valueSrc));
|
|
200
|
+
break;
|
|
194
201
|
case 'call':
|
|
195
202
|
for (const arg of instr.args)
|
|
196
203
|
temps.push(...getOperandTemps(arg));
|
|
@@ -75,6 +75,10 @@ function rewriteUses(instr, copies) {
|
|
|
75
75
|
return { ...instr, a: resolve(instr.a, copies), b: resolve(instr.b, copies) };
|
|
76
76
|
case 'nbt_write':
|
|
77
77
|
return { ...instr, src: resolve(instr.src, copies) };
|
|
78
|
+
case 'nbt_write_dynamic':
|
|
79
|
+
return { ...instr, indexSrc: resolve(instr.indexSrc, copies), valueSrc: resolve(instr.valueSrc, copies) };
|
|
80
|
+
case 'nbt_read_dynamic':
|
|
81
|
+
return { ...instr, indexSrc: resolve(instr.indexSrc, copies) };
|
|
78
82
|
case 'call':
|
|
79
83
|
return { ...instr, args: instr.args.map(a => resolve(a, copies)) };
|
|
80
84
|
case 'call_macro':
|
|
@@ -104,6 +108,7 @@ function getDst(instr) {
|
|
|
104
108
|
case 'or':
|
|
105
109
|
case 'not':
|
|
106
110
|
case 'nbt_read':
|
|
111
|
+
case 'nbt_read_dynamic':
|
|
107
112
|
return instr.dst;
|
|
108
113
|
case 'call':
|
|
109
114
|
case 'call_macro':
|
|
@@ -713,8 +713,12 @@ function rewriteInstr(instr, promoted) {
|
|
|
713
713
|
return { ...instr, dst: rTemp(instr.dst), src: rOp(instr.src) };
|
|
714
714
|
case 'nbt_read':
|
|
715
715
|
return { ...instr, dst: rTemp(instr.dst) };
|
|
716
|
+
case 'nbt_read_dynamic':
|
|
717
|
+
return { ...instr, dst: rTemp(instr.dst), indexSrc: rOp(instr.indexSrc) };
|
|
716
718
|
case 'nbt_write':
|
|
717
719
|
return { ...instr, src: rOp(instr.src) };
|
|
720
|
+
case 'nbt_write_dynamic':
|
|
721
|
+
return { ...instr, indexSrc: rOp(instr.indexSrc), valueSrc: rOp(instr.valueSrc) };
|
|
718
722
|
case 'call':
|
|
719
723
|
return { ...instr, dst: instr.dst ? rTemp(instr.dst) : null, args: instr.args.map(rOp) };
|
|
720
724
|
case 'call_macro':
|
|
@@ -770,6 +774,7 @@ function getDst(instr) {
|
|
|
770
774
|
case 'or':
|
|
771
775
|
case 'not':
|
|
772
776
|
case 'nbt_read':
|
|
777
|
+
case 'nbt_read_dynamic':
|
|
773
778
|
return instr.dst;
|
|
774
779
|
case 'call':
|
|
775
780
|
case 'call_macro':
|
|
@@ -802,6 +807,13 @@ function getUsedTemps(instr) {
|
|
|
802
807
|
case 'nbt_write':
|
|
803
808
|
addOp(instr.src);
|
|
804
809
|
break;
|
|
810
|
+
case 'nbt_write_dynamic':
|
|
811
|
+
addOp(instr.indexSrc);
|
|
812
|
+
addOp(instr.valueSrc);
|
|
813
|
+
break;
|
|
814
|
+
case 'nbt_read_dynamic':
|
|
815
|
+
addOp(instr.indexSrc);
|
|
816
|
+
break;
|
|
805
817
|
case 'call':
|
|
806
818
|
instr.args.forEach(addOp);
|
|
807
819
|
break;
|
|
@@ -75,6 +75,7 @@ function recomputePreds(blocks) {
|
|
|
75
75
|
function hasSideEffects(instr) {
|
|
76
76
|
if (instr.kind === 'call' || instr.kind === 'call_macro' ||
|
|
77
77
|
instr.kind === 'call_context' || instr.kind === 'nbt_write' ||
|
|
78
|
+
instr.kind === 'nbt_write_dynamic' ||
|
|
78
79
|
instr.kind === 'score_write')
|
|
79
80
|
return true;
|
|
80
81
|
// Return field temps (__rf_) write to global return slots — not dead even if unused locally
|
|
@@ -106,6 +107,7 @@ function getDst(instr) {
|
|
|
106
107
|
case 'or':
|
|
107
108
|
case 'not':
|
|
108
109
|
case 'nbt_read':
|
|
110
|
+
case 'nbt_read_dynamic':
|
|
109
111
|
return instr.dst;
|
|
110
112
|
case 'call':
|
|
111
113
|
case 'call_macro':
|
|
@@ -140,6 +142,13 @@ function getUsedTemps(instr) {
|
|
|
140
142
|
case 'nbt_write':
|
|
141
143
|
addOp(instr.src);
|
|
142
144
|
break;
|
|
145
|
+
case 'nbt_write_dynamic':
|
|
146
|
+
addOp(instr.indexSrc);
|
|
147
|
+
addOp(instr.valueSrc);
|
|
148
|
+
break;
|
|
149
|
+
case 'nbt_read_dynamic':
|
|
150
|
+
addOp(instr.indexSrc);
|
|
151
|
+
break;
|
|
143
152
|
case 'call':
|
|
144
153
|
instr.args.forEach(addOp);
|
|
145
154
|
break;
|
|
@@ -287,6 +287,8 @@ function substituteInstr(instr, sub) {
|
|
|
287
287
|
return { ...instr, a: substituteOp(instr.a, sub), b: substituteOp(instr.b, sub) };
|
|
288
288
|
case 'nbt_write':
|
|
289
289
|
return { ...instr, src: substituteOp(instr.src, sub) };
|
|
290
|
+
case 'nbt_write_dynamic':
|
|
291
|
+
return { ...instr, indexSrc: substituteOp(instr.indexSrc, sub), valueSrc: substituteOp(instr.valueSrc, sub) };
|
|
290
292
|
case 'call':
|
|
291
293
|
return { ...instr, args: instr.args.map(a => substituteOp(a, sub)) };
|
|
292
294
|
case 'call_macro':
|
|
@@ -317,6 +319,7 @@ function getInstrDst(instr) {
|
|
|
317
319
|
case 'or':
|
|
318
320
|
case 'not':
|
|
319
321
|
case 'nbt_read':
|
|
322
|
+
case 'nbt_read_dynamic':
|
|
320
323
|
return instr.dst;
|
|
321
324
|
case 'call':
|
|
322
325
|
case 'call_macro':
|
package/dist/src/parser/index.js
CHANGED
|
@@ -1048,6 +1048,11 @@ class Parser {
|
|
|
1048
1048
|
const value = this.parseAssignment();
|
|
1049
1049
|
return this.withLoc({ kind: 'member_assign', obj: left.obj, field: left.field, op, value }, this.getLocToken(left) ?? token);
|
|
1050
1050
|
}
|
|
1051
|
+
// Index assignment: arr[0] = val, arr[i] = val
|
|
1052
|
+
if (left.kind === 'index') {
|
|
1053
|
+
const value = this.parseAssignment();
|
|
1054
|
+
return this.withLoc({ kind: 'index_assign', obj: left.obj, index: left.index, op, value }, this.getLocToken(left) ?? token);
|
|
1055
|
+
}
|
|
1051
1056
|
}
|
|
1052
1057
|
return left;
|
|
1053
1058
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ln(x) polynomial approximation adapter — atanh series form.
|
|
3
|
+
*
|
|
4
|
+
* Algorithm:
|
|
5
|
+
* Input x: fixed-point integer (×10000), e.g. 10000 = 1.0
|
|
6
|
+
* 1. Range reduction: find k s.t. xr ∈ [10000, 20000)
|
|
7
|
+
* 2. s = (xr - 10000) * 10000 / (xr + 10000) → s ∈ [0, 3333]
|
|
8
|
+
* 3. ln(xr/10000) ≈ A1*s/SCALE + A3*s³/SCALE² + A5*s⁵/SCALE³
|
|
9
|
+
* (coefficients absorb the factor of 2; theoretical: A1=20000, A3=6667, A5=4000)
|
|
10
|
+
* 4. ln(x/10000) = k * LN2 + ln(xr/10000)
|
|
11
|
+
*
|
|
12
|
+
* Intermediate overflow analysis (s ≤ 3333, SCALE = 10000):
|
|
13
|
+
* s² = s*s ≤ 11M — fits int32 (max ~2.1B)
|
|
14
|
+
* s2 = s²/SCALE ≤ 1111
|
|
15
|
+
* s3 = s*s2 ≤ 3.7M — fits int32
|
|
16
|
+
* s5 = s3*s2 ≤ 4.1M — fits int32
|
|
17
|
+
* A1*s ≤ 22000*3333 ≤ 73M — fits int32
|
|
18
|
+
* A3*s3 ≤ 7000*3703 ≤ 26M — fits int32
|
|
19
|
+
* A5*s5 ≤ 5000*4115 ≤ 21M — fits int32
|
|
20
|
+
*/
|
|
21
|
+
import { TunerAdapter } from '../types';
|
|
22
|
+
export declare const defaultParams: Record<string, number>;
|
|
23
|
+
export declare const lnPolynomialAdapter: TunerAdapter;
|