redscript-mc 1.2.27 → 1.2.29

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.
@@ -183,6 +183,7 @@ var require_lexer = __commonJS({
183
183
  enum: "enum",
184
184
  trigger: "trigger",
185
185
  namespace: "namespace",
186
+ module: "module",
186
187
  execute: "execute",
187
188
  run: "run",
188
189
  unless: "unless",
@@ -716,11 +717,21 @@ var require_parser = __commonJS({
716
717
  "Creeper",
717
718
  "Spider",
718
719
  "Enderman",
720
+ "Blaze",
721
+ "Witch",
722
+ "Slime",
723
+ "ZombieVillager",
724
+ "Husk",
725
+ "Drowned",
726
+ "Stray",
727
+ "WitherSkeleton",
728
+ "CaveSpider",
719
729
  "Pig",
720
730
  "Cow",
721
731
  "Sheep",
722
732
  "Chicken",
723
733
  "Villager",
734
+ "WanderingTrader",
724
735
  "ArmorStand",
725
736
  "Item",
726
737
  "Arrow"
@@ -735,6 +746,7 @@ var require_parser = __commonJS({
735
746
  var Parser = class _Parser {
736
747
  constructor(tokens, source, filePath) {
737
748
  this.pos = 0;
749
+ this.inLibraryMode = false;
738
750
  this.tokens = tokens;
739
751
  this.sourceLines = source?.split("\n") ?? [];
740
752
  this.filePath = filePath;
@@ -806,12 +818,22 @@ var require_parser = __commonJS({
806
818
  const implBlocks = [];
807
819
  const enums = [];
808
820
  const consts = [];
821
+ let isLibrary = false;
809
822
  if (this.check("namespace")) {
810
823
  this.advance();
811
824
  const name = this.expect("ident");
812
825
  namespace = name.value;
813
826
  this.expect(";");
814
827
  }
828
+ if (this.check("module")) {
829
+ this.advance();
830
+ const modKind = this.expect("ident");
831
+ if (modKind.value === "library") {
832
+ isLibrary = true;
833
+ this.inLibraryMode = true;
834
+ }
835
+ this.expect(";");
836
+ }
815
837
  while (!this.check("eof")) {
816
838
  if (this.check("let")) {
817
839
  globals.push(this.parseGlobalDecl(true));
@@ -830,7 +852,7 @@ var require_parser = __commonJS({
830
852
  declarations.push(this.parseFnDecl());
831
853
  }
832
854
  }
833
- return { namespace, globals, declarations, structs, implBlocks, enums, consts };
855
+ return { namespace, globals, declarations, structs, implBlocks, enums, consts, isLibrary };
834
856
  }
835
857
  // -------------------------------------------------------------------------
836
858
  // Struct Declaration
@@ -923,7 +945,8 @@ var require_parser = __commonJS({
923
945
  returnType = this.parseType();
924
946
  }
925
947
  const body = this.parseBlock();
926
- return this.withLoc({ name, params, returnType, decorators, body }, fnToken);
948
+ const fn = this.withLoc({ name, params, returnType, decorators, body, isLibraryFn: this.inLibraryMode || void 0 }, fnToken);
949
+ return fn;
927
950
  }
928
951
  /** Parse a `declare fn name(params): returnType;` stub — no body, just discard. */
929
952
  parseDeclareStub() {
@@ -985,6 +1008,22 @@ var require_parser = __commonJS({
985
1008
  return { name, args };
986
1009
  }
987
1010
  }
1011
+ if (name === "require_on_load") {
1012
+ const rawArgs = [];
1013
+ for (const part of argsStr.split(",")) {
1014
+ const trimmed = part.trim();
1015
+ const identMatch = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
1016
+ if (identMatch) {
1017
+ rawArgs.push({ kind: "string", value: identMatch[1] });
1018
+ } else {
1019
+ const strMatch = trimmed.match(/^"([^"]*)"$/);
1020
+ if (strMatch) {
1021
+ rawArgs.push({ kind: "string", value: strMatch[1] });
1022
+ }
1023
+ }
1024
+ }
1025
+ return { name, rawArgs };
1026
+ }
988
1027
  for (const part of argsStr.split(",")) {
989
1028
  const [key, val] = part.split("=").map((s) => s.trim());
990
1029
  if (key === "rate") {
@@ -1034,7 +1073,16 @@ var require_parser = __commonJS({
1034
1073
  type = { kind: "named", name: token.kind };
1035
1074
  } else if (token.kind === "ident") {
1036
1075
  this.advance();
1037
- type = { kind: "struct", name: token.value };
1076
+ if (token.value === "selector" && this.check("<")) {
1077
+ this.advance();
1078
+ const entityType = this.expect("ident").value;
1079
+ this.expect(">");
1080
+ type = { kind: "selector", entityType };
1081
+ } else if (token.value === "selector") {
1082
+ type = { kind: "selector" };
1083
+ } else {
1084
+ type = { kind: "struct", name: token.value };
1085
+ }
1038
1086
  } else {
1039
1087
  this.error(`Expected type, got '${token.kind}'`);
1040
1088
  }
@@ -2248,11 +2296,21 @@ var require_typechecker = __commonJS({
2248
2296
  "Creeper": "HostileMob",
2249
2297
  "Spider": "HostileMob",
2250
2298
  "Enderman": "HostileMob",
2299
+ "Blaze": "HostileMob",
2300
+ "Witch": "HostileMob",
2301
+ "Slime": "HostileMob",
2302
+ "ZombieVillager": "HostileMob",
2303
+ "Husk": "HostileMob",
2304
+ "Drowned": "HostileMob",
2305
+ "Stray": "HostileMob",
2306
+ "WitherSkeleton": "HostileMob",
2307
+ "CaveSpider": "HostileMob",
2251
2308
  "Pig": "PassiveMob",
2252
2309
  "Cow": "PassiveMob",
2253
2310
  "Sheep": "PassiveMob",
2254
2311
  "Chicken": "PassiveMob",
2255
2312
  "Villager": "PassiveMob",
2313
+ "WanderingTrader": "PassiveMob",
2256
2314
  "ArmorStand": "entity",
2257
2315
  "Item": "entity",
2258
2316
  "Arrow": "entity"
@@ -2268,6 +2326,24 @@ var require_typechecker = __commonJS({
2268
2326
  "minecraft:spider": "Spider",
2269
2327
  "enderman": "Enderman",
2270
2328
  "minecraft:enderman": "Enderman",
2329
+ "blaze": "Blaze",
2330
+ "minecraft:blaze": "Blaze",
2331
+ "witch": "Witch",
2332
+ "minecraft:witch": "Witch",
2333
+ "slime": "Slime",
2334
+ "minecraft:slime": "Slime",
2335
+ "zombie_villager": "ZombieVillager",
2336
+ "minecraft:zombie_villager": "ZombieVillager",
2337
+ "husk": "Husk",
2338
+ "minecraft:husk": "Husk",
2339
+ "drowned": "Drowned",
2340
+ "minecraft:drowned": "Drowned",
2341
+ "stray": "Stray",
2342
+ "minecraft:stray": "Stray",
2343
+ "wither_skeleton": "WitherSkeleton",
2344
+ "minecraft:wither_skeleton": "WitherSkeleton",
2345
+ "cave_spider": "CaveSpider",
2346
+ "minecraft:cave_spider": "CaveSpider",
2271
2347
  "pig": "Pig",
2272
2348
  "minecraft:pig": "Pig",
2273
2349
  "cow": "Cow",
@@ -2278,6 +2354,8 @@ var require_typechecker = __commonJS({
2278
2354
  "minecraft:chicken": "Chicken",
2279
2355
  "villager": "Villager",
2280
2356
  "minecraft:villager": "Villager",
2357
+ "wandering_trader": "WanderingTrader",
2358
+ "minecraft:wandering_trader": "WanderingTrader",
2281
2359
  "armor_stand": "ArmorStand",
2282
2360
  "minecraft:armor_stand": "ArmorStand",
2283
2361
  "item": "Item",
@@ -3275,6 +3353,99 @@ var require_builder = __commonJS({
3275
3353
  }
3276
3354
  });
3277
3355
 
3356
+ // ../../dist/types/entity-hierarchy.js
3357
+ var require_entity_hierarchy = __commonJS({
3358
+ "../../dist/types/entity-hierarchy.js"(exports2) {
3359
+ "use strict";
3360
+ Object.defineProperty(exports2, "__esModule", { value: true });
3361
+ exports2.ENTITY_TYPE_BY_MCID = exports2.ENTITY_TYPE_MAP = exports2.ENTITY_TYPES = void 0;
3362
+ exports2.isSubtype = isSubtype;
3363
+ exports2.areCompatibleTypes = areCompatibleTypes;
3364
+ exports2.getConcreteSubtypes = getConcreteSubtypes;
3365
+ exports2.getSelectorEntityType = getSelectorEntityType;
3366
+ exports2.getBaseSelectorType = getBaseSelectorType;
3367
+ exports2.ENTITY_TYPES = [
3368
+ // Root
3369
+ { name: "Entity", mcId: null, abstract: true, parent: null },
3370
+ // Direct children of Entity
3371
+ { name: "Player", mcId: "minecraft:player", abstract: false, parent: "Entity" },
3372
+ { name: "ArmorStand", mcId: "minecraft:armor_stand", abstract: false, parent: "Entity" },
3373
+ { name: "Item", mcId: "minecraft:item", abstract: false, parent: "Entity" },
3374
+ { name: "Arrow", mcId: "minecraft:arrow", abstract: false, parent: "Entity" },
3375
+ // Mob hierarchy
3376
+ { name: "Mob", mcId: null, abstract: true, parent: "Entity" },
3377
+ // Hostile mobs
3378
+ { name: "HostileMob", mcId: null, abstract: true, parent: "Mob" },
3379
+ { name: "Zombie", mcId: "minecraft:zombie", abstract: false, parent: "HostileMob" },
3380
+ { name: "Skeleton", mcId: "minecraft:skeleton", abstract: false, parent: "HostileMob" },
3381
+ { name: "Creeper", mcId: "minecraft:creeper", abstract: false, parent: "HostileMob" },
3382
+ { name: "Spider", mcId: "minecraft:spider", abstract: false, parent: "HostileMob" },
3383
+ { name: "Enderman", mcId: "minecraft:enderman", abstract: false, parent: "HostileMob" },
3384
+ { name: "Blaze", mcId: "minecraft:blaze", abstract: false, parent: "HostileMob" },
3385
+ { name: "Witch", mcId: "minecraft:witch", abstract: false, parent: "HostileMob" },
3386
+ { name: "Slime", mcId: "minecraft:slime", abstract: false, parent: "HostileMob" },
3387
+ { name: "ZombieVillager", mcId: "minecraft:zombie_villager", abstract: false, parent: "HostileMob" },
3388
+ { name: "Husk", mcId: "minecraft:husk", abstract: false, parent: "HostileMob" },
3389
+ { name: "Drowned", mcId: "minecraft:drowned", abstract: false, parent: "HostileMob" },
3390
+ { name: "Stray", mcId: "minecraft:stray", abstract: false, parent: "HostileMob" },
3391
+ { name: "WitherSkeleton", mcId: "minecraft:wither_skeleton", abstract: false, parent: "HostileMob" },
3392
+ { name: "CaveSpider", mcId: "minecraft:cave_spider", abstract: false, parent: "HostileMob" },
3393
+ // Passive mobs
3394
+ { name: "PassiveMob", mcId: null, abstract: true, parent: "Mob" },
3395
+ { name: "Pig", mcId: "minecraft:pig", abstract: false, parent: "PassiveMob" },
3396
+ { name: "Cow", mcId: "minecraft:cow", abstract: false, parent: "PassiveMob" },
3397
+ { name: "Sheep", mcId: "minecraft:sheep", abstract: false, parent: "PassiveMob" },
3398
+ { name: "Chicken", mcId: "minecraft:chicken", abstract: false, parent: "PassiveMob" },
3399
+ { name: "Villager", mcId: "minecraft:villager", abstract: false, parent: "PassiveMob" },
3400
+ { name: "WanderingTrader", mcId: "minecraft:wandering_trader", abstract: false, parent: "PassiveMob" }
3401
+ ];
3402
+ exports2.ENTITY_TYPE_MAP = new Map(exports2.ENTITY_TYPES.map((t) => [t.name.toLowerCase(), t]));
3403
+ exports2.ENTITY_TYPE_BY_MCID = new Map(exports2.ENTITY_TYPES.filter((t) => t.mcId !== null).map((t) => [t.mcId.replace("minecraft:", ""), t]));
3404
+ function isSubtype(typeA, typeB) {
3405
+ if (typeA === typeB)
3406
+ return true;
3407
+ const node = exports2.ENTITY_TYPE_MAP.get(typeA.toLowerCase());
3408
+ if (!node || !node.parent)
3409
+ return false;
3410
+ return isSubtype(node.parent, typeB);
3411
+ }
3412
+ function areCompatibleTypes(outerType, innerType) {
3413
+ return isSubtype(outerType, innerType) || isSubtype(innerType, outerType);
3414
+ }
3415
+ function getConcreteSubtypes(typeName) {
3416
+ const results = [];
3417
+ for (const node of exports2.ENTITY_TYPES) {
3418
+ if (!node.abstract && isSubtype(node.name, typeName)) {
3419
+ results.push(node);
3420
+ }
3421
+ }
3422
+ return results;
3423
+ }
3424
+ function getSelectorEntityType(selector) {
3425
+ const match = selector.match(/type=(?:minecraft:)?([a-z_]+)/);
3426
+ if (!match)
3427
+ return null;
3428
+ const mcId = match[1];
3429
+ const node = exports2.ENTITY_TYPE_BY_MCID.get(mcId);
3430
+ return node ? node.name : null;
3431
+ }
3432
+ function getBaseSelectorType(selector) {
3433
+ const trimmed = selector.trim();
3434
+ const typeFromFilter = getSelectorEntityType(trimmed);
3435
+ if (trimmed.startsWith("@a") || trimmed.startsWith("@p") || trimmed.startsWith("@r")) {
3436
+ return typeFromFilter ?? "Player";
3437
+ }
3438
+ if (trimmed.startsWith("@e")) {
3439
+ return typeFromFilter ?? "Entity";
3440
+ }
3441
+ if (trimmed.startsWith("@s")) {
3442
+ return typeFromFilter ?? null;
3443
+ }
3444
+ return null;
3445
+ }
3446
+ }
3447
+ });
3448
+
3278
3449
  // ../../dist/lowering/index.js
3279
3450
  var require_lowering = __commonJS({
3280
3451
  "../../dist/lowering/index.js"(exports2) {
@@ -3317,11 +3488,17 @@ var require_lowering = __commonJS({
3317
3488
  };
3318
3489
  })();
3319
3490
  Object.defineProperty(exports2, "__esModule", { value: true });
3320
- exports2.Lowering = void 0;
3491
+ exports2.Lowering = exports2.LOWERING_OBJ = void 0;
3492
+ exports2.setScoreboardObjective = setScoreboardObjective;
3321
3493
  var builder_1 = require_builder();
3322
3494
  var diagnostics_1 = require_diagnostics();
3323
3495
  var path2 = __importStar(require("path"));
3324
3496
  var types_1 = require_types();
3497
+ var entity_hierarchy_1 = require_entity_hierarchy();
3498
+ exports2.LOWERING_OBJ = "rs";
3499
+ function setScoreboardObjective(obj) {
3500
+ exports2.LOWERING_OBJ = obj;
3501
+ }
3325
3502
  var BUILTINS2 = {
3326
3503
  say: ([msg]) => `say ${msg}`,
3327
3504
  tell: ([sel, msg]) => `tellraw ${sel} {"text":"${msg}"}`,
@@ -3339,9 +3516,10 @@ var require_lowering = __commonJS({
3339
3516
  const pos = [x ?? "~", y ?? "~", z ?? "~"].join(" ");
3340
3517
  return nbt ? `summon ${type} ${pos} ${nbt}` : `summon ${type} ${pos}`;
3341
3518
  },
3342
- particle: ([name, x, y, z]) => {
3519
+ particle: ([name, x, y, z, dx, dy, dz, speed, count]) => {
3343
3520
  const pos = [x ?? "~", y ?? "~", z ?? "~"].join(" ");
3344
- return `particle ${name} ${pos}`;
3521
+ const extra = [dx, dy, dz, speed, count].filter((v) => v !== void 0 && v !== null);
3522
+ return extra.length > 0 ? `particle ${name} ${pos} ${extra.join(" ")}` : `particle ${name} ${pos}`;
3345
3523
  },
3346
3524
  playsound: ([sound, source, sel, x, y, z, volume, pitch, minVolume]) => ["playsound", sound, source, sel, x, y, z, volume, pitch, minVolume].filter(Boolean).join(" "),
3347
3525
  tp: () => null,
@@ -3428,8 +3606,14 @@ var require_lowering = __commonJS({
3428
3606
  // Special handling
3429
3607
  setInterval: () => null,
3430
3608
  // Special handling
3431
- clearInterval: () => null
3609
+ clearInterval: () => null,
3432
3610
  // Special handling
3611
+ storage_get_int: () => null,
3612
+ // Special handling (dynamic NBT array read via macro)
3613
+ storage_set_array: () => null,
3614
+ // Special handling (write literal NBT array to storage)
3615
+ storage_set_int: () => null
3616
+ // Special handling (dynamic NBT array write via macro)
3433
3617
  };
3434
3618
  function getSpan(node) {
3435
3619
  return node?.span;
@@ -3443,11 +3627,21 @@ var require_lowering = __commonJS({
3443
3627
  Creeper: "minecraft:creeper",
3444
3628
  Spider: "minecraft:spider",
3445
3629
  Enderman: "minecraft:enderman",
3630
+ Blaze: "minecraft:blaze",
3631
+ Witch: "minecraft:witch",
3632
+ Slime: "minecraft:slime",
3633
+ ZombieVillager: "minecraft:zombie_villager",
3634
+ Husk: "minecraft:husk",
3635
+ Drowned: "minecraft:drowned",
3636
+ Stray: "minecraft:stray",
3637
+ WitherSkeleton: "minecraft:wither_skeleton",
3638
+ CaveSpider: "minecraft:cave_spider",
3446
3639
  Pig: "minecraft:pig",
3447
3640
  Cow: "minecraft:cow",
3448
3641
  Sheep: "minecraft:sheep",
3449
3642
  Chicken: "minecraft:chicken",
3450
3643
  Villager: "minecraft:villager",
3644
+ WanderingTrader: "minecraft:wandering_trader",
3451
3645
  ArmorStand: "minecraft:armor_stand",
3452
3646
  Item: "minecraft:item",
3453
3647
  Arrow: "minecraft:arrow"
@@ -3485,6 +3679,16 @@ var require_lowering = __commonJS({
3485
3679
  return `${emitCoord(pos.x)} ${emitCoord(pos.y)} ${emitCoord(pos.z)}`;
3486
3680
  }
3487
3681
  var Lowering = class {
3682
+ /** Unique IR variable name for a local variable, scoped to the current function.
3683
+ * Prevents cross-function scoreboard slot collisions: $fn_x ≠ $gn_x.
3684
+ * Only applies to user-defined locals/params; internal slots ($p0, $ret) are
3685
+ * intentionally global (calling convention). */
3686
+ fnVar(name) {
3687
+ return `$${this.currentFn}_${name}`;
3688
+ }
3689
+ currentEntityContext() {
3690
+ return this.entityContextStack.length > 0 ? this.entityContextStack[this.entityContextStack.length - 1] : "Entity";
3691
+ }
3488
3692
  constructor(namespace, sourceRanges = []) {
3489
3693
  this.functions = [];
3490
3694
  this.globals = [];
@@ -3498,6 +3702,7 @@ var require_lowering = __commonJS({
3498
3702
  this.timeoutCounter = 0;
3499
3703
  this.intervalCounter = 0;
3500
3704
  this.warnings = [];
3705
+ this.entityContextStack = [];
3501
3706
  this.varMap = /* @__PURE__ */ new Map();
3502
3707
  this.lambdaBindings = /* @__PURE__ */ new Map();
3503
3708
  this.intervalBindings = /* @__PURE__ */ new Map();
@@ -3601,11 +3806,23 @@ var require_lowering = __commonJS({
3601
3806
  }
3602
3807
  preScanExpr(expr, paramNames, macroParams) {
3603
3808
  if (expr.kind === "call" && BUILTINS2[expr.fn] !== void 0) {
3604
- for (const arg of expr.args) {
3605
- if (arg.kind === "ident" && paramNames.has(arg.name)) {
3606
- macroParams.add(arg.name);
3809
+ const handler = BUILTINS2[expr.fn];
3810
+ const isSpecialHandled = (() => {
3811
+ try {
3812
+ return handler() === null;
3813
+ } catch {
3814
+ return false;
3815
+ }
3816
+ })();
3817
+ if (!isSpecialHandled) {
3818
+ for (const arg of expr.args) {
3819
+ if (arg.kind === "ident" && paramNames.has(arg.name)) {
3820
+ macroParams.add(arg.name);
3821
+ }
3607
3822
  }
3608
3823
  }
3824
+ for (const arg of expr.args)
3825
+ this.preScanExpr(arg, paramNames, macroParams);
3609
3826
  return;
3610
3827
  }
3611
3828
  if (expr.kind === "call") {
@@ -3670,6 +3887,12 @@ var require_lowering = __commonJS({
3670
3887
  if (expr.kind === "struct_lit" || expr.kind === "array_lit") {
3671
3888
  return { str: this.exprToSnbt(expr) };
3672
3889
  }
3890
+ if (expr.kind === "float_lit") {
3891
+ return { str: expr.value.toString() };
3892
+ }
3893
+ if (expr.kind === "unary" && expr.op === "-" && expr.operand.kind === "float_lit") {
3894
+ return { str: (-expr.operand.value).toString() };
3895
+ }
3673
3896
  return { str: this.exprToString(expr) };
3674
3897
  }
3675
3898
  /**
@@ -3682,9 +3905,9 @@ var require_lowering = __commonJS({
3682
3905
  for (let i = 0; i < loweredArgs.length; i++) {
3683
3906
  const operand = loweredArgs[i];
3684
3907
  if (operand.kind === "const") {
3685
- this.builder.emitRaw(`scoreboard players set $p${i} rs ${operand.value}`);
3908
+ this.builder.emitRaw(`scoreboard players set $p${i} ${exports2.LOWERING_OBJ} ${operand.value}`);
3686
3909
  } else if (operand.kind === "var") {
3687
- this.builder.emitRaw(`scoreboard players operation $p${i} rs = ${operand.name} rs`);
3910
+ this.builder.emitRaw(`scoreboard players operation $p${i} ${exports2.LOWERING_OBJ} = ${operand.name} ${exports2.LOWERING_OBJ}`);
3688
3911
  }
3689
3912
  }
3690
3913
  for (const macroParam of macroParamNames) {
@@ -3695,12 +3918,12 @@ var require_lowering = __commonJS({
3695
3918
  if (operand.kind === "const") {
3696
3919
  this.builder.emitRaw(`data modify storage rs:macro_args ${macroParam} set value ${operand.value}`);
3697
3920
  } else if (operand.kind === "var") {
3698
- this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} rs`);
3921
+ this.builder.emitRaw(`execute store result storage rs:macro_args ${macroParam} int 1 run scoreboard players get ${operand.name} ${exports2.LOWERING_OBJ}`);
3699
3922
  }
3700
3923
  }
3701
3924
  this.builder.emitRaw(`function ${this.namespace}:${fnName} with storage rs:macro_args`);
3702
3925
  const dst = this.builder.freshTemp();
3703
- this.builder.emitRaw(`scoreboard players operation ${dst} rs = $ret rs`);
3926
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} = $ret ${exports2.LOWERING_OBJ}`);
3704
3927
  return { kind: "var", name: dst };
3705
3928
  }
3706
3929
  lower(program) {
@@ -3796,12 +4019,12 @@ var require_lowering = __commonJS({
3796
4019
  this.stringValues.set(param.name, "");
3797
4020
  continue;
3798
4021
  }
3799
- this.varMap.set(param.name, `$${param.name}`);
4022
+ this.varMap.set(param.name, this.fnVar(param.name));
3800
4023
  }
3801
4024
  } else {
3802
4025
  for (const param of runtimeParams) {
3803
4026
  const paramName = param.name;
3804
- this.varMap.set(paramName, `$${paramName}`);
4027
+ this.varMap.set(paramName, this.fnVar(paramName));
3805
4028
  this.varTypes.set(paramName, this.normalizeType(param.type));
3806
4029
  }
3807
4030
  }
@@ -3813,15 +4036,15 @@ var require_lowering = __commonJS({
3813
4036
  this.builder.startBlock("entry");
3814
4037
  for (let i = 0; i < runtimeParams.length; i++) {
3815
4038
  const paramName = runtimeParams[i].name;
3816
- const varName = `$${paramName}`;
3817
- this.builder.emitAssign(varName, { kind: "var", name: `$p${i}` });
4039
+ const varName = this.fnVar(paramName);
4040
+ this.builder.emitAssign(varName, { kind: "param", index: i });
3818
4041
  }
3819
4042
  if (staticEventDec) {
3820
4043
  for (let i = 0; i < fn.params.length; i++) {
3821
4044
  const param = fn.params[i];
3822
4045
  const expected = eventParamSpecs[i];
3823
4046
  if (expected?.type.kind === "named" && expected.type.name !== "string") {
3824
- this.builder.emitAssign(`$${param.name}`, { kind: "const", value: 0 });
4047
+ this.builder.emitAssign(this.fnVar(param.name), { kind: "const", value: 0 });
3825
4048
  }
3826
4049
  }
3827
4050
  }
@@ -3868,6 +4091,19 @@ var require_lowering = __commonJS({
3868
4091
  if (fn.decorators.some((d) => d.name === "load")) {
3869
4092
  irFn.isLoadInit = true;
3870
4093
  }
4094
+ const requiredLoads = [];
4095
+ for (const d of fn.decorators) {
4096
+ if (d.name === "require_on_load") {
4097
+ for (const arg of d.rawArgs ?? []) {
4098
+ if (arg.kind === "string") {
4099
+ requiredLoads.push(arg.value);
4100
+ }
4101
+ }
4102
+ }
4103
+ }
4104
+ if (requiredLoads.length > 0) {
4105
+ irFn.requiredLoads = requiredLoads;
4106
+ }
3871
4107
  if (tickRate && tickRate > 1) {
3872
4108
  this.wrapWithTickRate(irFn, tickRate);
3873
4109
  }
@@ -3889,7 +4125,7 @@ var require_lowering = __commonJS({
3889
4125
  const originalInstrs = [...entry.instrs];
3890
4126
  const originalTerm = entry.term;
3891
4127
  entry.instrs = [
3892
- { op: "raw", cmd: `scoreboard players add ${counterVar} rs 1` }
4128
+ { op: "raw", cmd: `scoreboard players add ${counterVar} ${exports2.LOWERING_OBJ} 1` }
3893
4129
  ];
3894
4130
  const bodyLabel = "tick_body";
3895
4131
  const skipLabel = "tick_skip";
@@ -3901,12 +4137,12 @@ var require_lowering = __commonJS({
3901
4137
  };
3902
4138
  entry.instrs.push({
3903
4139
  op: "raw",
3904
- cmd: `execute store success score ${counterVar}_check rs if score ${counterVar} rs matches ${rate}..`
4140
+ cmd: `execute store success score ${counterVar}_check ${exports2.LOWERING_OBJ} if score ${counterVar} ${exports2.LOWERING_OBJ} matches ${rate}..`
3905
4141
  });
3906
4142
  fn.blocks.push({
3907
4143
  label: bodyLabel,
3908
4144
  instrs: [
3909
- { op: "raw", cmd: `scoreboard players set ${counterVar} rs 0` },
4145
+ { op: "raw", cmd: `scoreboard players set ${counterVar} ${exports2.LOWERING_OBJ} 0` },
3910
4146
  ...originalInstrs
3911
4147
  ],
3912
4148
  term: originalTerm
@@ -3982,7 +4218,7 @@ var require_lowering = __commonJS({
3982
4218
  if (this.currentContext.binding === stmt.name) {
3983
4219
  throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot redeclare foreach binding '${stmt.name}'`, stmt.span ?? { line: 0, col: 0 });
3984
4220
  }
3985
- const varName = `$${stmt.name}`;
4221
+ const varName = this.fnVar(stmt.name);
3986
4222
  this.varMap.set(stmt.name, varName);
3987
4223
  const declaredType = stmt.type ? this.normalizeType(stmt.type) : this.inferExprType(stmt.init);
3988
4224
  if (declaredType) {
@@ -4013,7 +4249,7 @@ var require_lowering = __commonJS({
4013
4249
  if (fieldValue.kind === "const") {
4014
4250
  this.builder.emitRaw(`data modify storage ${path3} set value ${fieldValue.value}`);
4015
4251
  } else if (fieldValue.kind === "var") {
4016
- this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${fieldValue.name} rs`);
4252
+ this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${fieldValue.name} ${exports2.LOWERING_OBJ}`);
4017
4253
  }
4018
4254
  }
4019
4255
  return;
@@ -4039,7 +4275,7 @@ var require_lowering = __commonJS({
4039
4275
  this.builder.emitRaw(`data modify storage rs:heap ${stmt.name} append value ${elemValue.value}`);
4040
4276
  } else if (elemValue.kind === "var") {
4041
4277
  this.builder.emitRaw(`data modify storage rs:heap ${stmt.name} append value 0`);
4042
- this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} rs`);
4278
+ this.builder.emitRaw(`execute store result storage rs:heap ${stmt.name}[-1] int 1 run scoreboard players get ${elemValue.name} ${exports2.LOWERING_OBJ}`);
4043
4279
  }
4044
4280
  }
4045
4281
  return;
@@ -4079,7 +4315,7 @@ var require_lowering = __commonJS({
4079
4315
  if (fieldValue.kind === "const") {
4080
4316
  this.builder.emitRaw(`data modify storage ${path3} set value ${fieldValue.value}`);
4081
4317
  } else if (fieldValue.kind === "var") {
4082
- this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${fieldValue.name} rs`);
4318
+ this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${fieldValue.name} ${exports2.LOWERING_OBJ}`);
4083
4319
  }
4084
4320
  }
4085
4321
  this.builder.emitReturn({ kind: "const", value: 0 });
@@ -4143,11 +4379,22 @@ var require_lowering = __commonJS({
4143
4379
  throw new diagnostics_1.DiagnosticError("LoweringError", "'is' checks require an entity selector or entity binding", cond.span ?? stmt.span ?? { line: 0, col: 0 });
4144
4380
  }
4145
4381
  const mcType = ENTITY_TO_MC_TYPE[cond.entityType];
4382
+ const thenFnName = `${this.currentFn}/then_${this.foreachCounter++}`;
4146
4383
  if (!mcType) {
4147
- throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot lower entity type check for '${cond.entityType}'`, cond.span ?? stmt.span ?? { line: 0, col: 0 });
4384
+ const subtypes = (0, entity_hierarchy_1.getConcreteSubtypes)(cond.entityType);
4385
+ if (subtypes.length === 0) {
4386
+ throw new diagnostics_1.DiagnosticError("LoweringError", `Cannot lower entity type check for '${cond.entityType}'`, cond.span ?? stmt.span ?? { line: 0, col: 0 });
4387
+ }
4388
+ this.builder.emitRaw(`scoreboard players set __is_result rs:temp 0`);
4389
+ for (const subtype of subtypes) {
4390
+ if (subtype.mcId) {
4391
+ this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, subtype.mcId)} run scoreboard players set __is_result rs:temp 1`);
4392
+ }
4393
+ }
4394
+ this.builder.emitRaw(`execute if score __is_result rs:temp matches 1 run function ${this.namespace}:${thenFnName}`);
4395
+ } else {
4396
+ this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, mcType)} run function ${this.namespace}:${thenFnName}`);
4148
4397
  }
4149
- const thenFnName = `${this.currentFn}/then_${this.foreachCounter++}`;
4150
- this.builder.emitRaw(`execute if entity ${this.appendTypeFilter(selector, mcType)} run function ${this.namespace}:${thenFnName}`);
4151
4398
  const savedBuilder = this.builder;
4152
4399
  const savedVarMap = new Map(this.varMap);
4153
4400
  const savedBlockPosVars = new Map(this.blockPosVars);
@@ -4208,14 +4455,14 @@ var require_lowering = __commonJS({
4208
4455
  this.builder.startBlock(exitLabel);
4209
4456
  }
4210
4457
  lowerForRangeStmt(stmt) {
4211
- const loopVar = `$${stmt.varName}`;
4458
+ const loopVar = this.fnVar(stmt.varName);
4212
4459
  const subFnName = `${this.currentFn}/__for_${this.foreachCounter++}`;
4213
4460
  this.varMap.set(stmt.varName, loopVar);
4214
4461
  const startVal = this.lowerExpr(stmt.start);
4215
4462
  if (startVal.kind === "const") {
4216
- this.builder.emitRaw(`scoreboard players set ${loopVar} rs ${startVal.value}`);
4463
+ this.builder.emitRaw(`scoreboard players set ${loopVar} ${exports2.LOWERING_OBJ} ${startVal.value}`);
4217
4464
  } else if (startVal.kind === "var") {
4218
- this.builder.emitRaw(`scoreboard players operation ${loopVar} rs = ${startVal.name} rs`);
4465
+ this.builder.emitRaw(`scoreboard players operation ${loopVar} ${exports2.LOWERING_OBJ} = ${startVal.name} ${exports2.LOWERING_OBJ}`);
4219
4466
  }
4220
4467
  this.builder.emitRaw(`function ${this.namespace}:${subFnName}`);
4221
4468
  const savedBuilder = this.builder;
@@ -4228,10 +4475,10 @@ var require_lowering = __commonJS({
4228
4475
  this.blockPosVars = new Map(savedBlockPosVars);
4229
4476
  this.builder.startBlock("entry");
4230
4477
  this.lowerBlock(stmt.body);
4231
- this.builder.emitRaw(`scoreboard players add ${loopVar} rs 1`);
4478
+ this.builder.emitRaw(`scoreboard players add ${loopVar} ${exports2.LOWERING_OBJ} 1`);
4232
4479
  const endVal = this.lowerExpr(stmt.end);
4233
4480
  const endNum = endVal.kind === "const" ? endVal.value - 1 : "?";
4234
- this.builder.emitRaw(`execute if score ${loopVar} rs matches ..${endNum} run function ${this.namespace}:${subFnName}`);
4481
+ this.builder.emitRaw(`execute if score ${loopVar} ${exports2.LOWERING_OBJ} matches ..${endNum} run function ${this.namespace}:${subFnName}`);
4235
4482
  if (!this.builder.isBlockSealed()) {
4236
4483
  this.builder.emitReturn();
4237
4484
  }
@@ -4260,11 +4507,18 @@ var require_lowering = __commonJS({
4260
4507
  this.currentContext = { binding: stmt.binding };
4261
4508
  this.blockPosVars = new Map(savedBlockPosVars);
4262
4509
  this.varMap.set(stmt.binding, "@s");
4510
+ const selectorEntityType = (0, entity_hierarchy_1.getBaseSelectorType)(selector);
4511
+ if (selectorEntityType) {
4512
+ this.entityContextStack.push(selectorEntityType);
4513
+ }
4263
4514
  this.builder.startBlock("entry");
4264
4515
  this.lowerBlock(stmt.body);
4265
4516
  if (!this.builder.isBlockSealed()) {
4266
4517
  this.builder.emitReturn();
4267
4518
  }
4519
+ if (selectorEntityType) {
4520
+ this.entityContextStack.pop();
4521
+ }
4268
4522
  const subFn = this.builder.build(subFnName, [], false);
4269
4523
  this.functions.push(subFn);
4270
4524
  this.builder = savedBuilder;
@@ -4302,12 +4556,12 @@ var require_lowering = __commonJS({
4302
4556
  matchCondition = String(patternValue.value);
4303
4557
  }
4304
4558
  const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
4305
- this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 if score ${subject} rs matches ${matchCondition} run function ${this.namespace}:${subFnName}`);
4559
+ this.builder.emitRaw(`execute if score ${matchedVar} ${exports2.LOWERING_OBJ} matches ..0 if score ${subject} ${exports2.LOWERING_OBJ} matches ${matchCondition} run function ${this.namespace}:${subFnName}`);
4306
4560
  this.emitMatchArmSubFunction(subFnName, matchedVar, arm.body, true);
4307
4561
  }
4308
4562
  if (defaultArm) {
4309
4563
  const subFnName = `${this.currentFn}/match_${this.foreachCounter++}`;
4310
- this.builder.emitRaw(`execute if score ${matchedVar} rs matches ..0 run function ${this.namespace}:${subFnName}`);
4564
+ this.builder.emitRaw(`execute if score ${matchedVar} ${exports2.LOWERING_OBJ} matches ..0 run function ${this.namespace}:${subFnName}`);
4311
4565
  this.emitMatchArmSubFunction(subFnName, matchedVar, defaultArm.body, false);
4312
4566
  }
4313
4567
  }
@@ -4322,7 +4576,7 @@ var require_lowering = __commonJS({
4322
4576
  this.blockPosVars = new Map(savedBlockPosVars);
4323
4577
  this.builder.startBlock("entry");
4324
4578
  if (setMatched) {
4325
- this.builder.emitRaw(`scoreboard players set ${matchedVar} rs 1`);
4579
+ this.builder.emitRaw(`scoreboard players set ${matchedVar} ${exports2.LOWERING_OBJ} 1`);
4326
4580
  }
4327
4581
  this.lowerBlock(body);
4328
4582
  if (!this.builder.isBlockSealed()) {
@@ -4341,7 +4595,7 @@ var require_lowering = __commonJS({
4341
4595
  return;
4342
4596
  }
4343
4597
  const arrayType = this.inferExprType(stmt.iterable);
4344
- const bindingVar = `$${stmt.binding}`;
4598
+ const bindingVar = this.fnVar(stmt.binding);
4345
4599
  const indexVar = this.builder.freshTemp();
4346
4600
  const lengthVar = this.builder.freshTemp();
4347
4601
  const condVar = this.builder.freshTemp();
@@ -4354,7 +4608,7 @@ var require_lowering = __commonJS({
4354
4608
  }
4355
4609
  this.builder.emitAssign(indexVar, { kind: "const", value: 0 });
4356
4610
  this.builder.emitAssign(oneVar, { kind: "const", value: 1 });
4357
- this.builder.emitRaw(`execute store result score ${lengthVar} rs run data get storage rs:heap ${arrayName}`);
4611
+ this.builder.emitRaw(`execute store result score ${lengthVar} ${exports2.LOWERING_OBJ} run data get storage rs:heap ${arrayName}`);
4358
4612
  const checkLabel = this.builder.freshLabel("foreach_array_check");
4359
4613
  const bodyLabel = this.builder.freshLabel("foreach_array_body");
4360
4614
  const exitLabel = this.builder.freshLabel("foreach_array_exit");
@@ -4367,7 +4621,7 @@ var require_lowering = __commonJS({
4367
4621
  this.builder.emitAssign(bindingVar, element);
4368
4622
  this.lowerBlock(stmt.body);
4369
4623
  if (!this.builder.isBlockSealed()) {
4370
- this.builder.emitRaw(`scoreboard players operation ${indexVar} rs += ${oneVar} rs`);
4624
+ this.builder.emitRaw(`scoreboard players operation ${indexVar} ${exports2.LOWERING_OBJ} += ${oneVar} ${exports2.LOWERING_OBJ}`);
4371
4625
  this.builder.emitJump(checkLabel);
4372
4626
  }
4373
4627
  this.builder.startBlock(exitLabel);
@@ -4385,6 +4639,16 @@ var require_lowering = __commonJS({
4385
4639
  lowerAsBlockStmt(stmt) {
4386
4640
  const selector = this.selectorToString(stmt.selector);
4387
4641
  const subFnName = `${this.currentFn}/as_${this.foreachCounter++}`;
4642
+ const innerType = (0, entity_hierarchy_1.getBaseSelectorType)(selector);
4643
+ const outerType = this.currentEntityContext();
4644
+ if (innerType && outerType !== "Entity" && innerType !== "Entity" && !(0, entity_hierarchy_1.areCompatibleTypes)(outerType, innerType)) {
4645
+ this.warnings.push({
4646
+ message: `Impossible type assertion: @s is ${outerType} but as-block targets ${innerType}`,
4647
+ code: "W_IMPOSSIBLE_AS",
4648
+ line: stmt.span?.line,
4649
+ col: stmt.span?.col
4650
+ });
4651
+ }
4388
4652
  this.builder.emitRaw(`execute as ${selector} run function ${this.namespace}:${subFnName}`);
4389
4653
  const savedBuilder = this.builder;
4390
4654
  const savedVarMap = new Map(this.varMap);
@@ -4392,11 +4656,17 @@ var require_lowering = __commonJS({
4392
4656
  this.builder = new LoweringBuilder();
4393
4657
  this.varMap = new Map(savedVarMap);
4394
4658
  this.blockPosVars = new Map(savedBlockPosVars);
4659
+ if (innerType) {
4660
+ this.entityContextStack.push(innerType);
4661
+ }
4395
4662
  this.builder.startBlock("entry");
4396
4663
  this.lowerBlock(stmt.body);
4397
4664
  if (!this.builder.isBlockSealed()) {
4398
4665
  this.builder.emitReturn();
4399
4666
  }
4667
+ if (innerType) {
4668
+ this.entityContextStack.pop();
4669
+ }
4400
4670
  const subFn = this.builder.build(subFnName, [], false);
4401
4671
  this.functions.push(subFn);
4402
4672
  this.builder = savedBuilder;
@@ -4644,19 +4914,19 @@ var require_lowering = __commonJS({
4644
4914
  const mapped = this.varMap.get(expr.obj.name);
4645
4915
  if (mapped && mapped.startsWith("@e[tag=__rs_obj_")) {
4646
4916
  const dst = this.builder.freshTemp();
4647
- this.builder.emitRaw(`scoreboard players operation ${dst} rs = ${mapped} rs`);
4917
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} = ${mapped} ${exports2.LOWERING_OBJ}`);
4648
4918
  return { kind: "var", name: dst };
4649
4919
  }
4650
4920
  if (varType?.kind === "struct") {
4651
4921
  const structName = varType.name.toLowerCase();
4652
4922
  const path3 = `rs:heap ${structName}_${expr.obj.name}.${expr.field}`;
4653
4923
  const dst = this.builder.freshTemp();
4654
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path3}`);
4924
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage ${path3}`);
4655
4925
  return { kind: "var", name: dst };
4656
4926
  }
4657
4927
  if (varType?.kind === "array" && expr.field === "len") {
4658
4928
  const dst = this.builder.freshTemp();
4659
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${expr.obj.name}`);
4929
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage rs:heap ${expr.obj.name}`);
4660
4930
  return { kind: "var", name: dst };
4661
4931
  }
4662
4932
  }
@@ -4670,9 +4940,9 @@ var require_lowering = __commonJS({
4670
4940
  const value2 = this.lowerExpr(expr.value);
4671
4941
  if (expr.op === "=") {
4672
4942
  if (value2.kind === "const") {
4673
- this.builder.emitRaw(`scoreboard players set ${mapped} rs ${value2.value}`);
4943
+ this.builder.emitRaw(`scoreboard players set ${mapped} ${exports2.LOWERING_OBJ} ${value2.value}`);
4674
4944
  } else if (value2.kind === "var") {
4675
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs = ${value2.name} rs`);
4945
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports2.LOWERING_OBJ} = ${value2.name} ${exports2.LOWERING_OBJ}`);
4676
4946
  }
4677
4947
  } else {
4678
4948
  const binOp = expr.op.slice(0, -1);
@@ -4680,9 +4950,9 @@ var require_lowering = __commonJS({
4680
4950
  if (value2.kind === "const") {
4681
4951
  const constTemp = this.builder.freshTemp();
4682
4952
  this.builder.emitAssign(constTemp, value2);
4683
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs ${opMap[binOp]} ${constTemp} rs`);
4953
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports2.LOWERING_OBJ} ${opMap[binOp]} ${constTemp} ${exports2.LOWERING_OBJ}`);
4684
4954
  } else if (value2.kind === "var") {
4685
- this.builder.emitRaw(`scoreboard players operation ${mapped} rs ${opMap[binOp]} ${value2.name} rs`);
4955
+ this.builder.emitRaw(`scoreboard players operation ${mapped} ${exports2.LOWERING_OBJ} ${opMap[binOp]} ${value2.name} ${exports2.LOWERING_OBJ}`);
4686
4956
  }
4687
4957
  }
4688
4958
  return { kind: "const", value: 0 };
@@ -4695,14 +4965,14 @@ var require_lowering = __commonJS({
4695
4965
  if (value2.kind === "const") {
4696
4966
  this.builder.emitRaw(`data modify storage ${path3} set value ${value2.value}`);
4697
4967
  } else if (value2.kind === "var") {
4698
- this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${value2.name} rs`);
4968
+ this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${value2.name} ${exports2.LOWERING_OBJ}`);
4699
4969
  }
4700
4970
  } else {
4701
4971
  const dst = this.builder.freshTemp();
4702
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${path3}`);
4972
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage ${path3}`);
4703
4973
  const binOp = expr.op.slice(0, -1);
4704
4974
  this.builder.emitBinop(dst, { kind: "var", name: dst }, binOp, value2);
4705
- this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${dst} rs`);
4975
+ this.builder.emitRaw(`execute store result storage ${path3} int 1 run scoreboard players get ${dst} ${exports2.LOWERING_OBJ}`);
4706
4976
  }
4707
4977
  return { kind: "const", value: 0 };
4708
4978
  }
@@ -4727,11 +4997,11 @@ var require_lowering = __commonJS({
4727
4997
  if (expr.op === "&&") {
4728
4998
  this.builder.emitAssign(dst, left);
4729
4999
  const rightVar = this.operandToVar(right);
4730
- this.builder.emitRaw(`execute if score ${dst} rs matches 1.. run scoreboard players operation ${dst} rs = ${rightVar} rs`);
5000
+ this.builder.emitRaw(`execute if score ${dst} ${exports2.LOWERING_OBJ} matches 1.. run scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} = ${rightVar} ${exports2.LOWERING_OBJ}`);
4731
5001
  } else {
4732
5002
  this.builder.emitAssign(dst, left);
4733
5003
  const rightVar = this.operandToVar(right);
4734
- this.builder.emitRaw(`execute if score ${dst} rs matches ..0 run scoreboard players operation ${dst} rs = ${rightVar} rs`);
5004
+ this.builder.emitRaw(`execute if score ${dst} ${exports2.LOWERING_OBJ} matches ..0 run scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} = ${rightVar} ${exports2.LOWERING_OBJ}`);
4735
5005
  }
4736
5006
  return { kind: "var", name: dst };
4737
5007
  }
@@ -4744,14 +5014,14 @@ var require_lowering = __commonJS({
4744
5014
  this.builder.emitBinop(dst, left, "*", right);
4745
5015
  const constDiv = this.builder.freshTemp();
4746
5016
  this.builder.emitAssign(constDiv, { kind: "const", value: 1e3 });
4747
- this.builder.emitRaw(`scoreboard players operation ${dst} rs /= ${constDiv} rs`);
5017
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} /= ${constDiv} ${exports2.LOWERING_OBJ}`);
4748
5018
  } else {
4749
5019
  const constMul = this.builder.freshTemp();
4750
5020
  this.builder.emitAssign(constMul, { kind: "const", value: 1e3 });
4751
5021
  this.builder.emitAssign(dst, left);
4752
- this.builder.emitRaw(`scoreboard players operation ${dst} rs *= ${constMul} rs`);
5022
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} *= ${constMul} ${exports2.LOWERING_OBJ}`);
4753
5023
  const rightVar = this.operandToVar(right);
4754
- this.builder.emitRaw(`scoreboard players operation ${dst} rs /= ${rightVar} rs`);
5024
+ this.builder.emitRaw(`scoreboard players operation ${dst} ${exports2.LOWERING_OBJ} /= ${rightVar} ${exports2.LOWERING_OBJ}`);
4755
5025
  }
4756
5026
  return { kind: "var", name: dst };
4757
5027
  }
@@ -4815,7 +5085,7 @@ var require_lowering = __commonJS({
4815
5085
  const storagePath = this.getStringStoragePath(expr.args[0]);
4816
5086
  if (storagePath) {
4817
5087
  const dst = this.builder.freshTemp();
4818
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage ${storagePath}`);
5088
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage ${storagePath}`);
4819
5089
  return { kind: "var", name: dst };
4820
5090
  }
4821
5091
  const staticString = this.resolveStaticString(expr.args[0]);
@@ -4846,7 +5116,7 @@ var require_lowering = __commonJS({
4846
5116
  const entity = this.exprToString(expr.args[0]);
4847
5117
  const tagName = this.exprToString(expr.args[1]);
4848
5118
  const dst = this.builder.freshTemp();
4849
- this.builder.emitRaw(`execute store result score ${dst} rs if entity ${entity}[tag=${tagName}]`);
5119
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} if entity ${entity}[tag=${tagName}]`);
4850
5120
  return { kind: "var", name: dst };
4851
5121
  }
4852
5122
  if (expr.fn === "__array_push") {
@@ -4859,7 +5129,7 @@ var require_lowering = __commonJS({
4859
5129
  this.builder.emitRaw(`data modify storage rs:heap ${arrName} append value ${value.value}`);
4860
5130
  } else if (value.kind === "var") {
4861
5131
  this.builder.emitRaw(`data modify storage rs:heap ${arrName} append value 0`);
4862
- this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} rs`);
5132
+ this.builder.emitRaw(`execute store result storage rs:heap ${arrName}[-1] int 1 run scoreboard players get ${value.name} ${exports2.LOWERING_OBJ}`);
4863
5133
  }
4864
5134
  }
4865
5135
  return { kind: "const", value: 0 };
@@ -4868,7 +5138,7 @@ var require_lowering = __commonJS({
4868
5138
  const arrName = this.getArrayStorageName(expr.args[0]);
4869
5139
  const dst = this.builder.freshTemp();
4870
5140
  if (arrName) {
4871
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${arrName}[-1]`);
5141
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage rs:heap ${arrName}[-1]`);
4872
5142
  this.builder.emitRaw(`data remove storage rs:heap ${arrName}[-1]`);
4873
5143
  } else {
4874
5144
  this.builder.emitAssign(dst, { kind: "const", value: 0 });
@@ -5117,14 +5387,14 @@ var require_lowering = __commonJS({
5117
5387
  const dst = this.builder.freshTemp();
5118
5388
  const min = args[0] ? this.exprToLiteral(args[0]) : "0";
5119
5389
  const max = args[1] ? this.exprToLiteral(args[1]) : "100";
5120
- this.builder.emitRaw(`scoreboard players random ${dst} rs ${min} ${max}`);
5390
+ this.builder.emitRaw(`scoreboard players random ${dst} ${exports2.LOWERING_OBJ} ${min} ${max}`);
5121
5391
  return { kind: "var", name: dst };
5122
5392
  }
5123
5393
  if (name === "random_native") {
5124
5394
  const dst = this.builder.freshTemp();
5125
5395
  const min = args[0] ? this.exprToLiteral(args[0]) : "0";
5126
5396
  const max = args[1] ? this.exprToLiteral(args[1]) : "100";
5127
- this.builder.emitRaw(`execute store result score ${dst} rs run random value ${min} ${max}`);
5397
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run random value ${min} ${max}`);
5128
5398
  return { kind: "var", name: dst };
5129
5399
  }
5130
5400
  if (name === "random_sequence") {
@@ -5137,7 +5407,7 @@ var require_lowering = __commonJS({
5137
5407
  const dst = this.builder.freshTemp();
5138
5408
  const player = this.exprToTargetString(args[0]);
5139
5409
  const objective = this.resolveScoreboardObjective(args[0], args[1], callSpan);
5140
- this.builder.emitRaw(`execute store result score ${dst} rs run scoreboard players get ${player} ${objective}`);
5410
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run scoreboard players get ${player} ${objective}`);
5141
5411
  return { kind: "var", name: dst };
5142
5412
  }
5143
5413
  if (name === "scoreboard_set") {
@@ -5147,7 +5417,7 @@ var require_lowering = __commonJS({
5147
5417
  if (value.kind === "const") {
5148
5418
  this.builder.emitRaw(`scoreboard players set ${player} ${objective} ${value.value}`);
5149
5419
  } else if (value.kind === "var") {
5150
- this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} rs`);
5420
+ this.builder.emitRaw(`execute store result score ${player} ${objective} run scoreboard players get ${value.name} ${exports2.LOWERING_OBJ}`);
5151
5421
  }
5152
5422
  return { kind: "const", value: 0 };
5153
5423
  }
@@ -5210,7 +5480,7 @@ var require_lowering = __commonJS({
5210
5480
  }
5211
5481
  if (name === "bossbar_get_value") {
5212
5482
  const dst = this.builder.freshTemp();
5213
- this.builder.emitRaw(`execute store result score ${dst} rs run bossbar get ${this.exprToString(args[0])} value`);
5483
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run bossbar get ${this.exprToString(args[0])} value`);
5214
5484
  return { kind: "var", name: dst };
5215
5485
  }
5216
5486
  if (name === "team_add") {
@@ -5244,9 +5514,54 @@ var require_lowering = __commonJS({
5244
5514
  const target = targetType === "entity" ? this.exprToTargetString(args[1]) : this.exprToString(args[1]);
5245
5515
  const path3 = this.exprToString(args[2]);
5246
5516
  const scale = args[3] ? this.exprToString(args[3]) : "1";
5247
- this.builder.emitRaw(`execute store result score ${dst} rs run data get ${targetType} ${target} ${path3} ${scale}`);
5517
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get ${targetType} ${target} ${path3} ${scale}`);
5518
+ return { kind: "var", name: dst };
5519
+ }
5520
+ if (name === "storage_get_int") {
5521
+ const storageNs = this.exprToString(args[0]);
5522
+ const arrayKey = this.exprToString(args[1]);
5523
+ const indexOperand = this.lowerExpr(args[2]);
5524
+ const dst = this.builder.freshTemp();
5525
+ if (indexOperand.kind === "const") {
5526
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[${indexOperand.value}] 1`);
5527
+ } else {
5528
+ const macroKey = `__sgi_${this.foreachCounter++}`;
5529
+ const subFnName = `${this.currentFn}/__sgi_${this.foreachCounter++}`;
5530
+ const indexVar = indexOperand.kind === "var" ? indexOperand.name : this.operandToVar(indexOperand);
5531
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${exports2.LOWERING_OBJ}`);
5532
+ this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
5533
+ this.emitRawSubFunction(subFnName, `execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage ${storageNs} ${arrayKey}[$(${macroKey})] 1`);
5534
+ }
5248
5535
  return { kind: "var", name: dst };
5249
5536
  }
5537
+ if (name === "storage_set_array") {
5538
+ const storageNs = this.exprToString(args[0]);
5539
+ const arrayKey = this.exprToString(args[1]);
5540
+ const nbtLiteral = this.exprToString(args[2]);
5541
+ this.builder.emitRaw(`data modify storage ${storageNs} ${arrayKey} set value ${nbtLiteral}`);
5542
+ return { kind: "const", value: 0 };
5543
+ }
5544
+ if (name === "storage_set_int") {
5545
+ const storageNs = this.exprToString(args[0]);
5546
+ const arrayKey = this.exprToString(args[1]);
5547
+ const indexOperand = this.lowerExpr(args[2]);
5548
+ const valueOperand = this.lowerExpr(args[3]);
5549
+ if (indexOperand.kind === "const") {
5550
+ const valVar = valueOperand.kind === "var" ? valueOperand.name : this.operandToVar(valueOperand);
5551
+ this.builder.emitRaw(`execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar} ${exports2.LOWERING_OBJ}`);
5552
+ } else {
5553
+ const macroIdxKey = `__ssi_i_${this.foreachCounter++}`;
5554
+ const macroValKey = `__ssi_v_${this.foreachCounter++}`;
5555
+ const subFnName = `${this.currentFn}/__ssi_${this.foreachCounter++}`;
5556
+ const indexVar = indexOperand.kind === "var" ? indexOperand.name : this.operandToVar(indexOperand);
5557
+ const valVar = valueOperand.kind === "var" ? valueOperand.name : this.operandToVar(valueOperand);
5558
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar} ${exports2.LOWERING_OBJ}`);
5559
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar} ${exports2.LOWERING_OBJ}`);
5560
+ this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
5561
+ this.emitRawSubFunction(subFnName, `execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar} ${exports2.LOWERING_OBJ}`);
5562
+ }
5563
+ return { kind: "const", value: 0 };
5564
+ }
5250
5565
  if (name === "data_merge") {
5251
5566
  const target = args[0];
5252
5567
  const nbt = args[1];
@@ -5279,7 +5594,7 @@ var require_lowering = __commonJS({
5279
5594
  const dst = this.builder.freshTemp();
5280
5595
  const setId = this.exprToString(args[0]);
5281
5596
  const value = this.exprToString(args[1]);
5282
- this.builder.emitRaw(`execute store result score ${dst} rs if data storage rs:sets ${setId}[{value:${value}}]`);
5597
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} if data storage rs:sets ${setId}[{value:${value}}]`);
5283
5598
  return { kind: "var", name: dst };
5284
5599
  }
5285
5600
  if (name === "set_remove") {
@@ -5543,7 +5858,7 @@ var require_lowering = __commonJS({
5543
5858
  components.push({ text: operand.value.toString() });
5544
5859
  return;
5545
5860
  }
5546
- components.push({ score: { name: this.operandToVar(operand), objective: "rs" } });
5861
+ components.push({ score: { name: this.operandToVar(operand), objective: exports2.LOWERING_OBJ } });
5547
5862
  }
5548
5863
  exprToString(expr) {
5549
5864
  switch (expr.kind) {
@@ -5696,11 +6011,15 @@ var require_lowering = __commonJS({
5696
6011
  }
5697
6012
  exprToScoreboardObjective(expr, span) {
5698
6013
  if (expr.kind === "mc_name") {
5699
- return expr.value;
6014
+ return expr.value === "rs" ? exports2.LOWERING_OBJ : expr.value;
5700
6015
  }
5701
6016
  const objective = this.exprToString(expr);
5702
6017
  if (objective.startsWith("#") || objective.includes(".")) {
5703
- return objective.startsWith("#") ? objective.slice(1) : objective;
6018
+ if (objective.startsWith("#")) {
6019
+ const name = objective.slice(1);
6020
+ return name === "rs" ? exports2.LOWERING_OBJ : name;
6021
+ }
6022
+ return objective;
5704
6023
  }
5705
6024
  return `${this.getObjectiveNamespace(span)}.${objective}`;
5706
6025
  }
@@ -5716,7 +6035,7 @@ var require_lowering = __commonJS({
5716
6035
  if (!filePath) {
5717
6036
  return this.namespace;
5718
6037
  }
5719
- return this.isStdlibFile(filePath) ? "rs" : this.namespace;
6038
+ return this.isStdlibFile(filePath) ? exports2.LOWERING_OBJ : this.namespace;
5720
6039
  }
5721
6040
  tryGetStdlibInternalObjective(playerExpr, objectiveExpr, span) {
5722
6041
  if (!span || !this.currentStdlibCallSite || objectiveExpr.kind !== "mc_name" || objectiveExpr.value !== "rs") {
@@ -5731,7 +6050,7 @@ var require_lowering = __commonJS({
5731
6050
  return null;
5732
6051
  }
5733
6052
  const hash = this.shortHash(this.serializeCallSite(this.currentStdlibCallSite));
5734
- return `rs._${resourceBase}_${hash}`;
6053
+ return `${exports2.LOWERING_OBJ}._${resourceBase}_${hash}`;
5735
6054
  }
5736
6055
  getStdlibInternalResourceBase(playerExpr) {
5737
6056
  if (!playerExpr || playerExpr.kind !== "str_lit") {
@@ -6011,15 +6330,15 @@ var require_lowering = __commonJS({
6011
6330
  readArrayElement(arrayName, index) {
6012
6331
  const dst = this.builder.freshTemp();
6013
6332
  if (index.kind === "const") {
6014
- this.builder.emitRaw(`execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[${index.value}]`);
6333
+ this.builder.emitRaw(`execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage rs:heap ${arrayName}[${index.value}]`);
6015
6334
  return { kind: "var", name: dst };
6016
6335
  }
6017
6336
  const macroKey = `__rs_index_${this.foreachCounter++}`;
6018
6337
  const subFnName = `${this.currentFn}/array_get_${this.foreachCounter++}`;
6019
6338
  const indexVar = index.kind === "var" ? index.name : this.operandToVar(index);
6020
- this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} rs`);
6339
+ this.builder.emitRaw(`execute store result storage rs:heap ${macroKey} int 1 run scoreboard players get ${indexVar} ${exports2.LOWERING_OBJ}`);
6021
6340
  this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`);
6022
- this.emitRawSubFunction(subFnName, `$execute store result score ${dst} rs run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
6341
+ this.emitRawSubFunction(subFnName, `execute store result score ${dst} ${exports2.LOWERING_OBJ} run data get storage rs:heap ${arrayName}[$(${macroKey})]`);
6023
6342
  return { kind: "var", name: dst };
6024
6343
  }
6025
6344
  emitRawSubFunction(name, ...commands) {
@@ -6228,13 +6547,20 @@ var require_commands = __commonJS({
6228
6547
  "../../dist/optimizer/commands.js"(exports2) {
6229
6548
  "use strict";
6230
6549
  Object.defineProperty(exports2, "__esModule", { value: true });
6550
+ exports2.setOptimizerObjective = setOptimizerObjective;
6231
6551
  exports2.createEmptyOptimizationStats = createEmptyOptimizationStats;
6232
6552
  exports2.mergeOptimizationStats = mergeOptimizationStats;
6233
6553
  exports2.applyLICM = applyLICM;
6234
6554
  exports2.applyCSE = applyCSE;
6235
6555
  exports2.batchSetblocks = batchSetblocks;
6236
6556
  exports2.optimizeCommandFunctions = optimizeCommandFunctions;
6237
- var SCOREBOARD_READ_RE = /^execute store result score (\$[A-Za-z0-9_]+) rs run scoreboard players get (\S+) (\S+)$/;
6557
+ var _OBJ_PATTERN = "rs";
6558
+ function setOptimizerObjective(obj) {
6559
+ _OBJ_PATTERN = obj;
6560
+ }
6561
+ function scoreboardReadRe() {
6562
+ return new RegExp(`^execute store result score (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} run scoreboard players get (\\S+) (\\S+)$`);
6563
+ }
6238
6564
  var SCOREBOARD_WRITE_RE = /^(?:scoreboard players (?:set|add|remove|reset)\s+(\S+)\s+(\S+)|scoreboard players operation\s+(\S+)\s+(\S+)\s+[+\-*/%]?= )/;
6239
6565
  var EXECUTE_STORE_SCORE_RE = /^execute store result score (\S+) (\S+) run /;
6240
6566
  var FUNCTION_CALL_RE = /^execute as (.+) run function ([^:]+):(.+)$/;
@@ -6319,7 +6645,7 @@ var require_commands = __commonJS({
6319
6645
  const readInfo = /* @__PURE__ */ new Map();
6320
6646
  const scoreboardWrites = /* @__PURE__ */ new Set();
6321
6647
  for (const inner of loopFn.commands) {
6322
- const readMatch = inner.cmd.match(SCOREBOARD_READ_RE);
6648
+ const readMatch = inner.cmd.match(scoreboardReadRe());
6323
6649
  if (readMatch) {
6324
6650
  const [, temp, player, objective] = readMatch;
6325
6651
  const key = `${player} ${objective}`;
@@ -6334,7 +6660,7 @@ var require_commands = __commonJS({
6334
6660
  for (const info of readInfo.values()) {
6335
6661
  const matches = inner.cmd.match(TEMP_RE) ?? [];
6336
6662
  const usageCount = matches.filter((name) => name === info.temp).length;
6337
- const isDef = inner.cmd.startsWith(`execute store result score ${info.temp} rs run scoreboard players get `);
6663
+ const isDef = inner.cmd.startsWith(`execute store result score ${info.temp} ${_OBJ_PATTERN} run scoreboard players get `);
6338
6664
  if (!isDef) {
6339
6665
  info.uses += usageCount;
6340
6666
  }
@@ -6356,7 +6682,7 @@ var require_commands = __commonJS({
6356
6682
  const hoistedTemps = new Set(hoistable.map((item) => item.temp));
6357
6683
  const rewrittenLoopCommands = [];
6358
6684
  for (const inner of loopFn.commands) {
6359
- const readMatch = inner.cmd.match(SCOREBOARD_READ_RE);
6685
+ const readMatch = inner.cmd.match(scoreboardReadRe());
6360
6686
  if (readMatch && hoistedTemps.has(readMatch[1])) {
6361
6687
  continue;
6362
6688
  }
@@ -6364,7 +6690,7 @@ var require_commands = __commonJS({
6364
6690
  }
6365
6691
  loopFn.commands = rewrittenLoopCommands;
6366
6692
  nextCommands.push(...hoistable.map((item) => ({
6367
- cmd: `execute store result score ${item.temp} rs run scoreboard players get ${item.player} ${item.objective}`
6693
+ cmd: `execute store result score ${item.temp} ${_OBJ_PATTERN} run scoreboard players get ${item.player} ${item.objective}`
6368
6694
  })), command);
6369
6695
  stats.licmHoists = (stats.licmHoists ?? 0) + hoistable.length;
6370
6696
  stats.licmLoopBodies = (stats.licmLoopBodies ?? 0) + 1;
@@ -6374,8 +6700,8 @@ var require_commands = __commonJS({
6374
6700
  return stats;
6375
6701
  }
6376
6702
  function extractArithmeticExpression(commands, index) {
6377
- const assign = commands[index]?.cmd.match(/^scoreboard players operation (\$[A-Za-z0-9_]+) rs = (\$[A-Za-z0-9_]+|\$const_-?\d+) rs$/) ?? commands[index]?.cmd.match(/^scoreboard players set (\$[A-Za-z0-9_]+) rs (-?\d+)$/);
6378
- const op = commands[index + 1]?.cmd.match(/^scoreboard players operation (\$[A-Za-z0-9_]+) rs ([+\-*/%]=) (\$[A-Za-z0-9_]+|\$const_-?\d+) rs$/);
6703
+ const assign = commands[index]?.cmd.match(new RegExp(`^scoreboard players operation (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} = (\\$[A-Za-z0-9_]+|\\$const_-?\\d+) ${_OBJ_PATTERN}$`)) ?? commands[index]?.cmd.match(new RegExp(`^scoreboard players set (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} (-?\\d+)$`));
6704
+ const op = commands[index + 1]?.cmd.match(new RegExp(`^scoreboard players operation (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} ([+\\-*/%]=) (\\$[A-Za-z0-9_]+|\\$const_-?\\d+) ${_OBJ_PATTERN}$`));
6379
6705
  if (!assign || !op || assign[1] !== op[1]) {
6380
6706
  return null;
6381
6707
  }
@@ -6405,14 +6731,14 @@ var require_commands = __commonJS({
6405
6731
  const rewritten = [];
6406
6732
  for (let i = 0; i < commands.length; i++) {
6407
6733
  const command = commands[i];
6408
- const readMatch = command.cmd.match(SCOREBOARD_READ_RE);
6734
+ const readMatch = command.cmd.match(scoreboardReadRe());
6409
6735
  if (readMatch) {
6410
6736
  const [, dst, player, objective] = readMatch;
6411
6737
  const key = `${player} ${objective}`;
6412
6738
  const cached = readCache.get(key);
6413
6739
  if (cached) {
6414
6740
  stats.cseRedundantReads = (stats.cseRedundantReads ?? 0) + 1;
6415
- rewritten.push({ ...command, cmd: `scoreboard players operation ${dst} rs = ${cached} rs` });
6741
+ rewritten.push({ ...command, cmd: `scoreboard players operation ${dst} ${_OBJ_PATTERN} = ${cached} ${_OBJ_PATTERN}` });
6416
6742
  } else {
6417
6743
  readCache.set(key, dst);
6418
6744
  rewritten.push(command);
@@ -6425,7 +6751,7 @@ var require_commands = __commonJS({
6425
6751
  if (expr) {
6426
6752
  const cached = exprCache.get(expr.key);
6427
6753
  if (cached) {
6428
- rewritten.push({ ...commands[i], cmd: `scoreboard players operation ${expr.dst} rs = ${cached} rs` });
6754
+ rewritten.push({ ...commands[i], cmd: `scoreboard players operation ${expr.dst} ${_OBJ_PATTERN} = ${cached} ${_OBJ_PATTERN}` });
6429
6755
  stats.cseArithmetic = (stats.cseArithmetic ?? 0) + 1;
6430
6756
  i += 1;
6431
6757
  } else {
@@ -6725,30 +7051,36 @@ var require_passes = __commonJS({
6725
7051
  return op;
6726
7052
  return copies.get(op.name) ?? op;
6727
7053
  }
7054
+ function invalidate(written) {
7055
+ copies.delete(written);
7056
+ for (const [k, v] of copies) {
7057
+ if (v.kind === "var" && v.name === written)
7058
+ copies.delete(k);
7059
+ }
7060
+ }
6728
7061
  const newInstrs = [];
6729
7062
  for (const instr of block.instrs) {
6730
7063
  switch (instr.op) {
6731
7064
  case "assign": {
6732
7065
  const src = resolve(instr.src);
7066
+ invalidate(instr.dst);
6733
7067
  if (src.kind === "var" || src.kind === "const") {
6734
7068
  copies.set(instr.dst, src);
6735
- } else {
6736
- copies.delete(instr.dst);
6737
7069
  }
6738
7070
  newInstrs.push({ ...instr, src });
6739
7071
  break;
6740
7072
  }
6741
7073
  case "binop":
6742
- copies.delete(instr.dst);
7074
+ invalidate(instr.dst);
6743
7075
  newInstrs.push({ ...instr, lhs: resolve(instr.lhs), rhs: resolve(instr.rhs) });
6744
7076
  break;
6745
7077
  case "cmp":
6746
- copies.delete(instr.dst);
7078
+ invalidate(instr.dst);
6747
7079
  newInstrs.push({ ...instr, lhs: resolve(instr.lhs), rhs: resolve(instr.rhs) });
6748
7080
  break;
6749
7081
  case "call":
6750
7082
  if (instr.dst)
6751
- copies.delete(instr.dst);
7083
+ invalidate(instr.dst);
6752
7084
  newInstrs.push({ ...instr, args: instr.args.map(resolve) });
6753
7085
  break;
6754
7086
  default:
@@ -6850,1168 +7182,1439 @@ var require_passes = __commonJS({
6850
7182
  }
6851
7183
  });
6852
7184
 
6853
- // ../../dist/optimizer/dce.js
6854
- var require_dce = __commonJS({
6855
- "../../dist/optimizer/dce.js"(exports2) {
7185
+ // ../../dist/codegen/var-allocator.js
7186
+ var require_var_allocator = __commonJS({
7187
+ "../../dist/codegen/var-allocator.js"(exports2) {
6856
7188
  "use strict";
6857
7189
  Object.defineProperty(exports2, "__esModule", { value: true });
6858
- exports2.DeadCodeEliminator = void 0;
6859
- exports2.eliminateDeadCode = eliminateDeadCode;
6860
- function copySpan(target, source) {
6861
- const descriptor = Object.getOwnPropertyDescriptor(source, "span");
6862
- if (descriptor) {
6863
- Object.defineProperty(target, "span", descriptor);
7190
+ exports2.VarAllocator = void 0;
7191
+ var VarAllocator = class {
7192
+ constructor(mangle = true) {
7193
+ this.seq = 0;
7194
+ this.varCache = /* @__PURE__ */ new Map();
7195
+ this.constCache = /* @__PURE__ */ new Map();
7196
+ this.internalCache = /* @__PURE__ */ new Map();
7197
+ this.mangle = mangle;
7198
+ }
7199
+ /** Allocate a name for a user variable. Strips leading '$' if present. */
7200
+ alloc(originalName) {
7201
+ const clean = originalName.startsWith("$") ? originalName.slice(1) : originalName;
7202
+ const cached = this.varCache.get(clean);
7203
+ if (cached)
7204
+ return cached;
7205
+ const name = this.mangle ? `$${this.nextSeqName()}` : `$${clean}`;
7206
+ this.varCache.set(clean, name);
7207
+ return name;
6864
7208
  }
6865
- return target;
6866
- }
6867
- function isConstantBoolean(expr) {
6868
- if (expr.kind === "bool_lit") {
6869
- return expr.value;
7209
+ /** Allocate a name for a constant value (content-addressed). */
7210
+ constant(value) {
7211
+ const cached = this.constCache.get(value);
7212
+ if (cached)
7213
+ return cached;
7214
+ const name = this.mangle ? `$${this.nextSeqName()}` : `$const_${value}`;
7215
+ this.constCache.set(value, name);
7216
+ return name;
6870
7217
  }
6871
- return null;
6872
- }
6873
- function isPureExpr(expr) {
6874
- switch (expr.kind) {
6875
- case "int_lit":
6876
- case "float_lit":
6877
- case "byte_lit":
6878
- case "short_lit":
6879
- case "long_lit":
6880
- case "double_lit":
6881
- case "rel_coord":
6882
- case "local_coord":
6883
- case "bool_lit":
6884
- case "str_lit":
6885
- case "mc_name":
6886
- case "range_lit":
6887
- case "selector":
6888
- case "ident":
6889
- case "blockpos":
6890
- return true;
6891
- case "str_interp":
6892
- return expr.parts.every((part) => typeof part === "string" || isPureExpr(part));
6893
- case "f_string":
6894
- return expr.parts.every((part) => part.kind === "text" || isPureExpr(part.expr));
6895
- case "binary":
6896
- return isPureExpr(expr.left) && isPureExpr(expr.right);
6897
- case "is_check":
6898
- return isPureExpr(expr.expr);
6899
- case "unary":
6900
- return isPureExpr(expr.operand);
6901
- case "member":
6902
- return isPureExpr(expr.obj);
6903
- case "index":
6904
- return isPureExpr(expr.obj) && isPureExpr(expr.index);
6905
- case "array_lit":
6906
- return expr.elements.every(isPureExpr);
6907
- case "struct_lit":
6908
- return expr.fields.every((field) => isPureExpr(field.value));
6909
- case "lambda":
6910
- return true;
6911
- case "assign":
6912
- case "member_assign":
6913
- case "call":
6914
- case "invoke":
6915
- case "static_call":
6916
- return false;
7218
+ /**
7219
+ * Look up the allocated name for a raw scoreboard fake-player name such as
7220
+ * "$_2", "$x", "$p0", or "$ret". Returns the mangled name when mangle=true,
7221
+ * or the original name when mangle=false or the name is not yet known.
7222
+ *
7223
+ * Unlike alloc/internal/constant this does NOT create a new slot — it only
7224
+ * resolves names that were already registered. Used by the codegen to
7225
+ * rewrite variable references inside `raw` IR instructions.
7226
+ */
7227
+ resolve(rawName) {
7228
+ const clean = rawName.startsWith("$") ? rawName.slice(1) : rawName;
7229
+ return this.varCache.get(clean) ?? this.internalCache.get(clean) ?? rawName;
6917
7230
  }
6918
- }
6919
- var DeadCodeEliminator = class {
6920
- constructor() {
6921
- this.functionMap = /* @__PURE__ */ new Map();
6922
- this.reachableFunctions = /* @__PURE__ */ new Set();
6923
- this.usedConstants = /* @__PURE__ */ new Set();
6924
- this.localReads = /* @__PURE__ */ new Set();
6925
- this.localDeclIds = /* @__PURE__ */ new WeakMap();
6926
- this.localIdCounter = 0;
6927
- this.warnings = [];
7231
+ /**
7232
+ * Rewrite all $varname tokens in a raw mcfunction command string so that
7233
+ * IR variable names are replaced by their allocated (possibly mangled) names.
7234
+ * Tokens that are not registered in the allocator are left untouched (they
7235
+ * are literal scoreboard fake-player names like "out" or "#rs").
7236
+ */
7237
+ resolveRaw(cmd) {
7238
+ return cmd.replace(/\$[A-Za-z_][A-Za-z0-9_]*/g, (tok) => this.resolve(tok));
6928
7239
  }
6929
- eliminate(program) {
6930
- this.functionMap.clear();
6931
- this.reachableFunctions.clear();
6932
- this.usedConstants.clear();
6933
- this.localReads.clear();
6934
- this.localIdCounter = 0;
6935
- this.warnings.length = 0;
6936
- for (const fn of program.declarations) {
6937
- this.functionMap.set(fn.name, fn);
7240
+ /** Allocate a name for a compiler internal (e.g. "ret", "p0"). */
7241
+ internal(suffix) {
7242
+ const cached = this.internalCache.get(suffix);
7243
+ if (cached)
7244
+ return cached;
7245
+ const name = this.mangle ? `$${this.nextSeqName()}` : `$${suffix}`;
7246
+ this.internalCache.set(suffix, name);
7247
+ return name;
7248
+ }
7249
+ /** Generate the next sequential name: a, b, ..., z, aa, ab, ..., az, ba, ... */
7250
+ nextSeqName() {
7251
+ const n = this.seq++;
7252
+ let result = "";
7253
+ let remaining = n;
7254
+ do {
7255
+ result = String.fromCharCode(97 + remaining % 26) + result;
7256
+ remaining = Math.floor(remaining / 26) - 1;
7257
+ } while (remaining >= 0);
7258
+ return result;
7259
+ }
7260
+ /**
7261
+ * Returns a sourcemap object mapping allocated name → original name.
7262
+ * Useful for debugging: write to <output>.map.json alongside the datapack.
7263
+ * Only meaningful when mangle=true.
7264
+ */
7265
+ toSourceMap() {
7266
+ const map = {};
7267
+ for (const [orig, alloc] of this.varCache) {
7268
+ if (/^_\d+$/.test(orig))
7269
+ continue;
7270
+ map[alloc] = orig;
6938
7271
  }
6939
- const entryPoints = this.findEntryPoints(program);
6940
- if (entryPoints.length === 0) {
6941
- for (const fn of program.declarations) {
6942
- this.markReachable(fn.name);
7272
+ for (const [val, alloc] of this.constCache)
7273
+ map[alloc] = `const:${val}`;
7274
+ for (const [suf, alloc] of this.internalCache)
7275
+ map[alloc] = `internal:${suf}`;
7276
+ return map;
7277
+ }
7278
+ };
7279
+ exports2.VarAllocator = VarAllocator;
7280
+ }
7281
+ });
7282
+
7283
+ // ../../dist/codegen/mcfunction/index.js
7284
+ var require_mcfunction = __commonJS({
7285
+ "../../dist/codegen/mcfunction/index.js"(exports2) {
7286
+ "use strict";
7287
+ Object.defineProperty(exports2, "__esModule", { value: true });
7288
+ exports2.countMcfunctionCommands = countMcfunctionCommands;
7289
+ exports2.generateDatapackWithStats = generateDatapackWithStats;
7290
+ exports2.generateDatapack = generateDatapack;
7291
+ var commands_1 = require_commands();
7292
+ var types_1 = require_types();
7293
+ var var_allocator_1 = require_var_allocator();
7294
+ var OBJ = "rs";
7295
+ function operandToScore(op, alloc) {
7296
+ if (op.kind === "var")
7297
+ return `${alloc.alloc(op.name)} ${OBJ}`;
7298
+ if (op.kind === "const")
7299
+ return `${alloc.constant(op.value)} ${OBJ}`;
7300
+ if (op.kind === "param")
7301
+ return `${alloc.internal(`p${op.index}`)} ${OBJ}`;
7302
+ throw new Error(`Cannot convert storage operand to score: ${op.path}`);
7303
+ }
7304
+ function collectConsts(fn) {
7305
+ const consts = /* @__PURE__ */ new Set();
7306
+ for (const block of fn.blocks) {
7307
+ for (const instr of block.instrs) {
7308
+ if (instr.op === "assign" && instr.src.kind === "const")
7309
+ consts.add(instr.src.value);
7310
+ if (instr.op === "binop") {
7311
+ if (instr.lhs.kind === "const")
7312
+ consts.add(instr.lhs.value);
7313
+ if (instr.rhs.kind === "const")
7314
+ consts.add(instr.rhs.value);
6943
7315
  }
6944
- } else {
6945
- for (const fnName of entryPoints) {
6946
- this.markReachable(fnName);
7316
+ if (instr.op === "cmp") {
7317
+ if (instr.lhs.kind === "const")
7318
+ consts.add(instr.lhs.value);
7319
+ if (instr.rhs.kind === "const")
7320
+ consts.add(instr.rhs.value);
6947
7321
  }
6948
7322
  }
6949
- for (const global of program.globals) {
6950
- this.collectExprRefs(global.init, []);
6951
- }
6952
- for (const implBlock of program.implBlocks) {
6953
- for (const method of implBlock.methods) {
6954
- this.collectFunctionRefs(method);
7323
+ const t = block.term;
7324
+ if (t.op === "return" && t.value?.kind === "const")
7325
+ consts.add(t.value.value);
7326
+ }
7327
+ return consts;
7328
+ }
7329
+ var BOP_OP = {
7330
+ "+": "+=",
7331
+ "-": "-=",
7332
+ "*": "*=",
7333
+ "/": "/=",
7334
+ "%": "%="
7335
+ };
7336
+ function emitInstr(instr, ns, alloc) {
7337
+ const lines = [];
7338
+ switch (instr.op) {
7339
+ case "assign": {
7340
+ const dst = alloc.alloc(instr.dst);
7341
+ const src = instr.src;
7342
+ if (src.kind === "const") {
7343
+ lines.push(`scoreboard players set ${dst} ${OBJ} ${src.value}`);
7344
+ } else if (src.kind === "var") {
7345
+ lines.push(`scoreboard players operation ${dst} ${OBJ} = ${alloc.alloc(src.name)} ${OBJ}`);
7346
+ } else if (src.kind === "param") {
7347
+ lines.push(`scoreboard players operation ${dst} ${OBJ} = ${alloc.internal(`p${src.index}`)} ${OBJ}`);
7348
+ } else {
7349
+ lines.push(`execute store result score ${dst} ${OBJ} run data get storage ${src.path}`);
6955
7350
  }
7351
+ break;
7352
+ }
7353
+ case "binop": {
7354
+ const dst = alloc.alloc(instr.dst);
7355
+ const bop = BOP_OP[instr.bop] ?? "+=";
7356
+ lines.push(...emitInstr({ op: "assign", dst: instr.dst, src: instr.lhs }, ns, alloc));
7357
+ lines.push(`scoreboard players operation ${dst} ${OBJ} ${bop} ${operandToScore(instr.rhs, alloc)}`);
7358
+ break;
7359
+ }
7360
+ case "cmp": {
7361
+ const dst = alloc.alloc(instr.dst);
7362
+ const lhsScore = operandToScore(instr.lhs, alloc);
7363
+ const rhsScore = operandToScore(instr.rhs, alloc);
7364
+ lines.push(`scoreboard players set ${dst} ${OBJ} 0`);
7365
+ switch (instr.cop) {
7366
+ case "==":
7367
+ lines.push(`execute if score ${lhsScore} = ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7368
+ break;
7369
+ case "!=":
7370
+ lines.push(`execute unless score ${lhsScore} = ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7371
+ break;
7372
+ case "<":
7373
+ lines.push(`execute if score ${lhsScore} < ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7374
+ break;
7375
+ case "<=":
7376
+ lines.push(`execute if score ${lhsScore} <= ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7377
+ break;
7378
+ case ">":
7379
+ lines.push(`execute if score ${lhsScore} > ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7380
+ break;
7381
+ case ">=":
7382
+ lines.push(`execute if score ${lhsScore} >= ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7383
+ break;
7384
+ }
7385
+ break;
7386
+ }
7387
+ case "call": {
7388
+ for (let i = 0; i < instr.args.length; i++) {
7389
+ const paramSlot = alloc.internal(`p${i}`);
7390
+ const arg = instr.args[i];
7391
+ if (arg.kind === "const") {
7392
+ lines.push(`scoreboard players set ${paramSlot} ${OBJ} ${arg.value}`);
7393
+ } else if (arg.kind === "var") {
7394
+ lines.push(`scoreboard players operation ${paramSlot} ${OBJ} = ${alloc.alloc(arg.name)} ${OBJ}`);
7395
+ } else if (arg.kind === "param") {
7396
+ lines.push(`scoreboard players operation ${paramSlot} ${OBJ} = ${alloc.internal(`p${arg.index}`)} ${OBJ}`);
7397
+ }
7398
+ }
7399
+ lines.push(`function ${ns}:${instr.fn}`);
7400
+ if (instr.dst) {
7401
+ const retSlot = alloc.internal("ret");
7402
+ lines.push(`scoreboard players operation ${alloc.alloc(instr.dst)} ${OBJ} = ${retSlot} ${OBJ}`);
7403
+ }
7404
+ break;
7405
+ }
7406
+ case "raw": {
7407
+ const rawResolved = alloc.resolveRaw(instr.cmd).replace(/^\x01/, "$");
7408
+ lines.push(rawResolved);
7409
+ break;
6956
7410
  }
6957
- return {
6958
- ...program,
6959
- declarations: program.declarations.filter((fn) => this.reachableFunctions.has(fn.name)).map((fn) => this.transformFunction(fn)),
6960
- consts: program.consts.filter((constDecl) => this.usedConstants.has(constDecl.name)),
6961
- implBlocks: program.implBlocks.map((implBlock) => ({
6962
- ...implBlock,
6963
- methods: implBlock.methods.map((method) => this.transformFunction(method))
6964
- }))
6965
- };
6966
7411
  }
6967
- findEntryPoints(program) {
6968
- const entries = /* @__PURE__ */ new Set();
6969
- for (const fn of program.declarations) {
6970
- if (!fn.name.startsWith("_")) {
6971
- entries.add(fn.name);
7412
+ return lines;
7413
+ }
7414
+ function emitTerm(term, ns, fnName, alloc) {
7415
+ const lines = [];
7416
+ switch (term.op) {
7417
+ case "jump":
7418
+ lines.push(`function ${ns}:${fnName}/${term.target}`);
7419
+ break;
7420
+ case "jump_if":
7421
+ lines.push(`execute if score ${alloc.alloc(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.then}`);
7422
+ lines.push(`execute if score ${alloc.alloc(term.cond)} ${OBJ} matches ..0 run function ${ns}:${fnName}/${term.else_}`);
7423
+ break;
7424
+ case "jump_unless":
7425
+ lines.push(`execute if score ${alloc.alloc(term.cond)} ${OBJ} matches ..0 run function ${ns}:${fnName}/${term.then}`);
7426
+ lines.push(`execute if score ${alloc.alloc(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.else_}`);
7427
+ break;
7428
+ case "return": {
7429
+ const retSlot = alloc.internal("ret");
7430
+ if (term.value) {
7431
+ if (term.value.kind === "const") {
7432
+ lines.push(`scoreboard players set ${retSlot} ${OBJ} ${term.value.value}`);
7433
+ } else if (term.value.kind === "var") {
7434
+ lines.push(`scoreboard players operation ${retSlot} ${OBJ} = ${alloc.alloc(term.value.name)} ${OBJ}`);
7435
+ } else if (term.value.kind === "param") {
7436
+ lines.push(`scoreboard players operation ${retSlot} ${OBJ} = ${alloc.internal(`p${term.value.index}`)} ${OBJ}`);
7437
+ }
6972
7438
  }
6973
- if (fn.decorators.some((decorator) => [
6974
- "tick",
6975
- "load",
6976
- "on",
6977
- "on_trigger",
6978
- "on_advancement",
6979
- "on_craft",
6980
- "on_death",
6981
- "on_login",
6982
- "on_join_team",
6983
- "keep"
6984
- ].includes(decorator.name))) {
6985
- entries.add(fn.name);
7439
+ if (term.value?.kind === "const") {
7440
+ lines.push(`return ${term.value.value}`);
7441
+ } else if (term.value?.kind === "var") {
7442
+ lines.push(`return run scoreboard players get ${alloc.alloc(term.value.name)} ${OBJ}`);
7443
+ } else if (term.value?.kind === "param") {
7444
+ lines.push(`return run scoreboard players get ${alloc.internal(`p${term.value.index}`)} ${OBJ}`);
6986
7445
  }
7446
+ break;
6987
7447
  }
6988
- return [...entries];
7448
+ case "tick_yield":
7449
+ lines.push(`schedule function ${ns}:${fnName}/${term.continuation} 1t replace`);
7450
+ break;
6989
7451
  }
6990
- markReachable(fnName) {
6991
- if (this.reachableFunctions.has(fnName)) {
6992
- return;
7452
+ return lines;
7453
+ }
7454
+ function toFunctionName(file) {
7455
+ const match = file.path.match(/^data\/[^/]+\/function\/(.+)\.mcfunction$/);
7456
+ return match?.[1] ?? null;
7457
+ }
7458
+ function applyFunctionOptimization(files) {
7459
+ const functionFiles = files.map((file) => {
7460
+ const functionName = toFunctionName(file);
7461
+ if (!functionName)
7462
+ return null;
7463
+ const commands = file.content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).map((cmd) => ({ cmd }));
7464
+ return { file, functionName, commands };
7465
+ }).filter((entry) => entry !== null);
7466
+ const optimized = (0, commands_1.optimizeCommandFunctions)(functionFiles.map((entry) => ({
7467
+ name: entry.functionName,
7468
+ commands: entry.commands
7469
+ })));
7470
+ const commandMap = new Map(optimized.functions.map((fn) => [fn.name, fn.commands]));
7471
+ const optimizedNames = new Set(optimized.functions.map((fn) => fn.name));
7472
+ return {
7473
+ files: files.filter((file) => {
7474
+ const functionName = toFunctionName(file);
7475
+ return !functionName || optimizedNames.has(functionName);
7476
+ }).map((file) => {
7477
+ const functionName = toFunctionName(file);
7478
+ if (!functionName)
7479
+ return file;
7480
+ const commands = commandMap.get(functionName);
7481
+ if (!commands)
7482
+ return file;
7483
+ const lines = file.content.split("\n");
7484
+ const header = lines.filter((line) => line.trim().startsWith("#"));
7485
+ return {
7486
+ ...file,
7487
+ content: [...header, ...commands.map((command) => command.cmd)].join("\n")
7488
+ };
7489
+ }),
7490
+ stats: optimized.stats
7491
+ };
7492
+ }
7493
+ function countMcfunctionCommands(files) {
7494
+ return files.reduce((sum, file) => {
7495
+ if (!toFunctionName(file)) {
7496
+ return sum;
6993
7497
  }
6994
- const fn = this.functionMap.get(fnName);
6995
- if (!fn) {
6996
- return;
7498
+ return sum + file.content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).length;
7499
+ }, 0);
7500
+ }
7501
+ function preAllocInstr(instr, alloc) {
7502
+ switch (instr.op) {
7503
+ case "assign":
7504
+ alloc.alloc(instr.dst);
7505
+ if (instr.src.kind === "var")
7506
+ alloc.alloc(instr.src.name);
7507
+ break;
7508
+ case "binop":
7509
+ alloc.alloc(instr.dst);
7510
+ if (instr.lhs.kind === "var")
7511
+ alloc.alloc(instr.lhs.name);
7512
+ if (instr.rhs.kind === "var")
7513
+ alloc.alloc(instr.rhs.name);
7514
+ break;
7515
+ case "cmp":
7516
+ alloc.alloc(instr.dst);
7517
+ if (instr.lhs.kind === "var")
7518
+ alloc.alloc(instr.lhs.name);
7519
+ if (instr.rhs.kind === "var")
7520
+ alloc.alloc(instr.rhs.name);
7521
+ break;
7522
+ case "call":
7523
+ for (const arg of instr.args) {
7524
+ if (arg.kind === "var")
7525
+ alloc.alloc(arg.name);
7526
+ }
7527
+ if (instr.dst)
7528
+ alloc.alloc(instr.dst);
7529
+ break;
7530
+ case "raw":
7531
+ ;
7532
+ instr.cmd.replace(/\$[A-Za-z_][A-Za-z0-9_]*/g, (tok) => {
7533
+ alloc.alloc(tok);
7534
+ return tok;
7535
+ });
7536
+ break;
7537
+ }
7538
+ }
7539
+ function preAllocTerm(term, alloc) {
7540
+ switch (term.op) {
7541
+ case "jump_if":
7542
+ case "jump_unless":
7543
+ alloc.alloc(term.cond);
7544
+ break;
7545
+ case "return":
7546
+ if (term.value?.kind === "var")
7547
+ alloc.alloc(term.value.name);
7548
+ break;
7549
+ }
7550
+ }
7551
+ function generateDatapackWithStats(module3, options = {}) {
7552
+ const { optimizeCommands = true, mangle = false, scoreboardObjective = "rs" } = options;
7553
+ OBJ = scoreboardObjective;
7554
+ (0, commands_1.setOptimizerObjective)(scoreboardObjective);
7555
+ const alloc = new var_allocator_1.VarAllocator(mangle);
7556
+ const files = [];
7557
+ const advancements = [];
7558
+ const ns = module3.namespace;
7559
+ const triggerHandlers = module3.functions.filter((fn) => fn.isTriggerHandler && fn.triggerName);
7560
+ const triggerNames = new Set(triggerHandlers.map((fn) => fn.triggerName));
7561
+ const eventHandlers = module3.functions.filter((fn) => !!fn.eventHandler && (0, types_1.isEventTypeName)(fn.eventHandler.eventType));
7562
+ const eventTypes = new Set(eventHandlers.map((fn) => fn.eventHandler.eventType));
7563
+ const tickFunctionNames = [];
7564
+ for (const fn of module3.functions) {
7565
+ if (fn.isTickLoop) {
7566
+ tickFunctionNames.push(fn.name);
6997
7567
  }
6998
- this.reachableFunctions.add(fnName);
6999
- this.collectFunctionRefs(fn);
7000
7568
  }
7001
- collectFunctionRefs(fn) {
7002
- const scope = [fn.params.map((param) => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
7003
- for (const param of fn.params) {
7004
- if (param.default) {
7005
- this.collectExprRefs(param.default, scope);
7006
- }
7569
+ files.push({
7570
+ path: "pack.mcmeta",
7571
+ content: JSON.stringify({
7572
+ pack: { pack_format: 26, description: `${ns} datapack \u2014 compiled by redscript` }
7573
+ }, null, 2)
7574
+ });
7575
+ const loadLines = [
7576
+ `# RedScript runtime init`,
7577
+ `scoreboard objectives add ${OBJ} dummy`
7578
+ ];
7579
+ for (const g of module3.globals) {
7580
+ loadLines.push(`scoreboard players set ${alloc.alloc(g.name)} ${OBJ} ${g.init}`);
7581
+ }
7582
+ for (const triggerName of triggerNames) {
7583
+ loadLines.push(`scoreboard objectives add ${triggerName} trigger`);
7584
+ loadLines.push(`scoreboard players enable @a ${triggerName}`);
7585
+ }
7586
+ for (const eventType of eventTypes) {
7587
+ const detection = types_1.EVENT_TYPES[eventType].detection;
7588
+ if (eventType === "PlayerDeath") {
7589
+ loadLines.push(`scoreboard objectives add ${OBJ}.deaths deathCount`);
7590
+ } else if (eventType === "EntityKill") {
7591
+ loadLines.push(`scoreboard objectives add ${OBJ}.kills totalKillCount`);
7592
+ } else if (eventType === "ItemUse") {
7593
+ loadLines.push("# ItemUse detection requires a project-specific objective/tag setup");
7594
+ } else if (detection === "tag" || detection === "advancement") {
7595
+ loadLines.push(`# ${eventType} detection expects tag ${types_1.EVENT_TYPES[eventType].tag} to be set externally`);
7007
7596
  }
7008
- this.collectStmtRefs(fn.body, scope);
7009
7597
  }
7010
- collectStmtRefs(block, scope) {
7011
- scope.push([]);
7012
- for (const stmt of block) {
7013
- this.collectStmtRef(stmt, scope);
7598
+ for (const triggerName of triggerNames) {
7599
+ const handlers = triggerHandlers.filter((fn) => fn.triggerName === triggerName);
7600
+ const dispatchLines = [
7601
+ `# Trigger dispatch for ${triggerName}`
7602
+ ];
7603
+ for (const handler of handlers) {
7604
+ dispatchLines.push(`function ${ns}:${handler.name}`);
7014
7605
  }
7015
- scope.pop();
7606
+ dispatchLines.push(`scoreboard players set @s ${triggerName} 0`);
7607
+ dispatchLines.push(`scoreboard players enable @s ${triggerName}`);
7608
+ files.push({
7609
+ path: `data/${ns}/function/__trigger_${triggerName}_dispatch.mcfunction`,
7610
+ content: dispatchLines.join("\n")
7611
+ });
7016
7612
  }
7017
- collectStmtRef(stmt, scope) {
7018
- switch (stmt.kind) {
7019
- case "let": {
7020
- this.collectExprRefs(stmt.init, scope);
7021
- const id = `local:${stmt.name}:${this.localIdCounter++}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`;
7022
- this.localDeclIds.set(stmt, id);
7023
- scope[scope.length - 1].push({ id, name: stmt.name });
7024
- break;
7025
- }
7026
- case "expr":
7027
- this.collectExprRefs(stmt.expr, scope);
7028
- break;
7029
- case "return":
7030
- if (stmt.value) {
7031
- this.collectExprRefs(stmt.value, scope);
7032
- }
7033
- break;
7034
- case "if": {
7035
- this.collectExprRefs(stmt.cond, scope);
7036
- const constant = isConstantBoolean(stmt.cond);
7037
- if (constant === true) {
7038
- this.collectStmtRefs(stmt.then, scope);
7039
- } else if (constant === false) {
7040
- if (stmt.else_) {
7041
- this.collectStmtRefs(stmt.else_, scope);
7042
- }
7043
- } else {
7044
- this.collectStmtRefs(stmt.then, scope);
7045
- if (stmt.else_) {
7046
- this.collectStmtRefs(stmt.else_, scope);
7047
- }
7613
+ const allConsts = /* @__PURE__ */ new Set();
7614
+ for (const fn of module3.functions) {
7615
+ for (const c of collectConsts(fn))
7616
+ allConsts.add(c);
7617
+ }
7618
+ if (allConsts.size > 0) {
7619
+ loadLines.push(...Array.from(allConsts).sort((a, b) => a - b).map((value) => `scoreboard players set ${alloc.constant(value)} ${OBJ} ${value}`));
7620
+ }
7621
+ if (mangle) {
7622
+ for (const fn of module3.functions) {
7623
+ for (let i = 0; i < fn.params.length; i++)
7624
+ alloc.internal(`p${i}`);
7625
+ alloc.internal("ret");
7626
+ for (const block of fn.blocks) {
7627
+ for (const instr of block.instrs) {
7628
+ preAllocInstr(instr, alloc);
7048
7629
  }
7049
- break;
7630
+ preAllocTerm(block.term, alloc);
7050
7631
  }
7051
- case "while":
7052
- this.collectExprRefs(stmt.cond, scope);
7053
- this.collectStmtRefs(stmt.body, scope);
7054
- break;
7055
- case "for":
7056
- scope.push([]);
7057
- if (stmt.init) {
7058
- this.collectStmtRef(stmt.init, scope);
7059
- }
7060
- this.collectExprRefs(stmt.cond, scope);
7061
- this.collectExprRefs(stmt.step, scope);
7062
- this.collectStmtRefs(stmt.body, scope);
7063
- scope.pop();
7064
- break;
7065
- case "foreach":
7066
- this.collectExprRefs(stmt.iterable, scope);
7067
- scope.push([{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]);
7068
- this.collectStmtRefs(stmt.body, scope);
7069
- scope.pop();
7070
- break;
7071
- case "for_range":
7072
- this.collectExprRefs(stmt.start, scope);
7073
- this.collectExprRefs(stmt.end, scope);
7074
- scope.push([{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]);
7075
- this.collectStmtRefs(stmt.body, scope);
7076
- scope.pop();
7077
- break;
7078
- case "match":
7079
- this.collectExprRefs(stmt.expr, scope);
7080
- for (const arm of stmt.arms) {
7081
- if (arm.pattern) {
7082
- this.collectExprRefs(arm.pattern, scope);
7083
- }
7084
- this.collectStmtRefs(arm.body, scope);
7085
- }
7086
- break;
7087
- case "as_block":
7088
- case "at_block":
7089
- case "as_at":
7090
- case "execute":
7091
- this.collectNestedStmtRefs(stmt, scope);
7092
- break;
7093
- case "raw":
7094
- case "break":
7095
- case "continue":
7096
- break;
7097
7632
  }
7098
7633
  }
7099
- collectNestedStmtRefs(stmt, scope) {
7100
- if (stmt.kind === "execute") {
7101
- for (const sub of stmt.subcommands) {
7102
- if ("varName" in sub && sub.varName) {
7103
- const resolved = this.resolveLocal(sub.varName, scope);
7104
- if (resolved) {
7105
- this.localReads.add(resolved.id);
7106
- }
7107
- }
7634
+ for (const fn of module3.functions) {
7635
+ for (let i = 0; i < fn.blocks.length; i++) {
7636
+ const block = fn.blocks[i];
7637
+ const lines = [`# block: ${block.label}`];
7638
+ for (const instr of block.instrs) {
7639
+ lines.push(...emitInstr(instr, ns, alloc));
7108
7640
  }
7641
+ lines.push(...emitTerm(block.term, ns, fn.name, alloc));
7642
+ const filePath = i === 0 ? `data/${ns}/function/${fn.name}.mcfunction` : `data/${ns}/function/${fn.name}/${block.label}.mcfunction`;
7643
+ const hasRealContent = lines.some((l) => !l.startsWith("#") && l.trim() !== "");
7644
+ if (i !== 0 && !hasRealContent)
7645
+ continue;
7646
+ files.push({ path: filePath, content: lines.join("\n") });
7109
7647
  }
7110
- this.collectStmtRefs(stmt.body, scope);
7111
7648
  }
7112
- collectExprRefs(expr, scope) {
7113
- switch (expr.kind) {
7114
- case "ident": {
7115
- const resolved = this.resolveLocal(expr.name, scope);
7116
- if (resolved) {
7117
- this.localReads.add(resolved.id);
7118
- } else {
7119
- this.usedConstants.add(expr.name);
7120
- }
7121
- break;
7649
+ const loadCalls = /* @__PURE__ */ new Set();
7650
+ for (const fn of module3.functions) {
7651
+ if (fn.isLoadInit) {
7652
+ loadCalls.add(fn.name);
7653
+ }
7654
+ for (const dep of fn.requiredLoads ?? []) {
7655
+ loadCalls.add(dep);
7656
+ }
7657
+ }
7658
+ for (const name of loadCalls) {
7659
+ loadLines.push(`function ${ns}:${name}`);
7660
+ }
7661
+ files.push({
7662
+ path: `data/${ns}/function/__load.mcfunction`,
7663
+ content: loadLines.join("\n")
7664
+ });
7665
+ files.push({
7666
+ path: `data/minecraft/tags/function/load.json`,
7667
+ content: JSON.stringify({ values: [`${ns}:__load`] }, null, 2)
7668
+ });
7669
+ const tickLines = ["# RedScript tick dispatcher"];
7670
+ for (const fnName of tickFunctionNames) {
7671
+ tickLines.push(`function ${ns}:${fnName}`);
7672
+ }
7673
+ if (triggerNames.size > 0) {
7674
+ tickLines.push(`# Trigger checks`);
7675
+ for (const triggerName of triggerNames) {
7676
+ tickLines.push(`execute as @a[scores={${triggerName}=1..}] run function ${ns}:__trigger_${triggerName}_dispatch`);
7677
+ }
7678
+ }
7679
+ if (eventHandlers.length > 0) {
7680
+ tickLines.push("# Event checks");
7681
+ for (const eventType of eventTypes) {
7682
+ const tag = types_1.EVENT_TYPES[eventType].tag;
7683
+ const handlers = eventHandlers.filter((fn) => fn.eventHandler?.eventType === eventType);
7684
+ for (const handler of handlers) {
7685
+ tickLines.push(`execute as @a[tag=${tag}] run function ${ns}:${handler.name}`);
7122
7686
  }
7123
- case "call":
7124
- {
7125
- const resolved = this.resolveLocal(expr.fn, scope);
7126
- if (resolved) {
7127
- this.localReads.add(resolved.id);
7128
- } else if (this.functionMap.has(expr.fn)) {
7129
- this.markReachable(expr.fn);
7687
+ tickLines.push(`tag @a[tag=${tag}] remove ${tag}`);
7688
+ }
7689
+ }
7690
+ if (tickFunctionNames.length > 0 || triggerNames.size > 0 || eventHandlers.length > 0) {
7691
+ files.push({
7692
+ path: `data/${ns}/function/__tick.mcfunction`,
7693
+ content: tickLines.join("\n")
7694
+ });
7695
+ files.push({
7696
+ path: `data/minecraft/tags/function/tick.json`,
7697
+ content: JSON.stringify({ values: [`${ns}:__tick`] }, null, 2)
7698
+ });
7699
+ }
7700
+ for (const fn of module3.functions) {
7701
+ const eventTrigger = fn.eventTrigger;
7702
+ if (!eventTrigger) {
7703
+ continue;
7704
+ }
7705
+ let path2 = "";
7706
+ let criteria = {};
7707
+ switch (eventTrigger.kind) {
7708
+ case "advancement":
7709
+ path2 = `data/${ns}/advancements/on_advancement_${fn.name}.json`;
7710
+ criteria = {
7711
+ trigger: {
7712
+ trigger: `minecraft:${eventTrigger.value}`
7130
7713
  }
7131
- }
7132
- for (const arg of expr.args) {
7133
- this.collectExprRefs(arg, scope);
7134
- }
7135
- break;
7136
- case "static_call":
7137
- for (const arg of expr.args) {
7138
- this.collectExprRefs(arg, scope);
7139
- }
7140
- break;
7141
- case "invoke":
7142
- this.collectExprRefs(expr.callee, scope);
7143
- for (const arg of expr.args) {
7144
- this.collectExprRefs(arg, scope);
7145
- }
7146
- break;
7147
- case "member":
7148
- this.collectExprRefs(expr.obj, scope);
7149
- break;
7150
- case "member_assign":
7151
- this.collectExprRefs(expr.obj, scope);
7152
- this.collectExprRefs(expr.value, scope);
7153
- break;
7154
- case "index":
7155
- this.collectExprRefs(expr.obj, scope);
7156
- this.collectExprRefs(expr.index, scope);
7157
- break;
7158
- case "array_lit":
7159
- expr.elements.forEach((element) => this.collectExprRefs(element, scope));
7160
- break;
7161
- case "struct_lit":
7162
- expr.fields.forEach((field) => this.collectExprRefs(field.value, scope));
7163
- break;
7164
- case "binary":
7165
- this.collectExprRefs(expr.left, scope);
7166
- this.collectExprRefs(expr.right, scope);
7167
- break;
7168
- case "is_check":
7169
- this.collectExprRefs(expr.expr, scope);
7170
- break;
7171
- case "unary":
7172
- this.collectExprRefs(expr.operand, scope);
7173
- break;
7174
- case "assign": {
7175
- this.collectExprRefs(expr.value, scope);
7714
+ };
7176
7715
  break;
7177
- }
7178
- case "str_interp":
7179
- expr.parts.forEach((part) => {
7180
- if (typeof part !== "string") {
7181
- this.collectExprRefs(part, scope);
7716
+ case "craft":
7717
+ path2 = `data/${ns}/advancements/on_craft_${fn.name}.json`;
7718
+ criteria = {
7719
+ crafted: {
7720
+ trigger: "minecraft:inventory_changed",
7721
+ conditions: {
7722
+ items: [
7723
+ {
7724
+ items: [eventTrigger.value]
7725
+ }
7726
+ ]
7727
+ }
7182
7728
  }
7183
- });
7729
+ };
7184
7730
  break;
7185
- case "f_string":
7186
- expr.parts.forEach((part) => {
7187
- if (part.kind === "expr") {
7188
- this.collectExprRefs(part.expr, scope);
7731
+ case "death":
7732
+ path2 = `data/${ns}/advancements/on_death_${fn.name}.json`;
7733
+ criteria = {
7734
+ death: {
7735
+ trigger: "minecraft:entity_killed_player"
7189
7736
  }
7190
- });
7191
- break;
7192
- case "lambda": {
7193
- const lambdaScope = [
7194
- ...scope.map((entries) => [...entries]),
7195
- expr.params.map((param) => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name }))
7196
- ];
7197
- if (Array.isArray(expr.body)) {
7198
- this.collectStmtRefs(expr.body, lambdaScope);
7199
- } else {
7200
- this.collectExprRefs(expr.body, lambdaScope);
7201
- }
7202
- break;
7203
- }
7204
- case "blockpos":
7205
- case "bool_lit":
7206
- case "byte_lit":
7207
- case "double_lit":
7208
- case "float_lit":
7209
- case "int_lit":
7210
- case "long_lit":
7211
- case "mc_name":
7212
- case "range_lit":
7213
- case "selector":
7214
- case "short_lit":
7215
- case "str_lit":
7737
+ };
7216
7738
  break;
7739
+ case "login":
7740
+ case "join_team":
7741
+ continue;
7217
7742
  }
7218
- }
7219
- resolveLocal(name, scope) {
7220
- for (let i = scope.length - 1; i >= 0; i--) {
7221
- for (let j = scope[i].length - 1; j >= 0; j--) {
7222
- if (scope[i][j].name === name) {
7223
- return scope[i][j];
7743
+ advancements.push({
7744
+ path: path2,
7745
+ content: JSON.stringify({
7746
+ criteria,
7747
+ rewards: {
7748
+ function: `${ns}:${fn.name}`
7224
7749
  }
7225
- }
7226
- }
7227
- return null;
7228
- }
7229
- transformFunction(fn) {
7230
- const scope = [fn.params.map((param) => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
7231
- const body = this.transformBlock(fn.body, scope);
7232
- return body === fn.body ? fn : copySpan({ ...fn, body }, fn);
7233
- }
7234
- transformBlock(block, scope) {
7235
- scope.push([]);
7236
- const transformed = [];
7237
- for (const stmt of block) {
7238
- const next = this.transformStmt(stmt, scope);
7239
- transformed.push(...next);
7240
- }
7241
- scope.pop();
7242
- return transformed;
7243
- }
7244
- transformStmt(stmt, scope) {
7245
- switch (stmt.kind) {
7246
- case "let": {
7247
- const init = this.transformExpr(stmt.init, scope);
7248
- const id = this.localDeclIds.get(stmt) ?? `local:${stmt.name}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`;
7249
- scope[scope.length - 1].push({ id, name: stmt.name });
7250
- if (this.localReads.has(id)) {
7251
- if (init === stmt.init) {
7252
- return [stmt];
7253
- }
7254
- return [copySpan({ ...stmt, init }, stmt)];
7255
- }
7256
- this.warnings.push({
7257
- message: `Unused variable '${stmt.name}'`,
7258
- code: "W_UNUSED_VAR",
7259
- line: stmt.span?.line,
7260
- col: stmt.span?.col
7261
- });
7262
- if (isPureExpr(init)) {
7263
- return [];
7264
- }
7265
- return [copySpan({ kind: "expr", expr: init }, stmt)];
7266
- }
7267
- case "expr": {
7268
- const expr = this.transformExpr(stmt.expr, scope);
7269
- if (expr.kind === "assign") {
7270
- const resolved = this.resolveLocal(expr.target, scope);
7271
- if (resolved && !this.localReads.has(resolved.id)) {
7272
- if (isPureExpr(expr.value)) {
7273
- return [];
7274
- }
7275
- return [copySpan({ kind: "expr", expr: expr.value }, stmt)];
7276
- }
7277
- }
7278
- if (expr === stmt.expr) {
7279
- return [stmt];
7280
- }
7281
- return [copySpan({ ...stmt, expr }, stmt)];
7282
- }
7283
- case "return": {
7284
- if (!stmt.value) {
7285
- return [stmt];
7286
- }
7287
- const value = this.transformExpr(stmt.value, scope);
7288
- if (value === stmt.value) {
7289
- return [stmt];
7290
- }
7291
- return [copySpan({ ...stmt, value }, stmt)];
7292
- }
7293
- case "if": {
7294
- const cond = this.transformExpr(stmt.cond, scope);
7295
- const constant = isConstantBoolean(cond);
7296
- if (constant === true) {
7297
- return this.transformBlock(stmt.then, scope);
7298
- }
7299
- if (constant === false) {
7300
- return stmt.else_ ? this.transformBlock(stmt.else_, scope) : [];
7301
- }
7302
- const thenBlock = this.transformBlock(stmt.then, scope);
7303
- const elseBlock = stmt.else_ ? this.transformBlock(stmt.else_, scope) : void 0;
7304
- if (cond === stmt.cond && thenBlock === stmt.then && elseBlock === stmt.else_) {
7305
- return [stmt];
7306
- }
7307
- return [copySpan({ ...stmt, cond, then: thenBlock, else_: elseBlock }, stmt)];
7308
- }
7309
- case "while": {
7310
- const cond = this.transformExpr(stmt.cond, scope);
7311
- if (isConstantBoolean(cond) === false) {
7312
- return [];
7313
- }
7314
- const body = this.transformBlock(stmt.body, scope);
7315
- return [copySpan({ ...stmt, cond, body }, stmt)];
7316
- }
7317
- case "for": {
7318
- const forScope = [...scope, []];
7319
- const init = stmt.init ? this.transformStmt(stmt.init, forScope)[0] : void 0;
7320
- const cond = this.transformExpr(stmt.cond, forScope);
7321
- if (isConstantBoolean(cond) === false) {
7322
- return init ? [init] : [];
7323
- }
7324
- const step = this.transformExpr(stmt.step, forScope);
7325
- const body = this.transformBlock(stmt.body, forScope);
7326
- return [copySpan({ ...stmt, init, cond, step, body }, stmt)];
7327
- }
7328
- case "foreach": {
7329
- const iterable = this.transformExpr(stmt.iterable, scope);
7330
- const foreachScope = [...scope, [{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]];
7331
- const body = this.transformBlock(stmt.body, foreachScope);
7332
- return [copySpan({ ...stmt, iterable, body }, stmt)];
7333
- }
7334
- case "for_range": {
7335
- const start = this.transformExpr(stmt.start, scope);
7336
- const end = this.transformExpr(stmt.end, scope);
7337
- const rangeScope = [...scope, [{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]];
7338
- const body = this.transformBlock(stmt.body, rangeScope);
7339
- return [copySpan({ ...stmt, start, end, body }, stmt)];
7340
- }
7341
- case "match": {
7342
- const expr = this.transformExpr(stmt.expr, scope);
7343
- const arms = stmt.arms.map((arm) => ({
7344
- pattern: arm.pattern ? this.transformExpr(arm.pattern, scope) : null,
7345
- body: this.transformBlock(arm.body, scope)
7346
- }));
7347
- return [copySpan({ ...stmt, expr, arms }, stmt)];
7348
- }
7349
- case "as_block":
7350
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
7351
- case "at_block":
7352
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
7353
- case "as_at":
7354
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
7355
- case "execute":
7356
- return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
7357
- case "raw":
7358
- return [stmt];
7359
- case "break":
7360
- return [stmt];
7361
- case "continue":
7362
- return [stmt];
7363
- }
7750
+ }, null, 2)
7751
+ });
7364
7752
  }
7365
- transformExpr(expr, scope) {
7366
- switch (expr.kind) {
7367
- case "call":
7368
- return copySpan({ ...expr, args: expr.args.map((arg) => this.transformExpr(arg, scope)) }, expr);
7369
- case "static_call":
7370
- return copySpan({ ...expr, args: expr.args.map((arg) => this.transformExpr(arg, scope)) }, expr);
7371
- case "invoke":
7372
- return copySpan({
7373
- ...expr,
7374
- callee: this.transformExpr(expr.callee, scope),
7375
- args: expr.args.map((arg) => this.transformExpr(arg, scope))
7376
- }, expr);
7377
- case "binary":
7378
- return copySpan({
7379
- ...expr,
7380
- left: this.transformExpr(expr.left, scope),
7381
- right: this.transformExpr(expr.right, scope)
7382
- }, expr);
7383
- case "is_check":
7384
- return copySpan({ ...expr, expr: this.transformExpr(expr.expr, scope) }, expr);
7385
- case "unary":
7386
- return copySpan({ ...expr, operand: this.transformExpr(expr.operand, scope) }, expr);
7387
- case "assign":
7388
- return copySpan({ ...expr, value: this.transformExpr(expr.value, scope) }, expr);
7389
- case "member":
7390
- return copySpan({ ...expr, obj: this.transformExpr(expr.obj, scope) }, expr);
7391
- case "member_assign":
7392
- return copySpan({
7393
- ...expr,
7394
- obj: this.transformExpr(expr.obj, scope),
7395
- value: this.transformExpr(expr.value, scope)
7396
- }, expr);
7397
- case "index":
7398
- return copySpan({
7399
- ...expr,
7400
- obj: this.transformExpr(expr.obj, scope),
7401
- index: this.transformExpr(expr.index, scope)
7402
- }, expr);
7403
- case "array_lit":
7404
- return copySpan({ ...expr, elements: expr.elements.map((element) => this.transformExpr(element, scope)) }, expr);
7405
- case "struct_lit":
7406
- return copySpan({
7407
- ...expr,
7408
- fields: expr.fields.map((field) => ({ ...field, value: this.transformExpr(field.value, scope) }))
7409
- }, expr);
7410
- case "str_interp":
7411
- return copySpan({
7412
- ...expr,
7413
- parts: expr.parts.map((part) => typeof part === "string" ? part : this.transformExpr(part, scope))
7414
- }, expr);
7415
- case "f_string":
7416
- return copySpan({
7417
- ...expr,
7418
- parts: expr.parts.map((part) => part.kind === "text" ? part : { kind: "expr", expr: this.transformExpr(part.expr, scope) })
7419
- }, expr);
7420
- case "lambda": {
7421
- const lambdaScope = [
7422
- ...scope.map((entries) => [...entries]),
7423
- expr.params.map((param) => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name }))
7424
- ];
7425
- const body = Array.isArray(expr.body) ? this.transformBlock(expr.body, lambdaScope) : this.transformExpr(expr.body, lambdaScope);
7426
- return copySpan({ ...expr, body }, expr);
7427
- }
7428
- case "blockpos":
7429
- case "bool_lit":
7430
- case "byte_lit":
7431
- case "double_lit":
7432
- case "float_lit":
7433
- case "ident":
7434
- case "int_lit":
7435
- case "long_lit":
7436
- case "mc_name":
7437
- case "range_lit":
7438
- case "rel_coord":
7439
- case "local_coord":
7440
- case "selector":
7441
- case "short_lit":
7442
- case "str_lit":
7443
- return expr;
7444
- }
7753
+ const stats = (0, commands_1.createEmptyOptimizationStats)();
7754
+ const sourceMap = mangle ? alloc.toSourceMap() : void 0;
7755
+ if (!optimizeCommands) {
7756
+ return { files, advancements, stats, sourceMap };
7445
7757
  }
7446
- };
7447
- exports2.DeadCodeEliminator = DeadCodeEliminator;
7448
- function eliminateDeadCode(program) {
7449
- const eliminator = new DeadCodeEliminator();
7450
- const result = eliminator.eliminate(program);
7451
- return { program: result, warnings: eliminator.warnings };
7758
+ const optimized = applyFunctionOptimization(files);
7759
+ (0, commands_1.mergeOptimizationStats)(stats, optimized.stats);
7760
+ return { files: optimized.files, advancements, stats, sourceMap };
7761
+ }
7762
+ function generateDatapack(module3) {
7763
+ const generated = generateDatapackWithStats(module3);
7764
+ return [...generated.files, ...generated.advancements];
7452
7765
  }
7453
7766
  }
7454
7767
  });
7455
7768
 
7456
- // ../../dist/codegen/mcfunction/index.js
7457
- var require_mcfunction = __commonJS({
7458
- "../../dist/codegen/mcfunction/index.js"(exports2) {
7769
+ // ../../dist/compile.js
7770
+ var require_compile = __commonJS({
7771
+ "../../dist/compile.js"(exports2) {
7459
7772
  "use strict";
7773
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
7774
+ if (k2 === void 0) k2 = k;
7775
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7776
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7777
+ desc = { enumerable: true, get: function() {
7778
+ return m[k];
7779
+ } };
7780
+ }
7781
+ Object.defineProperty(o, k2, desc);
7782
+ }) : (function(o, m, k, k2) {
7783
+ if (k2 === void 0) k2 = k;
7784
+ o[k2] = m[k];
7785
+ }));
7786
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
7787
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
7788
+ }) : function(o, v) {
7789
+ o["default"] = v;
7790
+ });
7791
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
7792
+ var ownKeys = function(o) {
7793
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
7794
+ var ar = [];
7795
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
7796
+ return ar;
7797
+ };
7798
+ return ownKeys(o);
7799
+ };
7800
+ return function(mod) {
7801
+ if (mod && mod.__esModule) return mod;
7802
+ var result = {};
7803
+ if (mod != null) {
7804
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
7805
+ }
7806
+ __setModuleDefault(result, mod);
7807
+ return result;
7808
+ };
7809
+ })();
7460
7810
  Object.defineProperty(exports2, "__esModule", { value: true });
7461
- exports2.countMcfunctionCommands = countMcfunctionCommands;
7462
- exports2.generateDatapackWithStats = generateDatapackWithStats;
7463
- exports2.generateDatapack = generateDatapack;
7464
- var commands_1 = require_commands();
7465
- var types_1 = require_types();
7466
- var OBJ = "rs";
7467
- function varRef(name) {
7468
- return name.startsWith("$") ? name : `$${name}`;
7811
+ exports2.resolveSourceLine = resolveSourceLine;
7812
+ exports2.preprocessSourceWithMetadata = preprocessSourceWithMetadata;
7813
+ exports2.preprocessSource = preprocessSource;
7814
+ exports2.compile = compile;
7815
+ exports2.formatCompileError = formatCompileError;
7816
+ var fs2 = __importStar(require("fs"));
7817
+ var path2 = __importStar(require("path"));
7818
+ var lexer_1 = require_lexer();
7819
+ var parser_1 = require_parser();
7820
+ var lowering_1 = require_lowering();
7821
+ var passes_1 = require_passes();
7822
+ var dce_1 = require_dce();
7823
+ var mcfunction_1 = require_mcfunction();
7824
+ var diagnostics_1 = require_diagnostics();
7825
+ function resolveSourceLine(combinedLine, ranges, fallbackFile) {
7826
+ for (const range of ranges) {
7827
+ if (combinedLine >= range.startLine && combinedLine <= range.endLine) {
7828
+ const localLine = combinedLine - range.startLine + 1;
7829
+ return { filePath: range.filePath, line: localLine };
7830
+ }
7831
+ }
7832
+ return { filePath: fallbackFile, line: combinedLine };
7469
7833
  }
7470
- function operandToScore(op) {
7471
- if (op.kind === "var")
7472
- return `${varRef(op.name)} ${OBJ}`;
7473
- if (op.kind === "const")
7474
- return `$const_${op.value} ${OBJ}`;
7475
- throw new Error(`Cannot convert storage operand to score: ${op.path}`);
7834
+ var IMPORT_RE = /^\s*import\s+"([^"]+)"\s*;?\s*$/;
7835
+ function isLibrarySource(source) {
7836
+ for (const line of source.split("\n")) {
7837
+ const trimmed = line.trim();
7838
+ if (!trimmed || trimmed.startsWith("//"))
7839
+ continue;
7840
+ return /^module\s+library\s*;/.test(trimmed);
7841
+ }
7842
+ return false;
7476
7843
  }
7477
- function constSetup(value) {
7478
- return `scoreboard players set $const_${value} ${OBJ} ${value}`;
7844
+ function countLines(source) {
7845
+ return source === "" ? 0 : source.split("\n").length;
7479
7846
  }
7480
- function collectConsts(fn) {
7481
- const consts = /* @__PURE__ */ new Set();
7482
- for (const block of fn.blocks) {
7483
- for (const instr of block.instrs) {
7484
- if (instr.op === "assign" && instr.src.kind === "const")
7485
- consts.add(instr.src.value);
7486
- if (instr.op === "binop") {
7487
- if (instr.lhs.kind === "const")
7488
- consts.add(instr.lhs.value);
7489
- if (instr.rhs.kind === "const")
7490
- consts.add(instr.rhs.value);
7491
- }
7492
- if (instr.op === "cmp") {
7493
- if (instr.lhs.kind === "const")
7494
- consts.add(instr.lhs.value);
7495
- if (instr.rhs.kind === "const")
7496
- consts.add(instr.rhs.value);
7497
- }
7498
- }
7499
- const t = block.term;
7500
- if (t.op === "return" && t.value?.kind === "const")
7501
- consts.add(t.value.value);
7502
- }
7503
- return consts;
7847
+ function offsetRanges(ranges, lineOffset) {
7848
+ return ranges.map((range) => ({
7849
+ startLine: range.startLine + lineOffset,
7850
+ endLine: range.endLine + lineOffset,
7851
+ filePath: range.filePath
7852
+ }));
7504
7853
  }
7505
- var BOP_OP = {
7506
- "+": "+=",
7507
- "-": "-=",
7508
- "*": "*=",
7509
- "/": "/=",
7510
- "%": "%="
7511
- };
7512
- function emitInstr(instr, ns) {
7513
- const lines = [];
7514
- switch (instr.op) {
7515
- case "assign": {
7516
- const dst = varRef(instr.dst);
7517
- const src = instr.src;
7518
- if (src.kind === "const") {
7519
- lines.push(`scoreboard players set ${dst} ${OBJ} ${src.value}`);
7520
- } else if (src.kind === "var") {
7521
- lines.push(`scoreboard players operation ${dst} ${OBJ} = ${varRef(src.name)} ${OBJ}`);
7522
- } else {
7523
- lines.push(`execute store result score ${dst} ${OBJ} run data get storage ${src.path}`);
7524
- }
7525
- break;
7526
- }
7527
- case "binop": {
7528
- const dst = varRef(instr.dst);
7529
- const bop = BOP_OP[instr.bop] ?? "+=";
7530
- lines.push(...emitInstr({ op: "assign", dst: instr.dst, src: instr.lhs }, ns));
7531
- lines.push(`scoreboard players operation ${dst} ${OBJ} ${bop} ${operandToScore(instr.rhs)}`);
7532
- break;
7533
- }
7534
- case "cmp": {
7535
- const dst = varRef(instr.dst);
7536
- const lhsScore = operandToScore(instr.lhs);
7537
- const rhsScore = operandToScore(instr.rhs);
7538
- lines.push(`scoreboard players set ${dst} ${OBJ} 0`);
7539
- switch (instr.cop) {
7540
- case "==":
7541
- lines.push(`execute if score ${lhsScore} = ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7542
- break;
7543
- case "!=":
7544
- lines.push(`execute unless score ${lhsScore} = ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7545
- break;
7546
- case "<":
7547
- lines.push(`execute if score ${lhsScore} < ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7548
- break;
7549
- case "<=":
7550
- lines.push(`execute if score ${lhsScore} <= ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7551
- break;
7552
- case ">":
7553
- lines.push(`execute if score ${lhsScore} > ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7554
- break;
7555
- case ">=":
7556
- lines.push(`execute if score ${lhsScore} >= ${rhsScore} run scoreboard players set ${dst} ${OBJ} 1`);
7557
- break;
7558
- }
7559
- break;
7560
- }
7561
- case "call": {
7562
- for (let i = 0; i < instr.args.length; i++) {
7563
- lines.push(...emitInstr({ op: "assign", dst: `$p${i}`, src: instr.args[i] }, ns));
7854
+ function preprocessSourceWithMetadata(source, options = {}) {
7855
+ const { filePath } = options;
7856
+ const seen = options.seen ?? /* @__PURE__ */ new Set();
7857
+ if (filePath) {
7858
+ seen.add(path2.resolve(filePath));
7859
+ }
7860
+ const lines = source.split("\n");
7861
+ const imports = [];
7862
+ const libraryImports = [];
7863
+ const bodyLines = [];
7864
+ let parsingHeader = true;
7865
+ for (let i = 0; i < lines.length; i++) {
7866
+ const line = lines[i];
7867
+ const trimmed = line.trim();
7868
+ const match = line.match(IMPORT_RE);
7869
+ if (parsingHeader && match) {
7870
+ if (!filePath) {
7871
+ throw new diagnostics_1.DiagnosticError("ParseError", "Import statements require a file path", { line: i + 1, col: 1 }, lines);
7564
7872
  }
7565
- lines.push(`function ${ns}:${instr.fn}`);
7566
- if (instr.dst) {
7567
- lines.push(`scoreboard players operation ${varRef(instr.dst)} ${OBJ} = $ret ${OBJ}`);
7873
+ const importPath = path2.resolve(path2.dirname(filePath), match[1]);
7874
+ if (!seen.has(importPath)) {
7875
+ seen.add(importPath);
7876
+ let importedSource;
7877
+ try {
7878
+ importedSource = fs2.readFileSync(importPath, "utf-8");
7879
+ } catch {
7880
+ throw new diagnostics_1.DiagnosticError("ParseError", `Cannot import '${match[1]}'`, { file: filePath, line: i + 1, col: 1 }, lines);
7881
+ }
7882
+ if (isLibrarySource(importedSource)) {
7883
+ const nested = preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen });
7884
+ libraryImports.push({ source: importedSource, filePath: importPath });
7885
+ if (nested.libraryImports)
7886
+ libraryImports.push(...nested.libraryImports);
7887
+ } else {
7888
+ imports.push(preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen }));
7889
+ }
7568
7890
  }
7569
- break;
7891
+ continue;
7570
7892
  }
7571
- case "raw":
7572
- lines.push(instr.cmd);
7573
- break;
7893
+ if (parsingHeader && (trimmed === "" || trimmed.startsWith("//"))) {
7894
+ bodyLines.push(line);
7895
+ continue;
7896
+ }
7897
+ parsingHeader = false;
7898
+ bodyLines.push(line);
7574
7899
  }
7575
- return lines;
7576
- }
7577
- function emitTerm(term, ns, fnName) {
7578
- const lines = [];
7579
- switch (term.op) {
7580
- case "jump":
7581
- lines.push(`function ${ns}:${fnName}/${term.target}`);
7582
- break;
7583
- case "jump_if":
7584
- lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.then}`);
7585
- lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches ..0 run function ${ns}:${fnName}/${term.else_}`);
7586
- break;
7587
- case "jump_unless":
7588
- lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches ..0 run function ${ns}:${fnName}/${term.then}`);
7589
- lines.push(`execute if score ${varRef(term.cond)} ${OBJ} matches 1.. run function ${ns}:${fnName}/${term.else_}`);
7590
- break;
7591
- case "return":
7592
- if (term.value) {
7593
- lines.push(...emitInstr({ op: "assign", dst: "$ret", src: term.value }, ns));
7594
- }
7595
- if (term.value?.kind === "const") {
7596
- lines.push(`return ${term.value.value}`);
7597
- } else if (term.value?.kind === "var") {
7598
- lines.push(`return run scoreboard players get ${varRef(term.value.name)} ${OBJ}`);
7599
- }
7600
- break;
7601
- case "tick_yield":
7602
- lines.push(`schedule function ${ns}:${fnName}/${term.continuation} 1t replace`);
7603
- break;
7900
+ const body = bodyLines.join("\n");
7901
+ const parts = [...imports.map((entry) => entry.source), body].filter(Boolean);
7902
+ const combined = parts.join("\n");
7903
+ const ranges = [];
7904
+ let lineOffset = 0;
7905
+ for (const entry of imports) {
7906
+ ranges.push(...offsetRanges(entry.ranges, lineOffset));
7907
+ lineOffset += countLines(entry.source);
7908
+ }
7909
+ if (filePath && body) {
7910
+ ranges.push({
7911
+ startLine: lineOffset + 1,
7912
+ endLine: lineOffset + countLines(body),
7913
+ filePath: path2.resolve(filePath)
7914
+ });
7604
7915
  }
7605
- return lines;
7606
- }
7607
- function toFunctionName(file) {
7608
- const match = file.path.match(/^data\/[^/]+\/function\/(.+)\.mcfunction$/);
7609
- return match?.[1] ?? null;
7610
- }
7611
- function applyFunctionOptimization(files) {
7612
- const functionFiles = files.map((file) => {
7613
- const functionName = toFunctionName(file);
7614
- if (!functionName)
7615
- return null;
7616
- const commands = file.content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).map((cmd) => ({ cmd }));
7617
- return { file, functionName, commands };
7618
- }).filter((entry) => entry !== null);
7619
- const optimized = (0, commands_1.optimizeCommandFunctions)(functionFiles.map((entry) => ({
7620
- name: entry.functionName,
7621
- commands: entry.commands
7622
- })));
7623
- const commandMap = new Map(optimized.functions.map((fn) => [fn.name, fn.commands]));
7624
- const optimizedNames = new Set(optimized.functions.map((fn) => fn.name));
7625
7916
  return {
7626
- files: files.filter((file) => {
7627
- const functionName = toFunctionName(file);
7628
- return !functionName || optimizedNames.has(functionName);
7629
- }).map((file) => {
7630
- const functionName = toFunctionName(file);
7631
- if (!functionName)
7632
- return file;
7633
- const commands = commandMap.get(functionName);
7634
- if (!commands)
7635
- return file;
7636
- const lines = file.content.split("\n");
7637
- const header = lines.filter((line) => line.trim().startsWith("#"));
7638
- return {
7639
- ...file,
7640
- content: [...header, ...commands.map((command) => command.cmd)].join("\n")
7641
- };
7642
- }),
7643
- stats: optimized.stats
7917
+ source: combined,
7918
+ ranges,
7919
+ libraryImports: libraryImports.length > 0 ? libraryImports : void 0
7644
7920
  };
7645
7921
  }
7646
- function countMcfunctionCommands(files) {
7647
- return files.reduce((sum, file) => {
7648
- if (!toFunctionName(file)) {
7649
- return sum;
7650
- }
7651
- return sum + file.content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !line.startsWith("#")).length;
7652
- }, 0);
7922
+ function preprocessSource(source, options = {}) {
7923
+ return preprocessSourceWithMetadata(source, options).source;
7653
7924
  }
7654
- function generateDatapackWithStats(module3, options = {}) {
7655
- const { optimizeCommands = true } = options;
7656
- const files = [];
7657
- const advancements = [];
7658
- const ns = module3.namespace;
7659
- const triggerHandlers = module3.functions.filter((fn) => fn.isTriggerHandler && fn.triggerName);
7660
- const triggerNames = new Set(triggerHandlers.map((fn) => fn.triggerName));
7661
- const eventHandlers = module3.functions.filter((fn) => !!fn.eventHandler && (0, types_1.isEventTypeName)(fn.eventHandler.eventType));
7662
- const eventTypes = new Set(eventHandlers.map((fn) => fn.eventHandler.eventType));
7663
- const tickFunctionNames = [];
7664
- for (const fn of module3.functions) {
7665
- if (fn.isTickLoop) {
7666
- tickFunctionNames.push(fn.name);
7925
+ function compile(source, options = {}) {
7926
+ const { namespace = "redscript", filePath, optimize: shouldOptimize = true } = options;
7927
+ const shouldRunDce = options.dce ?? shouldOptimize;
7928
+ let sourceLines = source.split("\n");
7929
+ try {
7930
+ const preprocessed = preprocessSourceWithMetadata(source, { filePath });
7931
+ const preprocessedSource = preprocessed.source;
7932
+ sourceLines = preprocessedSource.split("\n");
7933
+ const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
7934
+ const parsedAst = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
7935
+ const allLibrarySources = [];
7936
+ for (const libSrc of options.librarySources ?? []) {
7937
+ allLibrarySources.push({ src: libSrc });
7938
+ }
7939
+ for (const li of preprocessed.libraryImports ?? []) {
7940
+ allLibrarySources.push({ src: li.source, fp: li.filePath });
7941
+ }
7942
+ for (const { src, fp } of allLibrarySources) {
7943
+ const libPreprocessed = preprocessSourceWithMetadata(src, fp ? { filePath: fp } : {});
7944
+ const libTokens = new lexer_1.Lexer(libPreprocessed.source, fp).tokenize();
7945
+ const libAst = new parser_1.Parser(libTokens, libPreprocessed.source, fp).parse(namespace);
7946
+ for (const fn of libAst.declarations)
7947
+ fn.isLibraryFn = true;
7948
+ parsedAst.declarations.push(...libAst.declarations);
7949
+ parsedAst.structs.push(...libAst.structs);
7950
+ parsedAst.implBlocks.push(...libAst.implBlocks);
7951
+ parsedAst.enums.push(...libAst.enums);
7952
+ parsedAst.consts.push(...libAst.consts);
7953
+ parsedAst.globals.push(...libAst.globals);
7954
+ }
7955
+ const dceResult = shouldRunDce ? (0, dce_1.eliminateDeadCode)(parsedAst) : { program: parsedAst, warnings: [] };
7956
+ const ast = dceResult.program;
7957
+ const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`;
7958
+ (0, lowering_1.setScoreboardObjective)(scoreboardObj);
7959
+ const ir = new lowering_1.Lowering(namespace, preprocessed.ranges).lower(ast);
7960
+ const optimized = shouldOptimize ? { ...ir, functions: ir.functions.map((fn) => (0, passes_1.optimize)(fn)) } : ir;
7961
+ const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized, {
7962
+ mangle: options.mangle ?? true,
7963
+ scoreboardObjective: scoreboardObj
7964
+ });
7965
+ return {
7966
+ success: true,
7967
+ files: [...generated.files, ...generated.advancements],
7968
+ advancements: generated.advancements,
7969
+ ast,
7970
+ ir: optimized
7971
+ };
7972
+ } catch (err) {
7973
+ if (err instanceof diagnostics_1.DiagnosticError) {
7974
+ return { success: false, error: err };
7975
+ }
7976
+ if (err instanceof Error) {
7977
+ const diagnostic = (0, diagnostics_1.parseErrorMessage)("ParseError", err.message, sourceLines, filePath);
7978
+ return { success: false, error: diagnostic };
7667
7979
  }
7980
+ return {
7981
+ success: false,
7982
+ error: new diagnostics_1.DiagnosticError("ParseError", String(err), { file: filePath, line: 1, col: 1 }, sourceLines)
7983
+ };
7668
7984
  }
7669
- files.push({
7670
- path: "pack.mcmeta",
7671
- content: JSON.stringify({
7672
- pack: { pack_format: 26, description: `${ns} datapack \u2014 compiled by redscript` }
7673
- }, null, 2)
7674
- });
7675
- const loadLines = [
7676
- `# RedScript runtime init`,
7677
- `scoreboard objectives add ${OBJ} dummy`
7678
- ];
7679
- for (const g of module3.globals) {
7680
- loadLines.push(`scoreboard players set ${varRef(g.name)} ${OBJ} ${g.init}`);
7985
+ }
7986
+ function formatCompileError(result) {
7987
+ if (result.success) {
7988
+ return "Compilation successful";
7681
7989
  }
7682
- for (const triggerName of triggerNames) {
7683
- loadLines.push(`scoreboard objectives add ${triggerName} trigger`);
7684
- loadLines.push(`scoreboard players enable @a ${triggerName}`);
7990
+ if (result.error) {
7991
+ return (0, diagnostics_1.formatError)(result.error, result.error.sourceLines?.join("\n"));
7992
+ }
7993
+ return "Unknown error";
7994
+ }
7995
+ }
7996
+ });
7997
+
7998
+ // ../../dist/optimizer/dce.js
7999
+ var require_dce = __commonJS({
8000
+ "../../dist/optimizer/dce.js"(exports2) {
8001
+ "use strict";
8002
+ Object.defineProperty(exports2, "__esModule", { value: true });
8003
+ exports2.DeadCodeEliminator = void 0;
8004
+ exports2.eliminateDeadCode = eliminateDeadCode;
8005
+ function copySpan(target, source) {
8006
+ const descriptor = Object.getOwnPropertyDescriptor(source, "span");
8007
+ if (descriptor) {
8008
+ Object.defineProperty(target, "span", descriptor);
8009
+ }
8010
+ return target;
8011
+ }
8012
+ function isConstantBoolean(expr) {
8013
+ if (expr.kind === "bool_lit") {
8014
+ return expr.value;
8015
+ }
8016
+ return null;
8017
+ }
8018
+ function isPureExpr(expr) {
8019
+ switch (expr.kind) {
8020
+ case "int_lit":
8021
+ case "float_lit":
8022
+ case "byte_lit":
8023
+ case "short_lit":
8024
+ case "long_lit":
8025
+ case "double_lit":
8026
+ case "rel_coord":
8027
+ case "local_coord":
8028
+ case "bool_lit":
8029
+ case "str_lit":
8030
+ case "mc_name":
8031
+ case "range_lit":
8032
+ case "selector":
8033
+ case "ident":
8034
+ case "blockpos":
8035
+ return true;
8036
+ case "str_interp":
8037
+ return expr.parts.every((part) => typeof part === "string" || isPureExpr(part));
8038
+ case "f_string":
8039
+ return expr.parts.every((part) => part.kind === "text" || isPureExpr(part.expr));
8040
+ case "binary":
8041
+ return isPureExpr(expr.left) && isPureExpr(expr.right);
8042
+ case "is_check":
8043
+ return isPureExpr(expr.expr);
8044
+ case "unary":
8045
+ return isPureExpr(expr.operand);
8046
+ case "member":
8047
+ return isPureExpr(expr.obj);
8048
+ case "index":
8049
+ return isPureExpr(expr.obj) && isPureExpr(expr.index);
8050
+ case "array_lit":
8051
+ return expr.elements.every(isPureExpr);
8052
+ case "struct_lit":
8053
+ return expr.fields.every((field) => isPureExpr(field.value));
8054
+ case "lambda":
8055
+ return true;
8056
+ case "assign":
8057
+ case "member_assign":
8058
+ case "call":
8059
+ case "invoke":
8060
+ case "static_call":
8061
+ return false;
8062
+ }
8063
+ }
8064
+ var DeadCodeEliminator = class {
8065
+ constructor() {
8066
+ this.functionMap = /* @__PURE__ */ new Map();
8067
+ this.reachableFunctions = /* @__PURE__ */ new Set();
8068
+ this.usedConstants = /* @__PURE__ */ new Set();
8069
+ this.localReads = /* @__PURE__ */ new Set();
8070
+ this.localDeclIds = /* @__PURE__ */ new WeakMap();
8071
+ this.localIdCounter = 0;
8072
+ this.warnings = [];
7685
8073
  }
7686
- for (const eventType of eventTypes) {
7687
- const detection = types_1.EVENT_TYPES[eventType].detection;
7688
- if (eventType === "PlayerDeath") {
7689
- loadLines.push("scoreboard objectives add rs.deaths deathCount");
7690
- } else if (eventType === "EntityKill") {
7691
- loadLines.push("scoreboard objectives add rs.kills totalKillCount");
7692
- } else if (eventType === "ItemUse") {
7693
- loadLines.push("# ItemUse detection requires a project-specific objective/tag setup");
7694
- } else if (detection === "tag" || detection === "advancement") {
7695
- loadLines.push(`# ${eventType} detection expects tag ${types_1.EVENT_TYPES[eventType].tag} to be set externally`);
8074
+ eliminate(program) {
8075
+ this.functionMap.clear();
8076
+ this.reachableFunctions.clear();
8077
+ this.usedConstants.clear();
8078
+ this.localReads.clear();
8079
+ this.localIdCounter = 0;
8080
+ this.warnings.length = 0;
8081
+ for (const fn of program.declarations) {
8082
+ this.functionMap.set(fn.name, fn);
7696
8083
  }
7697
- }
7698
- for (const triggerName of triggerNames) {
7699
- const handlers = triggerHandlers.filter((fn) => fn.triggerName === triggerName);
7700
- const dispatchLines = [
7701
- `# Trigger dispatch for ${triggerName}`
7702
- ];
7703
- for (const handler of handlers) {
7704
- dispatchLines.push(`function ${ns}:${handler.name}`);
8084
+ const entryPoints = this.findEntryPoints(program);
8085
+ if (entryPoints.length === 0) {
8086
+ for (const fn of program.declarations) {
8087
+ this.markReachable(fn.name);
8088
+ }
8089
+ } else {
8090
+ for (const fnName of entryPoints) {
8091
+ this.markReachable(fnName);
8092
+ }
7705
8093
  }
7706
- dispatchLines.push(`scoreboard players set @s ${triggerName} 0`);
7707
- dispatchLines.push(`scoreboard players enable @s ${triggerName}`);
7708
- files.push({
7709
- path: `data/${ns}/function/__trigger_${triggerName}_dispatch.mcfunction`,
7710
- content: dispatchLines.join("\n")
7711
- });
7712
- }
7713
- for (const fn of module3.functions) {
7714
- const consts = collectConsts(fn);
7715
- if (consts.size > 0) {
7716
- loadLines.push(...Array.from(consts).map(constSetup));
8094
+ for (const global of program.globals) {
8095
+ this.collectExprRefs(global.init, []);
7717
8096
  }
7718
- for (let i = 0; i < fn.blocks.length; i++) {
7719
- const block = fn.blocks[i];
7720
- const lines = [`# block: ${block.label}`];
7721
- if (i === 0) {
7722
- for (let j = 0; j < fn.params.length; j++) {
7723
- lines.push(`scoreboard players operation ${varRef(fn.params[j])} ${OBJ} = $p${j} ${OBJ}`);
8097
+ for (const implBlock of program.implBlocks) {
8098
+ for (const method of implBlock.methods) {
8099
+ this.collectFunctionRefs(method);
8100
+ }
8101
+ }
8102
+ return {
8103
+ ...program,
8104
+ declarations: program.declarations.filter((fn) => this.reachableFunctions.has(fn.name)).map((fn) => this.transformFunction(fn)),
8105
+ consts: program.consts.filter((constDecl) => this.usedConstants.has(constDecl.name)),
8106
+ implBlocks: program.implBlocks.map((implBlock) => ({
8107
+ ...implBlock,
8108
+ methods: implBlock.methods.map((method) => this.transformFunction(method))
8109
+ }))
8110
+ };
8111
+ }
8112
+ findEntryPoints(program) {
8113
+ const entries = /* @__PURE__ */ new Set();
8114
+ for (const fn of program.declarations) {
8115
+ if (!fn.isLibraryFn) {
8116
+ if (!fn.name.startsWith("_")) {
8117
+ entries.add(fn.name);
7724
8118
  }
7725
8119
  }
7726
- for (const instr of block.instrs) {
7727
- lines.push(...emitInstr(instr, ns));
8120
+ if (fn.decorators.some((decorator) => [
8121
+ "tick",
8122
+ "load",
8123
+ "on",
8124
+ "on_trigger",
8125
+ "on_advancement",
8126
+ "on_craft",
8127
+ "on_death",
8128
+ "on_login",
8129
+ "on_join_team",
8130
+ "keep"
8131
+ ].includes(decorator.name))) {
8132
+ entries.add(fn.name);
7728
8133
  }
7729
- lines.push(...emitTerm(block.term, ns, fn.name));
7730
- const filePath = i === 0 ? `data/${ns}/function/${fn.name}.mcfunction` : `data/${ns}/function/${fn.name}/${block.label}.mcfunction`;
7731
- files.push({ path: filePath, content: lines.join("\n") });
7732
8134
  }
8135
+ return [...entries];
7733
8136
  }
7734
- for (const fn of module3.functions) {
7735
- if (fn.isLoadInit) {
7736
- loadLines.push(`function ${ns}:${fn.name}`);
8137
+ markReachable(fnName) {
8138
+ if (this.reachableFunctions.has(fnName)) {
8139
+ return;
8140
+ }
8141
+ const fn = this.functionMap.get(fnName);
8142
+ if (!fn) {
8143
+ return;
8144
+ }
8145
+ this.reachableFunctions.add(fnName);
8146
+ this.collectFunctionRefs(fn);
8147
+ for (const decorator of fn.decorators) {
8148
+ if (decorator.name === "require_on_load") {
8149
+ for (const arg of decorator.rawArgs ?? []) {
8150
+ if (arg.kind === "string") {
8151
+ this.markReachable(arg.value);
8152
+ }
8153
+ }
8154
+ }
7737
8155
  }
7738
8156
  }
7739
- files.push({
7740
- path: `data/${ns}/function/__load.mcfunction`,
7741
- content: loadLines.join("\n")
7742
- });
7743
- files.push({
7744
- path: `data/minecraft/tags/function/load.json`,
7745
- content: JSON.stringify({ values: [`${ns}:__load`] }, null, 2)
7746
- });
7747
- const tickLines = ["# RedScript tick dispatcher"];
7748
- for (const fnName of tickFunctionNames) {
7749
- tickLines.push(`function ${ns}:${fnName}`);
8157
+ collectFunctionRefs(fn) {
8158
+ const scope = [fn.params.map((param) => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
8159
+ for (const param of fn.params) {
8160
+ if (param.default) {
8161
+ this.collectExprRefs(param.default, scope);
8162
+ }
8163
+ }
8164
+ this.collectStmtRefs(fn.body, scope);
7750
8165
  }
7751
- if (triggerNames.size > 0) {
7752
- tickLines.push(`# Trigger checks`);
7753
- for (const triggerName of triggerNames) {
7754
- tickLines.push(`execute as @a[scores={${triggerName}=1..}] run function ${ns}:__trigger_${triggerName}_dispatch`);
8166
+ collectStmtRefs(block, scope) {
8167
+ scope.push([]);
8168
+ for (const stmt of block) {
8169
+ this.collectStmtRef(stmt, scope);
7755
8170
  }
8171
+ scope.pop();
7756
8172
  }
7757
- if (eventHandlers.length > 0) {
7758
- tickLines.push("# Event checks");
7759
- for (const eventType of eventTypes) {
7760
- const tag = types_1.EVENT_TYPES[eventType].tag;
7761
- const handlers = eventHandlers.filter((fn) => fn.eventHandler?.eventType === eventType);
7762
- for (const handler of handlers) {
7763
- tickLines.push(`execute as @a[tag=${tag}] run function ${ns}:${handler.name}`);
8173
+ collectStmtRef(stmt, scope) {
8174
+ switch (stmt.kind) {
8175
+ case "let": {
8176
+ this.collectExprRefs(stmt.init, scope);
8177
+ const id = `local:${stmt.name}:${this.localIdCounter++}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`;
8178
+ this.localDeclIds.set(stmt, id);
8179
+ scope[scope.length - 1].push({ id, name: stmt.name });
8180
+ break;
7764
8181
  }
7765
- tickLines.push(`tag @a[tag=${tag}] remove ${tag}`);
8182
+ case "expr":
8183
+ this.collectExprRefs(stmt.expr, scope);
8184
+ break;
8185
+ case "return":
8186
+ if (stmt.value) {
8187
+ this.collectExprRefs(stmt.value, scope);
8188
+ }
8189
+ break;
8190
+ case "if": {
8191
+ this.collectExprRefs(stmt.cond, scope);
8192
+ const constant = isConstantBoolean(stmt.cond);
8193
+ if (constant === true) {
8194
+ this.collectStmtRefs(stmt.then, scope);
8195
+ } else if (constant === false) {
8196
+ if (stmt.else_) {
8197
+ this.collectStmtRefs(stmt.else_, scope);
8198
+ }
8199
+ } else {
8200
+ this.collectStmtRefs(stmt.then, scope);
8201
+ if (stmt.else_) {
8202
+ this.collectStmtRefs(stmt.else_, scope);
8203
+ }
8204
+ }
8205
+ break;
8206
+ }
8207
+ case "while":
8208
+ this.collectExprRefs(stmt.cond, scope);
8209
+ this.collectStmtRefs(stmt.body, scope);
8210
+ break;
8211
+ case "for":
8212
+ scope.push([]);
8213
+ if (stmt.init) {
8214
+ this.collectStmtRef(stmt.init, scope);
8215
+ }
8216
+ this.collectExprRefs(stmt.cond, scope);
8217
+ this.collectExprRefs(stmt.step, scope);
8218
+ this.collectStmtRefs(stmt.body, scope);
8219
+ scope.pop();
8220
+ break;
8221
+ case "foreach":
8222
+ this.collectExprRefs(stmt.iterable, scope);
8223
+ scope.push([{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]);
8224
+ this.collectStmtRefs(stmt.body, scope);
8225
+ scope.pop();
8226
+ break;
8227
+ case "for_range":
8228
+ this.collectExprRefs(stmt.start, scope);
8229
+ this.collectExprRefs(stmt.end, scope);
8230
+ scope.push([{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]);
8231
+ this.collectStmtRefs(stmt.body, scope);
8232
+ scope.pop();
8233
+ break;
8234
+ case "match":
8235
+ this.collectExprRefs(stmt.expr, scope);
8236
+ for (const arm of stmt.arms) {
8237
+ if (arm.pattern) {
8238
+ this.collectExprRefs(arm.pattern, scope);
8239
+ }
8240
+ this.collectStmtRefs(arm.body, scope);
8241
+ }
8242
+ break;
8243
+ case "as_block":
8244
+ case "at_block":
8245
+ case "as_at":
8246
+ case "execute":
8247
+ this.collectNestedStmtRefs(stmt, scope);
8248
+ break;
8249
+ case "raw":
8250
+ case "break":
8251
+ case "continue":
8252
+ break;
7766
8253
  }
7767
8254
  }
7768
- if (tickFunctionNames.length > 0 || triggerNames.size > 0 || eventHandlers.length > 0) {
7769
- files.push({
7770
- path: `data/${ns}/function/__tick.mcfunction`,
7771
- content: tickLines.join("\n")
7772
- });
7773
- files.push({
7774
- path: `data/minecraft/tags/function/tick.json`,
7775
- content: JSON.stringify({ values: [`${ns}:__tick`] }, null, 2)
7776
- });
7777
- }
7778
- for (const fn of module3.functions) {
7779
- const eventTrigger = fn.eventTrigger;
7780
- if (!eventTrigger) {
7781
- continue;
8255
+ collectNestedStmtRefs(stmt, scope) {
8256
+ if (stmt.kind === "execute") {
8257
+ for (const sub of stmt.subcommands) {
8258
+ if ("varName" in sub && sub.varName) {
8259
+ const resolved = this.resolveLocal(sub.varName, scope);
8260
+ if (resolved) {
8261
+ this.localReads.add(resolved.id);
8262
+ }
8263
+ }
8264
+ }
7782
8265
  }
7783
- let path2 = "";
7784
- let criteria = {};
7785
- switch (eventTrigger.kind) {
7786
- case "advancement":
7787
- path2 = `data/${ns}/advancements/on_advancement_${fn.name}.json`;
7788
- criteria = {
7789
- trigger: {
7790
- trigger: `minecraft:${eventTrigger.value}`
8266
+ this.collectStmtRefs(stmt.body, scope);
8267
+ }
8268
+ collectExprRefs(expr, scope) {
8269
+ switch (expr.kind) {
8270
+ case "ident": {
8271
+ const resolved = this.resolveLocal(expr.name, scope);
8272
+ if (resolved) {
8273
+ this.localReads.add(resolved.id);
8274
+ } else {
8275
+ this.usedConstants.add(expr.name);
8276
+ }
8277
+ break;
8278
+ }
8279
+ case "call":
8280
+ {
8281
+ const resolved = this.resolveLocal(expr.fn, scope);
8282
+ if (resolved) {
8283
+ this.localReads.add(resolved.id);
8284
+ } else if (this.functionMap.has(expr.fn)) {
8285
+ this.markReachable(expr.fn);
7791
8286
  }
7792
- };
8287
+ }
8288
+ for (const arg of expr.args) {
8289
+ this.collectExprRefs(arg, scope);
8290
+ }
7793
8291
  break;
7794
- case "craft":
7795
- path2 = `data/${ns}/advancements/on_craft_${fn.name}.json`;
7796
- criteria = {
7797
- crafted: {
7798
- trigger: "minecraft:inventory_changed",
7799
- conditions: {
7800
- items: [
7801
- {
7802
- items: [eventTrigger.value]
7803
- }
7804
- ]
7805
- }
8292
+ case "static_call":
8293
+ for (const arg of expr.args) {
8294
+ this.collectExprRefs(arg, scope);
8295
+ }
8296
+ break;
8297
+ case "invoke":
8298
+ this.collectExprRefs(expr.callee, scope);
8299
+ for (const arg of expr.args) {
8300
+ this.collectExprRefs(arg, scope);
8301
+ }
8302
+ break;
8303
+ case "member":
8304
+ this.collectExprRefs(expr.obj, scope);
8305
+ break;
8306
+ case "member_assign":
8307
+ this.collectExprRefs(expr.obj, scope);
8308
+ this.collectExprRefs(expr.value, scope);
8309
+ break;
8310
+ case "index":
8311
+ this.collectExprRefs(expr.obj, scope);
8312
+ this.collectExprRefs(expr.index, scope);
8313
+ break;
8314
+ case "array_lit":
8315
+ expr.elements.forEach((element) => this.collectExprRefs(element, scope));
8316
+ break;
8317
+ case "struct_lit":
8318
+ expr.fields.forEach((field) => this.collectExprRefs(field.value, scope));
8319
+ break;
8320
+ case "binary":
8321
+ this.collectExprRefs(expr.left, scope);
8322
+ this.collectExprRefs(expr.right, scope);
8323
+ break;
8324
+ case "is_check":
8325
+ this.collectExprRefs(expr.expr, scope);
8326
+ break;
8327
+ case "unary":
8328
+ this.collectExprRefs(expr.operand, scope);
8329
+ break;
8330
+ case "assign": {
8331
+ this.collectExprRefs(expr.value, scope);
8332
+ break;
8333
+ }
8334
+ case "str_interp":
8335
+ expr.parts.forEach((part) => {
8336
+ if (typeof part !== "string") {
8337
+ this.collectExprRefs(part, scope);
7806
8338
  }
7807
- };
8339
+ });
7808
8340
  break;
7809
- case "death":
7810
- path2 = `data/${ns}/advancements/on_death_${fn.name}.json`;
7811
- criteria = {
7812
- death: {
7813
- trigger: "minecraft:entity_killed_player"
8341
+ case "f_string":
8342
+ expr.parts.forEach((part) => {
8343
+ if (part.kind === "expr") {
8344
+ this.collectExprRefs(part.expr, scope);
7814
8345
  }
7815
- };
8346
+ });
7816
8347
  break;
7817
- case "login":
7818
- case "join_team":
7819
- continue;
7820
- }
7821
- advancements.push({
7822
- path: path2,
7823
- content: JSON.stringify({
7824
- criteria,
7825
- rewards: {
7826
- function: `${ns}:${fn.name}`
8348
+ case "lambda": {
8349
+ const lambdaScope = [
8350
+ ...scope.map((entries) => [...entries]),
8351
+ expr.params.map((param) => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name }))
8352
+ ];
8353
+ if (Array.isArray(expr.body)) {
8354
+ this.collectStmtRefs(expr.body, lambdaScope);
8355
+ } else {
8356
+ this.collectExprRefs(expr.body, lambdaScope);
7827
8357
  }
7828
- }, null, 2)
7829
- });
8358
+ break;
8359
+ }
8360
+ case "blockpos":
8361
+ case "bool_lit":
8362
+ case "byte_lit":
8363
+ case "double_lit":
8364
+ case "float_lit":
8365
+ case "int_lit":
8366
+ case "long_lit":
8367
+ case "mc_name":
8368
+ case "range_lit":
8369
+ case "selector":
8370
+ case "short_lit":
8371
+ case "str_lit":
8372
+ break;
8373
+ }
7830
8374
  }
7831
- const stats = (0, commands_1.createEmptyOptimizationStats)();
7832
- if (!optimizeCommands) {
7833
- return { files, advancements, stats };
8375
+ resolveLocal(name, scope) {
8376
+ for (let i = scope.length - 1; i >= 0; i--) {
8377
+ for (let j = scope[i].length - 1; j >= 0; j--) {
8378
+ if (scope[i][j].name === name) {
8379
+ return scope[i][j];
8380
+ }
8381
+ }
8382
+ }
8383
+ return null;
7834
8384
  }
7835
- const optimized = applyFunctionOptimization(files);
7836
- (0, commands_1.mergeOptimizationStats)(stats, optimized.stats);
7837
- return { files: optimized.files, advancements, stats };
7838
- }
7839
- function generateDatapack(module3) {
7840
- const generated = generateDatapackWithStats(module3);
7841
- return [...generated.files, ...generated.advancements];
7842
- }
7843
- }
7844
- });
7845
-
7846
- // ../../dist/compile.js
7847
- var require_compile = __commonJS({
7848
- "../../dist/compile.js"(exports2) {
7849
- "use strict";
7850
- var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
7851
- if (k2 === void 0) k2 = k;
7852
- var desc = Object.getOwnPropertyDescriptor(m, k);
7853
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7854
- desc = { enumerable: true, get: function() {
7855
- return m[k];
7856
- } };
8385
+ transformFunction(fn) {
8386
+ const scope = [fn.params.map((param) => ({ id: `param:${fn.name}:${param.name}`, name: param.name }))];
8387
+ const body = this.transformBlock(fn.body, scope);
8388
+ return body === fn.body ? fn : copySpan({ ...fn, body }, fn);
7857
8389
  }
7858
- Object.defineProperty(o, k2, desc);
7859
- }) : (function(o, m, k, k2) {
7860
- if (k2 === void 0) k2 = k;
7861
- o[k2] = m[k];
7862
- }));
7863
- var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
7864
- Object.defineProperty(o, "default", { enumerable: true, value: v });
7865
- }) : function(o, v) {
7866
- o["default"] = v;
7867
- });
7868
- var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
7869
- var ownKeys = function(o) {
7870
- ownKeys = Object.getOwnPropertyNames || function(o2) {
7871
- var ar = [];
7872
- for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
7873
- return ar;
7874
- };
7875
- return ownKeys(o);
7876
- };
7877
- return function(mod) {
7878
- if (mod && mod.__esModule) return mod;
7879
- var result = {};
7880
- if (mod != null) {
7881
- for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
8390
+ transformBlock(block, scope) {
8391
+ scope.push([]);
8392
+ const transformed = [];
8393
+ for (const stmt of block) {
8394
+ const next = this.transformStmt(stmt, scope);
8395
+ transformed.push(...next);
7882
8396
  }
7883
- __setModuleDefault(result, mod);
7884
- return result;
7885
- };
7886
- })();
7887
- Object.defineProperty(exports2, "__esModule", { value: true });
7888
- exports2.preprocessSourceWithMetadata = preprocessSourceWithMetadata;
7889
- exports2.preprocessSource = preprocessSource;
7890
- exports2.compile = compile;
7891
- exports2.formatCompileError = formatCompileError;
7892
- var fs2 = __importStar(require("fs"));
7893
- var path2 = __importStar(require("path"));
7894
- var lexer_1 = require_lexer();
7895
- var parser_1 = require_parser();
7896
- var lowering_1 = require_lowering();
7897
- var passes_1 = require_passes();
7898
- var dce_1 = require_dce();
7899
- var mcfunction_1 = require_mcfunction();
7900
- var diagnostics_1 = require_diagnostics();
7901
- var IMPORT_RE = /^\s*import\s+"([^"]+)"\s*;?\s*$/;
7902
- function countLines(source) {
7903
- return source === "" ? 0 : source.split("\n").length;
7904
- }
7905
- function offsetRanges(ranges, lineOffset) {
7906
- return ranges.map((range) => ({
7907
- startLine: range.startLine + lineOffset,
7908
- endLine: range.endLine + lineOffset,
7909
- filePath: range.filePath
7910
- }));
7911
- }
7912
- function preprocessSourceWithMetadata(source, options = {}) {
7913
- const { filePath } = options;
7914
- const seen = options.seen ?? /* @__PURE__ */ new Set();
7915
- if (filePath) {
7916
- seen.add(path2.resolve(filePath));
8397
+ scope.pop();
8398
+ return transformed;
7917
8399
  }
7918
- const lines = source.split("\n");
7919
- const imports = [];
7920
- const bodyLines = [];
7921
- let parsingHeader = true;
7922
- for (let i = 0; i < lines.length; i++) {
7923
- const line = lines[i];
7924
- const trimmed = line.trim();
7925
- const match = line.match(IMPORT_RE);
7926
- if (parsingHeader && match) {
7927
- if (!filePath) {
7928
- throw new diagnostics_1.DiagnosticError("ParseError", "Import statements require a file path", { line: i + 1, col: 1 }, lines);
8400
+ transformStmt(stmt, scope) {
8401
+ switch (stmt.kind) {
8402
+ case "let": {
8403
+ const init = this.transformExpr(stmt.init, scope);
8404
+ const id = this.localDeclIds.get(stmt) ?? `local:${stmt.name}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`;
8405
+ scope[scope.length - 1].push({ id, name: stmt.name });
8406
+ if (this.localReads.has(id)) {
8407
+ if (init === stmt.init) {
8408
+ return [stmt];
8409
+ }
8410
+ return [copySpan({ ...stmt, init }, stmt)];
8411
+ }
8412
+ this.warnings.push({
8413
+ message: `Unused variable '${stmt.name}'`,
8414
+ code: "W_UNUSED_VAR",
8415
+ line: stmt.span?.line,
8416
+ col: stmt.span?.col
8417
+ });
8418
+ if (isPureExpr(init)) {
8419
+ return [];
8420
+ }
8421
+ return [copySpan({ kind: "expr", expr: init }, stmt)];
7929
8422
  }
7930
- const importPath = path2.resolve(path2.dirname(filePath), match[1]);
7931
- if (!seen.has(importPath)) {
7932
- seen.add(importPath);
7933
- let importedSource;
7934
- try {
7935
- importedSource = fs2.readFileSync(importPath, "utf-8");
7936
- } catch {
7937
- throw new diagnostics_1.DiagnosticError("ParseError", `Cannot import '${match[1]}'`, { file: filePath, line: i + 1, col: 1 }, lines);
8423
+ case "expr": {
8424
+ const expr = this.transformExpr(stmt.expr, scope);
8425
+ if (expr.kind === "assign") {
8426
+ const resolved = this.resolveLocal(expr.target, scope);
8427
+ if (resolved && !this.localReads.has(resolved.id)) {
8428
+ if (isPureExpr(expr.value)) {
8429
+ return [];
8430
+ }
8431
+ return [copySpan({ kind: "expr", expr: expr.value }, stmt)];
8432
+ }
8433
+ }
8434
+ if (expr === stmt.expr) {
8435
+ return [stmt];
7938
8436
  }
7939
- imports.push(preprocessSourceWithMetadata(importedSource, { filePath: importPath, seen }));
8437
+ return [copySpan({ ...stmt, expr }, stmt)];
7940
8438
  }
7941
- continue;
7942
- }
7943
- if (parsingHeader && (trimmed === "" || trimmed.startsWith("//"))) {
7944
- bodyLines.push(line);
7945
- continue;
8439
+ case "return": {
8440
+ if (!stmt.value) {
8441
+ return [stmt];
8442
+ }
8443
+ const value = this.transformExpr(stmt.value, scope);
8444
+ if (value === stmt.value) {
8445
+ return [stmt];
8446
+ }
8447
+ return [copySpan({ ...stmt, value }, stmt)];
8448
+ }
8449
+ case "if": {
8450
+ const cond = this.transformExpr(stmt.cond, scope);
8451
+ const constant = isConstantBoolean(cond);
8452
+ if (constant === true) {
8453
+ return this.transformBlock(stmt.then, scope);
8454
+ }
8455
+ if (constant === false) {
8456
+ return stmt.else_ ? this.transformBlock(stmt.else_, scope) : [];
8457
+ }
8458
+ const thenBlock = this.transformBlock(stmt.then, scope);
8459
+ const elseBlock = stmt.else_ ? this.transformBlock(stmt.else_, scope) : void 0;
8460
+ if (cond === stmt.cond && thenBlock === stmt.then && elseBlock === stmt.else_) {
8461
+ return [stmt];
8462
+ }
8463
+ return [copySpan({ ...stmt, cond, then: thenBlock, else_: elseBlock }, stmt)];
8464
+ }
8465
+ case "while": {
8466
+ const cond = this.transformExpr(stmt.cond, scope);
8467
+ if (isConstantBoolean(cond) === false) {
8468
+ return [];
8469
+ }
8470
+ const body = this.transformBlock(stmt.body, scope);
8471
+ return [copySpan({ ...stmt, cond, body }, stmt)];
8472
+ }
8473
+ case "for": {
8474
+ const forScope = [...scope, []];
8475
+ const init = stmt.init ? this.transformStmt(stmt.init, forScope)[0] : void 0;
8476
+ const cond = this.transformExpr(stmt.cond, forScope);
8477
+ if (isConstantBoolean(cond) === false) {
8478
+ return init ? [init] : [];
8479
+ }
8480
+ const step = this.transformExpr(stmt.step, forScope);
8481
+ const body = this.transformBlock(stmt.body, forScope);
8482
+ return [copySpan({ ...stmt, init, cond, step, body }, stmt)];
8483
+ }
8484
+ case "foreach": {
8485
+ const iterable = this.transformExpr(stmt.iterable, scope);
8486
+ const foreachScope = [...scope, [{ id: `foreach:${stmt.binding}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.binding }]];
8487
+ const body = this.transformBlock(stmt.body, foreachScope);
8488
+ return [copySpan({ ...stmt, iterable, body }, stmt)];
8489
+ }
8490
+ case "for_range": {
8491
+ const start = this.transformExpr(stmt.start, scope);
8492
+ const end = this.transformExpr(stmt.end, scope);
8493
+ const rangeScope = [...scope, [{ id: `range:${stmt.varName}:${stmt.span?.line ?? 0}:${stmt.span?.col ?? 0}`, name: stmt.varName }]];
8494
+ const body = this.transformBlock(stmt.body, rangeScope);
8495
+ return [copySpan({ ...stmt, start, end, body }, stmt)];
8496
+ }
8497
+ case "match": {
8498
+ const expr = this.transformExpr(stmt.expr, scope);
8499
+ const arms = stmt.arms.map((arm) => ({
8500
+ pattern: arm.pattern ? this.transformExpr(arm.pattern, scope) : null,
8501
+ body: this.transformBlock(arm.body, scope)
8502
+ }));
8503
+ return [copySpan({ ...stmt, expr, arms }, stmt)];
8504
+ }
8505
+ case "as_block":
8506
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
8507
+ case "at_block":
8508
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
8509
+ case "as_at":
8510
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
8511
+ case "execute":
8512
+ return [copySpan({ ...stmt, body: this.transformBlock(stmt.body, scope) }, stmt)];
8513
+ case "raw":
8514
+ return [stmt];
8515
+ case "break":
8516
+ return [stmt];
8517
+ case "continue":
8518
+ return [stmt];
7946
8519
  }
7947
- parsingHeader = false;
7948
- bodyLines.push(line);
7949
- }
7950
- const body = bodyLines.join("\n");
7951
- const parts = [...imports.map((entry) => entry.source), body].filter(Boolean);
7952
- const combined = parts.join("\n");
7953
- const ranges = [];
7954
- let lineOffset = 0;
7955
- for (const entry of imports) {
7956
- ranges.push(...offsetRanges(entry.ranges, lineOffset));
7957
- lineOffset += countLines(entry.source);
7958
- }
7959
- if (filePath && body) {
7960
- ranges.push({
7961
- startLine: lineOffset + 1,
7962
- endLine: lineOffset + countLines(body),
7963
- filePath: path2.resolve(filePath)
7964
- });
7965
8520
  }
7966
- return { source: combined, ranges };
7967
- }
7968
- function preprocessSource(source, options = {}) {
7969
- return preprocessSourceWithMetadata(source, options).source;
7970
- }
7971
- function compile(source, options = {}) {
7972
- const { namespace = "redscript", filePath, optimize: shouldOptimize = true } = options;
7973
- const shouldRunDce = options.dce ?? shouldOptimize;
7974
- let sourceLines = source.split("\n");
7975
- try {
7976
- const preprocessed = preprocessSourceWithMetadata(source, { filePath });
7977
- const preprocessedSource = preprocessed.source;
7978
- sourceLines = preprocessedSource.split("\n");
7979
- const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
7980
- const parsedAst = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
7981
- const dceResult = shouldRunDce ? (0, dce_1.eliminateDeadCode)(parsedAst) : { program: parsedAst, warnings: [] };
7982
- const ast = dceResult.program;
7983
- const ir = new lowering_1.Lowering(namespace, preprocessed.ranges).lower(ast);
7984
- const optimized = shouldOptimize ? { ...ir, functions: ir.functions.map((fn) => (0, passes_1.optimize)(fn)) } : ir;
7985
- const generated = (0, mcfunction_1.generateDatapackWithStats)(optimized);
7986
- return {
7987
- success: true,
7988
- files: [...generated.files, ...generated.advancements],
7989
- advancements: generated.advancements,
7990
- ast,
7991
- ir: optimized
7992
- };
7993
- } catch (err) {
7994
- if (err instanceof diagnostics_1.DiagnosticError) {
7995
- return { success: false, error: err };
7996
- }
7997
- if (err instanceof Error) {
7998
- const diagnostic = (0, diagnostics_1.parseErrorMessage)("ParseError", err.message, sourceLines, filePath);
7999
- return { success: false, error: diagnostic };
8521
+ transformExpr(expr, scope) {
8522
+ switch (expr.kind) {
8523
+ case "call":
8524
+ return copySpan({ ...expr, args: expr.args.map((arg) => this.transformExpr(arg, scope)) }, expr);
8525
+ case "static_call":
8526
+ return copySpan({ ...expr, args: expr.args.map((arg) => this.transformExpr(arg, scope)) }, expr);
8527
+ case "invoke":
8528
+ return copySpan({
8529
+ ...expr,
8530
+ callee: this.transformExpr(expr.callee, scope),
8531
+ args: expr.args.map((arg) => this.transformExpr(arg, scope))
8532
+ }, expr);
8533
+ case "binary":
8534
+ return copySpan({
8535
+ ...expr,
8536
+ left: this.transformExpr(expr.left, scope),
8537
+ right: this.transformExpr(expr.right, scope)
8538
+ }, expr);
8539
+ case "is_check":
8540
+ return copySpan({ ...expr, expr: this.transformExpr(expr.expr, scope) }, expr);
8541
+ case "unary":
8542
+ return copySpan({ ...expr, operand: this.transformExpr(expr.operand, scope) }, expr);
8543
+ case "assign":
8544
+ return copySpan({ ...expr, value: this.transformExpr(expr.value, scope) }, expr);
8545
+ case "member":
8546
+ return copySpan({ ...expr, obj: this.transformExpr(expr.obj, scope) }, expr);
8547
+ case "member_assign":
8548
+ return copySpan({
8549
+ ...expr,
8550
+ obj: this.transformExpr(expr.obj, scope),
8551
+ value: this.transformExpr(expr.value, scope)
8552
+ }, expr);
8553
+ case "index":
8554
+ return copySpan({
8555
+ ...expr,
8556
+ obj: this.transformExpr(expr.obj, scope),
8557
+ index: this.transformExpr(expr.index, scope)
8558
+ }, expr);
8559
+ case "array_lit":
8560
+ return copySpan({ ...expr, elements: expr.elements.map((element) => this.transformExpr(element, scope)) }, expr);
8561
+ case "struct_lit":
8562
+ return copySpan({
8563
+ ...expr,
8564
+ fields: expr.fields.map((field) => ({ ...field, value: this.transformExpr(field.value, scope) }))
8565
+ }, expr);
8566
+ case "str_interp":
8567
+ return copySpan({
8568
+ ...expr,
8569
+ parts: expr.parts.map((part) => typeof part === "string" ? part : this.transformExpr(part, scope))
8570
+ }, expr);
8571
+ case "f_string":
8572
+ return copySpan({
8573
+ ...expr,
8574
+ parts: expr.parts.map((part) => part.kind === "text" ? part : { kind: "expr", expr: this.transformExpr(part.expr, scope) })
8575
+ }, expr);
8576
+ case "lambda": {
8577
+ const lambdaScope = [
8578
+ ...scope.map((entries) => [...entries]),
8579
+ expr.params.map((param) => ({ id: `lambda:${param.name}:${expr.span?.line ?? 0}:${expr.span?.col ?? 0}`, name: param.name }))
8580
+ ];
8581
+ const body = Array.isArray(expr.body) ? this.transformBlock(expr.body, lambdaScope) : this.transformExpr(expr.body, lambdaScope);
8582
+ return copySpan({ ...expr, body }, expr);
8583
+ }
8584
+ case "blockpos":
8585
+ case "bool_lit":
8586
+ case "byte_lit":
8587
+ case "double_lit":
8588
+ case "float_lit":
8589
+ case "ident":
8590
+ case "int_lit":
8591
+ case "long_lit":
8592
+ case "mc_name":
8593
+ case "range_lit":
8594
+ case "rel_coord":
8595
+ case "local_coord":
8596
+ case "selector":
8597
+ case "short_lit":
8598
+ case "str_lit":
8599
+ return expr;
8000
8600
  }
8001
- return {
8002
- success: false,
8003
- error: new diagnostics_1.DiagnosticError("ParseError", String(err), { file: filePath, line: 1, col: 1 }, sourceLines)
8004
- };
8005
- }
8006
- }
8007
- function formatCompileError(result) {
8008
- if (result.success) {
8009
- return "Compilation successful";
8010
8601
  }
8011
- if (result.error) {
8012
- return (0, diagnostics_1.formatError)(result.error, result.error.sourceLines?.join("\n"));
8602
+ };
8603
+ exports2.DeadCodeEliminator = DeadCodeEliminator;
8604
+ function eliminateDeadCode(program, sourceRanges) {
8605
+ const eliminator = new DeadCodeEliminator();
8606
+ const result = eliminator.eliminate(program);
8607
+ let warnings = eliminator.warnings;
8608
+ if (sourceRanges && sourceRanges.length > 0) {
8609
+ const { resolveSourceLine } = require_compile();
8610
+ warnings = warnings.map((w) => {
8611
+ if (w.line == null)
8612
+ return w;
8613
+ const resolved = resolveSourceLine(w.line, sourceRanges);
8614
+ return { ...w, line: resolved.line, filePath: resolved.filePath ?? w.filePath };
8615
+ });
8013
8616
  }
8014
- return "Unknown error";
8617
+ return { program: result, warnings };
8015
8618
  }
8016
8619
  }
8017
8620
  });
@@ -8366,22 +8969,42 @@ var require_dist = __commonJS({
8366
8969
  const shouldOptimize = options.optimize ?? true;
8367
8970
  const shouldTypeCheck = options.typeCheck ?? true;
8368
8971
  const shouldRunDce = options.dce ?? shouldOptimize;
8972
+ const mangle = options.mangle ?? false;
8369
8973
  const filePath = options.filePath;
8370
8974
  const preprocessed = (0, compile_1.preprocessSourceWithMetadata)(source, { filePath });
8371
8975
  const preprocessedSource = preprocessed.source;
8372
8976
  const tokens = new lexer_1.Lexer(preprocessedSource, filePath).tokenize();
8373
8977
  const parsedAst = new parser_1.Parser(tokens, preprocessedSource, filePath).parse(namespace);
8374
- const dceResult = shouldRunDce ? (0, dce_1.eliminateDeadCode)(parsedAst) : { program: parsedAst, warnings: [] };
8978
+ const allLibrarySources = [];
8979
+ for (const li of preprocessed.libraryImports ?? []) {
8980
+ allLibrarySources.push({ src: li.source, fp: li.filePath });
8981
+ }
8982
+ for (const { src, fp } of allLibrarySources) {
8983
+ const libPreprocessed = (0, compile_1.preprocessSourceWithMetadata)(src, fp ? { filePath: fp } : {});
8984
+ const libTokens = new lexer_1.Lexer(libPreprocessed.source, fp).tokenize();
8985
+ const libAst = new parser_1.Parser(libTokens, libPreprocessed.source, fp).parse(namespace);
8986
+ for (const fn of libAst.declarations)
8987
+ fn.isLibraryFn = true;
8988
+ parsedAst.declarations.push(...libAst.declarations);
8989
+ parsedAst.structs.push(...libAst.structs);
8990
+ parsedAst.implBlocks.push(...libAst.implBlocks);
8991
+ parsedAst.enums.push(...libAst.enums);
8992
+ parsedAst.consts.push(...libAst.consts);
8993
+ parsedAst.globals.push(...libAst.globals);
8994
+ }
8995
+ const dceResult = shouldRunDce ? (0, dce_1.eliminateDeadCode)(parsedAst, preprocessed.ranges) : { program: parsedAst, warnings: [] };
8375
8996
  const ast = dceResult.program;
8376
8997
  let typeErrors;
8377
8998
  if (shouldTypeCheck) {
8378
8999
  const checker = new typechecker_1.TypeChecker(preprocessedSource, filePath);
8379
9000
  typeErrors = checker.check(ast);
8380
9001
  }
9002
+ const scoreboardObj = options.scoreboardObjective ?? `__${namespace}`;
9003
+ (0, lowering_1.setScoreboardObjective)(scoreboardObj);
8381
9004
  const lowering = new lowering_1.Lowering(namespace, preprocessed.ranges);
8382
9005
  const ir = lowering.lower(ast);
8383
9006
  let optimizedIR = ir;
8384
- let generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: shouldOptimize });
9007
+ let generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: shouldOptimize, mangle, scoreboardObjective: scoreboardObj });
8385
9008
  let optimizationStats;
8386
9009
  if (shouldOptimize) {
8387
9010
  const stats = (0, commands_1.createEmptyOptimizationStats)();
@@ -8397,10 +9020,10 @@ var require_dist = __commonJS({
8397
9020
  }
8398
9021
  const copyPropagatedIR = { ...ir, functions: copyPropagatedFunctions };
8399
9022
  optimizedIR = { ...ir, functions: deadCodeEliminatedFunctions };
8400
- const baselineGenerated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false });
8401
- const beforeDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(copyPropagatedIR, { optimizeCommands: false });
8402
- const afterDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: false });
8403
- generated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: true });
9023
+ const baselineGenerated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
9024
+ const beforeDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(copyPropagatedIR, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
9025
+ const afterDceGenerated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
9026
+ generated = (0, mcfunction_1.generateDatapackWithStats)(optimizedIR, { optimizeCommands: true, mangle, scoreboardObjective: scoreboardObj });
8404
9027
  stats.deadCodeRemoved = (0, mcfunction_1.countMcfunctionCommands)(beforeDceGenerated.files) - (0, mcfunction_1.countMcfunctionCommands)(afterDceGenerated.files);
8405
9028
  stats.licmHoists = generated.stats.licmHoists;
8406
9029
  stats.licmLoopBodies = generated.stats.licmLoopBodies;
@@ -8414,7 +9037,7 @@ var require_dist = __commonJS({
8414
9037
  optimizationStats = stats;
8415
9038
  } else {
8416
9039
  optimizedIR = ir;
8417
- generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false });
9040
+ generated = (0, mcfunction_1.generateDatapackWithStats)(ir, { optimizeCommands: false, mangle, scoreboardObjective: scoreboardObj });
8418
9041
  }
8419
9042
  return {
8420
9043
  files: [...generated.files, ...generated.advancements],
@@ -8423,7 +9046,8 @@ var require_dist = __commonJS({
8423
9046
  ir: optimizedIR,
8424
9047
  typeErrors,
8425
9048
  warnings: [...dceResult.warnings, ...lowering.warnings],
8426
- stats: optimizationStats
9049
+ stats: optimizationStats,
9050
+ sourceMap: generated.sourceMap
8427
9051
  };
8428
9052
  }
8429
9053
  function check(source, namespace = "redscript", filePath) {
@@ -9343,6 +9967,22 @@ function registerHoverProvider(context) {
9343
9967
  vscode.languages.registerHoverProvider("redscript", {
9344
9968
  provideHover(document, position) {
9345
9969
  const line = document.lineAt(position.line).text;
9970
+ const rsRange = document.getWordRangeAtPosition(position, /#rs\b/);
9971
+ if (rsRange) {
9972
+ const md = new vscode.MarkdownString("", true);
9973
+ md.isTrusted = true;
9974
+ md.appendCodeblock("#rs", "redscript");
9975
+ md.appendMarkdown("**RS Internal Scoreboard Objective** *(compiler token)*\n\n");
9976
+ md.appendMarkdown("Resolves to the current datapack's internal scoreboard objective at compile time.\n\n");
9977
+ md.appendMarkdown("Default: `__<namespace>` (e.g. `__mygame` for namespace `mygame`).\n\n");
9978
+ md.appendMarkdown("Use `#rs` in `scoreboard_get` / `scoreboard_set` when you need to read or write\n");
9979
+ md.appendMarkdown("the compiler's own variable slots \u2014 such as in stdlib implementations.\n\n");
9980
+ md.appendMarkdown("> \u26A0\uFE0F Unlike other `#name` tokens, `#rs` does **not** compile to the literal string `rs`.\n");
9981
+ md.appendMarkdown("> It tracks the `--scoreboard` flag or the `__<namespace>` default.\n\n");
9982
+ md.appendMarkdown("**Example:**\n");
9983
+ md.appendCodeblock('scoreboard_set("timer_ticks", #rs, 0);\n// compiles to: scoreboard players set timer_ticks __mygame 0', "redscript");
9984
+ return new vscode.Hover(md, rsRange);
9985
+ }
9346
9986
  const mcRange = document.getWordRangeAtPosition(position, /#[a-zA-Z_][a-zA-Z0-9_]*/);
9347
9987
  if (mcRange) {
9348
9988
  const raw = document.getText(mcRange);