@scriptc/compiler 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/backend/emission/emit-exprs.js +136 -12
  2. package/dist/backend/emission/emit-exprs.js.map +1 -1
  3. package/dist/backend/emission/emit-walkers.js +29 -2
  4. package/dist/backend/emission/emit-walkers.js.map +1 -1
  5. package/dist/backend/llvm/dyn.d.ts +1 -0
  6. package/dist/backend/llvm/dyn.js +83 -1
  7. package/dist/backend/llvm/dyn.js.map +1 -1
  8. package/dist/backend/llvm/emitter.js +151 -9
  9. package/dist/backend/llvm/emitter.js.map +1 -1
  10. package/dist/diagnostics/diagnostic.d.ts +21 -7
  11. package/dist/diagnostics/diagnostic.js +35 -16
  12. package/dist/diagnostics/diagnostic.js.map +1 -1
  13. package/dist/diagnostics/render.js +8 -0
  14. package/dist/diagnostics/render.js.map +1 -1
  15. package/dist/frontend/lowering/lib-boundary.js +11 -1
  16. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  17. package/dist/frontend/lowering/lower-builtins.d.ts +13 -0
  18. package/dist/frontend/lowering/lower-builtins.js +146 -1
  19. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  20. package/dist/frontend/lowering/lower-calls.d.ts +41 -0
  21. package/dist/frontend/lowering/lower-calls.js +524 -10
  22. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  23. package/dist/frontend/lowering/lower-containers.d.ts +17 -0
  24. package/dist/frontend/lowering/lower-containers.js +193 -10
  25. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  26. package/dist/frontend/lowering/lower-dgram.js +92 -17
  27. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  28. package/dist/frontend/lowering/lower-exprs.js +179 -10
  29. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  30. package/dist/frontend/lowering/lower-modules.js +33 -2
  31. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  32. package/dist/frontend/lowering/lower-namespaces.d.ts +25 -11
  33. package/dist/frontend/lowering/lower-namespaces.js +50 -15
  34. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  35. package/dist/frontend/lowering/lower-server.js +211 -4
  36. package/dist/frontend/lowering/lower-server.js.map +1 -1
  37. package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
  38. package/dist/frontend/lowering/lower-stmts.js +216 -7
  39. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  40. package/dist/frontend/lowering/lower-stream.js +65 -4
  41. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  42. package/dist/frontend/lowering/lowerer.d.ts +36 -1
  43. package/dist/frontend/lowering/lowerer.js +106 -3
  44. package/dist/frontend/lowering/lowerer.js.map +1 -1
  45. package/dist/frontend/types.d.ts +13 -0
  46. package/dist/frontend/types.js +84 -7
  47. package/dist/frontend/types.js.map +1 -1
  48. package/dist/index.js +20 -8
  49. package/dist/index.js.map +1 -1
  50. package/dist/ir/nodes.d.ts +99 -5
  51. package/dist/ir/nodes.js +44 -3
  52. package/dist/ir/nodes.js.map +1 -1
  53. package/dist/ir/validate.js +53 -3
  54. package/dist/ir/validate.js.map +1 -1
  55. package/dist/library/fence-eval.d.ts +77 -0
  56. package/dist/library/fence-eval.js +385 -0
  57. package/dist/library/fence-eval.js.map +1 -0
  58. package/dist/library/profile.d.ts +11 -1
  59. package/dist/library/profile.js +102 -18
  60. package/dist/library/profile.js.map +1 -1
  61. package/package.json +2 -2
  62. package/surface-manifest.json +1 -1
@@ -274,6 +274,23 @@ export type ForOfIterProjection = "keys" | "values" | "entries";
274
274
  * enter/exit bracketing: the pair list compacts immediately (no
275
275
  * tombstones), so there is no iteration depth to keep exact. */
276
276
  export declare function lowerForOfSearchParams(L: Lowerer, stmt: ts.ForOfStatement, iterable: IrExpr, proj?: ForOfIterProjection): IrStmt;
