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/NOMEN_AGENTS.md +7 -6
- package/core/System/Controls/App.nm +8 -0
- package/core/System/Controls/Container.nm +148 -44
- package/core/System/Controls/Window.nm +363 -51
- package/core/assets/nomen.png +0 -0
- package/dist/index.mjs +3030 -404
- package/package.json +1 -1
- package/src/args.ts +1 -1
- package/src/index.ts +15 -1
- package/src/types/Config.ts +1 -1
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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 &&
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
57670
|
+
build_panic_node$2(node, status);
|
|
57652
57671
|
with_semicolon = false;
|
|
57653
57672
|
break;
|
|
57654
57673
|
case "todo":
|
|
57655
|
-
build_todo_node$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
61717
|
+
build_if_else_node$2(node, status);
|
|
61699
61718
|
with_semicolon = false;
|
|
61700
61719
|
break;
|
|
61701
61720
|
case "cast":
|
|
61702
|
-
build_cast_node$
|
|
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$
|
|
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$
|
|
61736
|
+
build_while_loop_node$2(node, status);
|
|
61718
61737
|
with_semicolon = false;
|
|
61719
61738
|
break;
|
|
61720
61739
|
case "break":
|
|
61721
|
-
build_break_node$
|
|
61740
|
+
build_break_node$2(status);
|
|
61722
61741
|
break;
|
|
61723
61742
|
case "continue":
|
|
61724
|
-
build_continue_node$
|
|
61743
|
+
build_continue_node$2(status);
|
|
61725
61744
|
break;
|
|
61726
61745
|
case "return":
|
|
61727
|
-
build_return_node$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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
|
|
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, `"${
|
|
77338
|
+
return new ValueNode(start, `"${escape_embedded_bytes(content)}"`);
|
|
74936
77339
|
}
|
|
74937
|
-
/** Encode file contents as a source-level string literal body
|
|
74938
|
-
|
|
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
|
|
74941
|
-
|
|
74942
|
-
|
|
74943
|
-
|
|
74944
|
-
|
|
74945
|
-
|
|
74946
|
-
|
|
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
|
-
|
|
76484
|
-
if (
|
|
76485
|
-
|
|
76486
|
-
|
|
76487
|
-
|
|
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");
|