nomen-lang 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -7168,6 +7168,15 @@ var FunctionCallNode = class extends BaseNode {
7168
7168
  is_static;
7169
7169
  is_func_param;
7170
7170
  type_args;
7171
+ /**
7172
+ * True when `type_args` was NOT written at the call site — the checker
7173
+ * recovered the arguments from the expected (annotation / return /
7174
+ * parameter) type (check_function_call_node's expected-args branch). The
7175
+ * redundant-type-annotation lint consults this: an annotation that
7176
+ * instantiates the constructor is load-bearing and must never be flagged
7177
+ * as redundant, even though `type_args` is populated after checking.
7178
+ */
7179
+ type_args_from_expected;
7171
7180
  ref_param_indices;
7172
7181
  move_param_indices;
7173
7182
  /**
@@ -11760,7 +11769,7 @@ function resolve_static_value(node, status) {
11760
11769
  }
11761
11770
  return null;
11762
11771
  }
11763
- function build_array_values_node$1(node, status) {
11772
+ function build_array_values_node$2(node, status) {
11764
11773
  node.values.forEach((value, i) => {
11765
11774
  if (i > 0) emit_asm(status, ", ");
11766
11775
  const resolved = resolve_static_value(value, status);
@@ -15170,11 +15179,11 @@ function block(ctx, stmts) {
15170
15179
  out.push(...block(ctx, s.statements));
15171
15180
  continue;
15172
15181
  }
15173
- out.push(stmt(ctx, s));
15182
+ out.push(stmt$1(ctx, s));
15174
15183
  }
15175
15184
  return out;
15176
15185
  }
15177
- function stmt(ctx, n) {
15186
+ function stmt$1(ctx, n) {
15178
15187
  switch (n.node_type) {
15179
15188
  case "declare": return declare_stmt(ctx, n);
15180
15189
  case "assign": return assign_stmt(ctx, n);
@@ -15183,7 +15192,7 @@ function stmt(ctx, n) {
15183
15192
  return {
15184
15193
  kind: "return",
15185
15194
  node: n,
15186
- value: ret.value ? expr(ctx, ret.value) : null
15195
+ value: ret.value ? expr$1(ctx, ret.value) : null
15187
15196
  };
15188
15197
  }
15189
15198
  case "break": return {
@@ -15205,7 +15214,7 @@ function stmt(ctx, n) {
15205
15214
  return {
15206
15215
  kind: "if",
15207
15216
  node: n,
15208
- cond: expr(ctx, iff.condition),
15217
+ cond: expr$1(ctx, iff.condition),
15209
15218
  then_branch: block(ctx, iff.if_branch?.statements),
15210
15219
  else_branch: block(ctx, iff.else_branch?.statements)
15211
15220
  };
@@ -15215,9 +15224,9 @@ function stmt(ctx, n) {
15215
15224
  return {
15216
15225
  kind: "while",
15217
15226
  node: n,
15218
- cond: expr(ctx, wh.condition),
15227
+ cond: expr$1(ctx, wh.condition),
15219
15228
  body: block(ctx, wh.statements),
15220
- update: wh.update ? stmt(ctx, wh.update) : null
15229
+ update: wh.update ? stmt$1(ctx, wh.update) : null
15221
15230
  };
15222
15231
  }
15223
15232
  case "for": {
@@ -15226,9 +15235,9 @@ function stmt(ctx, n) {
15226
15235
  kind: "for",
15227
15236
  node: n,
15228
15237
  item_name: String(f.item?.value ?? ""),
15229
- list: f.list ? expr(ctx, f.list) : null,
15238
+ list: f.list ? expr$1(ctx, f.list) : null,
15230
15239
  body: block(ctx, f.statements),
15231
- update: f.update ? stmt(ctx, f.update) : null
15240
+ update: f.update ? stmt$1(ctx, f.update) : null
15232
15241
  };
15233
15242
  }
15234
15243
  case "switch":
@@ -15240,7 +15249,7 @@ function stmt(ctx, n) {
15240
15249
  node: n,
15241
15250
  scrutinee: null,
15242
15251
  arms: sw.cases.map((c) => ({
15243
- condition: c.condition ? expr(ctx, c.condition) : null,
15252
+ condition: c.condition ? expr$1(ctx, c.condition) : null,
15244
15253
  branch: block(ctx, c.branch?.statements)
15245
15254
  })),
15246
15255
  otherwise: sw.else_branch ? block(ctx, sw.else_branch.statements) : null
@@ -15250,9 +15259,9 @@ function stmt(ctx, n) {
15250
15259
  return {
15251
15260
  kind: "switch_match",
15252
15261
  node: n,
15253
- scrutinee: m.value ? expr(ctx, m.value) : null,
15262
+ scrutinee: m.value ? expr$1(ctx, m.value) : null,
15254
15263
  arms: m.cases.map((c) => ({
15255
- condition: c.match_value ? expr(ctx, c.match_value) : null,
15264
+ condition: c.match_value ? expr$1(ctx, c.match_value) : null,
15256
15265
  branch: block(ctx, c.branch?.statements)
15257
15266
  })),
15258
15267
  otherwise: m.else_branch ? block(ctx, m.else_branch.statements) : null
@@ -15264,13 +15273,13 @@ function stmt(ctx, n) {
15264
15273
  return {
15265
15274
  kind: "eval",
15266
15275
  node: n,
15267
- expr: expr(ctx, n)
15276
+ expr: expr$1(ctx, n)
15268
15277
  };
15269
15278
  }
15270
15279
  case "spawn": return {
15271
15280
  kind: "spawn",
15272
15281
  node: n,
15273
- call: expr(ctx, n.call)
15282
+ call: expr$1(ctx, n.call)
15274
15283
  };
15275
15284
  case "async_block": return {
15276
15285
  kind: "async_block",
@@ -15314,7 +15323,7 @@ function stmt(ctx, n) {
15314
15323
  if (is_expr_node(n)) return {
15315
15324
  kind: "eval",
15316
15325
  node: n,
15317
- expr: expr(ctx, n)
15326
+ expr: expr$1(ctx, n)
15318
15327
  };
15319
15328
  record_unknown(ctx, n);
15320
15329
  return {
@@ -15328,10 +15337,10 @@ function assign_stmt(ctx, a) {
15328
15337
  return {
15329
15338
  kind: "assign",
15330
15339
  node: a,
15331
- target: expr(ctx, a.left_value),
15332
- rhs: expr(ctx, a.right_value),
15340
+ target: expr$1(ctx, a.left_value),
15341
+ rhs: expr$1(ctx, a.right_value),
15333
15342
  operator: a.operator ?? null,
15334
- swap: a.swap ? expr(ctx, a.swap) : null
15343
+ swap: a.swap ? expr$1(ctx, a.swap) : null
15335
15344
  };
15336
15345
  }
15337
15346
  function declare_stmt(ctx, d) {
@@ -15349,8 +15358,8 @@ function declare_stmt(ctx, d) {
15349
15358
  is_ref: t?.is_ref,
15350
15359
  is_nullable: t?.is_nullable
15351
15360
  },
15352
- init: d.value ? expr(ctx, d.value) : null,
15353
- swap: d.swap ? expr(ctx, d.swap) : null,
15361
+ init: d.value ? expr$1(ctx, d.value) : null,
15362
+ swap: d.swap ? expr$1(ctx, d.swap) : null,
15354
15363
  node: d
15355
15364
  }
15356
15365
  };
@@ -15372,10 +15381,10 @@ function is_expr_node(n) {
15372
15381
  }
15373
15382
  }
15374
15383
  function call_facts(ctx, call) {
15375
- const args = (call.params ?? []).map((p) => expr(ctx, p));
15384
+ const args = (call.params ?? []).map((p) => expr$1(ctx, p));
15376
15385
  const swaps = [];
15377
15386
  const swap_map = call.swap_params;
15378
- if (swap_map instanceof Map) for (const v of swap_map.values()) swaps.push(expr(ctx, v));
15387
+ if (swap_map instanceof Map) for (const v of swap_map.values()) swaps.push(expr$1(ctx, v));
15379
15388
  return {
15380
15389
  args,
15381
15390
  facts: {
@@ -15385,7 +15394,7 @@ function call_facts(ctx, call) {
15385
15394
  }
15386
15395
  };
15387
15396
  }
15388
- function expr(ctx, n) {
15397
+ function expr$1(ctx, n) {
15389
15398
  if (!n || typeof n !== "object") return {
15390
15399
  kind: "leaf",
15391
15400
  node: n ?? {},
@@ -15405,15 +15414,15 @@ function expr(ctx, n) {
15405
15414
  return {
15406
15415
  kind: "binary",
15407
15416
  node: n,
15408
- left: expr(ctx, op.left_value),
15409
- right: expr(ctx, op.right_value)
15417
+ left: expr$1(ctx, op.left_value),
15418
+ right: expr$1(ctx, op.right_value)
15410
15419
  };
15411
15420
  }
15412
15421
  case "range": return {
15413
15422
  kind: "binary",
15414
15423
  node: n,
15415
- left: expr(ctx, n.left_value),
15416
- right: expr(ctx, n.right_value)
15424
+ left: expr$1(ctx, n.left_value),
15425
+ right: expr$1(ctx, n.right_value)
15417
15426
  };
15418
15427
  case "grouped":
15419
15428
  case "cast":
@@ -15422,7 +15431,7 @@ function expr(ctx, n) {
15422
15431
  return {
15423
15432
  kind: "wrap",
15424
15433
  node: n,
15425
- inner: inner ? expr(ctx, inner) : null
15434
+ inner: inner ? expr$1(ctx, inner) : null
15426
15435
  };
15427
15436
  }
15428
15437
  case "array": return {
@@ -15430,7 +15439,7 @@ function expr(ctx, n) {
15430
15439
  node: n,
15431
15440
  callee: "[array literal]",
15432
15441
  facts: {
15433
- args: (n.values ?? []).map((v) => expr(ctx, v)),
15442
+ args: (n.values ?? []).map((v) => expr$1(ctx, v)),
15434
15443
  ref_arg_indices: [],
15435
15444
  swap_exprs: []
15436
15445
  }
@@ -15467,7 +15476,7 @@ function expr(ctx, n) {
15467
15476
  return {
15468
15477
  kind: "method_call",
15469
15478
  node: n,
15470
- receiver: expr(ctx, acc.target),
15479
+ receiver: expr$1(ctx, acc.target),
15471
15480
  name: acc.access.name,
15472
15481
  facts: {
15473
15482
  ...facts,
@@ -15482,7 +15491,7 @@ function expr(ctx, n) {
15482
15491
  return {
15483
15492
  kind: "path",
15484
15493
  node: n,
15485
- receiver: expr(ctx, acc.target),
15494
+ receiver: expr$1(ctx, acc.target),
15486
15495
  steps
15487
15496
  };
15488
15497
  }
@@ -15496,7 +15505,7 @@ function expr(ctx, n) {
15496
15505
  node: n,
15497
15506
  scrutinee: null,
15498
15507
  arms: [{
15499
- condition: iff.condition ? expr(ctx, iff.condition) : null,
15508
+ condition: iff.condition ? expr$1(ctx, iff.condition) : null,
15500
15509
  branch: block(ctx, iff.if_branch?.statements)
15501
15510
  }],
15502
15511
  otherwise: iff.else_branch ? block(ctx, iff.else_branch.statements) : null
@@ -15509,7 +15518,7 @@ function expr(ctx, n) {
15509
15518
  node: n,
15510
15519
  scrutinee: null,
15511
15520
  arms: sw.cases.map((c) => ({
15512
- condition: c.condition ? expr(ctx, c.condition) : null,
15521
+ condition: c.condition ? expr$1(ctx, c.condition) : null,
15513
15522
  branch: block(ctx, c.branch?.statements)
15514
15523
  })),
15515
15524
  otherwise: sw.else_branch ? block(ctx, sw.else_branch.statements) : null
@@ -15519,9 +15528,9 @@ function expr(ctx, n) {
15519
15528
  return {
15520
15529
  kind: "flow",
15521
15530
  node: n,
15522
- scrutinee: m.value ? expr(ctx, m.value) : null,
15531
+ scrutinee: m.value ? expr$1(ctx, m.value) : null,
15523
15532
  arms: m.cases.map((c) => ({
15524
- condition: c.match_value ? expr(ctx, c.match_value) : null,
15533
+ condition: c.match_value ? expr$1(ctx, c.match_value) : null,
15525
15534
  branch: block(ctx, c.branch?.statements)
15526
15535
  })),
15527
15536
  otherwise: m.else_branch ? block(ctx, m.else_branch.statements) : null
@@ -15530,14 +15539,14 @@ function expr(ctx, n) {
15530
15539
  case "spawn": return {
15531
15540
  kind: "spawn",
15532
15541
  node: n,
15533
- call: expr(ctx, n.call)
15542
+ call: expr$1(ctx, n.call)
15534
15543
  };
15535
15544
  case "anon_struct": {
15536
15545
  const anon = n;
15537
15546
  return {
15538
15547
  kind: "wrap",
15539
15548
  node: n,
15540
- inner: anon.base ? expr(ctx, anon.base) : null
15549
+ inner: anon.base ? expr$1(ctx, anon.base) : null
15541
15550
  };
15542
15551
  }
15543
15552
  case "func": return {
@@ -15564,8 +15573,8 @@ function expr(ctx, n) {
15564
15573
  return {
15565
15574
  kind: "binary",
15566
15575
  node: n,
15567
- left: expr(ctx, idx.target),
15568
- right: expr(ctx, idx.index)
15576
+ left: expr$1(ctx, idx.target),
15577
+ right: expr$1(ctx, idx.index)
15569
15578
  };
15570
15579
  }
15571
15580
  record_unknown(ctx, n);
@@ -26370,7 +26379,7 @@ function peephole_optimize(code) {
26370
26379
  }
26371
26380
  return lines.join("\n");
26372
26381
  }
26373
- function build_function_node$1(node, status) {
26382
+ function build_function_node$2(node, status) {
26374
26383
  if (node.is_generic) return;
26375
26384
  if (node.is_foreign) {
26376
26385
  build_foreign$1(node, status);
@@ -26964,6 +26973,8 @@ function build_function_node$1(node, status) {
26964
26973
  if (!status.heap_returning_functions) status.heap_returning_functions = /* @__PURE__ */ new Set();
26965
26974
  status.heap_returning_functions.add(label_name);
26966
26975
  }
26976
+ const tail_branch = `b ${return_label}\n`;
26977
+ if (status.code.endsWith(tail_branch)) status.code = status.code.slice(0, status.code.length - tail_branch.length);
26967
26978
  emit_asm(status, `${return_label}:\n`);