277
+ /** for-of over an ARRAY's keys()/entries() projections consumed directly
278
+ * by the head (`for (const [index, line] of lines.entries())` — the
279
+ * dominant formatter idiom): the LIVE index walk — the length re-reads
280
+ * every pass, exactly the array iterator's contract (elements appended
281
+ * mid-walk are visited; a shrink ends the loop early) — yielding the
282
+ * index (`keys`, a number) or the [index, element] pair (`entries`).
283
+ * `values` never lands here: lowerForOf's receiver unwrap owns it. A
284
+ * `[i, v]` head of plain identifiers binds straight from the reads (the
285
+ * tuple never materializes); identifier heads and the remaining
286
+ * patterns bind through the checker's own element type — the interned
287
+ * [number, T] tuple record. Stored iterator OBJECTS keep their fence
288
+ * (only the direct for-of position unwraps). */
289
+ export declare function lowerForOfArrayIter(L: Lowerer, stmt: ts.ForOfStatement, iterable: IrExpr & {
290
+ type: IrType & {
291
+ kind: "array";
292
+ };
293
+ }, proj: "keys" | "entries"): IrStmt;
277
294
  export declare function lowerForOfMap(L: Lowerer, stmt: ts.ForOfStatement, iterable: IrExpr, mapT: IrType & {
278
295
  kind: "map";
279
296
  }, proj?: ForOfIterProjection): IrStmt;
@@ -2649,23 +2649,27 @@ export function lowerIteratorHelperCall(L, call, access) {
2649
2649
  const terminal = access.name.text;
2650
2650
  if (!ITER_TERMINALS.has(terminal))
2651
2651
  return null;
2652
- // Walk receiver: stage* ← values() ← array-typed source.
2652
+ // Walk receiver: stage* ← values()/entries() ← array-typed source
2653
+ // (entries seeds the chain with [index, element] pairs — the
2654
+ // checker's own tuple type).
2653
2655
  const stages = [];
2654
2656
  let cur = access.expression;
2655
2657
  let source = null;
2658
+ let srcProj = "values";
2656
2659
  for (;;) {
2657
2660
  if (!ts.isCallExpression(cur) || !ts.isPropertyAccessExpression(cur.expression))
2658
2661
  return null;
2659
2662
  if (cur.questionDotToken || cur.expression.questionDotToken)
2660
2663
  return null;
2661
2664
  const name = cur.expression.name.text;
2662
- if (name === "values" && cur.arguments.length === 0) {
2665
+ if ((name === "values" || name === "entries") && cur.arguments.length === 0) {
2663
2666
  const recvIr = L.mapTypeOf(L.typeOf(cur.expression.expression));
2664
2667
  if (recvIr?.kind !== "array")
2665
2668
  return null;
2666
2669
  if (!L.isStdlibMember(cur.expression))
2667
2670
  return null;
2668
2671
  source = cur.expression.expression;
2672
+ srcProj = name;
2669
2673
  break;
2670
2674
  }
2671
2675
  if (!ITER_STAGES.has(name) || cur.arguments.length !== 1 || ts.isSpreadElement(cur.arguments[0]))
@@ -2687,7 +2691,12 @@ export function lowerIteratorHelperCall(L, call, access) {
2687
2691
  const items = L.lowerExpr(source);
2688
2692
  if (items.type.kind !== "array")
2689
2693
  return null;
2690
- let elem = items.type.elem;
2694
+ // The entries() seed: the interned [number, T] tuple — the same shape
2695
+ // the checker's own [number, T] maps to, so stage callbacks typed
2696
+ // against the lib's pairs intern equal.
2697
+ let elem = srcProj === "entries"
2698
+ ? { kind: "record", shapeId: L.shapes.intern([{ name: "0", type: F64 }, { name: "1", type: items.type.elem }], true) }
2699
+ : items.type.elem;
2691
2700
  const stageIrs = [];
2692
2701
  for (const s of stages) {
2693
2702
  if (s.name === "take" || s.name === "drop") {
@@ -2774,12 +2783,12 @@ export function lowerIteratorHelperCall(L, call, access) {
2774
2783
  L.noLowering(`.${terminal} at this result type`, call, "the chain's element and result types must be representable — annotate the callbacks' types");
2775
2784
  }
2776
2785
  const stageKeys = stageIrs.map((s) => (s.kind === "take" || s.kind === "drop" ? s.kind : `${s.kind}:${typeKey(s.fn.type)}`));
2777
- const key = `iter:${typeKey(items.type)}:${stageKeys.join(",")}:${terminal}:${termArgs.map((a) => typeKey(a.type)).join(",")}`;
2786
+ const key = `iter:${srcProj}:${typeKey(items.type)}:${stageKeys.join(",")}:${terminal}:${termArgs.map((a) => typeKey(a.type)).join(",")}`;
2778
2787
  let helper = L.arrHofHelpers.get(key);
2779
2788
  if (!helper) {
2780
2789
  helper = `%iter.${terminal}.${L.arrHofHelpers.size}`;
2781
2790
  L.arrHofHelpers.set(key, helper);
2782
- L.liftedFns.push(buildIterChainFn(L, helper, items.type, stageIrs, terminal, termArgs.map((a) => a.type), resultT, loc));
2791
+ L.liftedFns.push(buildIterChainFn(L, helper, items.type, srcProj, stageIrs, terminal, termArgs.map((a) => a.type), resultT, loc));
2783
2792
  }
2784
2793
  const budgetOrFn = stageIrs.map((s) => (s.kind === "take" || s.kind === "drop" ? s.budget : s.fn));
2785
2794
  return { kind: "call", callee: helper, args: [items, ...budgetOrFn, ...termArgs], type: resultT, loc };
@@ -2862,7 +2871,7 @@ function internBudgetChecker(L, loc) {
2862
2871
  * source order, flatMap as an inner loop over its returned array.
2863
2872
  * Pre-loop, a zero take budget marks done immediately — Node pulls
2864
2873
  * nothing at all through a take(0). */
2865
- function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT, loc) {
2874
+ function buildIterChainFn(L, name, itemsT, srcProj, stages, terminal, termArgTs, resultT, loc) {
2866
2875
  const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
2867
2876
  const num = (value) => ({ kind: "numLit", value, type: F64, loc });
2868
2877
  const boolLit = (value) => ({ kind: "boolLit", value, type: BOOL, loc });
@@ -2883,7 +2892,12 @@ function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT,
2883
2892
  const doneId = addLocal("done", BOOL, true);
2884
2893
  const done = () => ref(doneId, BOOL);
2885
2894
  const setDone = { kind: "assign", localId: doneId, value: boolLit(true), loc };
2886
- let elem = itemsT.elem;
2895
+ // The entries() seed builds each pass's [index, element] pair — the
2896
+ // same interned tuple the caller computed its stage types against.
2897
+ const seedT = srcProj === "entries"
2898
+ ? { kind: "record", shapeId: L.shapes.intern([{ name: "0", type: F64 }, { name: "1", type: itemsT.elem }], true) }
2899
+ : itemsT.elem;
2900
+ let elem = seedT;
2887
2901
  const slots = [];
2888
2902
  for (const s of stages) {
2889
2903
  if (s.kind === "take" || s.kind === "drop") {
@@ -3128,7 +3142,19 @@ function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT,
3128
3142
  }
3129
3143
  }
3130
3144
  const iId = addLocal("i", F64, true);
3131
- const vId = addLocal("v", itemsT.elem, false);
3145
+ const vId = addLocal("v", seedT, false);
3146
+ const elemRead = () => ({ kind: "arrayGet", arr: ref("items.0", itemsT), index: ref(iId, F64), type: itemsT.elem, loc });
3147
+ const seedInit = srcProj === "entries"
3148
+ ? {
3149
+ kind: "recordLit",
3150
+ fields: [
3151
+ { name: "0", value: ref(iId, F64) },
3152
+ { name: "1", value: elemRead() },
3153
+ ],
3154
+ type: seedT,
3155
+ loc,
3156
+ }
3157
+ : elemRead();
3132
3158
  const loop = {
3133
3159
  kind: "for",
3134
3160
  init: { kind: "varDecl", localId: iId, init: num(0), loc },
@@ -3142,8 +3168,8 @@ function buildIterChainFn(L, name, itemsT, stages, terminal, termArgTs, resultT,
3142
3168
  },
3143
3169
  update: { kind: "assign", localId: iId, value: { kind: "bin", op: "+", left: ref(iId, F64), right: num(1), type: F64, loc }, loc },
3144
3170
  body: [
3145
- { kind: "varDecl", localId: vId, init: { kind: "arrayGet", arr: ref("items.0", itemsT), index: ref(iId, F64), type: itemsT.elem, loc }, loc },
3146
- ...emit(ref(vId, itemsT.elem)),
3171
+ { kind: "varDecl", localId: vId, init: seedInit, loc },
3172
+ ...emit(ref(vId, seedT)),
3147
3173
  ],
3148
3174
  loc,
3149
3175
  };
@@ -3436,6 +3462,143 @@ export function lowerForOfSearchParams(L, stmt, iterable, proj) {
3436
3462
  L.scopes.pop();
3437
3463
  }
3438
3464
  }
3465
+ /** for-of over an ARRAY's keys()/entries() projections consumed directly
3466
+ * by the head (`for (const [index, line] of lines.entries())` — the
3467
+ * dominant formatter idiom): the LIVE index walk — the length re-reads
3468
+ * every pass, exactly the array iterator's contract (elements appended
3469
+ * mid-walk are visited; a shrink ends the loop early) — yielding the
3470
+ * index (`keys`, a number) or the [index, element] pair (`entries`).
3471
+ * `values` never lands here: lowerForOf's receiver unwrap owns it. A
3472
+ * `[i, v]` head of plain identifiers binds straight from the reads (the
3473
+ * tuple never materializes); identifier heads and the remaining
3474
+ * patterns bind through the checker's own element type — the interned
3475
+ * [number, T] tuple record. Stored iterator OBJECTS keep their fence
3476
+ * (only the direct for-of position unwraps). */
3477
+ export function lowerForOfArrayIter(L, stmt, iterable, proj) {
3478
+ const loc = locOf(stmt);
3479
+ const arrT = iterable.type;
3480
+ const elemT = arrT.elem;
3481
+ const yieldsPair = proj === "entries";
3482
+ if (!ts.isVariableDeclarationList(stmt.initializer)) {
3483
+ L.unsupported("SC1090", stmt.initializer, "for-of over a pre-declared variable (declare the loop variable in the loop: for (const x of ...))");
3484
+ }
3485
+ const list = stmt.initializer;
3486
+ if ((list.flags & ts.NodeFlags.Using) !== 0) {
3487
+ L.unsupported("SC1090", list, "'using' declarations (dispose-at-scope-exit semantics)");
3488
+ }
3489
+ const isVar = (list.flags & ts.NodeFlags.BlockScoped) === 0;
3490
+ const isLet = (list.flags & ts.NodeFlags.Let) !== 0 || isVar;
3491
+ const decl = list.declarations[0]; // the grammar allows exactly one
3492
+ L.scopes.push(new Map());
3493
+ try {
3494
+ const arr = L.declareHiddenLocal("%arof", arrT);
3495
+ const i = L.declareHiddenLocal("%iterof", F64);
3496
+ i.mutable = true;
3497
+ const ref = (localId, type) => ({ kind: "varRef", localId, type, loc });
3498
+ const num = (value) => ({ kind: "numLit", value, type: F64, loc });
3499
+ const keyRead = () => ref(i.id, F64);
3500
+ const valRead = () => ({ kind: "arrayGet", arr: ref(arr.id, arrT), index: ref(i.id, F64), type: elemT, loc });
3501
+ const binds = [];
3502
+ const isPlainIdent = (el) => ts.isBindingElement(el) && el.name !== undefined && ts.isIdentifier(el.name) && !el.initializer && !el.dotDotDotToken;
3503
+ if (yieldsPair &&
3504
+ !isVar && // var pattern names assign hoisted slots — the generic desugar below owns them
3505
+ ts.isArrayBindingPattern(decl.name) &&
3506
+ decl.name.elements.length >= 1 && decl.name.elements.length <= 2 &&
3507
+ decl.name.elements.every(isPlainIdent)) {
3508
+ // `for (const [i, v] of xs.entries())` — bind straight from the
3509
+ // reads; the tuple never exists. `[i]` alone reads only the index.
3510
+ const els = decl.name.elements;
3511
+ const kLocal = L.declareLocal(els[0].name, els[0].name.text, F64, isLet);
3512
+ binds.push({ kind: "varDecl", localId: kLocal.id, init: keyRead(), loc });
3513
+ if (els[1]) {
3514
+ const vLocal = L.declareLocal(els[1].name, els[1].name.text, elemT, isLet);
3515
+ binds.push({ kind: "varDecl", localId: vLocal.id, init: valRead(), loc });
3516
+ }
3517
+ }
3518
+ else {
3519
+ // Identifier heads and the remaining patterns bind through the
3520
+ // checker's own element type — [number, T] for pair yields,
3521
+ // number otherwise — the Map desugar's exact shape.
3522
+ const declT = L.mapTypeOf(L.checker.getTypeAtLocation(decl.name));
3523
+ if (yieldsPair) {
3524
+ const shape = declT?.kind === "record" ? L.shapes.get(declT.shapeId) : null;
3525
+ if (declT?.kind !== "record" || !shape?.tuple || shape.fields.length !== 2 ||
3526
+ shape.fields.find((f) => f.name === "0")?.type.kind !== "f64" ||
3527
+ !typeEquals(shape.fields.find((f) => f.name === "1")?.type ?? F64, elemT)) {
3528
+ L.badType(decl.name, L.checker.getTypeAtLocation(decl.name)); // defensive: the lib declares [number, T]
3529
+ }
3530
+ }
3531
+ else if (declT?.kind !== "f64") {
3532
+ L.badType(decl.name, L.checker.getTypeAtLocation(decl.name)); // defensive: the lib declares number
3533
+ }
3534
+ const elemInit = yieldsPair
3535
+ ? {
3536
+ kind: "recordLit",
3537
+ fields: [
3538
+ { name: "0", value: keyRead() },
3539
+ { name: "1", value: valRead() },
3540
+ ],
3541
+ type: declT,
3542
+ loc,
3543
+ }
3544
+ : keyRead();
3545
+ if (ts.isIdentifier(decl.name)) {
3546
+ const varTarget = forOfVarTarget(L, decl);
3547
+ if (varTarget) {
3548
+ // `for (var x of ...)`: one function-scoped binding, assigned
3549
+ // per pass — closures made in the loop share it, and the value
3550
+ // persists after the loop (both Node-exact for var).
3551
+ const tmp = L.declareHiddenLocal("%vof", declT);
3552
+ binds.push({ kind: "varDecl", localId: tmp.id, init: elemInit, loc });
3553
+ binds.push({
3554
+ kind: "assign",
3555
+ localId: varTarget.id,
3556
+ value: L.coerceInto(decl.name, { kind: "varRef", localId: tmp.id, type: declT, loc }, varTarget.type),
3557
+ loc,
3558
+ });
3559
+ }
3560
+ else {
3561
+ const local = L.declareLocal(decl.name, decl.name.text, declT, isLet);
3562
+ binds.push({ kind: "varDecl", localId: local.id, init: elemInit, loc });
3563
+ }
3564
+ }
3565
+ else {
3566
+ const tmp = L.declareHiddenLocal("%destr", declT);
3567
+ binds.push({ kind: "varDecl", localId: tmp.id, init: elemInit, loc });
3568
+ L.lowerBindingPattern(decl.name, () => ({ kind: "varRef", localId: tmp.id, type: declT, loc }), declT, isLet, binds);
3569
+ }
3570
+ }
3571
+ const body = L.inCtl("loop", () => L.lowerScopedBlock(stmt.statement));
3572
+ const loop = {
3573
+ kind: "for",
3574
+ init: { kind: "varDecl", localId: i.id, init: num(0), loc },
3575
+ cond: {
3576
+ kind: "bin",
3577
+ op: "<",
3578
+ left: ref(i.id, F64),
3579
+ right: { kind: "arrIntrinsic", method: "length", receiver: ref(arr.id, arrT), args: [], type: F64, loc },
3580
+ type: BOOL,
3581
+ loc,
3582
+ },
3583
+ update: {
3584
+ kind: "assign",
3585
+ localId: i.id,
3586
+ value: { kind: "bin", op: "+", left: ref(i.id, F64), right: num(1), type: F64, loc },
3587
+ loc,
3588
+ },
3589
+ body: [...binds, ...body],
3590
+ loc,
3591
+ };
3592
+ return {
3593
+ kind: "block",
3594
+ body: [{ kind: "varDecl", localId: arr.id, init: iterable, loc }, loop],
3595
+ loc,
3596
+ };
3597
+ }
3598
+ finally {
3599
+ L.scopes.pop();
3600
+ }
3601
+ }
3439
3602
  export function lowerForOfMap(L, stmt, iterable, mapT, proj) {
3440
3603
  return lowerForOfMapOrSet(L, stmt, iterable, mapT, proj);
3441
3604
  }
@@ -6305,6 +6468,26 @@ export function lowerDynArrayFlatMapCall(L, call, access, recv) {
6305
6468
  const { fnArg, arity } = hofCallbackArg(L, argNode, [DYN], DYN);
6306
6469
  const ret = fnArg.type.ret;
6307
6470
  if (ret.kind !== "array") {
6471
+ // A DYNAMIC-returning callback (`plugins.flatMap((plugin) =>
6472
+ // plugin.languages ?? [])` — the getSupportInfo shape, where the
6473
+ // callback's members ride the DOM): the call dispatches on the
6474
+ // RECEIVER's runtime kind (dynInvoke) with the callback boxed —
6475
+ // a wrapped ISLAND receiver runs the ENGINE's own JS-exact
6476
+ // Array.prototype.flatMap (the routed-ops lane), a native DOM
6477
+ // array runs the runtime's flatMap, and non-arrays throw Node's
6478
+ // TypeError. The result stays dyn, checked per use.
6479
+ if (ret.kind === "dyn" || ret.kind === "jsval") {
6480
+ const boxed = { kind: "dynFrom", value: fnArg, type: DYN, loc };
6481
+ return {
6482
+ kind: "dynInvoke",
6483
+ recv,
6484
+ method: "flatMap",
6485
+ calleeName: access.getText(),
6486
+ args: [boxed],
6487
+ type: DYN,
6488
+ loc,
6489
+ };
6490
+ }
6308
6491
  L.unsupported("SC1090", call, `'.flatMap' on 'unknown' receivers whose callback returns '${L.fmt(ret)}' ` +
6309
6492
  `(only callbacks returning a typed ARRAY compile — the results concatenate ` +
6310
6493
  `without validation; return one-element/empty arrays instead of bare values)`);