26968
26979
  if (node.name === "main") {
26969
26980
  if (status.used_fibers) {
@@ -27277,7 +27288,7 @@ function resolve_global_const_value(name, status) {
27277
27288
  return;
27278
27289
  }
27279
27290
  }
27280
- function build_struct_node$1(node, status) {
27291
+ function build_struct_node$2(node, status) {
27281
27292
  if (node.is_generic) return;
27282
27293
  const is_nested = !!status.function_return_label;
27283
27294
  for (const func of node.functions) ensure_runtime_for_method_a64(func, status);
@@ -27730,7 +27741,7 @@ function build_init_function(node, status) {
27730
27741
  } else if (field.value.node_type === "func" && field.type.name === "func") {
27731
27742
  const prev_return_label = status.function_return_label;
27732
27743
  status.function_return_label = `.return_${func_name}`;
27733
- build_function_node$1(field.value, status);
27744
+ build_function_node$2(field.value, status);
27734
27745
  status.function_return_label = prev_return_label;
27735
27746
  ensure_newline(status);
27736
27747
  emit_descriptor_address(status, "x1", materialize_lambda_descriptor_a64(field.value, status));
@@ -29145,11 +29156,11 @@ function build_block_node$1(node, status) {
29145
29156
  const declarations_before = node.node_type === "func" ? 0 : status.scoped_declarations.length;
29146
29157
  for (let child of node.statements) if (is_struct_node(child)) {
29147
29158
  if (!should_emit_definition(child, status.emit_mode, status.structs, status.system_struct_names)) continue;
29148
- build_struct_node$1(child, status);
29159
+ build_struct_node$2(child, status);
29149
29160
  }
29150
29161
  for (let child of node.statements) if (is_function_node(child)) {
29151
29162
  if (!should_emit_definition(child, status.emit_mode, status.structs, status.system_struct_names)) continue;
29152
- build_function_node$1(child, status);
29163
+ build_function_node$2(child, status);
29153
29164
  }
29154
29165
  if (node.node_type !== "root" && node.name?.toLocaleLowerCase() === "main" && status.module_init_statements?.length) {
29155
29166
  const prologue = status.module_init_statements;
@@ -30076,7 +30087,7 @@ let label_counter$4 = 0;
30076
30087
  function reset_label_counter$4() {
30077
30088
  label_counter$4 = 0;
30078
30089
  }
30079
- function build_for_loop_node$1(node, status, nir, vector) {
30090
+ function build_for_loop_node$2(node, status, nir, vector) {
30080
30091
  const old_scoped_declarations = enter_scope_frame(status);
30081
30092
  const label = label_counter$4++;
30082
30093
  const item_name = node.item.value;
@@ -30401,7 +30412,7 @@ function reset_label_counter$3() {
30401
30412
  function next_if_label() {
30402
30413
  return label_counter$3++;
30403
30414
  }
30404
- function build_if_else_node$1(node, status, nir) {
30415
+ function build_if_else_node$2(node, status, nir) {
30405
30416
  if (!node.condition) {
30406
30417
  if (!status.build_errors) status.build_errors = [];
30407
30418
  status.build_errors.push({
@@ -32390,7 +32401,7 @@ const COMPILER_TEMP_PREFIXES = [
32390
32401
  function is_compiler_temp_name$1(name) {
32391
32402
  return !!name && COMPILER_TEMP_PREFIXES.some((p) => name.startsWith(p));
32392
32403
  }
32393
- function build_function_call_node$1(node, status) {
32404
+ function build_function_call_node$2(node, status) {
32394
32405
  hoist_field_overrides(node, build_node, status);
32395
32406
  if (node.is_enum_shorthand && node.params.length > 0) {
32396
32407
  const found = find_enum_for_case(node.name, status);
@@ -33159,7 +33170,7 @@ function emit_return_value$1(value, nir_value, status) {
33159
33170
  }
33160
33171
  build_node(value, status);
33161
33172
  }
33162
- function build_return_node$1(node, status, nir_value) {
33173
+ function build_return_node$2(node, status, nir_value) {
33163
33174
  hoist_field_overrides(node.value, build_node, status);
33164
33175
  if (node.from_inline) return;
33165
33176
  const returns_nullable_struct = is_nullable_struct_type(status.function_return_type, status);
@@ -33719,7 +33730,7 @@ function mov_immediate_x0_and_store(status, name, value) {
33719
33730
  emit_asm(status, `mov x0, #${value}\n`);
33720
33731
  emit_var_store(status, "x0", name, 8);
33721
33732
  }
33722
- function build_while_loop_node$1(node, status, nir, vector, unroll) {
33733
+ function build_while_loop_node$2(node, status, nir, vector, unroll) {
33723
33734
  const old_scoped_declarations = enter_scope_frame(status);
33724
33735
  const promoted = [];
33725
33736
  const saved_reg_allocs = status.register_allocations ? new Map(status.register_allocations) : void 0;
@@ -35488,18 +35499,18 @@ function emit_stmt_dispatch(child, index, statements, status) {
35488
35499
  const nstmt = ctx.stmts[index];
35489
35500
  switch (nstmt.kind) {
35490
35501
  case "if":
35491
- build_if_else_node$1(child, status, nstmt);
35502
+ build_if_else_node$2(child, status, nstmt);
35492
35503
  return 1;
35493
35504
  case "while": {
35494
35505
  const neon_plan = neon_vectorization_enabled() ? plan_vector_loop(nstmt, index, ctx.stmts, status) : null;
35495
35506
  const unroll_plan = plan_full_unroll(nstmt, index, ctx.stmts, status.induction_const);
35496
35507
  const lease = region_pool_enter(status, child, !!neon_plan || !!unroll_plan);
35497
- build_while_loop_node$1(child, status, nstmt, neon_plan, unroll_plan);
35508
+ build_while_loop_node$2(child, status, nstmt, neon_plan, unroll_plan);
35498
35509
  region_pool_exit(status, lease);
35499
35510
  return 1;
35500
35511
  }
35501
35512
  case "for":
35502
- build_for_loop_node$1(child, status, nstmt, neon_vectorization_enabled() ? plan_vector_for(nstmt, index, ctx.stmts, status) : null);
35513
+ build_for_loop_node$2(child, status, nstmt, neon_vectorization_enabled() ? plan_vector_for(nstmt, index, ctx.stmts, status) : null);
35503
35514
  return 1;
35504
35515
  case "switch_match":
35505
35516
  if (child.node_type === "switch") build_switch_node$1(child, status, nstmt);
@@ -35508,7 +35519,7 @@ function emit_stmt_dispatch(child, index, statements, status) {
35508
35519
  case "return": {
35509
35520
  const restore_return = apply_forward_use(ctx.use_sites, nstmt.node);
35510
35521
  try {
35511
- build_return_node$1(child, status, nstmt.value);
35522
+ build_return_node$2(child, status, nstmt.value);
35512
35523
  ensure_newline(status);
35513
35524
  return 1;
35514
35525
  } finally {
@@ -35533,7 +35544,7 @@ function emit_stmt_dispatch(child, index, statements, status) {
35533
35544
  const slp = try_emit_slp_pair(index, statements, ctx.stmts, status);
35534
35545
  if (slp > 1) return slp;
35535
35546
  }
35536
- build_declaration_node(child, status, nstmt.decl.init, nstmt.decl.swap);
35547
+ build_declaration_node$1(child, status, nstmt.decl.init, nstmt.decl.swap);
35537
35548
  if (nstmt.decl.init && nstmt.decl.init.node.node_type !== "func") ensure_newline(status);
35538
35549
  return 1;
35539
35550
  } finally {
@@ -35546,7 +35557,7 @@ function emit_stmt_dispatch(child, index, statements, status) {
35546
35557
  const slp = try_emit_slp_pair(index, statements, ctx.stmts, status);
35547
35558
  if (slp > 1) return slp;
35548
35559
  }
35549
- build_assignment_node(nstmt.node, status, nstmt.rhs, nstmt.swap);
35560
+ build_assignment_node$1(nstmt.node, status, nstmt.rhs, nstmt.swap);
35550
35561
  ensure_newline(status);
35551
35562
  return 1;
35552
35563
  } finally {
@@ -35569,7 +35580,7 @@ function emit_stmt_dispatch(child, index, statements, status) {
35569
35580
  }
35570
35581
  }
35571
35582
  case "async_block":
35572
- build_async_block_node$1(child, status, nstmt);
35583
+ build_async_block_node$2(child, status, nstmt);
35573
35584
  return 1;
35574
35585
  case "block":
35575
35586
  build_nested_block_node$1(child, status, nstmt);
@@ -35616,7 +35627,7 @@ function try_emit_cset_pair(decl, nstmt, index, ctx, status) {
35616
35627
  ensure_newline(status);
35617
35628
  return 2;
35618
35629
  }
35619
- build_declaration_node(decl, status, nstmt.decl.init, nstmt.decl.swap);
35630
+ build_declaration_node$1(decl, status, nstmt.decl.init, nstmt.decl.swap);
35620
35631
  if (nstmt.decl.init && nstmt.decl.init.node.node_type !== "func") ensure_newline(status);
35621
35632
  emit_cond_cset(ifn.condition, status);
35622
35633
  emit_var_store(status, "x0", decl.name, aarch64_size(decl.type?.name ?? "int"));
@@ -35797,7 +35808,7 @@ function try_emit_carry_fold(decl, nstmt, index, ctx, status) {
35797
35808
  for (const s of intervening) {
35798
35809
  const restore = apply_forward_use(ctx.use_sites, s.node);
35799
35810
  try {
35800
- build_assignment_node(s.node, status, s.rhs, s.swap);
35811
+ build_assignment_node$1(s.node, status, s.rhs, s.swap);
35801
35812
  ensure_newline(status);
35802
35813
  } finally {
35803
35814
  restore?.();
@@ -35822,7 +35833,7 @@ function try_emit_carry_fold(decl, nstmt, index, ctx, status) {
35822
35833
  const flag_reg = status.register_allocations?.get(fd.ast.name);
35823
35834
  if (flag_reg?.startsWith("x") && !fd.n.decl.swap) emit_asm(status, `cset ${flag_reg}, ${cc}\n`);
35824
35835
  else {
35825
- build_declaration_node(fd.ast, status, fd.n.decl.init, fd.n.decl.swap);
35836
+ build_declaration_node$1(fd.ast, status, fd.n.decl.init, fd.n.decl.swap);
35826
35837
  ensure_newline(status);
35827
35838
  emit_asm(status, `cset x0, ${cc}\n`);
35828
35839
  emit_var_store(status, "x0", fd.ast.name, aarch64_size(fd_type));
@@ -35963,7 +35974,7 @@ function emit_expr_from_nir$1(expr, status) {
35963
35974
  * __nomen_future_release, __nomen_future_cancel) are C functions in the
35964
35975
  * companion file, called via `bl` from assembly.
35965
35976
  */
35966
- function build_async_block_node$1(node, status, nir) {
35977
+ function build_async_block_node$2(node, status, nir) {
35967
35978
  const id = status.spawn_counter ?? 0;
35968
35979
  status.spawn_counter = id + 1;
35969
35980
  ensure_concurrency_runtime_a64(status);
@@ -36120,7 +36131,7 @@ function emit_nursery_joins_on_return_a64(status) {
36120
36131
  }
36121
36132
  //#endregion
36122
36133
  //#region ../src/build_aarch64/build_break_node.ts
36123
- function build_break_node$1(status) {
36134
+ function build_break_node$2(status) {
36124
36135
  const loop = status.loop_labels?.[status.loop_labels.length - 1];
36125
36136
  if (loop) {
36126
36137
  emit_cleanup_to_loop_depth(status);
@@ -38829,7 +38840,7 @@ function is_trait_type(name, status) {
38829
38840
  return !!name && status.traits.some((t) => t.name === name);
38830
38841
  }
38831
38842
  /** Whether `name` is a declared VALUE struct (non-class, non-simple). */
38832
- function is_value_struct(name, status) {
38843
+ function is_value_struct$1(name, status) {
38833
38844
  if (!name) return false;
38834
38845
  const s = status.structs.find((st) => st.name === name);
38835
38846
  return !!s && !s.is_class && !s.is_simple_type;
@@ -38841,7 +38852,7 @@ function conforms_to_trait(struct_name, trait_name, status) {
38841
38852
  }
38842
38853
  /** Whether `struct_name` is a value struct that conforms to `trait_name`. */
38843
38854
  function is_value_struct_conformer(struct_name, trait_name, status) {
38844
- return !!struct_name && struct_name !== trait_name && is_value_struct(struct_name, status) && conforms_to_trait(struct_name, trait_name, status);
38855
+ return !!struct_name && struct_name !== trait_name && is_value_struct$1(struct_name, status) && conforms_to_trait(struct_name, trait_name, status);
38845
38856
  }
38846
38857
  /** The one error wording for boxing a value that has no owner to give (T2). */
38847
38858
  function boxing_owns_error() {
@@ -41825,7 +41836,7 @@ function check_struct_node(struct, status) {
41825
41836
  if (!is_owning_struct_type_requiring_move(new Type(mono_type_name(field.type)), status)) continue;
41826
41837
  if (!status.warnings) status.warnings = [];
41827
41838
  status.warnings.push({
41828
- message: `'mv' on field '${field.name}' is redundant: '${type_display(field.type)}' is a value struct — its ownership is derived from the type, so plain 'var' behaves identically`,
41839
+ message: `'mv' on field '${field.name}' is redundant: '${type_display$1(field.type)}' is a value struct — its ownership is derived from the type, so plain 'var' behaves identically`,
41829
41840
  start: field.start,
41830
41841
  line: 0,
41831
41842
  column: 0
@@ -42040,9 +42051,9 @@ function types_match(a, b) {
42040
42051
  return a.name === b.name && !!a.is_ref === !!b.is_ref && !!a.is_view === !!b.is_view && !!a.is_array === !!b.is_array;
42041
42052
  }
42042
42053
  /** The user-visible spelling of a type (`List<int>`, `Map<string, int>`). */
42043
- function type_display(t) {
42054
+ function type_display$1(t) {
42044
42055
  if (!t.type_args?.length) return t.name ?? "?";
42045
- return `${t.name}<${t.type_args.map(type_display).join(", ")}>`;
42056
+ return `${t.name}<${t.type_args.map(type_display$1).join(", ")}>`;
42046
42057
  }
42047
42058
  /**
42048
42059
  * B2 — walk `struct`'s inline value-struct fields (Box fields are pointers
@@ -45969,6 +45980,8 @@ function check_if_else_node(if_else, status) {
45969
45980
  const null_check = get_null_check_var(if_else.condition, status);
45970
45981
  status.stack.push(if_else);
45971
45982
  const arms_base = status.values.length;
45983
+ const condition_allocations = status.allocations;
45984
+ status.allocations = [];
45972
45985
  let if_status = clone_status(status);
45973
45986
  let else_status = clone_status(status);
45974
45987
  if (null_check) {
@@ -46001,6 +46014,7 @@ function check_if_else_node(if_else, status) {
46001
46014
  if (if_else.if_branch) check_block_node(if_else.if_branch, if_status);
46002
46015
  if (if_else.else_branch) check_block_node(if_else.else_branch, else_status);
46003
46016
  status.stack.pop();
46017
+ status.allocations = condition_allocations.concat(status.allocations);
46004
46018
  const const_cond = evaluate_const_condition(if_else.condition, status);
46005
46019
  const if_reachable = const_cond !== false;
46006
46020
  const else_reachable = const_cond !== true;
@@ -46183,6 +46197,8 @@ function check_match_node(match_node, status) {
46183
46197
  check_node(match_node.value, status);
46184
46198
  const match_type = type_from_value_node(match_node.value, status);
46185
46199
  match_node.value_type = match_type.name;
46200
+ const scrutinee_allocations = status.allocations;
46201
+ status.allocations = [];
46186
46202
  status.stack.push(match_node);
46187
46203
  const arms_base = status.values.length;
46188
46204
  let branch_statuses = [];
@@ -46216,6 +46232,7 @@ function check_match_node(match_node, status) {
46216
46232
  branch_statuses.push(else_status);
46217
46233
  }
46218
46234
  status.stack.pop();
46235
+ status.allocations = scrutinee_allocations.concat(status.allocations);
46219
46236
  for (let [i, value] of status.values.entries()) {
46220
46237
  if (value.declaration === "const" && !value.is_set) {
46221
46238
  let set_count = branch_statuses.filter((bs) => bs.values[i]?.is_set).length;
@@ -48873,6 +48890,7 @@ function check_function_call_node(node, status) {
48873
48890
  type.type_args = expected_args;
48874
48891
  node.type = type;
48875
48892
  node.type_args = expected_args;
48893
+ node.type_args_from_expected = true;
48876
48894
  node.name = target.name;
48877
48895
  if (is_overloaded(target, "#init")) node.mangled_name = mangled_label(func, target.name);
48878
48896
  }
@@ -50914,7 +50932,7 @@ function emit_case_cast_panic(status) {
50914
50932
  emit_asm(status, `mov x16, #1\n`);
50915
50933
  emit_asm(status, `svc #0x80\n`);
50916
50934
  }
50917
- function build_cast_node$1(node, status) {
50935
+ function build_cast_node$2(node, status) {
50918
50936
  if (node.operator_func) {
50919
50937
  build_struct_cast(node, status);
50920
50938
  return;
@@ -51027,7 +51045,7 @@ function build_struct_cast(node, status) {
51027
51045
  }
51028
51046
  //#endregion
51029
51047
  //#region ../src/build_aarch64/build_continue_node.ts
51030
- function build_continue_node$1(status) {
51048
+ function build_continue_node$2(status) {
51031
51049
  const loop = status.loop_labels?.[status.loop_labels.length - 1];
51032
51050
  if (loop) {
51033
51051
  emit_cleanup_to_loop_depth(status);
@@ -52532,7 +52550,7 @@ function build_index_node(node, status) {
52532
52550
  }
52533
52551
  //#endregion
52534
52552
  //#region ../src/build_c/build_operation_node.ts
52535
- function build_operation_node$1(node, status) {
52553
+ function build_operation_node$2(node, status) {
52536
52554
  if (node.operator_func?.deferred) {
52537
52555
  let left_type = type_from_value_node$1(node.left_value);
52538
52556
  let struct = status.structs.find((s) => s.name === left_type.name);
@@ -52955,7 +52973,7 @@ function build_parameter_node(node, status) {
52955
52973
  * v1: fixed capacity (64 tasks per nursery). Exceeding it is undefined
52956
52974
  * behavior — a real implementation would grow or use a linked list.
52957
52975
  */
52958
- function build_async_block_node(node, status, nir) {
52976
+ function build_async_block_node$1(node, status, nir) {
52959
52977
  const id = status.spawn_counter ?? 0;
52960
52978
  status.spawn_counter = id + 1;
52961
52979
  ensure_concurrency_runtime(status);
@@ -53124,7 +53142,7 @@ function build_lambda_value(node, status, emit_name = true) {
53124
53142
  status.c_scope_stack = [];
53125
53143
  status.c_loop_frame_depth = void 0;
53126
53144
  status.class_vars_frames = void 0;
53127
- build_function_node(node, status);
53145
+ build_function_node$1(node, status);
53128
53146
  status.lambda_definitions = (status.lambda_definitions ?? "") + status.code;
53129
53147
  status.code = saved_code;
53130
53148
  status.headers = saved_headers + status.headers;
@@ -53208,7 +53226,7 @@ function emit_swap_value$1(swap, nir, status) {
53208
53226
  }
53209
53227
  build_node$1(swap, status);
53210
53228
  }
53211
- function build_declaration_node$1(node, status, nir_init, nir_swap) {
53229
+ function build_declaration_node$2(node, status, nir_init, nir_swap) {
53212
53230
  if (node.in_place_push_slot && node.value?.node_type === "func_call") {
53213
53231
  const recv_type = type_from_value_node$1(node.value);
53214
53232
  const stamp = node.in_place_push_slot;
@@ -53268,7 +53286,7 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
53268
53286
  const var_name = `_${node.name}_${i}`;
53269
53287
  if ((var_type?.name ? status.structs.find((st) => st.name === var_type.name && !st.is_simple_type && !st.is_class) : void 0) && status.traits.find((t) => t.name === node.type.name)) {
53270
53288
  const trait_type = new Type(node.type.name);
53271
- build_declaration_node$1(new DeclarationNode(node.start, node.visibility, node.declaration, var_name, trait_type, value), status, nir_elems?.[i - 1]);
53289
+ build_declaration_node$2(new DeclarationNode(node.start, node.visibility, node.declaration, var_name, trait_type, value), status, nir_elems?.[i - 1]);
53272
53290
  variables.push(var_name);
53273
53291
  i += 1;
53274
53292
  continue;
@@ -53665,7 +53683,7 @@ function build_declaration_node$1(node, status, nir_init, nir_swap) {
53665
53683
  status.code += `${safe_name}->_vt = 0;\n`;
53666
53684
  status.code += `${safe_name}->length = ${count}L;\n`;
53667
53685
  status.code += `memcpy((char *)${safe_name} + sizeof(struct Array_${elem_name}), (${elem_c}[])`;
53668
- build_array_values_node(arr, status, nir_array_elements(nir_init, arr));
53686
+ build_array_values_node$1(arr, status, nir_array_elements(nir_init, arr));
53669
53687
  status.code += `, ${count}L * sizeof(${elem_c}));\n`;
53670
53688
  return;
53671
53689
  }
@@ -53912,7 +53930,7 @@ function range_count_c(range) {
53912
53930
  }
53913
53931
  //#endregion
53914
53932
  //#region ../src/build_c/build_for_loop_node.ts
53915
- function build_for_loop_node(node, status, nir) {
53933
+ function build_for_loop_node$1(node, status, nir) {
53916
53934
  const old_scoped_declarations = status.scoped_declarations;
53917
53935
  status.scoped_declarations = enter_c_scope(status);
53918
53936
  const old_deferred_frees = status.deferred_frees;
@@ -54106,7 +54124,7 @@ function strip_outer_parens(expr) {
54106
54124
  }
54107
54125
  //#endregion
54108
54126
  //#region ../src/build_c/build_if_else_node.ts
54109
- function build_if_else_node(node, status, nir) {
54127
+ function build_if_else_node$1(node, status, nir) {
54110
54128
  const old_scoped_declarations = status.scoped_declarations;
54111
54129
  status.scoped_declarations = enter_c_scope(status);
54112
54130
  const old_deferred_frees = status.deferred_frees;
@@ -54327,7 +54345,7 @@ function emit_return_value(value, nir_value, status) {
54327
54345
  }
54328
54346
  build_node$1(value, status);
54329
54347
  }
54330
- function build_return_node(node, status, nir_value) {
54348
+ function build_return_node$1(node, status, nir_value) {
54331
54349
  hoist_field_overrides(node.value, build_node$1, status, ";\n");
54332
54350
  const ret_has = status.nullable_ret_has_param;
54333
54351
  const returns_nullable_struct = !!ret_has;
@@ -54362,7 +54380,7 @@ function build_return_node(node, status, nir_value) {
54362
54380
  const elem_is_class = !!status.structs.find((s) => s.name === ret_type.name && !s.is_simple_type)?.is_class;
54363
54381
  const elem_c_type = ret_type.elem ? array_elem_c_type(ret_type) : elem_is_class ? `struct ${ret_type.name}*` : c_type(ret_type.name);
54364
54382
  status.code += `${elem_c_type} ${return_array_var}[${return_array_len}] = `;
54365
- build_array_values_node(arr, status, nir_array_elements(nir_value, arr));
54383
+ build_array_values_node$1(arr, status, nir_array_elements(nir_value, arr));
54366
54384
  status.code += ";\n";
54367
54385
  } else if (ret_type?.is_array && node.value.node_type === "value") {
54368
54386
  return_array_var = node.value.value;
@@ -54733,7 +54751,7 @@ function is_reevaluatable(alloc) {
54733
54751
  if (decl.is_heap_array_copy) return false;
54734
54752
  return true;
54735
54753
  }
54736
- function build_while_loop_node(node, status, nir) {
54754
+ function build_while_loop_node$1(node, status, nir) {
54737
54755
  const old_scoped_declarations = status.scoped_declarations;
54738
54756
  status.scoped_declarations = enter_c_scope(status);
54739
54757
  const old_deferred_frees = status.deferred_frees;
@@ -54919,28 +54937,28 @@ function emit_stmt_from_nir(child, index, statements, status) {
54919
54937
  const nstmt = ctx.stmts[index];
54920
54938
  switch (nstmt.kind) {
54921
54939
  case "if":
54922
- build_if_else_node(child, status, nstmt);
54940
+ build_if_else_node$1(child, status, nstmt);
54923
54941
  return;
54924
54942
  case "while":
54925
- build_while_loop_node(child, status, nstmt);
54943
+ build_while_loop_node$1(child, status, nstmt);
54926
54944
  return;
54927
54945
  case "for":
54928
- build_for_loop_node(child, status, nstmt);
54946
+ build_for_loop_node$1(child, status, nstmt);
54929
54947
  return;
54930
54948
  case "switch_match":
54931
54949
  if (child.node_type === "switch") build_switch_node(child, status, nstmt);
54932
54950
  else build_match_node(child, status, nstmt);
54933
54951
  return;
54934
54952
  case "return":
54935
- build_return_node(child, status, nstmt.value);
54953
+ build_return_node$1(child, status, nstmt.value);
54936
54954
  if (!child.from_inline) with_semicolon_tail(status, child);
54937
54955
  return;
54938
54956
  case "declare":
54939
- build_declaration_node$1(child, status, nstmt.decl.init, nstmt.decl.swap);
54957
+ build_declaration_node$2(child, status, nstmt.decl.init, nstmt.decl.swap);
54940
54958
  if (!nstmt.decl.init || nstmt.decl.init.node.node_type !== "func") with_semicolon_tail(status, child);
54941
54959
  return;
54942
54960
  case "assign":
54943
- build_assignment_node$1(nstmt.node, status, nstmt.rhs, nstmt.swap);
54961
+ build_assignment_node$2(nstmt.node, status, nstmt.rhs, nstmt.swap);
54944
54962
  with_semicolon_tail(status, nstmt.node);
54945
54963
  return;
54946
54964
  case "eval": {
@@ -54954,7 +54972,7 @@ function emit_stmt_from_nir(child, index, statements, status) {
54954
54972
  return;
54955
54973
  }
54956
54974
  case "async_block":
54957
- build_async_block_node(child, status, nstmt);
54975
+ build_async_block_node$1(child, status, nstmt);
54958
54976
  return;
54959
54977
  case "block":
54960
54978
  build_nested_block_node(child, status, nstmt);
@@ -55138,7 +55156,7 @@ function scan_borrow_only_strings(body) {
55138
55156
  }
55139
55157
  //#endregion
55140
55158
  //#region ../src/build_c/build_struct_node.ts
55141
- function build_struct_node(node, status) {
55159
+ function build_struct_node$1(node, status) {
55142
55160
  if (node.is_generic) return;
55143
55161
  for (const func of node.functions) ensure_runtime_for_method(func, status);
55144
55162
  if (node.is_simple_type) {
@@ -55874,7 +55892,7 @@ function build_block_node(node, status, with_declarations = true) {
55874
55892
  }
55875
55893
  for (let child of node.statements) if (is_struct_node(child)) {
55876
55894
  if (!should_emit_definition(child, status.emit_mode, status.structs, status.system_struct_names)) continue;
55877
- build_struct_node(child, status);
55895
+ build_struct_node$1(child, status);
55878
55896
  }
55879
55897
  if (node.node_type === "root") {
55880
55898
  for (let child of node.statements) if (child.node_type === "declare") {
@@ -55896,7 +55914,7 @@ function build_block_node(node, status, with_declarations = true) {
55896
55914
  }
55897
55915
  for (let child of node.statements) if (is_function_node(child)) {
55898
55916
  if (!should_emit_definition(child, status.emit_mode, status.structs, status.system_struct_names)) continue;
55899
- build_function_node(child, status);
55917
+ build_function_node$1(child, status);
55900
55918
  }
55901
55919
  }
55902
55920
  if (!with_declarations && node.name?.toLocaleLowerCase() === "main" && status.module_init_statements?.length) {
@@ -56090,7 +56108,7 @@ function statements_definitely_return(statements) {
56090
56108
  }
56091
56109
  return false;
56092
56110
  }
56093
- function build_function_node(node, status) {
56111
+ function build_function_node$1(node, status) {
56094
56112
  if (node.is_generic) return;
56095
56113
  if (node.is_foreign) {
56096
56114
  build_foreign(node, status);
@@ -56448,8 +56466,8 @@ function emit_nested_declarations(node, status) {
56448
56466
  for (let child of block.statements) if (is_trait_node(child)) build_trait_node(child, status);
56449
56467
  for (let child of block.statements) if (child.node_type === "enum") emit_enum_in_order(child, status);
56450
56468
  for (let child of block.statements) if (child.node_type === "bitset") build_bitset_node(child, status);
56451
- for (let child of block.statements) if (is_struct_node(child)) build_struct_node(child, status);
56452
- for (let child of block.statements) if (is_function_node(child)) build_function_node(child, status);
56469
+ for (let child of block.statements) if (is_struct_node(child)) build_struct_node$1(child, status);
56470
+ for (let child of block.statements) if (is_function_node(child)) build_function_node$1(child, status);
56453
56471
  }
56454
56472
  //#endregion
56455
56473
  //#region ../src/build_c/utils/closure.ts
@@ -56600,7 +56618,7 @@ function capturing_closure_value(node, status) {
56600
56618
  * yields a static descriptor.
56601
56619
  */
56602
56620
  function build_lambda_closure_value(node, status, already_built = false) {
56603
- if (!already_built) build_function_node(node, status);
56621
+ if (!already_built) build_function_node$1(node, status);
56604
56622
  if (node.captures?.length) return capturing_closure_value(node, status);
56605
56623
  return materialize_lambda_descriptor(node, status);
56606
56624
  }
@@ -56641,7 +56659,7 @@ const INT_LITERAL_SUFFIX = {
56641
56659
  int64: "LL",
56642
56660
  uint64: "ULL"
56643
56661
  };
56644
- function build_value_node$1(node, status) {
56662
+ function build_value_node$2(node, status) {
56645
56663
  if (node.is_struct_zero) {
56646
56664
  status.code += `(struct ${mono_type_name(node.type)}){0}`;
56647
56665
  return;
@@ -56697,7 +56715,7 @@ function escape_c_string(s) {
56697
56715
  * without them — or for an element whose lowered expr doesn't carry that
56698
56716
  * exact AST node — it is exactly the historical `build_node(value)`.
56699
56717
  */
56700
- function build_array_values_node(node, status, nir_elements) {
56718
+ function build_array_values_node$1(node, status, nir_elements) {
56701
56719
  status.code += `{`;
56702
56720
  const elem_is_string = node.type?.name === "string";
56703
56721
  const static_init = status.static_string_array_inits === true;
@@ -56722,14 +56740,14 @@ function build_array_values_node(node, status, nir_elements) {
56722
56740
  }
56723
56741
  //#endregion
56724
56742
  //#region ../src/build_c/build_break_node.ts
56725
- function build_break_node(node, status) {
56743
+ function build_break_node$1(node, status) {
56726
56744
  reclaim_to_loop_body(status);
56727
56745
  status.loop_writebacks?.[status.loop_writebacks.length - 1]?.();
56728
56746
  status.code += `break;\n`;
56729
56747
  }
56730
56748
  //#endregion
56731
56749
  //#region ../src/build_c/build_cast_node.ts
56732
- function build_cast_node(node, status) {
56750
+ function build_cast_node$1(node, status) {
56733
56751
  if (node.operator_func) {
56734
56752
  const { struct_name, func_name, mangled_name } = node.operator_func;
56735
56753
  const call_name = mangled_name || `${struct_name}_${c_function_name(func_name)}`;
@@ -56803,7 +56821,7 @@ function build_cast_node(node, status) {
56803
56821
  }
56804
56822
  //#endregion
56805
56823
  //#region ../src/build_c/build_continue_node.ts
56806
- function build_continue_node(node, status) {
56824
+ function build_continue_node$1(node, status) {
56807
56825
  reclaim_to_loop_body(status);
56808
56826
  status.loop_writebacks?.[status.loop_writebacks.length - 1]?.();
56809
56827
  status.code += `continue;\n`;
@@ -56896,7 +56914,7 @@ function is_compiler_temp_name(name) {
56896
56914
  "_param_"
56897
56915
  ].some((p) => name.startsWith(p));
56898
56916
  }
56899
- function build_function_call_node(node, status) {
56917
+ function build_function_call_node$1(node, status) {
56900
56918
  if (node.is_enum_shorthand) {
56901
56919
  if (node.move_param_indices?.length) for (const idx of node.move_param_indices) {
56902
56920
  const param = node.params[idx];
@@ -57329,9 +57347,10 @@ function emit_nullable_arg_flag(arg, status) {
57329
57347
  //#endregion
57330
57348
  //#region ../src/build_c/build_let_node.ts
57331
57349
  function build_let_node(node, status) {
57332
- if (status.return_assign) {
57350
+ const joins_through_branches = node.value.node_type === "if" || node.value.node_type === "match" || node.value.node_type === "switch";
57351
+ if (status.return_assign && !joins_through_branches) {
57333
57352
  status.code += `${status.return_assign} = `;
57334
- if (status.join_needs_owned_string && node.value.node_type !== "if" && node.value.node_type !== "match" && node.value.node_type !== "switch" && !is_owned_string_branch_value(node.value, status)) {
57353
+ if (status.join_needs_owned_string && !is_owned_string_branch_value(node.value, status)) {
57335
57354
  status.code += `nomen_str_dup(`;
57336
57355
  build_node$1(node.value, status);
57337
57356
  status.code += `)`;
@@ -57342,7 +57361,7 @@ function build_let_node(node, status) {
57342
57361
  }
57343
57362
  //#endregion
57344
57363
  //#region ../src/build_c/build_panic_node.ts
57345
- function build_panic_node$1(node, status) {
57364
+ function build_panic_node$2(node, status) {
57346
57365
  status.code += `printf("${node.message}\\n");\n`;
57347
57366
  status.code += `exit(EXIT_FAILURE);\n`;
57348
57367
  }
@@ -57368,7 +57387,7 @@ function collect_traits(node, acc = []) {
57368
57387
  for (const child of child_nodes(node)) collect_traits(child, acc);
57369
57388
  return acc;
57370
57389
  }
57371
- function build_root_node(node, status) {
57390
+ function build_root_node$1(node, status) {
57372
57391
  status.headers += `#include <stdint.h>\n`;
57373
57392
  status.headers += `#ifndef NOMEN_CLOSURE_STRUCT\n#define NOMEN_CLOSURE_STRUCT\nstruct nomen_closure { void *code; void *env; int owned; void (*destroy_env)(void *); };\n#endif\n`;
57374
57393
  status.code += `
@@ -57547,7 +57566,7 @@ void **_get_trait_func(void **obj, int trait_index, int func_index)
57547
57566
  }
57548
57567
  //#endregion
57549
57568
  //#region ../src/build_c/build_todo_node.ts
57550
- function build_todo_node$1(node, status) {
57569
+ function build_todo_node$2(node, status) {
57551
57570
  status.code += `printf("${node.message}\\n");\n`;
57552
57571
  status.code += `exit(EXIT_FAILURE);\n`;
57553
57572
  }
@@ -57564,19 +57583,19 @@ function build_node$1(node, status, with_semicolon = false) {
57564
57583
  }
57565
57584
  switch (node.node_type) {
57566
57585
  case "root":
57567
- build_root_node(node, status);
57586
+ build_root_node$1(node, status);
57568
57587
  break;
57569
57588
  case "declare": {
57570
57589
  const decl = node;
57571
- build_declaration_node$1(decl, status);
57590
+ build_declaration_node$2(decl, status);
57572
57591
  if (decl.value?.node_type === "func") with_semicolon = false;
57573
57592
  break;
57574
57593
  }
57575
57594
  case "assign":
57576
- build_assignment_node$1(node, status);
57595
+ build_assignment_node$2(node, status);
57577
57596
  break;
57578
57597
  case "struct":
57579
- build_struct_node(node, status);
57598
+ build_struct_node$1(node, status);
57580
57599
  break;
57581
57600
  case "extend": break;
57582
57601
  case "trait":
@@ -57598,7 +57617,7 @@ function build_node$1(node, status, with_semicolon = false) {
57598
57617
  build_magic_ctor_node$1(fc, status);
57599
57618
  break;
57600
57619
  }
57601
- build_function_call_node(node, status);
57620
+ build_function_call_node$1(node, status);
57602
57621
  break;
57603
57622
  }
57604
57623
  case "access": {
@@ -57607,7 +57626,7 @@ function build_node$1(node, status, with_semicolon = false) {
57607
57626
  const afn = access.access;
57608
57627
  if (afn.is_nursery_spawn) afn.is_statement = true;
57609
57628
  }
57610
- build_access_node$1(access, status);
57629
+ build_access_node$2(access, status);
57611
57630
  break;
57612
57631
  }
57613
57632
  case "grouped":
@@ -57616,13 +57635,13 @@ function build_node$1(node, status, with_semicolon = false) {
57616
57635
  status.code += ")";
57617
57636
  break;
57618
57637
  case "cast":
57619
- build_cast_node(node, status);
57638
+ build_cast_node$1(node, status);
57620
57639
  break;
57621
57640
  case "op":
57622
- build_operation_node$1(node, status);
57641
+ build_operation_node$2(node, status);
57623
57642
  break;
57624
57643
  case "if":
57625
- build_if_else_node(node, status);
57644
+ build_if_else_node$1(node, status);
57626
57645
  with_semicolon = false;
57627
57646
  break;
57628
57647
  case "match":
@@ -57634,39 +57653,39 @@ function build_node$1(node, status, with_semicolon = false) {
57634
57653
  with_semicolon = false;
57635
57654
  break;
57636
57655
  case "for":
57637
- build_for_loop_node(node, status);
57656
+ build_for_loop_node$1(node, status);
57638
57657
  with_semicolon = false;
57639
57658
  break;
57640
57659
  case "while":
57641
- build_while_loop_node(node, status);
57660
+ build_while_loop_node$1(node, status);
57642
57661
  with_semicolon = false;
57643
57662
  break;
57644
57663
  case "break":
57645
- build_break_node(node, status);
57664
+ build_break_node$1(node, status);
57646
57665
  break;
57647
57666
  case "continue":
57648
- build_continue_node(node, status);
57667
+ build_continue_node$1(node, status);
57649
57668
  break;
57650
57669
  case "panic":
57651
- build_panic_node$1(node, status);
57670
+ build_panic_node$2(node, status);
57652
57671
  with_semicolon = false;
57653
57672
  break;
57654
57673
  case "todo":
57655
- build_todo_node$1(node, status);
57674
+ build_todo_node$2(node, status);
57656
57675
  with_semicolon = false;
57657
57676
  break;
57658
57677
  case "return":
57659
57678
  if (node.from_inline) with_semicolon = false;
57660
- else build_return_node(node, status);
57679
+ else build_return_node$1(node, status);
57661
57680
  break;
57662
57681
  case "let":
57663
57682
  build_let_node(node, status);
57664
57683
  break;
57665
57684
  case "value":
57666
- build_value_node$1(node, status);
57685
+ build_value_node$2(node, status);
57667
57686
  break;
57668
57687
  case "array":
57669
- build_array_values_node(node, status);
57688
+ build_array_values_node$1(node, status);
57670
57689
  break;
57671
57690
  case "range":
57672
57691
  build_range_node$1(node, status);
@@ -57676,7 +57695,7 @@ function build_node$1(node, status, with_semicolon = false) {
57676
57695
  with_semicolon = false;
57677
57696
  break;
57678
57697
  case "async_block":
57679
- build_async_block_node(node, status);
57698
+ build_async_block_node$1(node, status);
57680
57699
  with_semicolon = false;
57681
57700
  break;
57682
57701
  case "block":
@@ -58665,7 +58684,7 @@ function field_path_root_struct$1(status, target) {
58665
58684
  if (!path?.fields.length) return void 0;
58666
58685
  return in_place_field_path_root_struct_name(status, path.root);
58667
58686
  }
58668
- function build_access_node$1(node, status) {
58687
+ function build_access_node$2(node, status) {
58669
58688
  let string_transfer_callee;
58670
58689
  let target_type = type_from_value_node$1(node.target);
58671
58690
  if (!target_type?.name && node.target.node_type === "value") {
@@ -59527,7 +59546,7 @@ function emit_rhs_value$1(rhs, nir, status) {
59527
59546
  }
59528
59547
  build_node$1(rhs, status);
59529
59548
  }
59530
- function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
59549
+ function build_assignment_node$2(node, status, nir_rhs, nir_swap) {
59531
59550
  hoist_field_overrides(node.right_value, build_node$1, status, ";\n", node.left_value.node_type === "value" ? node.left_value.value : void 0);
59532
59551
  const lhs_nullable_type = lhs_nullable_struct_type(node, status);
59533
59552
  if (!node.operator && lhs_nullable_type) {
@@ -61322,7 +61341,7 @@ function build_weak_ctor_a64(node, status) {
61322
61341
  }
61323
61342
  //#endregion
61324
61343
  //#region ../src/build_aarch64/build_panic_node.ts
61325
- function build_panic_node(node, status) {
61344
+ function build_panic_node$1(node, status) {
61326
61345
  const msg = node.message + "\\n";
61327
61346
  const label = `_str_panic_${node.message.replace(/[^a-zA-Z0-9]/g, "_")}`;
61328
61347
  status.strings.set(label, `"${msg}"`);
@@ -61364,7 +61383,7 @@ function evaluate_constant(node) {
61364
61383
  }
61365
61384
  //#endregion
61366
61385
  //#region ../src/build_aarch64/build_todo_node.ts
61367
- function build_todo_node(node, status) {
61386
+ function build_todo_node$1(node, status) {
61368
61387
  const msg = node.message + "\\n";
61369
61388
  const label = `_str_todo_${node.message.replace(/[^a-zA-Z0-9]/g, "_")}`;
61370
61389
  status.strings.set(label, `"${msg}"`);
@@ -61415,7 +61434,7 @@ function deref_load_instr(reg, type_name) {
61415
61434
  if (size === 4) return signed ? `ldrsw x0, [${reg}]\n` : `ldr w0, [${reg}]\n`;
61416
61435
  return `ldr x0, [${reg}]\n`;
61417
61436
  }
61418
- function build_value_node(node, status) {
61437
+ function build_value_node$1(node, status) {
61419
61438
  const original_value = node.value;
61420
61439
  if (node.is_struct_zero) {
61421
61440
  const struct_size = get_struct_size(mono_type_name(node.type), status);
@@ -61663,15 +61682,15 @@ function build_node(node, status, with_semicolon = false) {
61663
61682
  break;
61664
61683
  case "declare": {
61665
61684
  const decl = node;
61666
- build_declaration_node(decl, status);
61685
+ build_declaration_node$1(decl, status);
61667
61686
  if (decl.value?.node_type === "func") with_semicolon = false;
61668
61687
  break;
61669
61688
  }
61670
61689
  case "assign":
61671
- build_assignment_node(node, status);
61690
+ build_assignment_node$1(node, status);
61672
61691
  break;
61673
61692
  case "func":
61674
- build_function_node$1(node, status);
61693
+ build_function_node$2(node, status);
61675
61694
  if (status.function_return_label) {
61676
61695
  const fn = node;
61677
61696
  if (fn.captures?.length) emit_capturing_closure_value_a64(fn, status);
@@ -61685,21 +61704,21 @@ function build_node(node, status, with_semicolon = false) {
61685
61704
  build_magic_ctor_node(fc, status);
61686
61705
  break;
61687
61706
  }
61688
- build_function_call_node$1(node, status);
61707
+ build_function_call_node$2(node, status);
61689
61708
  break;
61690
61709
  }
61691
61710
  case "grouped":
61692
61711
  build_node(node.value, status);
61693
61712
  break;
61694
61713
  case "op":
61695
- build_operation_node(node, status);
61714
+ build_operation_node$1(node, status);
61696
61715
  break;
61697
61716
  case "if":
61698
- build_if_else_node$1(node, status);
61717
+ build_if_else_node$2(node, status);
61699
61718
  with_semicolon = false;
61700
61719
  break;
61701
61720
  case "cast":
61702
- build_cast_node$1(node, status);
61721
+ build_cast_node$2(node, status);
61703
61722
  break;
61704
61723
  case "match":
61705
61724
  build_match_node$1(node, status);
@@ -61710,30 +61729,30 @@ function build_node(node, status, with_semicolon = false) {
61710
61729
  with_semicolon = false;
61711
61730
  break;
61712
61731
  case "for":
61713
- build_for_loop_node$1(node, status);
61732
+ build_for_loop_node$2(node, status);
61714
61733
  with_semicolon = false;
61715
61734
  break;
61716
61735
  case "while":
61717
- build_while_loop_node$1(node, status);
61736
+ build_while_loop_node$2(node, status);
61718
61737
  with_semicolon = false;
61719
61738
  break;
61720
61739
  case "break":
61721
- build_break_node$1(status);
61740
+ build_break_node$2(status);
61722
61741
  break;
61723
61742
  case "continue":
61724
- build_continue_node$1(status);
61743
+ build_continue_node$2(status);
61725
61744
  break;
61726
61745
  case "return":
61727
- build_return_node$1(node, status);
61746
+ build_return_node$2(node, status);
61728
61747
  break;
61729
61748
  case "let":
61730
61749
  build_let_node$1(node, status);
61731
61750
  break;
61732
61751
  case "value":
61733
- build_value_node(node, status);
61752
+ build_value_node$1(node, status);
61734
61753
  break;
61735
61754
  case "array":
61736
- build_array_values_node$1(node, status);
61755
+ build_array_values_node$2(node, status);
61737
61756
  break;
61738
61757
  case "range":
61739
61758
  build_range_node(node, status);
@@ -61744,21 +61763,21 @@ function build_node(node, status, with_semicolon = false) {
61744
61763
  const afn = access.access;
61745
61764
  if (afn.is_nursery_spawn) afn.is_statement = true;
61746
61765
  }
61747
- build_access_node(access, status);
61766
+ build_access_node$1(access, status);
61748
61767
  break;
61749
61768
  }
61750
61769
  case "panic":
61751
- build_panic_node(node, status);
61770
+ build_panic_node$1(node, status);
61752
61771
  break;
61753
61772
  case "todo":
61754
- build_todo_node(node, status);
61773
+ build_todo_node$1(node, status);
61755
61774
  break;
61756
61775
  case "raw":
61757
61776
  build_raw_node$1(node, status);
61758
61777
  with_semicolon = false;
61759
61778
  break;
61760
61779
  case "async_block":
61761
- build_async_block_node$1(node, status);
61780
+ build_async_block_node$2(node, status);
61762
61781
  with_semicolon = false;
61763
61782
  break;
61764
61783
  case "block":
@@ -62847,7 +62866,7 @@ function emit_init_value(value, nir, status) {
62847
62866
  function nir_array_element(nir, arr, i) {
62848
62867
  return nir?.kind === "call" && nir.node === arr ? nir.facts.args[i] : void 0;
62849
62868
  }
62850
- function build_declaration_node(node, status, nir_init, nir_swap) {
62869
+ function build_declaration_node$1(node, status, nir_init, nir_swap) {
62851
62870
  if (!node.name && !node.type && !node.value) {
62852
62871
  if (!status.build_errors) status.build_errors = [];
62853
62872
  status.build_errors.push({
@@ -64231,7 +64250,7 @@ function build_declaration_node(node, status, nir_init, nir_swap) {
64231
64250
  } else {
64232
64251
  emit_asm(status, `.quad ${array_values.values.length}\n`);
64233
64252
  emit_asm(status, `${node.name}: ${directive} `);
64234
- build_array_values_node$1(array_values, status);
64253
+ build_array_values_node$2(array_values, status);
64235
64254
  emit_asm(status, `\n.p2align 2\n`);
64236
64255
  }
64237
64256
  } else if (node.value.node_type === "range") {
@@ -65070,7 +65089,7 @@ function get_source_address(value, status, nir) {
65070
65089
  ensure_newline(status);
65071
65090
  }
65072
65091
  }
65073
- function build_assignment_node(node, status, nir_rhs, nir_swap) {
65092
+ function build_assignment_node$1(node, status, nir_rhs, nir_swap) {
65074
65093
  hoist_field_overrides(node.right_value, build_node, status, "", node.left_value.node_type === "value" ? node.left_value.value : void 0);
65075
65094
  if (node.left_value.node_type === "index" || is_index_member_store(node)) {
65076
65095
  build_index_store(node, status, nir_rhs);
@@ -66990,7 +67009,7 @@ function build_operator_operand(node, target_reg, status) {
66990
67009
  }
66991
67010
  build_operand(node, target_reg, status);
66992
67011
  }
66993
- function build_operation_node(node, status) {
67012
+ function build_operation_node$1(node, status) {
66994
67013
  if (node.operator_func?.deferred) {
66995
67014
  let left_type = type_from_value_node$1(node.left_value);
66996
67015
  let struct = status.structs.find((s) => s.name === left_type.name);
@@ -68860,9 +68879,9 @@ function build_func_field_call(node, access_func, status) {
68860
68879
  call.is_func_param = true;
68861
68880
  call.params = access_func.params;
68862
68881
  call.type = access_func.type;
68863
- build_function_call_node$1(call, status);
68882
+ build_function_call_node$2(call, status);
68864
68883
  }
68865
- function build_access_node(node, status) {
68884
+ function build_access_node$1(node, status) {
68866
68885
  status.at_addr_reg = void 0;
68867
68886
  switch (node.access.node_type) {
68868
68887
  case "access_field":
@@ -71268,8 +71287,2539 @@ function gather_normalized_struct_returners(root, status) {
71268
71287
  for (const name of address_taken) status.func_address_taken.add(name);
71269
71288
  }
71270
71289
  //#endregion
71290
+ //#region ../src/build_js/call_with_refs.ts
71291
+ /**
71292
+ * A Nomen `ref` argument is an OUT-parameter: the callee may write through it
71293
+ * and the caller observes the new value. JS has no references to bindings, so
71294
+ * each ref argument is boxed (`{ v }`), the callee copies in at entry and
71295
+ * writes back on assignment, and the call site copies the box back into the
71296
+ * argument's lvalue afterwards.
71297
+ *
71298
+ * The whole call becomes an IIFE so the temp boxes stay scoped:
71299
+ *
71300
+ * (() => { const t = { v: x }; const r = f(t); x = t.v; return r; })()
71301
+ */
71302
+ function call_with_refs(status, args, ref_indices, build_call) {
71303
+ const arg_exprs = args.map((arg) => expr(arg, status));
71304
+ if (!ref_indices?.length) return build_call(arg_exprs);
71305
+ const boxes = [];
71306
+ const write_backs = [];
71307
+ const call = build_call(arg_exprs.map((value, i) => {
71308
+ if (!ref_indices.includes(i)) return value;
71309
+ const temp = next_temp(status, "ref");
71310
+ boxes.push(`const ${temp} = { v: ${value} };`);
71311
+ write_backs.push(`${value} = ${temp}.v;`);
71312
+ const arg = args[i];
71313
+ if (arg.node_type === "value") {
71314
+ const name = arg.value;
71315
+ if (status.ref_params?.has(name)) write_backs.push(`__ref_${name}.v = ${name};`);
71316
+ }
71317
+ return temp;
71318
+ }));
71319
+ return `(() => { ${boxes.join(" ")} const __ret = ${call}; ${write_backs.join(" ")} return __ret; })()`;
71320
+ }
71321
+ //#endregion
71322
+ //#region ../src/build_js/types.ts
71323
+ /**
71324
+ * The checker stamps a `Type` on every expression node; the JS backend reads
71325
+ * it back without a CheckStatus (mirrors check/utils/type_from_value_node).
71326
+ */
71327
+ function stamped_type(node) {
71328
+ if (!node) return void 0;
71329
+ switch (node.node_type) {
71330
+ case "value": return node.type;
71331
+ case "op": return node.type;
71332
+ case "func_call": return node.type;
71333
+ case "access": return stamped_type(node.access);
71334
+ case "access_func": return node.type;
71335
+ case "access_field": return node.type;
71336
+ case "cast": return node.target_type;
71337
+ case "if": return node.return_type;
71338
+ case "array": return node.type;
71339
+ case "range": return node.type;
71340
+ default: return;
71341
+ }
71342
+ }
71343
+ function type_name_of(node) {
71344
+ return stamped_type(node)?.name ?? "";
71345
+ }
71346
+ /** The runtime representation family of an expression: JS `bigint` for every
71347
+ * int family, `number` for floats, `string` for strings/chars (a char is a
71348
+ * one-character string), `boolean` for bools, and a JS array for arrays. */
71349
+ function kind_of(node) {
71350
+ const t = stamped_type(node);
71351
+ if (!t) return "other";
71352
+ if (t.is_array) return "array";
71353
+ if (t.is_view) return t.name === "string" || t.name === "char" ? "string" : "array";
71354
+ switch (get_built_in_type(t.name)?.kind) {
71355
+ case "int": return "int";
71356
+ case "float": return "float";
71357
+ case "bool": return "bool";
71358
+ case "char": return "char";
71359
+ case "string": return "string";
71360
+ default: return "other";
71361
+ }
71362
+ }
71363
+ /** True when `name` is a user `struct` with value semantics (non-`mv`), so
71364
+ * binding/assigning it copies the fields (MEMORY.md: "Assigning a struct
71365
+ * creates an independent copy"). `mv`/owning structs move instead. */
71366
+ function is_value_struct(name, status) {
71367
+ if (!name) return false;
71368
+ const struct = status.structs.find((s) => s.name === name);
71369
+ return !!struct && !struct.is_owning && !struct.is_class;
71370
+ }
71371
+ /** Wrap an emitted struct value in a deep copy when its type has value
71372
+ * semantics; identity otherwise. */
71373
+ function copy_if_value_struct(value, type_name, status) {
71374
+ return is_value_struct(type_name, status) ? `__copy(${value})` : value;
71375
+ }
71376
+ /** Wrap a value for a declared type: ints wrap at their width, everything
71377
+ * else (arrays, views, structs, strings) is untouched. */
71378
+ function wrap_for_type(value, type) {
71379
+ if (!type || type.is_array || type.is_view || type.is_nullable) return value;
71380
+ return wrap_int_expression(value, type.name);
71381
+ }
71382
+ /** Wrap an int expression to its storage width. Nomen ints are 64-bit words
71383
+ * (`int`/`uint` included, matching the C backend's `long`), so overflow
71384
+ * wraps like the native backends instead of growing a JS `bigint`. Sized
71385
+ * types wrap at their declared width. */
71386
+ function wrap_int_expression(value, type_name) {
71387
+ const info = type_name ? get_built_in_type(type_name) : void 0;
71388
+ if (info?.kind !== "int") return value;
71389
+ const bits = type_name === "int" || type_name === "uint" ? 64 : info.bits ?? 64;
71390
+ const bounded = bits >= 64 ? 64 : bits;
71391
+ return info.signed ? `BigInt.asIntN(${bounded}, ${value})` : `BigInt.asUintN(${bounded}, ${value})`;
71392
+ }
71393
+ //#endregion
71394
+ //#region ../src/build_js/enums.ts
71395
+ /**
71396
+ * Enum values are plain JS objects: `{ $tag: n, <param>: value, … }`, where
71397
+ * `$tag` is the case index (the enum's declaration order, matching the C
71398
+ * backend's tag) and the payload fields are keyed by the ENUM's parameter
71399
+ * names (a match-site binding is positional — `.ok(x)` binds `x` to the
71400
+ * first payload field whatever the enum named it).
71401
+ */
71402
+ /** Resolve the checker's flattened `Enum_case` name (`Res_ok`) to its enum
71403
+ * and case index. */
71404
+ function find_enum_case(enums, value) {
71405
+ for (const enum_node of enums) {
71406
+ const prefix = `${enum_node.name}_`;
71407
+ if (!value.startsWith(prefix)) continue;
71408
+ const case_name = value.substring(prefix.length);
71409
+ const case_index = enum_node.cases.findIndex((c) => c.name === case_name);
71410
+ if (case_index !== -1) return {
71411
+ enum_node,
71412
+ case_index
71413
+ };
71414
+ }
71415
+ }
71416
+ /** The JS object literal for a case construction with the given argument
71417
+ * expressions. */
71418
+ function enum_case_value(enum_node, case_index, args, status) {
71419
+ const case_def = enum_node.cases[case_index];
71420
+ const parts = [`$tag: ${case_index}`];
71421
+ case_def.params.forEach((param, i) => {
71422
+ if (args[i] === void 0) return;
71423
+ const copied = status && is_value_struct(param.type?.name, status) ? `__copy(${args[i]})` : args[i];
71424
+ parts.push(`${param.name}: ${copied}`);
71425
+ });
71426
+ return `({ ${parts.join(", ")} })`;
71427
+ }
71428
+ //#endregion
71429
+ //#region ../src/build_js/library.ts
71430
+ /**
71431
+ * The System-library surface the JS backend supports natively. The library's
71432
+ * own Nomen bodies are not emitted (they are raw/unsafe-heavy); instead these
71433
+ * containers map onto JS representations:
71434
+ *
71435
+ * List<T> → a JS array
71436
+ * Set<T> → a JS Set
71437
+ * Map<K, V> → a JS Map
71438
+ * Box<T> → `{ __value }` (the checker rewrites transparent access
71439
+ * `box.f` to `box.__value.f`)
71440
+ * StringBuilder → `{ s }`
71441
+ * Buffer<T> → a JS array (capacity is the array length)
71442
+ * ClassBuffer<T> → a JS array (elements are object references)
71443
+ *
71444
+ * Method dispatch is by the RECEIVER's declared type name plus the method
71445
+ * name; anything unmapped falls through to a warning stub in the caller.
71446
+ */
71447
+ /** Strip a monomorphization suffix: `List_int` → `List`, `StableList_X` →
71448
+ * `StableList`. Longest prefixes first so `StringBuilder` wins over `String`. */
71449
+ function library_base_name(name) {
71450
+ for (const base of [
71451
+ "StringBuilder",
71452
+ "ClassBuffer",
71453
+ "StableList",
71454
+ "Buffer",
71455
+ "Atomic",
71456
+ "Channel",
71457
+ "Fiber",
71458
+ "Mutex",
71459
+ "Nursery",
71460
+ "List",
71461
+ "Task",
71462
+ "Thread",
71463
+ "Set",
71464
+ "Map",
71465
+ "Box"
71466
+ ]) if (name === base || name.startsWith(`${base}_`)) return base;
71467
+ return name;
71468
+ }
71469
+ /** Is `name` a `Buffer<T>` (value slots) or `ClassBuffer<T>` (class slots)? */
71470
+ function is_buffer_type(name) {
71471
+ return name === "Buffer" || name.startsWith("Buffer_") || name === "ClassBuffer" || name.startsWith("ClassBuffer_");
71472
+ }
71473
+ /** `ClassBuffer<T>` stores class references, so its stores are not copies. */
71474
+ function is_class_buffer_type(name) {
71475
+ return name === "ClassBuffer" || name.startsWith("ClassBuffer_");
71476
+ }
71477
+ /** Constructors for the supported library types (`List<int>()`,
71478
+ * `Box<int>(x)`, …). The checker monomorphizes the callee name
71479
+ * (`List_int`, `Map_string_int`, `Box_P`), so match by prefix. */
71480
+ function library_constructor(name, args) {
71481
+ if (name === "List" || name.startsWith("List_")) return args.length ? `[${args.join(", ")}]` : "[]";
71482
+ if (name === "Set" || name.startsWith("Set_")) return "new Set()";
71483
+ if (name === "Map" || name.startsWith("Map_")) return args.length ? `new Map(${args[0]})` : "new Map()";
71484
+ if (name === "Box" || name.startsWith("Box_")) return `({ __value: ${args[0] ?? "null"} })`;
71485
+ if (name === "StringBuilder") return `({ s: "" })`;
71486
+ if (name === "StableList" || name.startsWith("StableList_")) return "[]";
71487
+ if (name === "Channel" || name.startsWith("Channel_")) return "({ values: [], strings: [] })";
71488
+ if (name === "Mutex" || name.startsWith("Mutex_")) return "({})";
71489
+ if (name === "Atomic" || name.startsWith("Atomic_")) return "({ word: 0n })";
71490
+ if (name === "Nursery" || name.startsWith("Nursery_")) return "({})";
71491
+ if (is_buffer_type(name)) return "[]";
71492
+ }
71493
+ /** A method call on a `List<T>` receiver (a JS array). */
71494
+ function list_method(name, list, args) {
71495
+ switch (name) {
71496
+ case "length": return `BigInt(${list}.length)`;
71497
+ case "at":
71498
+ case "at_or_panic": return `__list_at(${list}, ${args[0]})`;
71499
+ case "slot_ptr": return `${list}[Number(${args[0]})]`;
71500
+ case "at_or": return `__list_at_or(${list}, ${args[0]}, ${args[1]})`;
71501
+ case "set":
71502
+ case "set_or_panic": return `(${list}[Number(${args[0]})] = ${args[1]})`;
71503
+ case "push": return `${list}.push(${args[0]})`;
71504
+ case "pop": return `${list}.pop()`;
71505
+ case "remove_at_or_panic": return `${list}.splice(Number(${args[0]}), 1)[0]`;
71506
+ case "slice": return `${list}.slice(Number(${args[0]}), Number(${args[1]}))`;
71507
+ case "copy": return `${list}.slice()`;
71508
+ case "clear": return `(${list}.length = 0)`;
71509
+ }
71510
+ }
71511
+ /** A method call on a `Set<T>` receiver (a JS Set). */
71512
+ function set_method(name, set, args) {
71513
+ switch (name) {
71514
+ case "add": return `${set}.add(${args[0]})`;
71515
+ case "has": return `${set}.has(${args[0]})`;
71516
+ case "get":
71517
+ case "get_or_panic": return `__set_get(${set}, ${args[0]})`;
71518
+ case "get_or": return `(${set}.has(${args[0]}) ? ${set}.get(${args[0]}) : ${args[1]})`;
71519
+ case "remove": return `${set}.delete(${args[0]})`;
71520
+ case "copy": return `new Set(${set})`;
71521
+ case "length": return `BigInt(${set}.size)`;
71522
+ }
71523
+ }
71524
+ /** A method call on a `Map<K, V>` receiver (a JS Map). */
71525
+ function map_method(name, map, args) {
71526
+ switch (name) {
71527
+ case "set": return `${map}.set(${args[0]}, ${args[1]})`;
71528
+ case "get":
71529
+ case "get_or_panic": return `__map_get(${map}, ${args[0]})`;
71530
+ case "get_or": return `(${map}.has(${args[0]}) ? ${map}.get(${args[0]}) : ${args[1]})`;
71531
+ case "has": return `${map}.has(${args[0]})`;
71532
+ case "remove": return `${map}.delete(${args[0]})`;
71533
+ case "copy": return `new Map(${map})`;
71534
+ case "length": return `BigInt(${map}.size)`;
71535
+ }
71536
+ }
71537
+ function buffer_method(name, buf, args, options) {
71538
+ const zero = /_(int|u32|u8)$/.test(name) ? "0n" : name.endsWith("_float") ? "0" : options.zero;
71539
+ const store_value = (value) => options.class_elements ? value : `__copy(${value})`;
71540
+ const load_value = (index) => options.copy_loads ? `__copy(${buf}[Number(${index})])` : `${buf}[Number(${index})]`;
71541
+ switch (name) {
71542
+ case "alloc":
71543
+ case "alloc_int":
71544
+ case "alloc_u32":
71545
+ case "alloc_u8":
71546
+ case "alloc_float": return `__buffer_alloc(${buf}, ${args[0]}, ${zero})`;
71547
+ case "grow":
71548
+ case "grow_int":
71549
+ case "grow_u32":
71550
+ case "grow_float": return `__buffer_grow(${buf}, ${args[0]}, ${zero})`;
71551
+ case "load":
71552
+ case "load_from":
71553
+ case "load_int":
71554
+ case "load_u32":
71555
+ case "load_u8":
71556
+ case "load_float": return load_value(args[0]);
71557
+ case "store":
71558
+ case "replace":
71559
+ case "set": return `(${buf}[Number(${args[0]})] = ${store_value(args[1])})`;
71560
+ case "store_int":
71561
+ case "replace_int":
71562
+ case "store_u32":
71563
+ case "store_u8":
71564
+ case "store_float":
71565
+ case "store_take": return `(${buf}[Number(${args[0]})] = ${args[1]})`;
71566
+ case "store_or_int":
71567
+ case "store_or_u32": return `(${buf}[Number(${args[0]})] |= ${args[1]})`;
71568
+ case "move":
71569
+ case "move_int": return `__buffer_move(${buf}, ${args[0]})`;
71570
+ case "modify": return `__buffer_modify(${buf}, ${args[0]}, ${args[1]})`;
71571
+ case "shift": return `__buffer_shift(${buf}, ${args[0]}, ${args[1]})`;
71572
+ case "move_range": return `__buffer_move_range(${buf}, ${args[0]}, ${args[1]}, ${args[2]})`;
71573
+ case "zero":
71574
+ case "zero_int":
71575
+ case "zero_u32": return `__buffer_zero(${buf}, ${args[0]}, ${zero})`;
71576
+ case "slice": return `${buf}.slice(Number(${args[0]}), Number(${args[1]}))`;
71577
+ case "copy": return `__copy(${buf})`;
71578
+ case "destroy_at": return "undefined";
71579
+ case "clear_view": return `(${buf}.length = 0)`;
71580
+ }
71581
+ }
71582
+ /** BigInt's raw per-limb accessors, over the `digits` Buffer (a JS array).
71583
+ * `data_ptr` hands back the array itself, so the loop-hoisted `get_at`/
71584
+ * `set_at` forms index it directly. */
71585
+ function bigint_method(name, self, args) {
71586
+ switch (name) {
71587
+ case "get": return `${self}.digits[Number(${args[0]})]`;
71588
+ case "set": return `(${self}.digits[Number(${args[0]})] = ${args[1]})`;
71589
+ case "data_ptr": return `${self}.digits`;
71590
+ case "get_at": return `${args[0]}[Number(${args[1]})]`;
71591
+ case "set_at": return `(${args[0]}[Number(${args[1]})] = ${args[2]})`;
71592
+ case "div128": return `__div128(${args[0]}, ${args[1]}, ${args[2]})`;
71593
+ }
71594
+ }
71595
+ /** A method call on a `StableList<T>` receiver (a JS array). */
71596
+ function stable_list_method(name, list, args) {
71597
+ switch (name) {
71598
+ case "push": return `${list}.push(__copy(${args[0]}))`;
71599
+ case "at":
71600
+ case "at_or_panic": return `__list_at(${list}, ${args[0]})`;
71601
+ case "slot_ptr": return `${list}[Number(${args[0]})]`;
71602
+ case "at_or": return `__list_at_or(${list}, ${args[0]}, ${args[1]})`;
71603
+ case "copy": return `__copy(${list})`;
71604
+ }
71605
+ }
71606
+ /**
71607
+ * Concurrency surface, run synchronously. The JS backend has no scheduler, so:
71608
+ * - `Thread`/`Fiber.start` runs the deferred thunk now and returns a finished
71609
+ * `Task`; `detach` runs and discards it;
71610
+ * - `Task.result`/`result_uint64`/`is_done` read that finished result,
71611
+ * `wait` is a no-op, `cancel`/`current_cancelled` are inert;
71612
+ * - `Channel` is two in-memory FIFOs (receive yields zero when empty);
71613
+ * - `Mutex.lock`/`unlock` and `Atomic`'s operations are plain value updates.
71614
+ */
71615
+ function concurrency_method(base, name, self, args, is_static) {
71616
+ switch (base) {
71617
+ case "Thread":
71618
+ case "Fiber":
71619
+ if (is_static) return void 0;
71620
+ if (name === "start") return `__spawn_start(${self})`;
71621
+ if (name === "detach") return `__spawn_detach(${self})`;
71622
+ return;
71623
+ case "Nursery":
71624
+ if (name === "start") return `__nursery_start(${self}, ${args[0]})`;
71625
+ return;
71626
+ case "Task":
71627
+ if (is_static) {
71628
+ if (name === "current_cancelled") return "false";
71629
+ if (name === "set_pool_size") return args[0] ?? "0n";
71630
+ if (name === "shutdown_pool") return "undefined";
71631
+ return;
71632
+ }
71633
+ switch (name) {
71634
+ case "wait": return `__task_wait(${self})`;
71635
+ case "result": return `__task_result(${self})`;
71636
+ case "result_uint64": return `__task_result_uint64(${self})`;
71637
+ case "is_done": return "true";
71638
+ case "cancel": return "undefined";
71639
+ }
71640
+ return;
71641
+ case "Channel":
71642
+ switch (name) {
71643
+ case "send": return `__channel_send(${self}, ${args[0]})`;
71644
+ case "send_string": return `__channel_send_string(${self}, ${args[0]})`;
71645
+ case "receive": return `__channel_receive(${self})`;
71646
+ case "receive_string": return `__channel_receive_string(${self})`;
71647
+ }
71648
+ return;
71649
+ case "Mutex":
71650
+ if (name === "lock" || name === "unlock") return "undefined";
71651
+ return;
71652
+ case "Atomic":
71653
+ switch (name) {
71654
+ case "load": return `${self}.word`;
71655
+ case "store": return `(${self}.word = ${args[0]})`;
71656
+ case "fetch_add": return `__atomic_fetch_add(${self}, ${args[0]})`;
71657
+ case "swap": return `__atomic_swap(${self}, ${args[0]})`;
71658
+ }
71659
+ return;
71660
+ }
71661
+ }
71662
+ /** A method call on a `StringBuilder` receiver (`{ s }`). */
71663
+ function string_builder_method(name, sb, args) {
71664
+ switch (name) {
71665
+ case "append":
71666
+ case "append_string":
71667
+ case "append_string_view":
71668
+ case "append_int":
71669
+ case "append_float":
71670
+ case "append_char":
71671
+ case "seed": return `(${sb}.s += __to_string(${args[0]}))`;
71672
+ case "length": return `BigInt(${sb}.s.length)`;
71673
+ case "to_string": return `${sb}.s`;
71674
+ case "truncate": return `(${sb}.s = ${sb}.s.slice(0, Number(${args[0]})))`;
71675
+ case "chomp": return `(${sb}.s = ${sb}.s.replace(/\\n$/, ""))`;
71676
+ case "ends_with": return `${sb}.s.endsWith(${args[0]})`;
71677
+ case "take_since": return `__sb_take_since(${sb}, ${args[0]})`;
71678
+ case "clear": return `(${sb}.s = "")`;
71679
+ case "ensure": return "undefined";
71680
+ }
71681
+ }
71682
+ //#endregion
71683
+ //#region ../src/build_js/names.ts
71684
+ /**
71685
+ * JavaScript identifiers that Nomen allows as names but JS reserves (or that
71686
+ * would shadow a global the generated code relies on). Binding names go
71687
+ * through `js_name`; property names after `.` never do (JS allows reserved
71688
+ * words there).
71689
+ */
71690
+ const JS_RESERVED = /* @__PURE__ */ new Set([
71691
+ "arguments",
71692
+ "await",
71693
+ "break",
71694
+ "case",
71695
+ "catch",
71696
+ "class",
71697
+ "const",
71698
+ "continue",
71699
+ "debugger",
71700
+ "default",
71701
+ "delete",
71702
+ "do",
71703
+ "else",
71704
+ "enum",
71705
+ "eval",
71706
+ "export",
71707
+ "extends",
71708
+ "false",
71709
+ "finally",
71710
+ "for",
71711
+ "function",
71712
+ "if",
71713
+ "implements",
71714
+ "import",
71715
+ "in",
71716
+ "instanceof",
71717
+ "interface",
71718
+ "let",
71719
+ "new",
71720
+ "null",
71721
+ "package",
71722
+ "private",
71723
+ "protected",
71724
+ "public",
71725
+ "return",
71726
+ "static",
71727
+ "super",
71728
+ "switch",
71729
+ "this",
71730
+ "throw",
71731
+ "true",
71732
+ "try",
71733
+ "typeof",
71734
+ "undefined",
71735
+ "var",
71736
+ "void",
71737
+ "while",
71738
+ "with",
71739
+ "yield"
71740
+ ]);
71741
+ /** The emitted JS binding name for a Nomen identifier. `#` markers (the
71742
+ * compiler's special names, e.g. `#init`) are stripped, and a name that JS
71743
+ * reserves is prefixed with `$`. */
71744
+ function js_name(name) {
71745
+ const base = name.replace(/#/g, "");
71746
+ return JS_RESERVED.has(base) ? `$${base}` : base;
71747
+ }
71748
+ /** The emitted JS name for a free/nested function (the checker's unique
71749
+ * `label_name` when present, else the source name). */
71750
+ function function_label(func) {
71751
+ return js_name(emission_label(func));
71752
+ }
71753
+ /** The emitted JS name for a struct method (`<Struct>_<method>`). */
71754
+ function method_label(struct_name, func_name) {
71755
+ return js_name(`${struct_name}_${func_name}`);
71756
+ }
71757
+ /** The emitted JS name for a struct constructor factory. */
71758
+ function init_label(struct_name) {
71759
+ return js_name(`${struct_name}_init`);
71760
+ }
71761
+ /** The emitted JS name for a struct's runtime method table (trait dispatch). */
71762
+ function type_label(struct_name) {
71763
+ return js_name(`${struct_name}_type`);
71764
+ }
71765
+ /** The emitted JS name for a struct's `#init` body (called from the
71766
+ * constructor IIFE, not part of the method table). */
71767
+ function init_body_label(struct_name, index) {
71768
+ return js_name(`${struct_name}_init_body_${index}`);
71769
+ }
71770
+ //#endregion
71771
+ //#region ../src/build_js/build_function_node.ts
71772
+ /** Emit a free or nested function declaration. */
71773
+ function build_function_node(node, status) {
71774
+ if (!node.has_body || node.is_generic) return;
71775
+ const old_ref_params = status.ref_params;
71776
+ status.ref_params = /* @__PURE__ */ new Set();
71777
+ const params = node.params.map((param) => boxed_param_signature(param, status)).join(", ");
71778
+ emit(status, `function ${function_label(node)}(${params}) {`);
71779
+ status.indent += 1;
71780
+ for (const param of node.params) emit_ref_prologue(param, status);
71781
+ for (const statement of node.statements) stmt(statement, status);
71782
+ status.indent -= 1;
71783
+ emit(status, "}");
71784
+ status.ref_params = old_ref_params;
71785
+ }
71786
+ /** A `ref` non-self parameter is passed as a box (`{ v }`); the JS parameter
71787
+ * takes the box name and the body's prologue copies the value in. `self` is
71788
+ * already a reference (an object), so a `ref self` receiver passes normally. */
71789
+ function boxed_param_signature(param, status) {
71790
+ if (is_ref_param(param)) {
71791
+ status.ref_params?.add(js_name(param.name));
71792
+ return `__ref_${js_name(param.name)}`;
71793
+ }
71794
+ return param_signature(param);
71795
+ }
71796
+ function emit_ref_prologue(param, status) {
71797
+ if (is_ref_param(param)) emit(status, `let ${js_name(param.name)} = __ref_${js_name(param.name)}.v;`);
71798
+ }
71799
+ /** A JS parameter list entry: `self` keeps its name; every other Nomen name
71800
+ * is sanitized. `ref`/`mv` are compile-time modifiers with no JS analogue. */
71801
+ function param_signature(param) {
71802
+ return param.is_self_param ? "self" : js_name(param.name);
71803
+ }
71804
+ /** A function LITERAL in expression position (`(x) => x * 2`, or a block-body
71805
+ * lambda): a JS function expression. Captures are lexical — JS closures give
71806
+ * the same shape the C backend builds environment structs for. */
71807
+ function build_function_expression(node, status) {
71808
+ const fn = `function (${node.params.map(param_signature).join(", ")}) {\n${capture_body(status, () => {
71809
+ for (const statement of node.statements) stmt(statement, status);
71810
+ })}${" ".repeat(status.indent)}}`;
71811
+ const copied = (node.captures ?? []).filter((capture) => !capture.is_move && is_value_struct(capture.type?.name, status));
71812
+ if (copied.length === 0) return fn;
71813
+ const names = copied.map((capture) => js_name(capture.name));
71814
+ return `((${names.join(", ")}) => ${fn})(${names.map((name) => `__copy(${name})`).join(", ")})`;
71815
+ }
71816
+ /** A `ref` parameter other than the `self` receiver. The parser records `ref`
71817
+ * on the type for ordinary parameters (`ref int x`) and on the parameter
71818
+ * itself for `ref self`. */
71819
+ function is_ref_param(param) {
71820
+ if (param.is_self_param) return false;
71821
+ return !!(param.is_ref || param.type?.is_ref);
71822
+ }
71823
+ //#endregion
71824
+ //#region ../src/build_js/library_queue.ts
71825
+ /**
71826
+ * On-demand lowering of System-library code written in plain Nomen.
71827
+ *
71828
+ * The JS runtime natively models the containers (List/Map/Set/Box/…) and I/O,
71829
+ * but much of the library — `Text.Regex`, `Text.Chars`, `Json`, … — is
71830
+ * ordinary Nomen over strings, arrays, and ints. Rather than hand-port those,
71831
+ * the backend emits the checked body of each referenced method, exactly like
71832
+ * user code. A library body that needs raw memory (`#arch`, `unsafe`, pointer
71833
+ * indexing, foreign calls, threads) becomes a loud `__unsupported` stub
71834
+ * instead of silently wrong code.
71835
+ */
71836
+ /** Library types the JS runtime models directly; their code is never lowered
71837
+ * generically (the native mapping handles their supported surface). */
71838
+ const NATIVE_TYPES = /* @__PURE__ */ new Set([
71839
+ "Array",
71840
+ "Atomic",
71841
+ "Box",
71842
+ "Buffer",
71843
+ "Channel",
71844
+ "Console",
71845
+ "Fiber",
71846
+ "Format",
71847
+ "List",
71848
+ "Map",
71849
+ "Mutex",
71850
+ "Nursery",
71851
+ "Set",
71852
+ "StableList",
71853
+ "String",
71854
+ "StringBuilder",
71855
+ "Task",
71856
+ "Thread"
71857
+ ]);
71858
+ /** Is this a library type we must not lower generically? */
71859
+ function is_native_library_type(name) {
71860
+ if (NATIVE_TYPES.has(name)) return true;
71861
+ if (NATIVE_TYPES.has(library_base_name(name))) return true;
71862
+ if (is_buffer_type(name)) return true;
71863
+ return !!get_built_in_type(name);
71864
+ }
71865
+ /** Queue a specific library method (only the one that is called). */
71866
+ function queue_library_method(struct, func, status) {
71867
+ if (status.emitted_library.has(func)) return;
71868
+ if (status.library_method_queue.some((entry) => entry.func === func)) return;
71869
+ status.library_method_queue.push({
71870
+ struct,
71871
+ func
71872
+ });
71873
+ }
71874
+ /** Queue a library struct's constructor factory. */
71875
+ function queue_library_init(struct, status) {
71876
+ if (status.emitted_library.has(struct)) return;
71877
+ if (status.library_init_queue.includes(struct)) return;
71878
+ status.library_init_queue.push(struct);
71879
+ }
71880
+ /** Queue a library free function. */
71881
+ function queue_library_function(func, status) {
71882
+ if (func.is_generic) return;
71883
+ if (status.emitted_library.has(func)) return;
71884
+ if (status.library_function_queue.includes(func)) return;
71885
+ status.library_function_queue.push(func);
71886
+ }
71887
+ /** Find a struct method by name (and arity, so overloads resolve). */
71888
+ function library_method(struct, name, arg_count, is_static) {
71889
+ const arity = is_static ? arg_count : arg_count + 1;
71890
+ const candidates = struct.functions.filter((f) => f.name === name && f.has_body);
71891
+ return candidates.find((f) => f.params.length === arity) ?? candidates.find((f) => f.params.length >= arity) ?? candidates[0];
71892
+ }
71893
+ /** The emitted JS label for a library method. Overloads (same name, different
71894
+ * arity) get an arity suffix so the declarations stay distinct. */
71895
+ function library_method_label(struct, func) {
71896
+ if (func.name === "#init") return js_name(`${struct.name}_lib_init_${func.params.length}`);
71897
+ if (struct.functions.filter((f) => f.name === func.name && f.has_body).length <= 1) return method_label(struct.name, func.name);
71898
+ return js_name(`${struct.name}_${func.name}_${func.params.length}`);
71899
+ }
71900
+ /** Emit every queued library declaration into `status.library_code`. The
71901
+ * queue grows while bodies are emitted (a library body calls other library
71902
+ * code), so this drains it to a fixed point. */
71903
+ function emit_library_queue(status) {
71904
+ const outer_code = status.code;
71905
+ const outer_indent = status.indent;
71906
+ status.code = "";
71907
+ status.indent = 0;
71908
+ while (status.library_init_queue.length || status.library_method_queue.length || status.library_function_queue.length) {
71909
+ const init = status.library_init_queue.shift();
71910
+ if (init) {
71911
+ if (!status.emitted_library.has(init)) {
71912
+ status.emitted_library.add(init);
71913
+ emit_library_init(init, status);
71914
+ }
71915
+ continue;
71916
+ }
71917
+ const entry = status.library_method_queue.shift();
71918
+ if (entry) {
71919
+ if (!status.emitted_library.has(entry.func)) {
71920
+ status.emitted_library.add(entry.func);
71921
+ emit_library_method(entry.struct, entry.func, status);
71922
+ }
71923
+ continue;
71924
+ }
71925
+ const func = status.library_function_queue.shift();
71926
+ if (!func) continue;
71927
+ if (status.emitted_library.has(func)) continue;
71928
+ status.emitted_library.add(func);
71929
+ emit_library_function(func, status);
71930
+ }
71931
+ status.library_code += status.code;
71932
+ status.code = outer_code;
71933
+ status.indent = outer_indent;
71934
+ }
71935
+ function emit_library_method(struct, func, status) {
71936
+ const label = library_method_label(struct, func);
71937
+ if (needs_raw_memory(func)) {
71938
+ emit_library_stub(label, func, status);
71939
+ return;
71940
+ }
71941
+ const old_ref_params = status.ref_params;
71942
+ status.ref_params = /* @__PURE__ */ new Set();
71943
+ emit(status, `function ${label}(${func.params.map((param) => boxed_param_signature(param, status)).join(", ")}) {`);
71944
+ status.indent += 1;
71945
+ for (const param of func.params) emit_ref_prologue(param, status);
71946
+ for (const statement of func.statements) stmt(statement, status);
71947
+ status.indent -= 1;
71948
+ emit(status, "}");
71949
+ status.ref_params = old_ref_params;
71950
+ }
71951
+ function emit_library_function(func, status) {
71952
+ const label = js_name(func.name);
71953
+ if (needs_raw_memory(func)) {
71954
+ emit_library_stub(label, func, status);
71955
+ return;
71956
+ }
71957
+ const old_ref_params = status.ref_params;
71958
+ status.ref_params = /* @__PURE__ */ new Set();
71959
+ emit(status, `function ${label}(${func.params.map((param) => boxed_param_signature(param, status)).join(", ")}) {`);
71960
+ status.indent += 1;
71961
+ for (const param of func.params) emit_ref_prologue(param, status);
71962
+ for (const statement of func.statements) stmt(statement, status);
71963
+ status.indent -= 1;
71964
+ emit(status, "}");
71965
+ status.ref_params = old_ref_params;
71966
+ }
71967
+ /** Emit a library struct's constructor factory (no methods, no `$type`). */
71968
+ function emit_library_init(struct, status) {
71969
+ const fields = struct.fields.filter((field) => !!field.name);
71970
+ const params = fields.map((field) => {
71971
+ const name = js_name(field.name);
71972
+ return field.value ? `${name} = ${expr(field.value, status)}` : name;
71973
+ });
71974
+ const properties = fields.map((field) => `${field.name}: ${js_name(field.name)}`);
71975
+ emit(status, `function ${init_label(struct.name)}(${params.join(", ")}) {`);
71976
+ status.indent += 1;
71977
+ emit(status, `return { ${properties.join(", ")} };`);
71978
+ status.indent -= 1;
71979
+ emit(status, "}");
71980
+ }
71981
+ /** Emit a library body the JS backend cannot lower as a warning stub: the
71982
+ * program keeps running without it (the call site sees a zero value). */
71983
+ function emit_library_stub(label, func, status) {
71984
+ const old_ref_params = status.ref_params;
71985
+ status.ref_params = /* @__PURE__ */ new Set();
71986
+ const params = func.params.map((param) => boxed_param_signature(param, status)).join(", ");
71987
+ const returned = func.return_type ? ` return ${zero_js_value(func.return_type)};` : "";
71988
+ emit(status, `function ${label}(${params}) { __skip(${JSON.stringify(`library ${label}`)});${returned} }`);
71989
+ status.ref_params = old_ref_params;
71990
+ }
71991
+ /** The JS value a skipped library body yields for its declared return type. */
71992
+ function zero_js_value(type) {
71993
+ if (!type) return "undefined";
71994
+ if (!type.name || type.name === "void" || type.name === "?") return "undefined";
71995
+ switch (get_built_in_type(type.name)?.kind) {
71996
+ case "int": return "0n";
71997
+ case "float": return "0";
71998
+ case "bool": return "false";
71999
+ case "string":
72000
+ case "char": return "\"\"";
72001
+ }
72002
+ if (type.is_array || type.is_view) return "[]";
72003
+ return "null";
72004
+ }
72005
+ /** Does this library body rely on constructs the JS backend cannot express
72006
+ * (raw asm/C, unsafe pointer work, foreign functions, threads)? */
72007
+ function needs_raw_memory(node) {
72008
+ if (UNSUPPORTED_NODE_TYPES.has(node.node_type)) return true;
72009
+ if (node.node_type === "func_call") {
72010
+ const call = node;
72011
+ if (call.name?.startsWith("foreign_")) return true;
72012
+ if (call.resolved_function?.is_foreign) return true;
72013
+ if (call.resolved_function && call.resolved_function.has_body === false) return true;
72014
+ }
72015
+ if (node.node_type === "access") {
72016
+ if (node.access?.name?.startsWith("foreign_")) return true;
72017
+ }
72018
+ for (const child of child_nodes(node)) if (needs_raw_memory(child)) return true;
72019
+ return false;
72020
+ }
72021
+ const UNSUPPORTED_NODE_TYPES = /* @__PURE__ */ new Set([
72022
+ "raw",
72023
+ "unsafe",
72024
+ "spawn",
72025
+ "async_block",
72026
+ "class",
72027
+ "index"
72028
+ ]);
72029
+ /** The call text for a library method (used by the access builder). */
72030
+ function library_method_call(struct, func, target, args, status) {
72031
+ queue_library_method(struct, func, status);
72032
+ const label = library_method_label(struct, func);
72033
+ if (target === void 0) return `${label}(${args.join(", ")})`;
72034
+ return `${label}(${[expr(target, status), ...args].join(", ")})`;
72035
+ }
72036
+ //#endregion
72037
+ //#region ../src/build_js/build_access_node.ts
72038
+ /**
72039
+ * Emit a JS expression for a member access:
72040
+ * - field access → `obj.field`;
72041
+ * - static library calls (`Console.write`, `Format.pad`, …) → runtime helpers;
72042
+ * - string/array builtins → the native JS equivalents;
72043
+ * - the supported System containers (List/Map/StringBuilder) → their JS
72044
+ * representations;
72045
+ * - user struct methods → `<Struct>_<method>(self, args…)`.
72046
+ * Anything outside the supported subset becomes a loud runtime error.
72047
+ */
72048
+ function build_access_node(node, status) {
72049
+ const access = node.access;
72050
+ if (access.node_type === "access_field") {
72051
+ const target_type = type_name_of(node.target);
72052
+ const bitset = status.bitsets.find((b) => b.name === target_type);
72053
+ if (bitset) {
72054
+ const index = bitset.cases.indexOf(access.name);
72055
+ if (index !== -1) return `(1n << ${index}n)`;
72056
+ return unsupported(`bitset ${target_type}.${access.name}`);
72057
+ }
72058
+ const enum_node = status.enums.find((e) => e.name === target_type);
72059
+ if (enum_node) {
72060
+ const case_index = enum_node.cases.findIndex((c) => c.name === access.name);
72061
+ if (case_index !== -1) return enum_case_value(enum_node, case_index, [], status);
72062
+ }
72063
+ if (access.name === "length" && library_base_name(target_type) === "StringBuilder") return `BigInt(${expr(node.target, status)}.s.length)`;
72064
+ if (access.name === "length" && library_base_name(target_type) === "Map") return `BigInt(${expr(node.target, status)}.size)`;
72065
+ if (access.name === "length" && library_base_name(target_type) === "Set") return `BigInt(${expr(node.target, status)}.size)`;
72066
+ if (access.name === "length" && library_base_name(target_type) === "List") return `BigInt(${expr(node.target, status)}.length)`;
72067
+ if (access.name === "length" && library_base_name(target_type) === "StableList") return `BigInt(${expr(node.target, status)}.length)`;
72068
+ if (is_buffer_type(target_type) && access.name === "cap") return `BigInt(${expr(node.target, status)}.length)`;
72069
+ if (is_buffer_type(target_type) && access.name === "data") return "0n";
72070
+ if (access.name === "length") {
72071
+ const target_kind = kind_of(node.target);
72072
+ if (target_kind === "array" || target_kind === "string") return `BigInt(${expr(node.target, status)}.length)`;
72073
+ }
72074
+ return `${expr(node.target, status)}.${access.name}`;
72075
+ }
72076
+ return build_method_call(node.target, access, status);
72077
+ }
72078
+ function build_method_call(target, access, status) {
72079
+ const name = access.name;
72080
+ const args = access.params.map((p) => expr(p, status));
72081
+ const target_type = type_name_of(target);
72082
+ const native_type = library_base_name(target_type);
72083
+ const target_kind = kind_of(target);
72084
+ const target_stamp = stamped_type(target);
72085
+ const receiver_class = access.devirtualized_class ?? (target_stamp?.type_args?.length ? mono_type_name(target_stamp) : target_type);
72086
+ if (access.is_func_field_call) return call_with_refs(status, access.params, access.ref_param_indices, (arg_exprs) => `${expr(target, status)}.${name}(${arg_exprs.join(", ")})`);
72087
+ if (access.is_static) {
72088
+ if (target_type === "Console") return `Console_${name}(${args.join(", ")})`;
72089
+ if (target_type === "Format") return `Format_${name}(${args.join(", ")})`;
72090
+ if (target_type === "Math" && name === "power") return `__math_power(${args.join(", ")})`;
72091
+ if (target_type === "Array" && name === "with") return `__array_with(${args.join(", ")})`;
72092
+ if (target_type === "int" && name === "parse") return `__parse_int(${args[0]})`;
72093
+ if (target_type === "Time") return time_call(name, args);
72094
+ if (target_type === "Utf8") return utf8_call(name, args);
72095
+ const static_concurrency = concurrency_method(library_base_name(target_type), name, "undefined", args, true);
72096
+ if (static_concurrency !== void 0) return static_concurrency;
72097
+ const enum_node = status.enums.find((e) => e.name === target_type);
72098
+ if (enum_node) {
72099
+ const case_index = enum_node.cases.findIndex((c) => c.name === name);
72100
+ if (case_index !== -1) return enum_case_value(enum_node, case_index, args, status);
72101
+ }
72102
+ if (status.structs.find((s) => s.name === target_type)) return call_with_refs(status, access.params, access.ref_param_indices, (arg_exprs) => `${method_label(target_type, name)}(${arg_exprs.join(", ")})`);
72103
+ const library_struct = status.library_structs.find((s) => s.name === receiver_class && !is_native_library_type(s.name));
72104
+ const library_func = library_struct && library_method(library_struct, name, args.length, true);
72105
+ if (library_struct && library_func) return call_with_refs(status, access.params, access.ref_param_indices, (arg_exprs) => library_method_call(library_struct, library_func, void 0, arg_exprs, status));
72106
+ return unsupported(`${target_type}.${name}`);
72107
+ }
72108
+ if (target_kind === "string" || target_kind === "char") {
72109
+ const text = expr(target, status);
72110
+ switch (name) {
72111
+ case "to_string": return text;
72112
+ case "length": return `BigInt(${text}.length)`;
72113
+ case "at":
72114
+ case "at_or_panic": return `__string_at(${text}, ${args[0]})`;
72115
+ case "at_or": return `__string_at_or(${text}, ${args[0]}, ${args[1]})`;
72116
+ case "slice":
72117
+ case "substring": return `${text}.slice(Number(${args[0]}), Number(${args[1]}))`;
72118
+ case "copy": return text;
72119
+ case "trim": return `${text}.trim()`;
72120
+ case "trim_start": return `${text}.replace(/^\\s+/, "")`;
72121
+ case "trim_end": return `${text}.replace(/\\s+$/, "")`;
72122
+ case "to_lowercase": return `${text}.toLowerCase()`;
72123
+ case "to_uppercase": return `${text}.toUpperCase()`;
72124
+ case "contains": return `${text}.includes(${args[0]})`;
72125
+ case "starts_with": return `${text}.startsWith(${args[0]})`;
72126
+ case "ends_with": return `${text}.endsWith(${args[0]})`;
72127
+ case "index_of": return `BigInt(${text}.indexOf(${args[0]}))`;
72128
+ case "index_of_from": return `BigInt(${text}.indexOf(${args[0]}, Number(${args[1]})))`;
72129
+ case "replace_first": return `${text}.replace(${args[0]}, ${args[1]})`;
72130
+ case "replace_all": return `${text}.replaceAll(${args[0]}, ${args[1]})`;
72131
+ case "char_code_at": return `BigInt(${text}.charCodeAt(Number(${args[0]})))`;
72132
+ case "char_code_at_or": return `__char_code_at_or(${text}, ${args[0]}, ${args[1]})`;
72133
+ case "hash": return `__string_hash(${text})`;
72134
+ }
72135
+ return unsupported(`string.${name}`);
72136
+ }
72137
+ if (target_kind === "array") {
72138
+ const list = expr(target, status);
72139
+ switch (name) {
72140
+ case "length": return `BigInt(${list}.length)`;
72141
+ case "at": return `${list}[Number(${args[0]})]`;
72142
+ case "set": return `(${list}[Number(${args[0]})] = ${stored_args(access.params, status)[1]})`;
72143
+ case "push": return `${list}.push(${stored_args(access.params, status)[0]})`;
72144
+ case "pop": return `${list}.pop()`;
72145
+ case "slice": return `${list}.slice(Number(${args[0]}), Number(${args[1]}))`;
72146
+ case "copy": return `${list}.slice()`;
72147
+ }
72148
+ return unsupported(`array.${name}`);
72149
+ }
72150
+ if (native_type === "List") {
72151
+ const mapped = list_method(name, expr(target, status), stored_args(access.params, status));
72152
+ if (mapped !== void 0) return mapped;
72153
+ }
72154
+ if (native_type === "Set") {
72155
+ const mapped = set_method(name, expr(target, status), stored_args(access.params, status));
72156
+ if (mapped !== void 0) return mapped;
72157
+ }
72158
+ if (native_type === "Map") {
72159
+ const mapped = map_method(name, expr(target, status), stored_args(access.params, status));
72160
+ if (mapped !== void 0) return mapped;
72161
+ }
72162
+ if (native_type === "StringBuilder") {
72163
+ const mapped = string_builder_method(name, expr(target, status), args);
72164
+ if (mapped !== void 0) return mapped;
72165
+ }
72166
+ if (native_type === "BigInt") {
72167
+ const mapped = bigint_method(name, expr(target, status), args);
72168
+ if (mapped !== void 0) return mapped;
72169
+ }
72170
+ if (native_type === "StableList") {
72171
+ const mapped = stable_list_method(name, expr(target, status), stored_args(access.params, status));
72172
+ if (mapped !== void 0) return mapped;
72173
+ }
72174
+ if (is_buffer_type(target_type)) {
72175
+ const mapped = buffer_method(name, expr(target, status), args, {
72176
+ class_elements: is_class_buffer_type(target_type),
72177
+ ...buffer_zero(target)
72178
+ });
72179
+ if (mapped !== void 0) return mapped;
72180
+ }
72181
+ const concurrency = concurrency_method(native_type, name, expr(target, status), args, false);
72182
+ if (concurrency !== void 0) return concurrency;
72183
+ if (status.structs.find((s) => s.name === receiver_class)) return call_with_refs(status, access.params, access.ref_param_indices, (arg_exprs) => `${method_label(receiver_class, name)}(${[expr(target, status), ...arg_exprs].join(", ")})`);
72184
+ if (name === "hash" && target_kind === "int") return `BigInt.asUintN(64, ${expr(target, status)})`;
72185
+ const library_struct = status.library_structs.find((s) => s.name === receiver_class && !is_native_library_type(s.name));
72186
+ const library_func = library_struct && library_method(library_struct, name, args.length, false);
72187
+ if (library_struct && library_func) return call_with_refs(status, access.params, access.ref_param_indices, (arg_exprs) => library_method_call(library_struct, library_func, target, arg_exprs, status));
72188
+ if (name === "to_string") return `__to_string(${expr(target, status)})`;
72189
+ if (status.traits.some((t) => t.name === target_type || t.name === receiver_class)) return `__dispatch(${[
72190
+ expr(target, status),
72191
+ JSON.stringify(name),
72192
+ ...args
72193
+ ].join(", ")})`;
72194
+ return unsupported(`${target_type}.${name}`);
72195
+ }
72196
+ /** The zero value a freshly grown Buffer slot holds, and whether loads must
72197
+ * copy: struct/enum slots have value semantics, scalars and strings don't. */
72198
+ function buffer_zero(target) {
72199
+ const type = stamped_type(target);
72200
+ const element_name = buffer_element_name(type);
72201
+ const info = element_name ? get_built_in_type(element_name) : void 0;
72202
+ if (is_class_buffer_type(type?.name ?? "")) return {
72203
+ zero: "null",
72204
+ copy_loads: false
72205
+ };
72206
+ switch (info?.kind) {
72207
+ case "int": return {
72208
+ zero: "0n",
72209
+ copy_loads: false
72210
+ };
72211
+ case "float": return {
72212
+ zero: "0",
72213
+ copy_loads: false
72214
+ };
72215
+ case "bool": return {
72216
+ zero: "false",
72217
+ copy_loads: false
72218
+ };
72219
+ case "string":
72220
+ case "char": return {
72221
+ zero: "\"\"",
72222
+ copy_loads: false
72223
+ };
72224
+ default: return {
72225
+ zero: "undefined",
72226
+ copy_loads: true
72227
+ };
72228
+ }
72229
+ }
72230
+ /** `Buffer_int` → `int`, `Buffer<T>` → `T`, `ClassBuffer_Animal` → `Animal`. */
72231
+ function buffer_element_name(type) {
72232
+ const arg = type?.type_args?.[0];
72233
+ if (arg) return arg.name;
72234
+ const name = type?.name ?? "";
72235
+ for (const prefix of ["ClassBuffer_", "Buffer_"]) if (name.startsWith(prefix)) return name.slice(prefix.length);
72236
+ }
72237
+ /** `System.Utf8` over the JS string model (code units, not bytes): decoding
72238
+ * advances one code unit for BMP text and two for astral characters. */
72239
+ function utf8_call(name, args) {
72240
+ switch (name) {
72241
+ case "decode_at": return `__utf8_decode(${args[0]}, ${args[1]})`;
72242
+ case "width_at": return `__utf8_decode(${args[0]}, ${args[1]}).byte_length`;
72243
+ case "encode": return `__utf8_encode(${args[0]})`;
72244
+ case "char_count": return `BigInt([...${args[0]}].length)`;
72245
+ }
72246
+ return unsupported(`Utf8.${name}`);
72247
+ }
72248
+ /** `System.Time` reads and sleeps, mapped onto JS clock/sleep primitives. */
72249
+ function time_call(name, args) {
72250
+ switch (name) {
72251
+ case "now_ms": return "BigInt(Date.now())";
72252
+ case "now_ns": return "(BigInt(Date.now()) * 1000000n)";
72253
+ case "now_unix": return "BigInt(Math.floor(Date.now() / 1000))";
72254
+ case "sleep_ms": return `__sleep_ms(${args[0]})`;
72255
+ }
72256
+ return unsupported(`Time.${name}`);
72257
+ }
72258
+ /** Container stores copy value structs (MEMORY.md: a copy into a container
72259
+ * slot is independent). Scalars/strings are identity under `__copy`. */
72260
+ function stored_args(args, status) {
72261
+ return args.map((arg) => copy_if_value_struct(expr(arg, status), stamped_type(arg)?.name, status));
72262
+ }
72263
+ function unsupported(what) {
72264
+ return `__skip(${JSON.stringify(what)})`;
72265
+ }
72266
+ //#endregion
72267
+ //#region ../src/build_js/build_array_values_node.ts
72268
+ /** Emit a JS array literal (`[a, b, c]`). */
72269
+ function build_array_values_node(node, status) {
72270
+ return `[${node.values.map((v) => expr(v, status)).join(", ")}]`;
72271
+ }
72272
+ //#endregion
72273
+ //#region ../src/build_js/build_assignment_node.ts
72274
+ /** Emit an assignment (plain, compound, or `x = y swap z`). */
72275
+ function build_assignment_node(node, status) {
72276
+ const left = expr(node.left_value, status);
72277
+ const right = copy_if_value_struct(expr(node.right_value, status), stamped_type(node.right_value)?.name, status);
72278
+ if (node.swap) {
72279
+ const temp = next_temp(status, "swap");
72280
+ emit(status, "{");
72281
+ status.indent += 1;
72282
+ emit(status, `const ${temp} = ${right};`);
72283
+ emit(status, `${left} = ${temp};`);
72284
+ emit(status, `${right} = ${expr(node.swap, status)};`);
72285
+ status.indent -= 1;
72286
+ emit(status, "}");
72287
+ return;
72288
+ }
72289
+ const operator = node.operator ?? "=";
72290
+ if (operator !== "=" && operator.endsWith("=")) {
72291
+ const base_op = operator.slice(0, -1);
72292
+ const target = stamped_type(node.left_value)?.name;
72293
+ emit(status, `${left} = ${wrap_int_expression(`(${left} ${base_op} ${right})`, target)};`);
72294
+ } else emit(status, `${left} = ${wrap_for_type(right, stamped_type(node.left_value))};`);
72295
+ write_back_ref_param(node, status);
72296
+ }
72297
+ /** A `ref` parameter is a local bound from its box: after every assignment
72298
+ * the box receives the new value (the caller copies it out at the call). */
72299
+ function write_back_ref_param(node, status) {
72300
+ if (node.left_value.node_type !== "value") return;
72301
+ const name = js_name(node.left_value.value);
72302
+ if (!status.ref_params?.has(name)) return;
72303
+ emit(status, `__ref_${name}.v = ${name};`);
72304
+ }
72305
+ //#endregion
72306
+ //#region ../src/build_js/build_async_block_node.ts
72307
+ /**
72308
+ * `async { ... }` — the JS backend runs the nursery INLINE, in source order.
72309
+ *
72310
+ * Concurrency is omitted rather than emulated: every `Thread(...).start()` /
72311
+ * `Fiber(...).start()` / `nursery.start(...)` inside the block runs its task
72312
+ * synchronously before returning, so the implicit join at the closing brace is
72313
+ * already satisfied. A named nursery (`async pool { ... }`) becomes a plain
72314
+ * object that `pool.start(Thread(...))` accepts.
72315
+ */
72316
+ function build_async_block_node(node, status) {
72317
+ emit(status, "{");
72318
+ status.indent += 1;
72319
+ build_nursery(node, status);
72320
+ if (node.timeout) emit(status, `${expr(node.timeout, status)};`);
72321
+ for (const statement of node.statements) stmt(statement, status);
72322
+ status.indent -= 1;
72323
+ emit(status, "}");
72324
+ }
72325
+ /** The value form of `async { ... }` (rare, but the checker can wrap one in a
72326
+ * `let`): an IIFE that runs the block and yields `undefined`. */
72327
+ function build_async_block_expression(node, status) {
72328
+ return `(() => {\n${capture_body(status, () => {
72329
+ build_nursery(node, status);
72330
+ if (node.timeout) emit(status, `${expr(node.timeout, status)};`);
72331
+ for (const statement of node.statements) stmt(statement, status);
72332
+ })}${" ".repeat(status.indent)}})()`;
72333
+ }
72334
+ function build_nursery(node, status) {
72335
+ if (!node.nursery_name) return;
72336
+ emit(status, `let ${js_name(node.nursery_name)} = {};`);
72337
+ }
72338
+ //#endregion
72339
+ //#region ../src/build_js/build_break_node.ts
72340
+ /** `break` (loop labels are not in the v1 subset). */
72341
+ function build_break_node(node, status) {
72342
+ emit(status, "break;");
72343
+ }
72344
+ //#endregion
72345
+ //#region ../src/build_js/build_cast_node.ts
72346
+ /** Emit a JS expression for an `as` cast between the numeric/bool/char
72347
+ * families. Sized int targets wrap with BigInt.asIntN/asUintN, matching the
72348
+ * C backend's truncation. */
72349
+ function build_cast_node(node, status) {
72350
+ const value = expr(node.value, status);
72351
+ const from = kind_of(node.value);
72352
+ const to = kind_of(node);
72353
+ const target = node.target_type;
72354
+ if (node.operator_func && status.structs.some((s) => s.name === node.operator_func?.struct_name)) {
72355
+ const { struct_name, func_name } = node.operator_func;
72356
+ return `${method_label(struct_name, func_name)}(${value})`;
72357
+ }
72358
+ if (node.enum_case_conversion) {
72359
+ if (node.enum_case_conversion.direction === "case_to_base") return value;
72360
+ const enum_node = status.enums.find((e) => e.name === node.target_type.name.split("_")[0]);
72361
+ const case_name = node.target_type.name.split("_").slice(1).join("_");
72362
+ const case_index = enum_node?.cases.findIndex((c) => c.name === case_name) ?? -1;
72363
+ if (enum_node && case_index !== -1) return `__enum_to_case(${value}, ${case_index}, ${JSON.stringify(node.target_type.name)})`;
72364
+ return value;
72365
+ }
72366
+ if (from === to) return value;
72367
+ if (to === "float") return `Number(${value})`;
72368
+ if (to === "int" && from === "float") return `BigInt(Math.trunc(${value}))`;
72369
+ if (to === "int" && from === "char") return `BigInt(${value}.charCodeAt(0))`;
72370
+ if (to === "int" && from === "bool") return `(${value} ? 1n : 0n)`;
72371
+ if (to === "bool" && from === "int") return `(${value} !== 0n)`;
72372
+ if (to === "char" && from === "int") return `String.fromCharCode(Number(${value}))`;
72373
+ if (to === "char" && from === "bool") return `(${value} ? "\\x01" : "\\x00")`;
72374
+ if (to === "string" && from !== "string") return `__to_string(${value})`;
72375
+ if (to === "int" && from === "int") {
72376
+ const info = get_built_in_type(target.name);
72377
+ const bits = info?.bits ?? 64;
72378
+ if (bits >= 64) return value;
72379
+ return info?.signed ? `BigInt.asIntN(${bits}, ${value})` : `BigInt.asUintN(${bits}, ${value})`;
72380
+ }
72381
+ return value;
72382
+ }
72383
+ //#endregion
72384
+ //#region ../src/build_js/build_continue_node.ts
72385
+ /** `continue` (loop labels are not in the v1 subset). */
72386
+ function build_continue_node(node, status) {
72387
+ emit(status, "continue;");
72388
+ }
72389
+ //#endregion
72390
+ //#region ../src/build_js/build_declaration_node.ts
72391
+ /** Emit a `var`/`const` declaration. Uninitialized locals get the type's
72392
+ * zero value (JS `let x;` would be `undefined`, which no Nomen value is). */
72393
+ function build_declaration_node(node, status) {
72394
+ const keyword = node.declaration === "const" ? "const" : "let";
72395
+ const name = js_name(node.name);
72396
+ const raw = node.value ? expr(node.value, status) : zero_value$1(node.type);
72397
+ const copied = node.value ? copy_if_value_struct(raw, node.type?.name, status) : raw;
72398
+ emit(status, `${keyword} ${name} = ${node.value ? wrap_for_type(copied, node.type) : copied};`);
72399
+ }
72400
+ function zero_value$1(type) {
72401
+ if (!type) return "null";
72402
+ if (type.is_array) return "[]";
72403
+ switch (get_built_in_type(type.name)?.kind) {
72404
+ case "int": return "0n";
72405
+ case "float": return "0";
72406
+ case "bool": return "false";
72407
+ case "char":
72408
+ case "string": return "\"\"";
72409
+ default: return "null";
72410
+ }
72411
+ }
72412
+ //#endregion
72413
+ //#region ../src/build_js/build_while_loop_node.ts
72414
+ /** Emit a while loop. The optional `; update` clause runs after each body
72415
+ * iteration — including on `continue`, so it lowers to a `for (; cond;
72416
+ * update)` loop rather than a `while` with the update appended to the body
72417
+ * (JS `continue` would otherwise skip it and spin forever). */
72418
+ function build_while_loop_node(node, status) {
72419
+ const condition = expr(node.condition, status);
72420
+ if (node.update) emit(status, `for (; ${condition}; ${update_expression(node.update, status)}) {`);
72421
+ else emit(status, `while (${condition}) {`);
72422
+ status.indent += 1;
72423
+ for (const statement of node.statements) stmt(statement, status);
72424
+ status.indent -= 1;
72425
+ emit(status, "}");
72426
+ }
72427
+ /** The `; update` clause as a JS expression (no trailing semicolon). */
72428
+ function update_expression(node, status) {
72429
+ if (!node) return "";
72430
+ if (node.node_type === "assign") {
72431
+ const assign = node;
72432
+ return `${expr(assign.left_value, status)} ${assign.operator ?? "="} ${expr(assign.right_value, status)}`;
72433
+ }
72434
+ return expr(node, status);
72435
+ }
72436
+ //#endregion
72437
+ //#region ../src/build_js/build_for_loop_node.ts
72438
+ /** Emit a `for` loop over a range (`for i of 0 .. n`) or an array
72439
+ * (`for x of xs`, optionally with an index: `for i, x of xs`). A `; update`
72440
+ * clause runs at the end of each iteration (matching the native backends). */
72441
+ function build_for_loop_node(node, status) {
72442
+ const item_name = js_name(node.item.value);
72443
+ const update = node.update ? update_expression(node.update, status) : void 0;
72444
+ if (node.list.node_type === "range") {
72445
+ const range = node.list;
72446
+ const increment = `${item_name} = ${item_name} + 1n`;
72447
+ emit(status, `for (let ${item_name} = ${expr(range.left_value, status)}, __end = ${expr(range.right_value, status)}; ${item_name} < __end; ${increment}) {`);
72448
+ status.indent += 1;
72449
+ for (const statement of node.statements) stmt(statement, status);
72450
+ if (update) emit(status, `${update};`);
72451
+ status.indent -= 1;
72452
+ emit(status, "}");
72453
+ return;
72454
+ }
72455
+ const list = expr(node.list, status);
72456
+ if (node.item_is_ref) {
72457
+ const array = next_temp(status, "arr");
72458
+ const index = next_temp(status, "idx");
72459
+ const increment = `${index} = ${index} + 1n`;
72460
+ emit(status, "{");
72461
+ status.indent += 1;
72462
+ emit(status, `const ${array} = ${list};`);
72463
+ emit(status, `for (let ${index} = 0n; ${index} < BigInt(${array}.length); ${increment}) {`);
72464
+ status.indent += 1;
72465
+ emit(status, `let ${item_name} = ${array}[Number(${index})];`);
72466
+ emit(status, "try {");
72467
+ status.indent += 1;
72468
+ for (const statement of node.statements) stmt(statement, status);
72469
+ if (update) emit(status, `${update};`);
72470
+ status.indent -= 1;
72471
+ emit(status, "} finally {");
72472
+ status.indent += 1;
72473
+ emit(status, `${array}[Number(${index})] = ${item_name};`);
72474
+ status.indent -= 1;
72475
+ emit(status, "}");
72476
+ status.indent -= 1;
72477
+ emit(status, "}");
72478
+ status.indent -= 1;
72479
+ emit(status, "}");
72480
+ return;
72481
+ }
72482
+ if (node.index) {
72483
+ const index_name = js_name(node.index.value);
72484
+ emit(status, `for (let ${index_name} = 0n; ${index_name} < BigInt(${list}.length); ${index_name} = ${index_name} + 1n) {`);
72485
+ status.indent += 1;
72486
+ emit(status, `const ${item_name} = ${list}[Number(${index_name})];`);
72487
+ for (const statement of node.statements) stmt(statement, status);
72488
+ if (update) emit(status, `${update};`);
72489
+ status.indent -= 1;
72490
+ emit(status, "}");
72491
+ return;
72492
+ }
72493
+ emit(status, `for (const ${item_name} of ${list}) {`);
72494
+ status.indent += 1;
72495
+ for (const statement of node.statements) stmt(statement, status);
72496
+ if (update) emit(status, `${update};`);
72497
+ status.indent -= 1;
72498
+ emit(status, "}");
72499
+ }
72500
+ //#endregion
72501
+ //#region ../src/build_js/build_struct_node.ts
72502
+ /**
72503
+ * Emit a user struct: a constructor factory plus its methods.
72504
+ *
72505
+ * - `P(a, b)` → `P_init(a, b)` returning a plain object `{ field: value }`
72506
+ * (fields in declaration order; declared defaults become JS defaults);
72507
+ * - `p.bump(x)` → `P_bump(p, x)` — `self` is the first JS parameter;
72508
+ * - `#init`/`#destroy`/other `#`-named synthesized members are not emitted:
72509
+ * construction is the factory, and the GC replaces destroy.
72510
+ */
72511
+ function build_struct_node(node, status) {
72512
+ if (node.is_generic) return;
72513
+ const fields = node.fields.filter((f) => !!f.name);
72514
+ const params = fields.map((field) => {
72515
+ const name = js_name(field.name);
72516
+ return field.value ? `${name} = ${copy_if_value_struct(expr(field.value, status), field.value ? void 0 : field.type?.name, status)}` : name;
72517
+ });
72518
+ const properties = [`$type: ${type_label(node.name)}`].concat(fields.map((field) => `${field.name}: ${copy_if_value_struct(js_name(field.name), field.type?.name, status)}`));
72519
+ emit(status, `function ${init_label(node.name)}(${params.join(", ")}) {`);
72520
+ status.indent += 1;
72521
+ emit(status, `return { ${properties.join(", ")} };`);
72522
+ status.indent -= 1;
72523
+ emit(status, "}");
72524
+ const methods = [];
72525
+ for (const func of node.functions) {
72526
+ if (!func.has_body || func.is_generic) continue;
72527
+ if (func.name.startsWith("#")) continue;
72528
+ emit_method(node, func, status);
72529
+ methods.push(`${js_name(func.name)}: ${method_label(node.name, func.name)}`);
72530
+ }
72531
+ emit(status, `const ${type_label(node.name)} = { ${methods.join(", ")} };`);
72532
+ node.functions.filter((func) => func.name === "#init" && func.has_body && !func.is_generic).forEach((func, index) => {
72533
+ emit_method(node, func, status, init_body_label(node.name, index));
72534
+ });
72535
+ }
72536
+ function emit_method(struct, func, status, label) {
72537
+ const old_ref_params = status.ref_params;
72538
+ status.ref_params = /* @__PURE__ */ new Set();
72539
+ const params = func.params.map((param) => boxed_param_signature(param, status)).join(", ");
72540
+ emit(status, `function ${label ?? method_label(struct.name, func.name)}(${params}) {`);
72541
+ status.indent += 1;
72542
+ for (const param of func.params) emit_ref_prologue(param, status);
72543
+ for (const statement of func.statements) stmt(statement, status);
72544
+ status.indent -= 1;
72545
+ emit(status, "}");
72546
+ status.ref_params = old_ref_params;
72547
+ }
72548
+ /** The literal for a freshly-constructed struct before its `#init` runs:
72549
+ * declared field defaults, or the type's zero value. */
72550
+ function struct_zero_object(struct, status, include_type = true) {
72551
+ const parts = include_type ? [`$type: ${type_label(struct.name)}`] : [];
72552
+ for (const field of struct.fields) {
72553
+ if (!field.name) continue;
72554
+ const value = field.value ? copy_if_value_struct(expr(field.value, status), field.type?.name, status) : zero_value(field.type?.name ?? "");
72555
+ parts.push(`${field.name}: ${value}`);
72556
+ }
72557
+ return `{ ${parts.join(", ")} }`;
72558
+ }
72559
+ /** The same object with its `#init` body applied. Emited at the constructor
72560
+ * call site (the checker resolved which overload runs). */
72561
+ function struct_init_expression(struct, init_index, args, status) {
72562
+ const args_text = args.length ? `, ${args.join(", ")}` : "";
72563
+ return `(() => { const __self = ${struct_zero_object(struct, status)}; ${init_body_label(struct.name, init_index)}(__self${args_text}); return __self; })()`;
72564
+ }
72565
+ function zero_value(type_name) {
72566
+ switch (type_name) {
72567
+ case "float":
72568
+ case "float32":
72569
+ case "float64":
72570
+ case "ufloat": return "0";
72571
+ case "bool": return "false";
72572
+ case "char": return "\"\\u0000\"";
72573
+ case "string": return "\"\"";
72574
+ case "": return "undefined";
72575
+ default:
72576
+ if (type_name.startsWith("int") || type_name.startsWith("uint")) return "0n";
72577
+ return "null";
72578
+ }
72579
+ }
72580
+ //#endregion
72581
+ //#region ../src/build_js/build_function_call_node.ts
72582
+ /**
72583
+ * Emit a JS expression for a call node:
72584
+ * - interpolation helpers (`_string_interpolate_N[_kinds]`) → `__interp`;
72585
+ * - enum case constructions → tagged object literals;
72586
+ * - struct constructors → the `<Struct>_init` factory;
72587
+ * - library containers (List/Map/Box/StringBuilder) → their JS values;
72588
+ * - free/nested functions → the checker's emission label (with `ref`
72589
+ * arguments boxed and copied back).
72590
+ */
72591
+ function build_function_call_node(node, status) {
72592
+ const name = node.name;
72593
+ if (node.is_thread_ctor || node.is_fiber_ctor) {
72594
+ const first = node.params[0];
72595
+ return `({ __task: ${first?.node_type === "func_call" ? `() => (${expr(first, status)})` : `() => (${expr(first, status)}())`}, __result: undefined, __zero: ${zero_js_value(node.function_return_type)}, __done: false, started: false, $type: __spawn_methods })`;
72596
+ }
72597
+ if (is_interp_name(name)) {
72598
+ const { kinds } = parse_interp_name(name);
72599
+ const pattern = expr(node.params[0], status);
72600
+ const args = node.params.slice(1).map((p) => expr(p, status));
72601
+ return `__interp(${pattern}, "${kinds ? kinds.join("") : "s".repeat(args.length)}"${args.length ? `, ${args.join(", ")}` : ""})`;
72602
+ }
72603
+ if (node.is_enum_shorthand) {
72604
+ const args = node.params.map((p) => expr(p, status));
72605
+ const hit = find_enum_case(status.enums, name);
72606
+ if (hit) return enum_case_value(hit.enum_node, hit.case_index, args, status);
72607
+ const enum_node = status.enums.find((e) => e.name === node.type?.name);
72608
+ if (enum_node) {
72609
+ const case_name = name.replace(/^\./, "").replace(`${enum_node.name}_`, "");
72610
+ const case_index = enum_node.cases.findIndex((c) => c.name === case_name);
72611
+ if (case_index !== -1) return enum_case_value(enum_node, case_index, args, status);
72612
+ }
72613
+ }
72614
+ const struct = status.structs.find((s) => s.name === name);
72615
+ if (struct) {
72616
+ const args = node.params.map((p) => expr(p, status));
72617
+ if (node.resolved_function?.name === "#init" && init_has_self(node.resolved_function)) {
72618
+ const index = struct.functions.filter((func) => func.name === "#init" && func.has_body && !func.is_generic && init_has_self(func)).indexOf(node.resolved_function);
72619
+ if (index !== -1) return struct_init_expression(struct, index, args, status);
72620
+ }
72621
+ return `${node.mangled_name ?? init_label(name)}(${args.join(", ")})`;
72622
+ }
72623
+ const args = node.params.map((p) => expr(p, status));
72624
+ const library = library_constructor(name, args);
72625
+ if (library !== void 0) return library;
72626
+ const library_struct = status.library_structs.find((s) => s.name === name && !is_native_library_type(s.name));
72627
+ if (library_struct) {
72628
+ if (node.resolved_function?.name === "#init" && init_has_self(node.resolved_function)) {
72629
+ queue_library_method(library_struct, node.resolved_function, status);
72630
+ const label = library_method_label(library_struct, node.resolved_function);
72631
+ const args_text = args.length ? `, ${args.join(", ")}` : "";
72632
+ return `(() => { const __self = ${struct_zero_object(library_struct, status, false)}; ${label}(__self${args_text}); return __self; })()`;
72633
+ }
72634
+ queue_library_init(library_struct, status);
72635
+ return `${init_label(name)}(${args.join(", ")})`;
72636
+ }
72637
+ if (name === "parse_int" && args.length === 1) return `__parse_int(${args[0]})`;
72638
+ if (node.resolved_function?.is_library) queue_library_function(node.resolved_function, status);
72639
+ const label = function_label(node.resolved_function ?? node);
72640
+ return call_with_refs(status, node.params, node.ref_param_indices, (arg_exprs) => `${label}(${arg_exprs.join(", ")})`);
72641
+ }
72642
+ /** A hand-written `#init` takes `self`; the checker's synthesized positional
72643
+ * initializer (`#init = (field, …)`) does not, and the field factory covers
72644
+ * it. */
72645
+ function init_has_self(func) {
72646
+ return func.params?.[0]?.is_self_param === true;
72647
+ }
72648
+ //#endregion
72649
+ //#region ../src/build_js/build_grouped_node.ts
72650
+ /** A parenthesized expression: keep the grouping in the emitted JS. */
72651
+ function build_grouped_node(node, status) {
72652
+ return `(${expr(node.value, status)})`;
72653
+ }
72654
+ //#endregion
72655
+ //#region ../src/build_js/build_if_else_node.ts
72656
+ /** Emit an if/else statement. (Expression-position `if` is not in the v1
72657
+ * subset; using one as a value hits `__unsupported`.) */
72658
+ function build_if_else_node(node, status) {
72659
+ emit(status, `if (${expr(node.condition, status)}) {`);
72660
+ status.indent += 1;
72661
+ for (const statement of node.if_branch?.statements ?? []) stmt(statement, status);
72662
+ status.indent -= 1;
72663
+ if (node.else_branch) {
72664
+ emit(status, "} else {");
72665
+ status.indent += 1;
72666
+ for (const statement of node.else_branch.statements ?? []) stmt(statement, status);
72667
+ status.indent -= 1;
72668
+ }
72669
+ emit(status, "}");
72670
+ }
72671
+ /** `if` in expression position (`const label = if c -> a else -> b`): an
72672
+ * IIFE whose branches return their value. */
72673
+ function build_if_expression(node, status) {
72674
+ return capture_expression(status, () => {
72675
+ emit(status, `if (${expr(node.condition, status)}) {`);
72676
+ status.indent += 1;
72677
+ branch_body(node.if_branch?.statements ?? [], status, true);
72678
+ status.indent -= 1;
72679
+ if (node.else_branch) {
72680
+ emit(status, "} else {");
72681
+ status.indent += 1;
72682
+ branch_body(node.else_branch.statements, status, true);
72683
+ status.indent -= 1;
72684
+ }
72685
+ emit(status, "}");
72686
+ });
72687
+ }
72688
+ //#endregion
72689
+ //#region ../src/build_js/build_match_node.ts
72690
+ /** `match` as a statement: an if/else chain over a hoisted scrutinee. Enum
72691
+ * cases test the tag and bind the payload positionally. */
72692
+ function build_match_statement(node, status) {
72693
+ emit_match(node, status, false);
72694
+ }
72695
+ /** `match` in expression position: an IIFE whose branches return. */
72696
+ function build_match_expression(node, status) {
72697
+ return capture_expression(status, () => emit_match(node, status, true));
72698
+ }
72699
+ function emit_match(node, status, value_mode) {
72700
+ const temp = next_temp(status, "match");
72701
+ emit(status, "{");
72702
+ status.indent += 1;
72703
+ emit(status, `const ${temp} = ${expr(node.value, status)};`);
72704
+ node.cases.forEach((match_case, index) => {
72705
+ const enum_hit = match_case.match_value.node_type === "value" ? find_enum_case(status.enums, match_case.match_value.value) : void 0;
72706
+ const condition = enum_hit ? `${temp}.$tag === ${enum_hit.case_index}` : `${temp} === ${expr(match_case.match_value, status)}`;
72707
+ emit(status, `${index === 0 ? "if" : "else if"} (${condition}) {`);
72708
+ status.indent += 1;
72709
+ if (enum_hit) {
72710
+ const case_def = enum_hit.enum_node.cases[enum_hit.case_index];
72711
+ (match_case.params ?? []).forEach((param, i) => {
72712
+ const field = case_def.params[i]?.name ?? `$${i}`;
72713
+ const type_name = case_def.params[i]?.type?.name;
72714
+ const value = is_value_struct(type_name, status) ? `__copy(${temp}.${field})` : `${temp}.${field}`;
72715
+ emit(status, `const ${js_name(param)} = ${value};`);
72716
+ });
72717
+ }
72718
+ branch_body(match_case.branch.statements ?? [], status, value_mode);
72719
+ status.indent -= 1;
72720
+ emit(status, "}");
72721
+ });
72722
+ emit(status, "else {");
72723
+ status.indent += 1;
72724
+ if (node.else_branch) branch_body(node.else_branch.statements ?? [], status, value_mode);
72725
+ else if (value_mode) emit(status, `return __unsupported("match: no matching case");`);
72726
+ else emit(status, `__panic("no matching match case");`);
72727
+ status.indent -= 1;
72728
+ emit(status, "}");
72729
+ status.indent -= 1;
72730
+ emit(status, "}");
72731
+ }
72732
+ //#endregion
72733
+ //#region ../src/build_js/build_operation_node.ts
72734
+ /** Emit a JS expression for a Nomen operator node. Ints are bigints and
72735
+ * floats are numbers, so the same JS operator serves both; the one special
72736
+ * case is string repetition (`"x" * n` → `.repeat(n)`). */
72737
+ function build_operation_node(node, status) {
72738
+ const op = node.op;
72739
+ if (op === "u-") return `(-${expr(node.left_value, status)})`;
72740
+ if (op === "!") return `(!${expr(node.left_value, status)})`;
72741
+ let left = expr(node.left_value, status);
72742
+ let right = expr(node.right_value, status);
72743
+ const result_kind = kind_of(node);
72744
+ const left_kind = kind_of(node.left_value);
72745
+ const right_kind = kind_of(node.right_value);
72746
+ const arithmetic = [
72747
+ "+",
72748
+ "-",
72749
+ "*",
72750
+ "/",
72751
+ "%",
72752
+ "<<",
72753
+ ">>",
72754
+ "&",
72755
+ "|",
72756
+ "^"
72757
+ ].includes(op);
72758
+ if (arithmetic && result_kind === "int") {
72759
+ if (left_kind === "char") left = `BigInt(${left}.charCodeAt(0))`;
72760
+ if (right_kind === "char") right = `BigInt(${right}.charCodeAt(0))`;
72761
+ }
72762
+ if (arithmetic && result_kind === "char") return `__char_from_code((${left_kind === "char" ? `BigInt(${left}.charCodeAt(0))` : left} ${op} ${right_kind === "char" ? `BigInt(${right}.charCodeAt(0))` : right}) & 255n)`;
72763
+ if (op === "*" && kind_of(node.left_value) === "string") return `(${left}.repeat(Number(${right})))`;
72764
+ if (op === "+" && kind_of(node.left_value) === "array") return `(${left}.concat(${right}))`;
72765
+ if (node.operator_func && !node.operator_func.deferred) {
72766
+ const { struct_name, func_name, invert } = node.operator_func;
72767
+ if (status.structs.some((s) => s.name === struct_name)) {
72768
+ const call = `${method_label(struct_name, func_name)}(${left}, ${right})`;
72769
+ return invert ? `(!${call})` : `(${call})`;
72770
+ }
72771
+ }
72772
+ if ((op === "==" || op === "!=") && is_enum(node.left_value, status)) {
72773
+ const compare = `__enum_eq(${left}, ${right})`;
72774
+ return op === "==" ? compare : `(!${compare})`;
72775
+ }
72776
+ if (op === "==") return `(${left} === ${right})`;
72777
+ if (op === "!=") return `(${left} !== ${right})`;
72778
+ const result = `(${left} ${op} ${right})`;
72779
+ if (arithmetic && result_kind === "int") return wrap_int_expression(result, stamped_type(node)?.name);
72780
+ return result;
72781
+ }
72782
+ function is_enum(node, status) {
72783
+ const type = stamped_type(node);
72784
+ if (!type?.name) return false;
72785
+ return status.enums.some((e) => e.name === type.name);
72786
+ }
72787
+ //#endregion
72788
+ //#region ../src/build_js/build_panic_node.ts
72789
+ /** `panic("…")` — the runtime prints the message to stderr and exits 2, the
72790
+ * same contract as the native backends. */
72791
+ function build_panic_node(node, status) {
72792
+ emit(status, `__panic(${JSON.stringify(node.message)});`);
72793
+ }
72794
+ //#endregion
72795
+ //#region ../src/build_js/build_return_node.ts
72796
+ /** Emit a return statement. */
72797
+ function build_return_node(node, status) {
72798
+ if (node.value) emit(status, `return ${copy_if_value_struct(expr(node.value, status), stamped_type(node.value)?.name, status)};`);
72799
+ else emit(status, "return;");
72800
+ }
72801
+ //#endregion
72802
+ //#region ../src/build_js/build_switch_node.ts
72803
+ /** `switch` as a statement: an if/else-if chain over boolean case
72804
+ * expressions (Nomen switch cases are conditions, not values). */
72805
+ function build_switch_statement(node, status) {
72806
+ emit_switch(node, status, false);
72807
+ }
72808
+ /** `switch` in expression position: an IIFE whose branches return. */
72809
+ function build_switch_expression(node, status) {
72810
+ return capture_expression(status, () => emit_switch(node, status, true));
72811
+ }
72812
+ function emit_switch(node, status, value_mode) {
72813
+ if (node.cases.length === 0) {
72814
+ if (value_mode) emit(status, `return __unsupported("empty switch");`);
72815
+ return;
72816
+ }
72817
+ node.cases.forEach((switch_case, index) => {
72818
+ const condition = expr(switch_case.condition, status);
72819
+ emit(status, `${index === 0 ? "if" : "else if"} (${condition}) {`);
72820
+ status.indent += 1;
72821
+ branch_body(switch_case.branch.statements ?? [], status, value_mode);
72822
+ status.indent -= 1;
72823
+ emit(status, "}");
72824
+ });
72825
+ if (node.else_branch) {
72826
+ emit(status, "else {");
72827
+ status.indent += 1;
72828
+ branch_body(node.else_branch.statements ?? [], status, value_mode);
72829
+ status.indent -= 1;
72830
+ emit(status, "}");
72831
+ } else if (value_mode) {
72832
+ emit(status, "else {");
72833
+ status.indent += 1;
72834
+ emit(status, `return __unsupported("switch: no matching case");`);
72835
+ status.indent -= 1;
72836
+ emit(status, "}");
72837
+ }
72838
+ }
72839
+ //#endregion
72840
+ //#region ../src/build_js/build_todo_node.ts
72841
+ /** `todo("…")` — same runtime contract as panic. */
72842
+ function build_todo_node(node, status) {
72843
+ emit(status, `__panic(${JSON.stringify(node.message ? `todo: ${node.message}` : "todo")});`);
72844
+ }
72845
+ //#endregion
72846
+ //#region ../src/build_js/build_value_node.ts
72847
+ /**
72848
+ * A Nomen string/char literal is stored as its raw source text (quotes and
72849
+ * escapes included). JS understands the same escapes (`\n`, `\t`, `\\`, `\"`),
72850
+ * so the inner text passes through; the only rewrite needed is a REAL newline
72851
+ * or tab (a Nomen multi-line string), which would terminate a JS string
72852
+ * literal early.
72853
+ */
72854
+ function string_inner(raw) {
72855
+ let out = "";
72856
+ for (const c of raw) if (c === "\n") out += "\\n";
72857
+ else if (c === "\r") out += "\\r";
72858
+ else if (c === " ") out += "\\t";
72859
+ else out += c;
72860
+ return out;
72861
+ }
72862
+ /** Escape a bare `"` (a char literal like `'"'`) so the JS string is valid;
72863
+ * an already-escaped `\"` is left alone. */
72864
+ function escape_quotes(text) {
72865
+ let out = "";
72866
+ let backslashes = 0;
72867
+ for (const c of text) {
72868
+ if (c === "\"" && backslashes % 2 === 0) out += "\\\"";
72869
+ else out += c;
72870
+ backslashes = c === "\\" ? backslashes + 1 : 0;
72871
+ }
72872
+ return out;
72873
+ }
72874
+ const FLOAT_LITERAL = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
72875
+ /** Emit an expression for a literal or identifier value. */
72876
+ function build_value_node(node, status) {
72877
+ const value = node.value;
72878
+ if (node.is_enum_shorthand) {
72879
+ const hit = find_enum_case(status.enums, value);
72880
+ if (hit) return enum_case_value(hit.enum_node, hit.case_index, [], status);
72881
+ }
72882
+ const allocation = status.allocations?.get(value);
72883
+ if (allocation) {
72884
+ if (!status.inlining) status.inlining = /* @__PURE__ */ new Set();
72885
+ if (status.inlining.has(value)) return "undefined";
72886
+ status.inlining.add(value);
72887
+ const text = expr(allocation, status);
72888
+ status.inlining.delete(value);
72889
+ return `(${text})`;
72890
+ }
72891
+ if (value === "true" || value === "false") return value;
72892
+ if (value === "null") return "null";
72893
+ if (value === "self") return "self";
72894
+ if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) return `"${escape_quotes(string_inner(value.slice(1, -1)))}"`;
72895
+ if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) return `"${escape_quotes(string_inner(value.slice(1, -1)))}"`;
72896
+ if (is_int_literal(value)) {
72897
+ const n = parse_int_literal_bigint(value);
72898
+ return n === null ? "0n" : `${n}n`;
72899
+ }
72900
+ if (FLOAT_LITERAL.test(value)) return value.replace(/_/g, "");
72901
+ if (/^(?:T|TK|TV)_(?:TAKES_OWNERSHIP|IS_VALUE_STRUCT|NEEDS_STRDUP|FAT)$/.test(value)) return "false";
72902
+ if (/^(?:T|TK|TV)_SIZE$/.test(value)) return "0n";
72903
+ const resolved = node.resolved_function;
72904
+ if (resolved) return function_label(resolved);
72905
+ return js_name(value);
72906
+ }
72907
+ //#endregion
72908
+ //#region ../src/build_js/build_node.ts
72909
+ /** Append a statement line at the current indentation. */
72910
+ function emit(status, text) {
72911
+ status.code += " ".repeat(status.indent) + text + "\n";
72912
+ }
72913
+ /** A fresh generated temp name (`__match_0`, `__switch_1`, …). */
72914
+ function next_temp(status, prefix) {
72915
+ const name = `__${prefix}_${status.temp}`;
72916
+ status.temp += 1;
72917
+ return name;
72918
+ }
72919
+ /**
72920
+ * Emit an expression-position construct (if/switch/match used as a value) as
72921
+ * an IIFE. The builder emits statements into a scratch buffer at one extra
72922
+ * indent level; the wrapper becomes a single JS expression.
72923
+ */
72924
+ function capture_expression(status, build) {
72925
+ return `(() => {\n${capture_body(status, build)}${" ".repeat(status.indent)}})()`;
72926
+ }
72927
+ /** Emit statements into a scratch buffer at one extra indent level and return
72928
+ * the text (used by expression-position function literals and the IIFE
72929
+ * wrapper). */
72930
+ function capture_body(status, build) {
72931
+ const saved_code = status.code;
72932
+ const saved_indent = status.indent;
72933
+ status.code = "";
72934
+ status.indent = saved_indent + 1;
72935
+ build();
72936
+ const body = status.code;
72937
+ status.code = saved_code;
72938
+ status.indent = saved_indent;
72939
+ return body;
72940
+ }
72941
+ /**
72942
+ * Emit a branch's statements. In value mode the branch's LAST statement is
72943
+ * its result: a trailing `let` wrapper (the parser's expression statement)
72944
+ * becomes `return <expr>;`, an explicit `return` stays as-is, and any other
72945
+ * statement is returned as an expression.
72946
+ */
72947
+ function branch_body(statements, status, value_mode) {
72948
+ if (!value_mode) {
72949
+ for (const statement of statements) stmt(statement, status);
72950
+ return;
72951
+ }
72952
+ for (let i = 0; i < statements.length; i++) {
72953
+ const statement = statements[i];
72954
+ if (!(i === statements.length - 1)) {
72955
+ stmt(statement, status);
72956
+ continue;
72957
+ }
72958
+ if (statement.node_type === "let") {
72959
+ const value = statement.value;
72960
+ emit(status, `return ${value ? expr(value, status) : "undefined"};`);
72961
+ return;
72962
+ }
72963
+ if (statement.node_type === "return") {
72964
+ stmt(statement, status);
72965
+ return;
72966
+ }
72967
+ emit(status, `return ${expr(statement, status)};`);
72968
+ return;
72969
+ }
72970
+ emit(status, "return undefined;");
72971
+ }
72972
+ /** Emit the JS expression text for a Nomen expression node. */
72973
+ function expr(node, status) {
72974
+ switch (node.node_type) {
72975
+ case "value": return build_value_node(node, status);
72976
+ case "op": return build_operation_node(node, status);
72977
+ case "if": return build_if_expression(node, status);
72978
+ case "switch": return build_switch_expression(node, status);
72979
+ case "match": return build_match_expression(node, status);
72980
+ case "cast": return build_cast_node(node, status);
72981
+ case "access": return build_access_node(node, status);
72982
+ case "func_call": return build_function_call_node(node, status);
72983
+ case "array": return build_array_values_node(node, status);
72984
+ case "async_block": return build_async_block_expression(node, status);
72985
+ case "spawn": return expr(node.call, status);
72986
+ case "grouped": return build_grouped_node(node, status);
72987
+ case "func": return build_function_expression(node, status);
72988
+ default: return `__skip(${JSON.stringify(`expression ${node.node_type}`)})`;
72989
+ }
72990
+ }
72991
+ /** Emit a Nomen statement node (with its checker-hoisted allocation temps). */
72992
+ function stmt(node, status) {
72993
+ switch (node.node_type) {
72994
+ case "declare":
72995
+ build_declaration_node(node, status);
72996
+ return;
72997
+ case "assign":
72998
+ build_assignment_node(node, status);
72999
+ return;
73000
+ case "if":
73001
+ build_if_else_node(node, status);
73002
+ return;
73003
+ case "switch":
73004
+ build_switch_statement(node, status);
73005
+ return;
73006
+ case "match":
73007
+ build_match_statement(node, status);
73008
+ return;
73009
+ case "while":
73010
+ build_while_loop_node(node, status);
73011
+ return;
73012
+ case "for":
73013
+ build_for_loop_node(node, status);
73014
+ return;
73015
+ case "return":
73016
+ build_return_node(node, status);
73017
+ return;
73018
+ case "break":
73019
+ build_break_node(node, status);
73020
+ return;
73021
+ case "continue":
73022
+ build_continue_node(node, status);
73023
+ return;
73024
+ case "panic":
73025
+ build_panic_node(node, status);
73026
+ return;
73027
+ case "todo":
73028
+ build_todo_node(node, status);
73029
+ return;
73030
+ case "func":
73031
+ build_function_node(node, status);
73032
+ return;
73033
+ case "struct":
73034
+ build_struct_node(node, status);
73035
+ return;
73036
+ case "enum":
73037
+ case "bitset":
73038
+ case "trait":
73039
+ case "extend":
73040
+ case "import": return;
73041
+ case "block":
73042
+ emit_block(node, status);
73043
+ return;
73044
+ case "async_block":
73045
+ build_async_block_node(node, status);
73046
+ return;
73047
+ case "spawn":
73048
+ emit(status, `${expr(node.call, status)};`);
73049
+ return;
73050
+ case "let": {
73051
+ const value = node.value;
73052
+ if (value) stmt(value, status);
73053
+ return;
73054
+ }
73055
+ case "raw": return;
73056
+ default: emit(status, `${expr(node, status)};`);
73057
+ }
73058
+ }
73059
+ function emit_block(node, status) {
73060
+ emit(status, "{");
73061
+ status.indent += 1;
73062
+ for (const statement of node.statements ?? []) stmt(statement, status);
73063
+ status.indent -= 1;
73064
+ emit(status, "}");
73065
+ }
73066
+ //#endregion
73067
+ //#region ../src/build_js/build_root_node.ts
73068
+ /**
73069
+ * Emit the program: every non-library root statement in order, then the
73070
+ * entry-point call. The System library's own declarations are skipped — the
73071
+ * JS runtime provides the supported slice of its surface (Console, Format,
73072
+ * the string/array builtins), and anything else fails loudly at the call
73073
+ * site.
73074
+ */
73075
+ function build_root_node(root, status) {
73076
+ let main;
73077
+ for (const statement of root.statements) {
73078
+ if (is_library(statement)) continue;
73079
+ if (statement.node_type === "import") continue;
73080
+ if (statement.node_type === "func" && statement.name === "main") main = statement;
73081
+ stmt(statement, status);
73082
+ }
73083
+ emit(status, `main${main && main.params.length > 0 ? "({})" : "()"};`);
73084
+ }
73085
+ function is_library(node) {
73086
+ return node.is_library === true;
73087
+ }
73088
+ //#endregion
73089
+ //#region ../src/build_js/BuildStatus.ts
73090
+ function create_status(root) {
73091
+ return {
73092
+ root,
73093
+ code: "",
73094
+ indent: 0,
73095
+ structs: [],
73096
+ library_structs: [],
73097
+ enums: [],
73098
+ bitsets: [],
73099
+ traits: [],
73100
+ temp: 0,
73101
+ library_code: "",
73102
+ library_method_queue: [],
73103
+ library_init_queue: [],
73104
+ library_function_queue: [],
73105
+ emitted_library: /* @__PURE__ */ new Set(),
73106
+ errors: []
73107
+ };
73108
+ }
73109
+ //#endregion
73110
+ //#region ../src/build_js/runtime.ts
73111
+ function runtime(target = "node") {
73112
+ return `${target === "browser" ? `const __out = (s) => {
73113
+ const write = globalThis.__nomen_write;
73114
+ if (write) write(String(s));
73115
+ else console.log(String(s));
73116
+ };
73117
+ const __stderr = (s) => {
73118
+ const write = globalThis.__nomen_write;
73119
+ if (write) write(String(s));
73120
+ else console.error(String(s));
73121
+ };
73122
+ const __read_stdin = () => "";
73123
+ const __exit = (code) => {
73124
+ throw new Error("__nomen_exit:" + code);
73125
+ };` : `import { readFileSync } from "node:fs";
73126
+
73127
+ const __out = (s) => process.stdout.write(String(s));
73128
+ const __stderr = (s) => process.stderr.write(String(s));
73129
+ const __read_stdin = () => {
73130
+ try {
73131
+ return readFileSync(0, "utf8");
73132
+ } catch (e) {
73133
+ return "";
73134
+ }
73135
+ };
73136
+ const __exit = (code) => process.exit(code);`}
73137
+
73138
+ // printf "%f" (six decimals). toFixed matches for the magnitudes demos use;
73139
+ // note the >1e21 divergence (printf keeps expanding, toFixed goes exponent).
73140
+ function __fmt_f(x) {
73141
+ if (Number.isNaN(x)) return "nan";
73142
+ if (x === Infinity) return "inf";
73143
+ if (x === -Infinity) return "-inf";
73144
+ return x.toFixed(6);
73145
+ }
73146
+
73147
+ function __to_string(v) {
73148
+ if (typeof v === "string") return v;
73149
+ if (typeof v === "bigint") return v.toString();
73150
+ if (typeof v === "number") return __fmt_f(v);
73151
+ if (typeof v === "boolean") return v ? "true" : "false";
73152
+ if (v === null || v === undefined) return "null";
73153
+ // An enum renders as its tag (the native backends delegate enum
73154
+ // to_string to the int tag; a bitset is already a bigint).
73155
+ if (typeof v === "object" && typeof v.$tag === "number") return String(v.$tag);
73156
+ return String(v);
73157
+ }
73158
+
73159
+ function __render(kind, v) {
73160
+ if (kind === "i" || kind === "u") return BigInt(v).toString();
73161
+ if (kind === "f") return __fmt_f(Number(v));
73162
+ return __to_string(v);
73163
+ }
73164
+
73165
+ // The compiler's interpolation helper: the pattern carries one "%s" per
73166
+ // argument (literal "%" bytes are copied verbatim — no printf directives).
73167
+ function __interp(pattern, kinds, ...args) {
73168
+ const parts = pattern.split("%s");
73169
+ let out = parts[0];
73170
+ for (let i = 0; i < args.length; i++) {
73171
+ out += __render(kinds[i] || "s", args[i]) + (parts[i + 1] || "");
73172
+ }
73173
+ return out;
73174
+ }
73175
+
73176
+ function __string_at(text, i) {
73177
+ const index = Number(i);
73178
+ if (index < 0 || index >= text.length) __panic("index out of range");
73179
+ return text.at(index);
73180
+ }
73181
+
73182
+ function __string_at_or(text, i, fallback) {
73183
+ const index = Number(i);
73184
+ if (index < 0 || index >= text.length) return fallback;
73185
+ return text.at(index);
73186
+ }
73187
+
73188
+ function __char_code_at_or(text, i, fallback) {
73189
+ const index = Number(i);
73190
+ if (index < 0 || index >= text.length) return fallback;
73191
+ return BigInt(text.charCodeAt(index));
73192
+ }
73193
+
73194
+ function __math_power(base, exp) {
73195
+ let result = 1n;
73196
+ let n = exp;
73197
+ while (n > 0n) {
73198
+ result = result * base;
73199
+ n = n - 1n;
73200
+ }
73201
+ return result;
73202
+ }
73203
+
73204
+ function __sleep_ms(ms) {
73205
+ const wait = Number(ms);
73206
+ if (wait > 0) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, wait);
73207
+ }
73208
+
73209
+ function __string_hash(text) {
73210
+ let h = 5381n;
73211
+ for (const ch of text) h = BigInt.asUintN(64, h * 33n + BigInt(ch.charCodeAt(0)));
73212
+ return h;
73213
+ }
73214
+
73215
+ function __char_from_code(code) {
73216
+ return String.fromCharCode(Number(code) & 0xff);
73217
+ }
73218
+
73219
+ function __utf8_decode(text, index) {
73220
+ const i = Number(index);
73221
+ if (i < 0 || i >= text.length) __panic("byte index out of range");
73222
+ const cp = text.codePointAt(i);
73223
+ const width = cp > 0xffff ? 2 : 1;
73224
+ return { code_point: BigInt(cp), byte_length: BigInt(width) };
73225
+ }
73226
+
73227
+ function __utf8_encode(code_point) {
73228
+ let cp = Number(code_point);
73229
+ if (cp < 0 || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) cp = 0xfffd;
73230
+ return String.fromCodePoint(cp);
73231
+ }
73232
+
73233
+ function __parse_int(text) {
73234
+ if (typeof text !== "string" || !/^[+-]?[0-9]+$/.test(text)) __panic("invalid int: " + text);
73235
+ return BigInt(text);
73236
+ }
73237
+
73238
+ function __dispatch(self, name, ...args) {
73239
+ const table = self && self.$type;
73240
+ const fn = table && table[name];
73241
+ if (!fn) __unsupported("trait method " + name);
73242
+ return fn(self, ...args);
73243
+ }
73244
+
73245
+ function __set_get(set, value) {
73246
+ if (!set.has(value)) __panic("set value not found");
73247
+ return set.get(value);
73248
+ }
73249
+
73250
+ function __array_with(value, count) {
73251
+ const n = Number(count);
73252
+ const out = [];
73253
+ for (let i = 0; i < n; i++) out.push(value);
73254
+ return out;
73255
+ }
73256
+
73257
+ // --- Buffer (a JS array stands in for the native slab) --------------------
73258
+
73259
+ // 128/64 unsigned division (BigInt.div128): the native form saturates when
73260
+ // the high word is at least the divisor.
73261
+ function __div128(hi, lo, d) {
73262
+ const h = BigInt.asUintN(64, BigInt(hi));
73263
+ const l = BigInt.asUintN(64, BigInt(lo));
73264
+ const divisor = BigInt.asUintN(64, BigInt(d));
73265
+ if (h >= divisor) return 18446744073709551615n;
73266
+ return ((h << 64n) | l) / divisor;
73267
+ }
73268
+
73269
+ function __buffer_alloc(buf, size, zero) {
73270
+ const n = Number(size);
73271
+ buf.length = 0;
73272
+ if (n > 0) buf.length = n;
73273
+ buf.fill(zero);
73274
+ return BigInt(n);
73275
+ }
73276
+
73277
+ function __buffer_grow(buf, needed, zero) {
73278
+ const n = Number(needed);
73279
+ if (buf.length >= n) return BigInt(buf.length);
73280
+ const old = buf.length;
73281
+ buf.length = n;
73282
+ for (let i = old; i < n; i++) buf[i] = zero;
73283
+ return BigInt(n);
73284
+ }
73285
+
73286
+ function __buffer_move(buf, i) {
73287
+ const index = Number(i);
73288
+ const value = buf[index];
73289
+ buf[index] = undefined;
73290
+ return value;
73291
+ }
73292
+
73293
+ function __buffer_shift(buf, dst, src) {
73294
+ const d = Number(dst);
73295
+ const s = Number(src);
73296
+ if (d === s) return;
73297
+ buf[d] = buf[s];
73298
+ buf[s] = undefined;
73299
+ }
73300
+
73301
+ function __buffer_move_range(buf, dst, src, count) {
73302
+ const d = Number(dst);
73303
+ const s = Number(src);
73304
+ const n = Number(count);
73305
+ if (n <= 0) return;
73306
+ for (let i = 0; i < n; i++) buf[d + i] = buf[s + i];
73307
+ // Zero the source slots the destination does not cover.
73308
+ for (let i = d + n; i < s + n; i++) buf[i] = undefined;
73309
+ }
73310
+
73311
+ function __buffer_modify(buf, i, f) {
73312
+ const index = Number(i);
73313
+ const value = f(buf[index]);
73314
+ buf[index] = value;
73315
+ return value;
73316
+ }
73317
+
73318
+ function __buffer_zero(buf, len, zero) {
73319
+ const n = Number(len);
73320
+ for (let i = 0; i < n && i < buf.length; i++) buf[i] = zero;
73321
+ }
73322
+
73323
+ // --- Concurrency, run synchronously --------------------------------------
73324
+ // There is no scheduler here: a started task runs its thunk to completion
73325
+ // before start() returns, and the returned Task already holds the result.
73326
+
73327
+ // Trait dispatch reads the concrete value's $type table, so the synthesized
73328
+ // values carry the methods their traits declare.
73329
+ const __spawn_methods = {
73330
+ start: (self) => __spawn_start(self),
73331
+ detach: (self) => __spawn_detach(self),
73332
+ };
73333
+
73334
+ const __task_methods = {
73335
+ wait: () => {},
73336
+ result: (self) => __task_result(self),
73337
+ result_uint64: (self) => __task_result_uint64(self),
73338
+ is_done: () => true,
73339
+ cancel: () => {},
73340
+ current_cancelled: () => false,
73341
+ };
73342
+
73343
+ function __spawn_start(thread) {
73344
+ if (!thread.started) {
73345
+ thread.started = true;
73346
+ if (typeof thread.__task === "function") thread.__result = thread.__task();
73347
+ thread.__done = true;
73348
+ }
73349
+ return {
73350
+ __result: thread.__result,
73351
+ __zero: thread.__zero,
73352
+ __done: true,
73353
+ __consumed: false,
73354
+ $type: __task_methods,
73355
+ };
73356
+ }
73357
+
73358
+ function __spawn_detach(thread) {
73359
+ __spawn_start(thread);
73360
+ }
73361
+
73362
+ function __nursery_start(nursery, thread) {
73363
+ return __spawn_start(thread);
73364
+ }
73365
+
73366
+ function __task_wait(task) {}
73367
+
73368
+ function __task_result(task) {
73369
+ if (task.__consumed) return task.__zero;
73370
+ task.__consumed = true;
73371
+ return task.__result === undefined ? task.__zero : task.__result;
73372
+ }
73373
+
73374
+ function __task_result_uint64(task) {
73375
+ const value = __task_result(task);
73376
+ return value === undefined ? 0n : BigInt(value);
73377
+ }
73378
+
73379
+ function __channel_send(channel, value) {
73380
+ channel.values.push(value);
73381
+ }
73382
+
73383
+ function __channel_send_string(channel, value) {
73384
+ channel.strings.push(String(value));
73385
+ }
73386
+
73387
+ function __channel_receive(channel) {
73388
+ return channel.values.length ? channel.values.shift() : 0n;
73389
+ }
73390
+
73391
+ function __channel_receive_string(channel) {
73392
+ return channel.strings.length ? channel.strings.shift() : "";
73393
+ }
73394
+
73395
+ function __atomic_fetch_add(atomic, value) {
73396
+ const previous = atomic.word;
73397
+ atomic.word = BigInt.asUintN(64, BigInt(previous) + BigInt(value));
73398
+ return previous;
73399
+ }
73400
+
73401
+ function __atomic_swap(atomic, value) {
73402
+ const previous = atomic.word;
73403
+ atomic.word = value;
73404
+ return previous;
73405
+ }
73406
+
73407
+ function __list_at(list, i) {
73408
+ const index = Number(i);
73409
+ if (index < 0 || index >= list.length) __panic("index out of range");
73410
+ return list[index];
73411
+ }
73412
+
73413
+ function __list_at_or(list, i, fallback) {
73414
+ const index = Number(i);
73415
+ if (index < 0 || index >= list.length) return fallback;
73416
+ return list[index];
73417
+ }
73418
+
73419
+ function __map_get(map, key) {
73420
+ if (!map.has(key)) __panic("map key not found");
73421
+ return map.get(key);
73422
+ }
73423
+
73424
+ function __sb_take_since(sb, mark) {
73425
+ const at = Number(mark);
73426
+ const taken = sb.s.slice(at);
73427
+ sb.s = sb.s.slice(0, at);
73428
+ return taken;
73429
+ }
73430
+
73431
+ // Nomen value structs are copied on binding, assignment, return, container
73432
+ // store, and closure capture. JS objects are by reference, so the builders
73433
+ // wrap those transfers in this deep clone.
73434
+ function __copy(value) {
73435
+ if (Array.isArray(value)) return value.map(__copy);
73436
+ if (value instanceof Map) return new Map([...value].map(([k, v]) => [__copy(k), __copy(v)]));
73437
+ if (value instanceof Set) return new Set([...value].map(__copy));
73438
+ if (value && typeof value === "object") {
73439
+ const out = {};
73440
+ for (const key of Object.keys(value)) out[key] = __copy(value[key]);
73441
+ return out;
73442
+ }
73443
+ return value;
73444
+ }
73445
+
73446
+ function __enum_to_case(value, tag, name) {
73447
+ if (!value || value.$tag !== tag) {
73448
+ __panic("base to case conversion: the value carries a different variant (" + name + ")");
73449
+ }
73450
+ return value;
73451
+ }
73452
+
73453
+ function __enum_eq(a, b) {
73454
+ if (a === b) return true;
73455
+ if (!a || !b) return false;
73456
+ if (a.$tag !== b.$tag) return false;
73457
+ const ka = Object.keys(a).filter((k) => k !== "$tag");
73458
+ const kb = Object.keys(b).filter((k) => k !== "$tag");
73459
+ if (ka.length !== kb.length) return false;
73460
+ for (const k of ka) {
73461
+ if (a[k] !== b[k]) return false;
73462
+ }
73463
+ return true;
73464
+ }
73465
+
73466
+ function __panic(message) {
73467
+ __stderr("panic: " + __to_string(message) + "\\n");
73468
+ __exit(2);
73469
+ }
73470
+
73471
+ function __unsupported(what) {
73472
+ throw new Error("js backend: unsupported feature: " + what);
73473
+ }
73474
+
73475
+ // A library feature the JS backend cannot express (raw memory, GUI, threads).
73476
+ // Cross-compiled programs keep running without it: warn once per feature and
73477
+ // let the caller see the statement's zero value.
73478
+ const __skipped = new Set();
73479
+ function __skip(what) {
73480
+ if (!__skipped.has(what)) {
73481
+ __skipped.add(what);
73482
+ __stderr("js backend: skipping unavailable feature: " + what + "\\n");
73483
+ }
73484
+ return undefined;
73485
+ }
73486
+
73487
+ // --- Console --------------------------------------------------------------
73488
+
73489
+ function Console_write(s) { __out(s); }
73490
+ function Console_write_line(s) { __out(s + "\\n"); }
73491
+
73492
+ let __stdin_lines = null;
73493
+ let __stdin_index = 0;
73494
+ function __ensure_stdin() {
73495
+ if (__stdin_lines !== null) return;
73496
+ try {
73497
+ __stdin_lines = __read_stdin().split("\\n");
73498
+ } catch (e) {
73499
+ __stdin_lines = [];
73500
+ }
73501
+ }
73502
+ function Console_read_line() {
73503
+ __ensure_stdin();
73504
+ const line = __stdin_lines[__stdin_index] || "";
73505
+ __stdin_index++;
73506
+ return line;
73507
+ }
73508
+ function Console_read_char() {
73509
+ __ensure_stdin();
73510
+ const line = __stdin_lines[__stdin_index] || "";
73511
+ if (line.length === 0) return "";
73512
+ __stdin_lines[__stdin_index] = line.slice(1);
73513
+ return line[0];
73514
+ }
73515
+ function Console_platform() { return "js"; }
73516
+
73517
+ // --- Format (the helpers interpolation specs lower to) --------------------
73518
+
73519
+ function __pad_to(s, width, zero, signFirst) {
73520
+ let w = width;
73521
+ let left = false;
73522
+ if (w < 0n) { left = true; w = -w; }
73523
+ const total = BigInt(s.length);
73524
+ if (w <= total) return s;
73525
+ const count = Number(w - total);
73526
+ if (left) return s + " ".repeat(count);
73527
+ if (zero && signFirst) return s[0] + "0".repeat(count) + s.slice(1);
73528
+ return (zero ? "0" : " ").repeat(count) + s;
73529
+ }
73530
+
73531
+ function Format_pad(text, width) {
73532
+ return __pad_to(text, width, false, false);
73533
+ }
73534
+
73535
+ function __int_digits(mag, base, upper) {
73536
+ const digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
73537
+ let s = "";
73538
+ if (mag === 0n) return "0";
73539
+ while (mag > 0n) {
73540
+ s = digits[Number(mag % base)] + s;
73541
+ mag /= base;
73542
+ }
73543
+ return s;
73544
+ }
73545
+
73546
+ function __int_base(signed, value, base, upper, width, zero, group) {
73547
+ let neg = signed && value < 0n;
73548
+ let mag = neg ? -value : value;
73549
+ let s = __int_digits(mag, base, upper);
73550
+ if (group && base === 10n) s = s.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ",");
73551
+ if (neg) s = "-" + s;
73552
+ return __pad_to(s, width, zero, neg);
73553
+ }
73554
+
73555
+ function Format_int_base(value, base, upper, width, zero, group) {
73556
+ return __int_base(true, value, base, upper, width, zero, group);
73557
+ }
73558
+
73559
+ function Format_uint_base(value, base, upper, width, zero, group) {
73560
+ return __int_base(false, value, base, upper, width, zero, group);
73561
+ }
73562
+
73563
+ // A printf float directive built by the checker: %[0][-][width][.places][fFeE]
73564
+ function Format_float_fixed(value, spec) {
73565
+ const m = /^%(0)?(-)?(\\d+)?(?:\\.(\\d+))?([fFeE])$/.exec(spec);
73566
+ if (!m) __unsupported("float directive " + spec);
73567
+ const zero = !!m[1];
73568
+ const left = !!m[2];
73569
+ const width = m[3] ? BigInt(m[3]) : 0n;
73570
+ const places = m[4] !== undefined ? Number(m[4]) : 6;
73571
+ const letter = m[5];
73572
+ let body;
73573
+ if (letter === "f") {
73574
+ body = value.toFixed(places);
73575
+ } else {
73576
+ body = value.toExponential(places);
73577
+ // printf pads the exponent to at least two digits (1.50e+00).
73578
+ body = body.replace(/e([+-])(\\d)$/, "e$10$2");
73579
+ if (letter === "E") body = body.replace("e", "E");
73580
+ }
73581
+ if (width === 0n) return body;
73582
+ return __pad_to(body, left ? -width : width, zero, false);
73583
+ }
73584
+
73585
+ function Format_float_grouped(value, places) {
73586
+ const text = value.toFixed(Number(places));
73587
+ const dot = text.indexOf(".");
73588
+ const head = dot === -1 ? text : text.slice(0, dot);
73589
+ const tail = dot === -1 ? "" : text.slice(dot);
73590
+ const grouped = head.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ",");
73591
+ return grouped + tail;
73592
+ }
73593
+
73594
+ // --- Custom patterns (#,##0.00, 0.0%, #,##0.00;(#,##0.00)) ----------
73595
+
73596
+ function __pat_sections(pattern) {
73597
+ let quoted = false;
73598
+ for (let i = 0; i < pattern.length; i++) {
73599
+ const c = pattern[i];
73600
+ if (quoted) {
73601
+ if (c === "'") quoted = false;
73602
+ } else if (c === "'") {
73603
+ quoted = true;
73604
+ } else if (c === ";") {
73605
+ return [pattern.slice(0, i), pattern.slice(i + 1)];
73606
+ }
73607
+ }
73608
+ return [pattern, null];
73609
+ }
73610
+
73611
+ function __pat_has_percent(pattern) {
73612
+ let quoted = false;
73613
+ for (let i = 0; i < pattern.length; i++) {
73614
+ const c = pattern[i];
73615
+ if (quoted) {
73616
+ if (c === "'") quoted = false;
73617
+ } else if (c === "'") {
73618
+ quoted = true;
73619
+ } else if (c === "%") {
73620
+ return true;
73621
+ }
73622
+ }
73623
+ return false;
73624
+ }
73625
+
73626
+ function __pat_max_frac(pattern) {
73627
+ let quoted = false;
73628
+ let inFrac = false;
73629
+ let count = 0;
73630
+ let best = 0;
73631
+ for (let i = 0; i < pattern.length; i++) {
73632
+ const c = pattern[i];
73633
+ if (quoted) {
73634
+ if (c === "'") quoted = false;
73635
+ continue;
73636
+ }
73637
+ if (c === "'") { quoted = true; continue; }
73638
+ if (c === ";") { inFrac = false; count = 0; continue; }
73639
+ if (c === ".") { inFrac = true; continue; }
73640
+ if (c === "0" || c === "#") {
73641
+ if (inFrac) {
73642
+ count += 1;
73643
+ if (count > best) best = count;
73644
+ }
73645
+ continue;
73646
+ }
73647
+ inFrac = false;
73648
+ count = 0;
73649
+ }
73650
+ return best;
73651
+ }
73652
+
73653
+ function __pat_shape_int(intDigits, padSlots, hasComma, groupSize) {
73654
+ let digits = intDigits;
73655
+ if (digits === "0" && padSlots === 0) return "";
73656
+ if (digits.length < padSlots) digits = "0".repeat(padSlots - digits.length) + digits;
73657
+ if (hasComma && groupSize > 0 && digits.length > groupSize) {
73658
+ digits = digits.replace(new RegExp("\\\\B(?=(\\\\d{" + groupSize + "})+(?!\\\\d))", "g"), ",");
73659
+ }
73660
+ return digits;
73661
+ }
73662
+
73663
+ function __pat_shape_frac(fracRaw, fracSlots, fracMin) {
73664
+ let kept = fracRaw.length > fracSlots ? fracRaw.slice(0, fracSlots) : fracRaw;
73665
+ let n = kept.length;
73666
+ while (n > fracMin) {
73667
+ if (kept[n - 1] === "0") n -= 1;
73668
+ else { n = fracMin; break; }
73669
+ }
73670
+ kept = kept.slice(0, n);
73671
+ if (kept.length < fracMin) kept += "0".repeat(fracMin - kept.length);
73672
+ return kept;
73673
+ }
73674
+
73675
+ function __pat_render(section, intDigits, fracRaw, negative) {
73676
+ let quoted = false;
73677
+ let firstSlot = -1;
73678
+ let lastSlot = -1;
73679
+ let dot = -1;
73680
+ let padSlots = 0;
73681
+ let fracSlots = 0;
73682
+ let fracMin = 0;
73683
+ let hasComma = false;
73684
+ let slotsAfterComma = 0;
73685
+
73686
+ for (let i = 0; i < section.length; i++) {
73687
+ const c = section[i];
73688
+ if (quoted) { if (c === "'") quoted = false; continue; }
73689
+ if (c === "'") { quoted = true; continue; }
73690
+ if (c === "." && dot === -1) dot = i;
73691
+ if (c === "0" || c === "#") {
73692
+ lastSlot = i;
73693
+ if (dot === -1) {
73694
+ if (firstSlot === -1) firstSlot = i;
73695
+ slotsAfterComma += 1;
73696
+ if (c === "0") padSlots += 1;
73697
+ } else {
73698
+ fracSlots += 1;
73699
+ if (c === "0") fracMin = fracSlots;
73700
+ }
73701
+ }
73702
+ if (c === "," && dot === -1) { hasComma = true; slotsAfterComma = 0; }
73703
+ }
73704
+
73705
+ let out = negative ? "-" : "";
73706
+ quoted = false;
73707
+ for (let i = 0; i < firstSlot; i++) {
73708
+ const c = section[i];
73709
+ if (quoted) {
73710
+ if (c === "'") quoted = false;
73711
+ else out += c;
73712
+ } else if (c === "'") {
73713
+ quoted = true;
73714
+ } else {
73715
+ out += c;
73716
+ }
73717
+ }
73718
+
73719
+ out += __pat_shape_int(intDigits, padSlots, hasComma, slotsAfterComma);
73720
+ const kept = __pat_shape_frac(fracRaw, fracSlots, fracMin);
73721
+ if (kept.length > 0) out += "." + kept;
73722
+
73723
+ quoted = false;
73724
+ for (let i = lastSlot + 1; i < section.length; i++) {
73725
+ const c = section[i];
73726
+ if (quoted) {
73727
+ if (c === "'") quoted = false;
73728
+ else out += c;
73729
+ } else if (c === "'") {
73730
+ quoted = true;
73731
+ } else if (i !== dot) {
73732
+ out += c;
73733
+ }
73734
+ }
73735
+ return out;
73736
+ }
73737
+
73738
+ function __pat_apply(pattern, negative, intDigits, fracRaw) {
73739
+ const [positive, negativeSection] = __pat_sections(pattern);
73740
+ if (negative && negativeSection !== null) {
73741
+ return __pat_render(negativeSection, intDigits, fracRaw, false);
73742
+ }
73743
+ return __pat_render(positive, intDigits, fracRaw, negative);
73744
+ }
73745
+
73746
+ function Format_int_pattern(value, pattern) {
73747
+ let negative = value < 0n;
73748
+ let mag = negative ? -value : value;
73749
+ if (__pat_has_percent(pattern)) mag = mag * 100n;
73750
+ return __pat_apply(pattern, negative, mag.toString(), "");
73751
+ }
73752
+
73753
+ function Format_uint_pattern(value, pattern) {
73754
+ let mag = value;
73755
+ if (__pat_has_percent(pattern)) mag = mag * 100n;
73756
+ return __pat_apply(pattern, false, mag.toString(), "");
73757
+ }
73758
+
73759
+ function Format_float_pattern(value, pattern) {
73760
+ let negative = value < 0;
73761
+ let mag = negative ? -value : value;
73762
+ if (__pat_has_percent(pattern)) mag = mag * 100;
73763
+ const maxFrac = __pat_max_frac(pattern);
73764
+ const rendered = mag.toFixed(maxFrac);
73765
+ const dot = rendered.indexOf(".");
73766
+ const intDigits = dot === -1 ? rendered : rendered.slice(0, dot);
73767
+ const frac = dot === -1 ? "" : rendered.slice(dot + 1);
73768
+ return __pat_apply(pattern, negative, intDigits, frac);
73769
+ }
73770
+
73771
+ `;
73772
+ }
73773
+ //#endregion
73774
+ //#region ../src/build_js/build_js.ts
73775
+ function build_js(root, options) {
73776
+ const status = create_status(root);
73777
+ collect_types(root, status);
73778
+ index_allocations(root, status);
73779
+ build_root_node(root, status);
73780
+ emit_library_queue(status);
73781
+ return {
73782
+ headers: "",
73783
+ code: `${runtime(options?.target ?? "node")}${status.library_code}${status.code}`,
73784
+ errors: status.errors.length ? status.errors : void 0
73785
+ };
73786
+ }
73787
+ /** Index every checker-hoisted temp by name so `build_value_node` can inline
73788
+ * its initializer at the reference site. */
73789
+ function index_allocations(root, status) {
73790
+ status.allocations = /* @__PURE__ */ new Map();
73791
+ const visit = (node) => {
73792
+ for (const allocation of node.allocations ?? []) {
73793
+ const name = allocation.name;
73794
+ const value = allocation.value;
73795
+ if (name && value) status.allocations?.set(name, value);
73796
+ }
73797
+ for (const child of child_nodes(node)) visit(child);
73798
+ };
73799
+ visit(root);
73800
+ }
73801
+ /** Pre-collect every user struct/enum in the tree (declarations may be
73802
+ * nested inside function bodies and are emitted where they appear, but the
73803
+ * expression builders need the full type table up front). Library types are
73804
+ * skipped — the runtime provides the supported slice of their surface. */
73805
+ function collect_types(root, status) {
73806
+ const visit = (node) => {
73807
+ const is_library = node.is_library === true;
73808
+ if (node.node_type === "struct") {
73809
+ if (is_library) status.library_structs.push(node);
73810
+ else status.structs.push(node);
73811
+ }
73812
+ if (!is_library && node.node_type === "enum") status.enums.push(node);
73813
+ if (node.node_type === "bitset") status.bitsets.push(node);
73814
+ if (node.node_type === "trait") status.traits.push(node);
73815
+ for (const child of child_nodes(node)) visit(child);
73816
+ };
73817
+ visit(root);
73818
+ }
73819
+ //#endregion
71271
73820
  //#region ../src/build.ts
71272
73821
  function build(root, options = {}) {
73822
+ if (options.arch === "js") return build_js(root, { target: options.js_target });
71273
73823
  let status = {
71274
73824
  root,
71275
73825
  structs: [],
@@ -71852,8 +74402,6 @@ const NO_SPACE_AFTER = /* @__PURE__ */ new Set([
71852
74402
  "#",
71853
74403
  "...",
71854
74404
  "!",
71855
- "???",
71856
- "!!!",
71857
74405
  "::"
71858
74406
  ]);
71859
74407
  const KEEP_SPACING = /* @__PURE__ */ new Set([
@@ -71971,11 +74519,7 @@ const COMPOUND_SYMBOLS = [
71971
74519
  "->",
71972
74520
  "=>"
71973
74521
  ];
71974
- const LONG_COMPOUND_SYMBOLS = [
71975
- "...",
71976
- "???",
71977
- "!!!"
71978
- ];
74522
+ const LONG_COMPOUND_SYMBOLS = ["..."];
71979
74523
  function tokenize(input, preserve_source = false) {
71980
74524
  let status = {
71981
74525
  i: 0,
@@ -73095,217 +75639,15 @@ function skip_balanced_block(source, brace) {
73095
75639
  return n;
73096
75640
  }
73097
75641
  //#endregion
73098
- //#region ../src/lib.ts
73099
- function read_library_config(lib_dir) {
73100
- const config_path = path.join(lib_dir, "package.jsonc");
73101
- const json = fs.readFileSync(config_path, "utf8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
73102
- return JSON.parse(json);
73103
- }
73104
- function resolve_export_files(lib_dir, pattern) {
73105
- const base = pattern.replace(/\/?\*$/, "").replace(/^\.\//, "");
73106
- const src_dir = path.resolve(lib_dir, base);
73107
- if (!fs.existsSync(src_dir)) return [];
73108
- return fs.readdirSync(src_dir).filter((f) => f.endsWith(".nm")).sort().map((f) => path.join(src_dir, f));
73109
- }
73110
- const FILE_ORDER = [
73111
- "Disposable",
73112
- "Stringable",
73113
- "Equatable",
73114
- "Hashable",
73115
- "Sendable",
73116
- "Task",
73117
- "Mutex",
73118
- "Channel",
73119
- "int",
73120
- "uint",
73121
- "int8",
73122
- "uint8",
73123
- "int16",
73124
- "uint16",
73125
- "int32",
73126
- "int64",
73127
- "uint64",
73128
- "float",
73129
- "char",
73130
- "String",
73131
- "Array",
73132
- "Console",
73133
- "Math",
73134
- "bool"
73135
- ];
73136
- function get_file_priority(file_path) {
73137
- const name = path.basename(file_path, ".nm");
73138
- const idx = FILE_ORDER.indexOf(name);
73139
- if (idx >= 0) return idx;
73140
- return FILE_ORDER.length;
73141
- }
73142
- function extract_deps(source) {
73143
- const deps = [];
73144
- for (const line of source.split("\n")) {
73145
- const trimmed = line.trim();
73146
- if (trimmed.startsWith("import ")) deps.push(trimmed.slice(7).trim());
73147
- else if (trimmed.startsWith("pub struct ") || trimmed.startsWith("internal struct ") || trimmed.startsWith("struct ")) {
73148
- const colon_idx = trimmed.indexOf(":");
73149
- if (colon_idx >= 0) {
73150
- const trait_name = trimmed.slice(colon_idx + 1).trim().split(/\s|<|{/)[0];
73151
- if (trait_name) deps.push(trait_name);
73152
- }
73153
- break;
73154
- } else if (trimmed.startsWith("pub class ") || trimmed.startsWith("internal class ") || trimmed.startsWith("class ")) {
73155
- const colon_idx = trimmed.indexOf(":");
73156
- if (colon_idx >= 0) {
73157
- const trait_name = trimmed.slice(colon_idx + 1).trim().split(/\s|<|{/)[0];
73158
- if (trait_name) deps.push(trait_name);
73159
- }
73160
- break;
73161
- } else if (trimmed.startsWith("pub trait ") || trimmed.startsWith("internal trait ") || trimmed.startsWith("trait ")) break;
73162
- else if (trimmed.length > 0 && !is_comment_line(trimmed)) break;
73163
- }
73164
- return deps;
73165
- }
73166
- function is_comment_line(trimmed) {
73167
- return trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*");
73168
- }
73169
- function extract_type_names(source) {
73170
- const names = [];
73171
- for (let line of source.split("\n")) {
73172
- const m = line.trim().match(/^(?:pub|internal) (?:checked )?(?:mv )?(?:struct|class|trait|enum|bitset) (\w+)/);
73173
- if (m) names.push(m[1]);
73174
- }
73175
- return names;
73176
- }
73177
- function extract_free_func_names(source) {
73178
- const names = [];
73179
- for (let line of source.split("\n")) {
73180
- const m = line.match(/^(?:(?:pub|internal)\s+|foreign\s+|pub\s+foreign\s+)func\s+(\w+)/);
73181
- if (m) names.push(m[1]);
73182
- }
73183
- return names;
73184
- }
73185
- function build_type_map(files, lib_dir) {
73186
- const all_type_names = /* @__PURE__ */ new Set();
73187
- const file_sources = /* @__PURE__ */ new Map();
73188
- const func_to_type = /* @__PURE__ */ new Map();
73189
- for (const f of files) {
73190
- const source = fs.readFileSync(f, "utf8");
73191
- file_sources.set(f, source);
73192
- const type_names = extract_type_names(source);
73193
- for (const name of type_names) all_type_names.add(name);
73194
- const first_type = type_names[0];
73195
- if (first_type) {
73196
- for (const fname of extract_free_func_names(source)) if (!func_to_type.has(fname)) func_to_type.set(fname, first_type);
73197
- }
73198
- }
73199
- const types = /* @__PURE__ */ new Map();
73200
- const functions = /* @__PURE__ */ new Map();
73201
- for (const f of files) {
73202
- const source = file_sources.get(f);
73203
- const deps = extract_deps(source);
73204
- for (const token of tokenize(source)) {
73205
- if (deps.includes(token.value)) continue;
73206
- if (all_type_names.has(token.value)) {
73207
- deps.push(token.value);
73208
- continue;
73209
- }
73210
- const dep_type = func_to_type.get(token.value);
73211
- if (dep_type && !deps.includes(dep_type)) deps.push(dep_type);
73212
- }
73213
- const parts = path.relative(path.resolve(lib_dir, "System"), f).split(path.sep);
73214
- const namespace = parts.length > 1 ? parts[0] : void 0;
73215
- const type_names = extract_type_names(source);
73216
- for (const name of type_names) types.set(name, {
73217
- name,
73218
- source,
73219
- path: f,
73220
- deps,
73221
- namespace
73222
- });
73223
- const free_funcs = extract_free_func_names(source);
73224
- if (type_names.length > 0) {
73225
- const shared = types.get(type_names[0]);
73226
- for (const fname of free_funcs) if (!functions.has(fname)) functions.set(fname, shared);
73227
- } else for (const fname of free_funcs) if (!functions.has(fname)) functions.set(fname, {
73228
- name: fname,
73229
- source,
73230
- path: f,
73231
- deps,
73232
- namespace
73233
- });
73234
- }
73235
- for (const [, entry] of types) {
73236
- if (entry.deps.includes("Buffer") && !entry.deps.includes("ClassBuffer")) entry.deps.push("ClassBuffer");
73237
- if (entry.deps.includes("ClassBuffer") && !entry.deps.includes("Buffer")) entry.deps.push("Buffer");
73238
- }
73239
- return {
73240
- types,
73241
- functions
73242
- };
73243
- }
73244
- function build_library(lib_dir) {
73245
- const config = read_library_config(lib_dir);
73246
- let all_files = [];
73247
- for (const pattern of Object.values(config.exports)) all_files = all_files.concat(resolve_export_files(lib_dir, pattern));
73248
- all_files.sort((a, b) => get_file_priority(a) - get_file_priority(b));
73249
- const source = all_files.map((f) => fs.readFileSync(f, "utf8")).join("\n");
73250
- const { types, functions } = build_type_map(all_files, lib_dir);
73251
- const namespaces = /* @__PURE__ */ new Map();
73252
- for (const [name, type] of types) if (type.namespace) {
73253
- let ns = namespaces.get(type.namespace);
73254
- if (!ns) {
73255
- ns = /* @__PURE__ */ new Set();
73256
- namespaces.set(type.namespace, ns);
73257
- }
73258
- ns.add(name);
73259
- }
73260
- return {
73261
- name: config.name,
73262
- source,
73263
- types,
73264
- functions,
73265
- namespaces,
73266
- dir: path.resolve(lib_dir)
73267
- };
73268
- }
73269
- const library_cache = /* @__PURE__ */ new Map();
73270
- function get_library(lib_dir) {
73271
- const cached = library_cache.get(lib_dir);
73272
- if (cached) return cached;
73273
- const lib = build_library(lib_dir);
73274
- library_cache.set(lib_dir, lib);
73275
- return lib;
73276
- }
73277
- /**
73278
- * Every valid namespace-path prefix implied by the library's file layout
73279
- * (`Controls`, `Controls/Geometry`, `Stream/File`, ... and their
73280
- * `System/`-rooted forms). A type's path (`core/System/Controls/Geometry.nm`)
73281
- * contributes the chain of directories leading to it plus the module file's
73282
- * own base name, so both `Controls::Button` (namespace) and
73283
- * `Controls::Geometry::Size` (module) validate.
73284
- */
73285
- function library_path_prefixes(library) {
73286
- const prefixes = /* @__PURE__ */ new Set();
73287
- const system_root = path.resolve(library.dir, "System");
73288
- for (const entry of library.types.values()) {
73289
- const rel = path.relative(system_root, entry.path);
73290
- if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) continue;
73291
- const parts = rel.replace(/\.nm$/, "").split(path.sep);
73292
- for (let i = 1; i <= parts.length; i++) {
73293
- const joined = parts.slice(0, i).join("/");
73294
- prefixes.add(joined);
73295
- prefixes.add(`System/${joined}`);
73296
- }
73297
- }
73298
- return prefixes;
73299
- }
73300
- //#endregion
73301
75642
  //#region ../src/check/warnings.ts
73302
75643
  /**
73303
- * Lint pass run after a successful check. It walks the AST to surface three
75644
+ * Lint pass run after a successful check. It walks the AST to surface four
73304
75645
  * kinds of warning:
73305
75646
  *
73306
75647
  * - an unused value/parameter (declared but never read)
73307
75648
  * - a `var` that is never reassigned (it could be a `const`)
73308
75649
  * - a function or method that is never called
75650
+ * - a type annotation that repeats what the initializer already infers
73309
75651
  *
73310
75652
  * The analysis is deliberately conservative: it biases towards not warning
73311
75653
  * when ownership (mv/ref/cp), trait vtables, library code or the `_`-discard
@@ -73362,6 +75704,9 @@ function emit_warnings(root, status) {
73362
75704
  else add_warning(status, `Ref parameter '${param.name}' is never written through — take it by value (or drop 'ref')`, param.name_start ?? param.start);
73363
75705
  }
73364
75706
  }
75707
+ for_each_node(root, (node) => {
75708
+ if (node.node_type === "declare") warn_redundant_annotation(node, status);
75709
+ });
73365
75710
  status.warnings = dedupe_warnings(status.warnings);
73366
75711
  }
73367
75712
  function dedupe_warnings(warnings) {
@@ -73437,6 +75782,61 @@ function warn_var_not_changed(decl, locals, status) {
73437
75782
  replace_text: "const"
73438
75783
  } : void 0);
73439
75784
  }
75785
+ /**
75786
+ * A declaration whose annotation repeats what its initializer already infers
75787
+ * (NOMEN_AGENTS.md, "Prefer implicit types"): `const Point p = Point(2, 3)`
75788
+ * or `var int x = 5`. The recognised forms mirror `nomen format`'s
75789
+ * conservative strip — a constructor call with every generic argument written
75790
+ * at the call, and a single self-describing literal — so everything the
75791
+ * warning flags is also what formatting removes. Forms whose annotation is
75792
+ * load-bearing stay silent: generic constructions the annotation instantiates
75793
+ * (`var List<int> v = List()`), enum shorthand (`.east`), wider/narrower
75794
+ * literals (`var int16 small = 300`), nullables, view/ref bindings, and
75795
+ * constrained declarations. Library code is exempt.
75796
+ */
75797
+ function warn_redundant_annotation(decl, status) {
75798
+ if (decl.is_library || decl.is_loop_iterator || decl.is_readonly) return;
75799
+ if (status.library_boundary !== void 0 && decl.start >= status.library_boundary) return;
75800
+ if (!decl.type.name || decl.type_start === void 0 || decl.name_start === void 0) return;
75801
+ if (decl.type.is_view || decl.type.is_ref || decl.type.is_nullable || decl.type.is_array) return;
75802
+ if (decl.declaration === "view" || decl.declaration === "ref") return;
75803
+ if (decl.constraint) return;
75804
+ const value = decl.value;
75805
+ if (!value) return;
75806
+ if (value.node_type === "func_call") {
75807
+ const call = value;
75808
+ if (call.is_thread_ctor || call.is_fiber_ctor || call.is_awaitable_ctor || call.is_weak_ctor || call.is_func_value_ctor || call.is_enum_shorthand || call.field_overrides?.length) return;
75809
+ if (call.resolved_function?.name !== "#init") return;
75810
+ const struct = status.structs.find((s) => s.name === call.name);
75811
+ if (!struct) return;
75812
+ if (call.type_args_from_expected) return;
75813
+ if (struct.type_params.length && (call.type_args?.length ?? 0) !== struct.type_params.length) return;
75814
+ } else if (value.node_type === "value") {
75815
+ const text = value.value;
75816
+ if (literal_type_name(text) === void 0) return;
75817
+ } else return;
75818
+ if (type_display(decl.type) !== type_display(type_from_value_node(value, status))) return;
75819
+ add_warning(status, `Type annotation on '${decl.name}' is redundant — '${type_display(decl.type)}' is already inferred; run 'nomen format' to drop it`, decl.type_start, {
75820
+ title: "Drop redundant type",
75821
+ param_start: decl.start,
75822
+ replace_start: decl.type_start,
75823
+ replace_end: decl.name_start,
75824
+ replace_text: ""
75825
+ });
75826
+ }
75827
+ /** The type a literal value spells out on its own; undefined when it doesn't. */
75828
+ function literal_type_name(text) {
75829
+ if (text === "true" || text === "false") return "bool";
75830
+ if (text.length >= 2 && text.startsWith("\"") && text.endsWith("\"")) return "string";
75831
+ if (text.length >= 3 && text.startsWith("'") && text.endsWith("'")) return "char";
75832
+ if (/^-?\d+$/.test(text)) return "int";
75833
+ if (/^-?(\d+\.\d*|\.\d+)([eE][+-]?\d+)?$/.test(text)) return "float";
75834
+ }
75835
+ /** The user-visible spelling of a type (`List<int>`, `Map<string, int>`). */
75836
+ function type_display(t) {
75837
+ if (!t.type_args?.length) return t.name ?? "?";
75838
+ return `${t.name}<${t.type_args.map(type_display).join(", ")}>`;
75839
+ }
73440
75840
  /** Names that count as a "use": call targets, method names, and value reads. */
73441
75841
  function collect_referenced_names(root) {
73442
75842
  const names = /* @__PURE__ */ new Set();
@@ -74927,26 +77327,31 @@ function parse_embed_file(status) {
74927
77327
  const full = path.isAbsolute(rel_path) ? rel_path : path.join(base, rel_path);
74928
77328
  let content;
74929
77329
  try {
74930
- content = fs.readFileSync(full, "utf8");
74931
- } catch {
77330
+ content = fs.readFileSync(full);
77331
+ } catch (cause) {
77332
+ const idx = status.tokens.findIndex((t) => t.value === "#embed_file" && t.i === start);
77333
+ const near = status.tokens.slice(Math.max(0, idx - 2), idx + 4).map((t) => t.value);
77334
+ console.log(`[embed-debug] tokens near: ${JSON.stringify(near)} embed_root=${JSON.stringify(status.embed_root)}`);
74932
77335
  add_error(status, `#embed_file could not read '${rel_path}' (resolved '${full}')`, start);
74933
77336
  return new ValueNode(start, "\"\"");
74934
77337
  }
74935
- return new ValueNode(start, `"${escape_embedded(content)}"`);
77338
+ return new ValueNode(start, `"${escape_embedded_bytes(content)}"`);
74936
77339
  }
74937
- /** Encode file contents as a source-level string literal body. */
74938
- function escape_embedded(content) {
77340
+ /** Encode file contents as a source-level string literal body —
77341
+ * byte-transparent: printable ASCII passes through, everything else
77342
+ * (control bytes, quotes, backslashes, and every byte ≥ 0x7F) becomes a
77343
+ * three-digit octal escape, which the escape scanner decodes back to the
77344
+ * same byte. Self-terminating in C string literals too (the decoder
77345
+ * consumes exactly three octal digits). */
77346
+ function escape_embedded_bytes(content) {
74939
77347
  let out = "";
74940
- for (const ch of content) {
74941
- const code = ch.charCodeAt(0);
74942
- if (ch === "\\") out += "\\\\";
74943
- else if (ch === "\"") out += "\\\"";
74944
- else if (ch === "\n") out += "\\n";
74945
- else if (ch === "\r") out += "\\r";
74946
- else if (ch === " ") out += "\\t";
74947
- else if (code < 32 || code === 127) out += `\\${code.toString(8).padStart(3, "0")}`;
74948
- else out += ch;
74949
- }
77348
+ for (const byte of content) if (byte === 92) out += "\\\\";
77349
+ else if (byte === 34) out += "\\\"";
77350
+ else if (byte === 10) out += "\\n";
77351
+ else if (byte === 13) out += "\\r";
77352
+ else if (byte === 9) out += "\\t";
77353
+ else if (byte < 32 || byte >= 127) out += `\\${byte.toString(8).padStart(3, "0")}`;
77354
+ else out += String.fromCharCode(byte);
74950
77355
  return out;
74951
77356
  }
74952
77357
  //#endregion
@@ -76454,6 +78859,9 @@ function validate_qualified_paths(status, prefixes) {
76454
78859
  }
76455
78860
  }
76456
78861
  function mark_library_nodes(root, boundary) {
78862
+ function is_node(value) {
78863
+ return !!value && typeof value === "object" && typeof value.node_type === "string";
78864
+ }
76457
78865
  function walk(node) {
76458
78866
  if (!node || typeof node !== "object") return;
76459
78867
  if (Array.isArray(node)) {
@@ -76461,33 +78869,16 @@ function mark_library_nodes(root, boundary) {
76461
78869
  return;
76462
78870
  }
76463
78871
  const type = node.node_type;
76464
- if (type === "func") {
76465
- if (node.start >= boundary) node.is_library = true;
76466
- if (node.statements) walk(node.statements);
76467
- if (node.allocations) walk(node.allocations);
76468
- return;
76469
- }
76470
- if (type === "struct") {
76471
- if (node.start >= boundary) node.is_library = true;
76472
- if (node.functions) for (const f of node.functions) walk(f);
76473
- if (node.fields) walk(node.fields);
76474
- }
76475
- if (type === "trait") {
76476
- if (node.start >= boundary) node.is_library = true;
76477
- if (node.functions) for (const f of node.functions) walk(f);
76478
- if (node.fields) walk(node.fields);
76479
- }
76480
- if (type === "enum" || type === "bitset") {
78872
+ if (type === "func" || type === "struct" || type === "trait" || type === "enum" || type === "bitset" || type === "declare") {
76481
78873
  if (node.start >= boundary) node.is_library = true;
76482
78874
  }
76483
- if (type === "declare") {
76484
- if (node.start >= boundary) node.is_library = true;
76485
- }
76486
- if (type === "extend") {
76487
- if (node.functions) for (const f of node.functions) walk(f);
78875
+ for (const key of Object.keys(node)) {
78876
+ if (key === "parent" || key === "scope") continue;
78877
+ const value = node[key];
78878
+ if (Array.isArray(value)) {
78879
+ for (const item of value) if (is_node(item)) walk(item);
78880
+ } else if (is_node(value)) walk(value);
76488
78881
  }
76489
- if (node.statements) walk(node.statements);
76490
- if (node.allocations) walk(node.allocations);
76491
78882
  }
76492
78883
  walk(root);
76493
78884
  }
@@ -76495,7 +78886,9 @@ function mark_library_nodes(root, boundary) {
76495
78886
  * The base directory for `#embed_file` paths: the package root (the nearest
76496
78887
  * enclosing directory with a package.jsonc), falling back to the entry
76497
78888
  * file's folder. No file path — undefined, so `#embed_file` resolves
76498
- * against the process CWD.
78889
+ * against the process CWD. Exported for the library builder, which inlines
78890
+ * library-level embeds against each source file's own root (the
78891
+ * concatenated library source is parsed without a file path).
76499
78892
  */
76500
78893
  function embed_root_for(file_path) {
76501
78894
  if (!file_path) return void 0;
@@ -76652,6 +79045,228 @@ function format_errors(source, errors) {
76652
79045
  return errors;
76653
79046
  }
76654
79047
  //#endregion
79048
+ //#region ../src/lib.ts
79049
+ function read_library_config(lib_dir) {
79050
+ const config_path = path.join(lib_dir, "package.jsonc");
79051
+ const json = fs.readFileSync(config_path, "utf8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
79052
+ return JSON.parse(json);
79053
+ }
79054
+ function resolve_export_files(lib_dir, pattern) {
79055
+ const base = pattern.replace(/\/?\*$/, "").replace(/^\.\//, "");
79056
+ const src_dir = path.resolve(lib_dir, base);
79057
+ if (!fs.existsSync(src_dir)) return [];
79058
+ return fs.readdirSync(src_dir).filter((f) => f.endsWith(".nm")).sort().map((f) => path.join(src_dir, f));
79059
+ }
79060
+ const FILE_ORDER = [
79061
+ "Disposable",
79062
+ "Stringable",
79063
+ "Equatable",
79064
+ "Hashable",
79065
+ "Sendable",
79066
+ "Task",
79067
+ "Mutex",
79068
+ "Channel",
79069
+ "int",
79070
+ "uint",
79071
+ "int8",
79072
+ "uint8",
79073
+ "int16",
79074
+ "uint16",
79075
+ "int32",
79076
+ "int64",
79077
+ "uint64",
79078
+ "float",
79079
+ "char",
79080
+ "String",
79081
+ "Array",
79082
+ "Console",
79083
+ "Math",
79084
+ "bool"
79085
+ ];
79086
+ function get_file_priority(file_path) {
79087
+ const name = path.basename(file_path, ".nm");
79088
+ const idx = FILE_ORDER.indexOf(name);
79089
+ if (idx >= 0) return idx;
79090
+ return FILE_ORDER.length;
79091
+ }
79092
+ function extract_deps(source) {
79093
+ const deps = [];
79094
+ for (const line of source.split("\n")) {
79095
+ const trimmed = line.trim();
79096
+ if (trimmed.startsWith("import ")) deps.push(trimmed.slice(7).trim());
79097
+ else if (trimmed.startsWith("pub struct ") || trimmed.startsWith("internal struct ") || trimmed.startsWith("struct ")) {
79098
+ const colon_idx = trimmed.indexOf(":");
79099
+ if (colon_idx >= 0) {
79100
+ const trait_name = trimmed.slice(colon_idx + 1).trim().split(/\s|<|{/)[0];
79101
+ if (trait_name) deps.push(trait_name);
79102
+ }
79103
+ break;
79104
+ } else if (trimmed.startsWith("pub class ") || trimmed.startsWith("internal class ") || trimmed.startsWith("class ")) {
79105
+ const colon_idx = trimmed.indexOf(":");
79106
+ if (colon_idx >= 0) {
79107
+ const trait_name = trimmed.slice(colon_idx + 1).trim().split(/\s|<|{/)[0];
79108
+ if (trait_name) deps.push(trait_name);
79109
+ }
79110
+ break;
79111
+ } else if (trimmed.startsWith("pub trait ") || trimmed.startsWith("internal trait ") || trimmed.startsWith("trait ")) break;
79112
+ else if (trimmed.length > 0 && !is_comment_line(trimmed)) break;
79113
+ }
79114
+ return deps;
79115
+ }
79116
+ function is_comment_line(trimmed) {
79117
+ return trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*");
79118
+ }
79119
+ function extract_type_names(source) {
79120
+ const names = [];
79121
+ for (let line of source.split("\n")) {
79122
+ const m = line.trim().match(/^(?:pub|internal) (?:checked )?(?:mv )?(?:struct|class|trait|enum|bitset) (\w+)/);
79123
+ if (m) names.push(m[1]);
79124
+ }
79125
+ return names;
79126
+ }
79127
+ function extract_free_func_names(source) {
79128
+ const names = [];
79129
+ for (let line of source.split("\n")) {
79130
+ const m = line.match(/^(?:(?:pub|internal)\s+|foreign\s+|pub\s+foreign\s+)func\s+(\w+)/);
79131
+ if (m) names.push(m[1]);
79132
+ }
79133
+ return names;
79134
+ }
79135
+ function build_type_map(files, lib_dir, file_texts) {
79136
+ const all_type_names = /* @__PURE__ */ new Set();
79137
+ const file_sources = /* @__PURE__ */ new Map();
79138
+ const func_to_type = /* @__PURE__ */ new Map();
79139
+ for (const f of files) {
79140
+ const source = file_texts?.get(f) ?? fs.readFileSync(f, "utf8");
79141
+ file_sources.set(f, source);
79142
+ const type_names = extract_type_names(source);
79143
+ for (const name of type_names) all_type_names.add(name);
79144
+ const first_type = type_names[0];
79145
+ if (first_type) {
79146
+ for (const fname of extract_free_func_names(source)) if (!func_to_type.has(fname)) func_to_type.set(fname, first_type);
79147
+ }
79148
+ }
79149
+ const types = /* @__PURE__ */ new Map();
79150
+ const functions = /* @__PURE__ */ new Map();
79151
+ for (const f of files) {
79152
+ const source = file_sources.get(f);
79153
+ const deps = extract_deps(source);
79154
+ for (const token of tokenize(source)) {
79155
+ if (deps.includes(token.value)) continue;
79156
+ if (all_type_names.has(token.value)) {
79157
+ deps.push(token.value);
79158
+ continue;
79159
+ }
79160
+ const dep_type = func_to_type.get(token.value);
79161
+ if (dep_type && !deps.includes(dep_type)) deps.push(dep_type);
79162
+ }
79163
+ const parts = path.relative(path.resolve(lib_dir, "System"), f).split(path.sep);
79164
+ const namespace = parts.length > 1 ? parts[0] : void 0;
79165
+ const type_names = extract_type_names(source);
79166
+ for (const name of type_names) types.set(name, {
79167
+ name,
79168
+ source,
79169
+ path: f,
79170
+ deps,
79171
+ namespace
79172
+ });
79173
+ const free_funcs = extract_free_func_names(source);
79174
+ if (type_names.length > 0) {
79175
+ const shared = types.get(type_names[0]);
79176
+ for (const fname of free_funcs) if (!functions.has(fname)) functions.set(fname, shared);
79177
+ } else for (const fname of free_funcs) if (!functions.has(fname)) functions.set(fname, {
79178
+ name: fname,
79179
+ source,
79180
+ path: f,
79181
+ deps,
79182
+ namespace
79183
+ });
79184
+ }
79185
+ for (const [, entry] of types) {
79186
+ if (entry.deps.includes("Buffer") && !entry.deps.includes("ClassBuffer")) entry.deps.push("ClassBuffer");
79187
+ if (entry.deps.includes("ClassBuffer") && !entry.deps.includes("Buffer")) entry.deps.push("Buffer");
79188
+ }
79189
+ return {
79190
+ types,
79191
+ functions
79192
+ };
79193
+ }
79194
+ function build_library(lib_dir) {
79195
+ const config = read_library_config(lib_dir);
79196
+ let all_files = [];
79197
+ for (const pattern of Object.values(config.exports)) all_files = all_files.concat(resolve_export_files(lib_dir, pattern));
79198
+ all_files.sort((a, b) => get_file_priority(a) - get_file_priority(b));
79199
+ const file_texts = new Map(all_files.map((f) => [f, inline_library_embeds(f, fs.readFileSync(f, "utf8"))]));
79200
+ const source = all_files.map((f) => file_texts.get(f)).join("\n");
79201
+ const { types, functions } = build_type_map(all_files, lib_dir, file_texts);
79202
+ const namespaces = /* @__PURE__ */ new Map();
79203
+ for (const [name, type] of types) if (type.namespace) {
79204
+ let ns = namespaces.get(type.namespace);
79205
+ if (!ns) {
79206
+ ns = /* @__PURE__ */ new Set();
79207
+ namespaces.set(type.namespace, ns);
79208
+ }
79209
+ ns.add(name);
79210
+ }
79211
+ return {
79212
+ name: config.name,
79213
+ source,
79214
+ types,
79215
+ functions,
79216
+ namespaces,
79217
+ dir: path.resolve(lib_dir)
79218
+ };
79219
+ }
79220
+ const library_cache = /* @__PURE__ */ new Map();
79221
+ /**
79222
+ * `#embed_file` inside LIBRARY sources resolves against the library's own
79223
+ * package root, but the concatenated library source is parsed without a
79224
+ * file path — the caller's CWD/package root would win, and the directive
79225
+ * would fail (or read the wrong file) for every program built outside the
79226
+ * repo. Inline the directive textually at concat time: the file's bytes
79227
+ * become an escape-encoded string literal exactly like parse_embed_file
79228
+ * produces, so downstream sees a plain literal. Library sources are
79229
+ * trusted repo files, so a plain directive match is fine.
79230
+ */
79231
+ function inline_library_embeds(file, text) {
79232
+ if (!text.includes("#embed_file")) return text;
79233
+ const base = embed_root_for(file) ?? path.dirname(file);
79234
+ return text.replace(/#embed_file\("([^"]+)"\)/g, (_match, rel_path) => {
79235
+ const full = path.isAbsolute(rel_path) ? rel_path : path.join(base, rel_path);
79236
+ return `"${escape_embedded_bytes(fs.readFileSync(full))}"`;
79237
+ });
79238
+ }
79239
+ function get_library(lib_dir) {
79240
+ const cached = library_cache.get(lib_dir);
79241
+ if (cached) return cached;
79242
+ const lib = build_library(lib_dir);
79243
+ library_cache.set(lib_dir, lib);
79244
+ return lib;
79245
+ }
79246
+ /**
79247
+ * Every valid namespace-path prefix implied by the library's file layout
79248
+ * (`Controls`, `Controls/Geometry`, `Stream/File`, ... and their
79249
+ * `System/`-rooted forms). A type's path (`core/System/Controls/Geometry.nm`)
79250
+ * contributes the chain of directories leading to it plus the module file's
79251
+ * own base name, so both `Controls::Button` (namespace) and
79252
+ * `Controls::Geometry::Size` (module) validate.
79253
+ */
79254
+ function library_path_prefixes(library) {
79255
+ const prefixes = /* @__PURE__ */ new Set();
79256
+ const system_root = path.resolve(library.dir, "System");
79257
+ for (const entry of library.types.values()) {
79258
+ const rel = path.relative(system_root, entry.path);
79259
+ if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) continue;
79260
+ const parts = rel.replace(/\.nm$/, "").split(path.sep);
79261
+ for (let i = 1; i <= parts.length; i++) {
79262
+ const joined = parts.slice(0, i).join("/");
79263
+ prefixes.add(joined);
79264
+ prefixes.add(`System/${joined}`);
79265
+ }
79266
+ }
79267
+ return prefixes;
79268
+ }
79269
+ //#endregion
76655
79270
  //#region src/args.ts
76656
79271
  const STRING_OPTIONS = /* @__PURE__ */ new Map([
76657
79272
  ["in", "in"],
@@ -76776,7 +79391,7 @@ function print_help() {
76776
79391
  " --config, -c <path> Path to a config file",
76777
79392
  " --watch, -w Watch for file changes",
76778
79393
  " --filter, -f <regex> Only run test files whose path matches this regex",
76779
- " --arch, -a <arch> Target architecture: aarch64 | c (default: aarch64)",
79394
+ " --arch, -a <arch> Target architecture: aarch64 | c | js (default: aarch64)",
76780
79395
  " --platform, -p <name> Target platform: macos, ios, linux, android, windows, web",
76781
79396
  " --lib, -l <path> Path to System library directory (containing package.jsonc)",
76782
79397
  " --audit Audit the generated program for memory issues",
@@ -77905,6 +80520,17 @@ function processFile(filename, config, mode, program_args) {
77905
80520
  }
77906
80521
  const basename = path.basename(filename, ".nm");
77907
80522
  const buildDir = build_dir_for(resolved, filename.endsWith(".test.nm"));
80523
+ if (arch === "js") {
80524
+ if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir, { recursive: true });
80525
+ const jsfile = path.join(buildDir, basename + ".mjs");
80526
+ fs.writeFileSync(jsfile, result.code);
80527
+ console.log(`Created ${jsfile} in ${(performance.now() - startTime).toFixed(2)}ms`);
80528
+ if (mode === "run") {
80529
+ execFileSync(process.env.NODE ?? "node", [jsfile, ...program_args], { stdio: "inherit" });
80530
+ console.log(`Completed in ${(performance.now() - startTime).toFixed(2)}ms`);
80531
+ }
80532
+ return;
80533
+ }
77908
80534
  if (!fs.existsSync(buildDir)) fs.mkdirSync(buildDir, { recursive: true });
77909
80535
  const ext = arch === "aarch64" ? ".s" : platform === "macos" || platform === "ios" ? ".m" : ".c";
77910
80536
  const headerfile = path.join(buildDir, "main.h");