@jalvin/compiler 2.0.38 → 2.0.39

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 (56) hide show
  1. package/dist/codegen.d.ts +3 -56
  2. package/dist/codegen.d.ts.map +1 -1
  3. package/dist/codegen.js +252 -898
  4. package/dist/codegen.js.map +1 -1
  5. package/dist/index.d.ts +1 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/typechecker.d.ts.map +1 -1
  10. package/dist/typechecker.js +1 -3
  11. package/dist/typechecker.js.map +1 -1
  12. package/package.json +1 -1
  13. package/dist/codegen/index.d.ts +0 -179
  14. package/dist/codegen/index.d.ts.map +0 -1
  15. package/dist/codegen/index.js +0 -2207
  16. package/dist/codegen/index.js.map +0 -1
  17. package/dist/codegen_freestanding_c.d.ts +0 -9
  18. package/dist/codegen_freestanding_c.d.ts.map +0 -1
  19. package/dist/codegen_freestanding_c.js +0 -321
  20. package/dist/codegen_freestanding_c.js.map +0 -1
  21. package/dist/lexer/chars.d.ts +0 -5
  22. package/dist/lexer/chars.d.ts.map +0 -1
  23. package/dist/lexer/chars.js +0 -20
  24. package/dist/lexer/chars.js.map +0 -1
  25. package/dist/lexer/index.d.ts +0 -37
  26. package/dist/lexer/index.d.ts.map +0 -1
  27. package/dist/lexer/index.js +0 -630
  28. package/dist/lexer/index.js.map +0 -1
  29. package/dist/lexer/tokens.d.ts +0 -135
  30. package/dist/lexer/tokens.d.ts.map +0 -1
  31. package/dist/lexer/tokens.js +0 -89
  32. package/dist/lexer/tokens.js.map +0 -1
  33. package/dist/parser/constants.d.ts +0 -3
  34. package/dist/parser/constants.d.ts.map +0 -1
  35. package/dist/parser/constants.js +0 -7
  36. package/dist/parser/constants.js.map +0 -1
  37. package/dist/parser/helpers.d.ts +0 -2
  38. package/dist/parser/helpers.d.ts.map +0 -1
  39. package/dist/parser/helpers.js +0 -9
  40. package/dist/parser/helpers.js.map +0 -1
  41. package/dist/parser/index.d.ts +0 -118
  42. package/dist/parser/index.d.ts.map +0 -1
  43. package/dist/parser/index.js +0 -2387
  44. package/dist/parser/index.js.map +0 -1
  45. package/dist/typechecker/globals.d.ts +0 -2
  46. package/dist/typechecker/globals.d.ts.map +0 -1
  47. package/dist/typechecker/globals.js +0 -15
  48. package/dist/typechecker/globals.js.map +0 -1
  49. package/dist/typechecker/index.d.ts +0 -146
  50. package/dist/typechecker/index.d.ts.map +0 -1
  51. package/dist/typechecker/index.js +0 -2288
  52. package/dist/typechecker/index.js.map +0 -1
  53. package/dist/typechecker/types.d.ts +0 -66
  54. package/dist/typechecker/types.d.ts.map +0 -1
  55. package/dist/typechecker/types.js +0 -39
  56. package/dist/typechecker/types.js.map +0 -1
package/dist/codegen.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  // ─────────────────────────────────────────────────────────────────────────────
3
- // Jalvin Code Generator — AST → TypeScript/TSX
3
+ // Jalvin Code Generator — AST → TypeScript
4
4
  //
5
5
  // Design principles:
6
- // • component declarations → React functional components (TSX)
6
+ // • component declarations → Functional components (standard TS)
7
7
  // • data class → class with auto-generated copy(), equals(), toString()
8
8
  // • sealed class → TypeScript discriminated union + base class
9
9
  // • extension functions → module-level functions with receiver as first param,
@@ -89,7 +89,6 @@ class Writer {
89
89
  setSourceLine(line) { this.currentLine = line + 1; }
90
90
  }
91
91
  exports.DEFAULT_CODEGEN_OPTIONS = {
92
- jsx: false,
93
92
  module: "esm",
94
93
  emitTypes: false,
95
94
  runtimeImport: "@jalvin/runtime",
@@ -99,7 +98,7 @@ class CodeGenerator {
99
98
  opts;
100
99
  hasComponents = false;
101
100
  runtimeSymbolsNeeded = new Set();
102
- /** Names of true Jalvin components — used to emit React.createElement boundaries */
101
+ /** Names of true Jalvin components — used to detect functional calls */
103
102
  userComponentNames = new Set();
104
103
  /** Names of all potential components (including UI primitives) — used to detect props-object calls */
105
104
  allComponentLikeNames = new Set();
@@ -139,10 +138,6 @@ class CodeGenerator {
139
138
  constructor(opts = {}) {
140
139
  this.opts = { ...exports.DEFAULT_CODEGEN_OPTIONS, ...opts };
141
140
  }
142
- /** Returns true if the output should be treated as JSX (React components) */
143
- get isJsxMode() {
144
- return this.opts.jsx || this.hasComponents;
145
- }
146
141
  generate(program, operatorOverloads, typeMap) {
147
142
  if (operatorOverloads)
148
143
  this.operatorOverloadMap = operatorOverloads;
@@ -217,7 +212,7 @@ class CodeGenerator {
217
212
  return {
218
213
  code,
219
214
  lineMap: this.w.lineMap,
220
- isJsx: this.hasComponents,
215
+ hasComponents: this.hasComponents,
221
216
  };
222
217
  }
223
218
  /** Walk AST to see if it contains any JsxElement nodes */
@@ -247,8 +242,12 @@ class CodeGenerator {
247
242
  // ── Preamble & imports ─────────────────────────────────────────────────────
248
243
  buildPreamble() {
249
244
  const lines = [];
245
+ lines.push("// ─────────────────────────────────────────────────────────────────────────────");
246
+ lines.push("// JALVIN COMPILER V5 - PURE VANILLA - NO REACT ALLOWED");
247
+ lines.push(`// BUILD ID: ${Date.now()}`);
248
+ lines.push("// ─────────────────────────────────────────────────────────────────────────────");
250
249
  if (this.hasComponents) {
251
- lines.push('import React from "react";');
250
+ this.runtimeSymbolsNeeded.add("jalvinCreateElement");
252
251
  }
253
252
  // Emit compiler-injected runtime symbols, but only those NOT already covered
254
253
  // by a star-import's named-import expansion (to avoid duplicate imports).
@@ -265,19 +264,6 @@ class CodeGenerator {
265
264
  const sourceRoot = this.opts.sourceRoot ?? process.cwd();
266
265
  // Re-emit user imports as ES imports
267
266
  for (const imp of program.imports) {
268
- // Build module specifier.
269
- //
270
- // Scoped packages (@org/pkg.Symbol): symbol is an export from the package,
271
- // strip the last segment.
272
- // import @jalvin/ui.Column → import { Column } from "@jalvin/ui"
273
- // import @jalvin/runtime.* → import * as runtime from "@jalvin/runtime"
274
- //
275
- // Local imports (a.b.C): check whether the preceding segments already
276
- // resolve to a file on disk.
277
- // import src.models.Rotation → src/models/Rotation.ts does NOT exist
278
- // → import { Rotation } from "src/models/Rotation"
279
- // import src.models.css.Css → src/models/css.ts DOES exist
280
- // → import { Css } from "src/models/css"
281
267
  const isScoped = imp.path[0].startsWith("@");
282
268
  let moduleSpecifier;
283
269
  if (imp.star) {
@@ -292,35 +278,28 @@ class CodeGenerator {
292
278
  moduleSpecifier = moduleParts[0] + "/" + moduleParts.slice(1).join("/");
293
279
  }
294
280
  else {
295
- // Non-scoped import: prefer local-file convention unless this looks like
296
- // an installed npm package path (e.g. cubing/twisty.TwistyPlayer).
281
+ // Non-scoped import: prefer local-file convention
297
282
  const precedingParts = imp.path.slice(0, -1);
298
283
  const precedingRelPath = precedingParts.join("/");
299
284
  const fileExts = [".ts", ".tsx", ".jalvin"];
300
285
  const precedingIsFile = precedingParts.length > 0 && fileExts.some((ext) => fs.existsSync(nodePath.join(sourceRoot, precedingRelPath + ext)));
301
286
  const looksLikeNpmPackage = this.isLikelyNodeModuleImport(imp.path, sourceRoot);
302
287
  moduleSpecifier = precedingIsFile
303
- ? precedingRelPath // src.models.css.Css → "src/models/css"
288
+ ? precedingRelPath
304
289
  : looksLikeNpmPackage
305
- ? precedingRelPath // cubing.twisty.TwistyPlayer → "cubing/twisty"
306
- : imp.path.join("/"); // src.models.Rotation → "src/models/Rotation"
290
+ ? precedingRelPath
291
+ : imp.path.join("/");
307
292
  }
308
293
  if (imp.star && isScoped && this.externalStarCandidates.size > 0) {
309
- // Named-import expansion of a scoped star import.
310
- // Emit explicit named imports for each external symbol used in the file so
311
- // that symbols like `ViewModel`, `mutableStateOf` are directly in scope.
312
294
  const symbols = [...this.externalStarCandidates].sort();
313
295
  this.w.writeIndentedLine(`import { ${symbols.join(", ")} } from "${moduleSpecifier}";`);
314
296
  this.handledStarImportModules.add(moduleSpecifier);
315
297
  }
316
298
  else if (imp.star && !isScoped && this.localStarExports.has(moduleSpecifier)) {
317
- // Local wildcard import: import src.module.* — expand to named imports
318
- // so all exported symbols are directly in scope (no namespace qualifier needed).
319
299
  const symbols = this.localStarExports.get(moduleSpecifier).slice().sort();
320
300
  this.w.writeIndentedLine(`import { ${symbols.join(", ")} } from "${moduleSpecifier}";`);
321
301
  }
322
302
  else if (imp.star) {
323
- // Fallback: namespace import (multiple scoped star imports or no candidates).
324
303
  this.w.writeIndentedLine(`import * as ${imp.path[imp.path.length - 1]} from "${moduleSpecifier}";`);
325
304
  }
326
305
  else if (imp.alias) {
@@ -334,11 +313,6 @@ class CodeGenerator {
334
313
  if (program.imports.length > 0)
335
314
  this.w.writeLine();
336
315
  }
337
- /**
338
- * Heuristic for non-scoped imports: if the first path segment resolves to an
339
- * installed package in node_modules, treat the import as npm/ESM and strip
340
- * the trailing symbol name from the module specifier.
341
- */
342
316
  isLikelyNodeModuleImport(pathParts, sourceRoot) {
343
317
  if (pathParts.length < 2)
344
318
  return false;
@@ -356,14 +330,12 @@ class CodeGenerator {
356
330
  return false;
357
331
  }
358
332
  // ── Top-level declarations ─────────────────────────────────────────────────
359
- /** Emit a @deprecated JSDoc block if the declaration has @Nuked. */
360
333
  emitAnnotations(mods) {
361
334
  for (const ann of mods.annotations) {
362
335
  if (ann.name === "Nuked") {
363
336
  const reason = ann.args ? ` ${ann.args.replace(/^["']|["']$/g, "")}` : "";
364
337
  this.w.writeIndentedLine(`/** @deprecated${reason} */`);
365
338
  }
366
- // Other annotations are silently ignored for now (pass-through model)
367
339
  }
368
340
  }
369
341
  emitTopLevelDecl(decl) {
@@ -418,7 +390,6 @@ class CodeGenerator {
418
390
  ? `: ${this.emitTypeRef(decl.returnType)}`
419
391
  : "";
420
392
  if (!decl.body) {
421
- // Abstract / interface method
422
393
  this.w.writeIndentedLine(`${vis}${asyncKw}${decl.name}${typeParams}(${params})${retType};`);
423
394
  return;
424
395
  }
@@ -435,7 +406,6 @@ class CodeGenerator {
435
406
  this.w.writeIndentedLine(`}`);
436
407
  }
437
408
  else {
438
- // Expression body
439
409
  if (memberOf) {
440
410
  this.w.writeIndentedLine(`${vis}${asyncKw}${decl.name}${typeParams}(${params})${retType} {`);
441
411
  }
@@ -476,93 +446,52 @@ class CodeGenerator {
476
446
  this.w.writeIndentedLine(`}`);
477
447
  }
478
448
  }
479
- // ── component declarations → React function components ────────────────────
449
+ // ── component declarations → Functional components ────────────────────
480
450
  emitComponentDecl(decl) {
481
451
  this.emitAnnotations(decl.modifiers);
482
452
  this.hasComponents = true;
483
453
  const vis = this.isInClass ? this.visibilityPrefix(decl.modifiers) : this.exportPrefix(decl.modifiers);
484
454
  const hasChildren = decl.params.some((p) => p.name === "children");
485
- if (this.isJsxMode) {
486
- // React.createElement format: children are part of props destructuring
487
- // Props interface
488
- if (!this.isInClass && decl.params.length > 0) {
489
- this.w.writeIndentedLine(`interface ${decl.name}Props {`);
490
- this.w.pushIndent();
491
- for (const p of decl.params) {
492
- const optional = p.defaultValue || p.name === "children" ? "?" : "";
493
- const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
494
- this.w.writeIndentedLine(`readonly ${p.name}${optional}: ${typeStr};`);
495
- }
496
- this.w.popIndent();
497
- this.w.writeIndentedLine(`}`);
498
- this.w.writeLine();
499
- }
500
- const propsType = this.isInClass
501
- ? `{ ${decl.params.map(p => {
502
- const optional = p.defaultValue || p.name === "children" ? "?" : "";
503
- const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
504
- return `readonly ${p.name}${optional}: ${typeStr}`;
505
- }).join("; ")} }`
506
- : `${decl.name}Props`;
507
- const propsDestructure = decl.params.length > 0
508
- ? `{ ${decl.params.map((p) => p.name + (p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "")).join(", ")} }: ${propsType}`
509
- : "";
510
- const sig = this.isInClass
511
- ? `${decl.name}(${propsDestructure})`
512
- : `function ${decl.name}(${propsDestructure})`;
513
- this.w.writeIndentedLine(`${vis}${sig} {`);
514
- }
515
- else {
516
- // Direct function call format: children are separate positional parameter
517
- const propsParams = decl.params.filter((p) => p.name !== "children");
518
- // Props interface
519
- if (!this.isInClass && propsParams.length > 0) {
520
- this.w.writeIndentedLine(`interface ${decl.name}Props {`);
521
- this.w.pushIndent();
522
- for (const p of propsParams) {
523
- const optional = p.defaultValue ? "?" : "";
524
- const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
525
- this.w.writeIndentedLine(`readonly ${p.name}${optional}: ${typeStr};`);
526
- }
527
- this.w.popIndent();
528
- this.w.writeIndentedLine(`}`);
529
- this.w.writeLine();
455
+ const propsParams = decl.params.filter((p) => p.name !== "children");
456
+ // Props interface
457
+ if (!this.isInClass && propsParams.length > 0) {
458
+ this.w.writeIndentedLine(`interface ${decl.name}Props {`);
459
+ this.w.pushIndent();
460
+ for (const p of propsParams) {
461
+ const optional = p.defaultValue ? "?" : "";
462
+ const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
463
+ this.w.writeIndentedLine(`readonly ${p.name}${optional}: ${typeStr};`);
530
464
  }
531
- const propsType = this.isInClass
532
- ? `{ ${propsParams.map(p => {
533
- const optional = p.defaultValue ? "?" : "";
534
- const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
535
- return `readonly ${p.name}${optional}: ${typeStr}`;
536
- }).join("; ")} }`
537
- : `${decl.name}Props`;
538
- // Build the function signature
539
- let signature = "";
540
- if (propsParams.length > 0) {
541
- signature = `{ ${propsParams.map((p) => p.name + (p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "")).join(", ")} }: ${propsType}`;
542
- }
543
- else if (hasChildren) {
544
- // Children-only component needs {} as first param to absorb the empty props object from call site
545
- signature = "{}";
546
- }
547
- if (hasChildren) {
548
- signature += signature ? ", children?: any" : "children?: any";
549
- }
550
- const sig = this.isInClass
551
- ? `${decl.name}(${signature})`
552
- : `function ${decl.name}(${signature})`;
553
- this.w.writeIndentedLine(`${vis}${sig} {`);
465
+ this.w.popIndent();
466
+ this.w.writeIndentedLine(`}`);
467
+ this.w.writeLine();
554
468
  }
469
+ const propsType = this.isInClass
470
+ ? `{ ${propsParams.map(p => {
471
+ const optional = p.defaultValue ? "?" : "";
472
+ const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
473
+ return `readonly ${p.name}${optional}: ${typeStr}`;
474
+ }).join("; ")} }`
475
+ : `${decl.name}Props`;
476
+ let signature = "";
477
+ if (propsParams.length > 0) {
478
+ signature = `{ ${propsParams.map((p) => p.name + (p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "")).join(", ")} }: ${propsType}`;
479
+ }
480
+ else if (hasChildren) {
481
+ signature = "{}";
482
+ }
483
+ if (hasChildren) {
484
+ signature += signature ? ", children?: any" : "children?: any";
485
+ }
486
+ const sig = this.isInClass
487
+ ? `${decl.name}(${signature})`
488
+ : `function ${decl.name}(${signature})`;
489
+ this.w.writeIndentedLine(`${vis}${sig} {`);
555
490
  this.w.pushIndent();
556
491
  this.emitComponentBlock(decl.body);
557
492
  this.w.popIndent();
558
493
  this.w.writeIndentedLine(`}`);
559
494
  }
560
- /**
561
- * Emit the body of a `component fun` block.
562
- * The last statement is emitted in tail position — if it is an expression,
563
- * an if/else chain, or a when block, the innermost UI call is implicitly
564
- * returned (implicit return of the last expression).
565
- */
566
495
  emitComponentBlock(block) {
567
496
  const stmts = block.statements;
568
497
  for (let i = 0; i < stmts.length; i++) {
@@ -575,10 +504,8 @@ class CodeGenerator {
575
504
  }
576
505
  }
577
506
  }
578
- /** Emit a statement in tail position, adding implicit return where applicable. */
579
507
  emitTailStmt(stmt) {
580
508
  if (stmt.kind === "ExprStmt") {
581
- // Implicit return: last expression in a component block
582
509
  this.w.writeIndentedLine(`return ${this.emitExpr(stmt.expr)};`);
583
510
  }
584
511
  else if (stmt.kind === "IfStmt") {
@@ -591,7 +518,6 @@ class CodeGenerator {
591
518
  this.emitStmt(stmt);
592
519
  }
593
520
  }
594
- /** Emit a block in tail position, treating its last statement as a tail expression. */
595
521
  emitTailBlock(block) {
596
522
  const stmts = block.statements;
597
523
  for (let i = 0; i < stmts.length; i++) {
@@ -604,7 +530,6 @@ class CodeGenerator {
604
530
  }
605
531
  }
606
532
  }
607
- /** Like emitIfStmt but with tail-return applied recursively to each branch body. */
608
533
  emitIfStmtWithTailReturn(stmt) {
609
534
  this.w.writeIndentedLine(`if (${this.emitExpr(stmt.condition)}) {`);
610
535
  this.w.pushIndent();
@@ -627,7 +552,6 @@ class CodeGenerator {
627
552
  this.w.writeIndentedLine(`}`);
628
553
  }
629
554
  }
630
- /** Like emitWhenStmt but with tail-return applied recursively to each branch body. */
631
555
  emitWhenStmtWithTailReturn(stmt) {
632
556
  const subjectVar = stmt.subject ? "__when_subject__" : null;
633
557
  if (stmt.subject) {
@@ -664,15 +588,12 @@ class CodeGenerator {
664
588
  const superTypes = this.emitSuperTypesStr(decl.superTypes);
665
589
  this.w.writeIndentedLine(`${vis}${abstract}class ${decl.name}${typeParams}${superTypes} {`);
666
590
  this.w.pushIndent();
667
- // The first supertype's delegateArgs become the `super(args)` call inside the constructor.
668
591
  const superDelegateArgs = decl.superTypes[0]?.delegateArgs ?? null;
669
592
  if (decl.primaryConstructor) {
670
593
  const initBlocks = decl.body?.members.filter((m) => m.kind === "InitBlock") ?? [];
671
594
  this.emitPrimaryConstructorProps(decl.primaryConstructor.params, initBlocks, superDelegateArgs);
672
595
  }
673
596
  else if (superDelegateArgs !== null) {
674
- // No primary constructor but the supertype has explicit delegation args —
675
- // emit a minimal constructor that forwards them to super().
676
597
  const superArgsStr = superDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
677
598
  this.w.writeIndentedLine(`constructor() {`);
678
599
  this.w.pushIndent();
@@ -695,24 +616,20 @@ class CodeGenerator {
695
616
  const props = decl.primaryConstructor.params;
696
617
  this.w.writeIndentedLine(`${vis}class ${decl.name}${typeParams}${superTypes} {`);
697
618
  this.w.pushIndent();
698
- // Discriminant for sealed class sub-types
699
619
  if (kindName) {
700
620
  this.w.writeIndentedLine(`readonly __kind = "${kindName}" as const;`);
701
621
  }
702
- // Constructor params → readonly properties
703
622
  for (const p of props) {
704
623
  const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
705
624
  this.w.writeIndentedLine(`readonly ${p.name}${t};`);
706
625
  }
707
626
  this.w.writeLine();
708
- // Constructor
709
627
  const ctorParams = props.map((p) => {
710
628
  const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
711
629
  return `${p.name}${t}`;
712
630
  }).join(", ");
713
631
  this.w.writeIndentedLine(`constructor(${ctorParams}) {`);
714
632
  this.w.pushIndent();
715
- // Emit super() with actual delegation args (or empty call if delegateArgs is []).
716
633
  const dataSuperDelegateArgs = decl.superTypes[0]?.delegateArgs ?? null;
717
634
  if (dataSuperDelegateArgs !== null) {
718
635
  const superArgsStr = dataSuperDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
@@ -724,7 +641,6 @@ class CodeGenerator {
724
641
  this.w.popIndent();
725
642
  this.w.writeIndentedLine(`}`);
726
643
  this.w.writeLine();
727
- // copy()
728
644
  const copyParams = props.map((p) => {
729
645
  const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
730
646
  return `${p.name}${t} = this.${p.name}`;
@@ -736,7 +652,6 @@ class CodeGenerator {
736
652
  this.w.popIndent();
737
653
  this.w.writeIndentedLine(`}`);
738
654
  this.w.writeLine();
739
- // equals()
740
655
  this.runtimeSymbolsNeeded.add("jalvinEquals");
741
656
  const eqChecks = props.map((p) => `jalvinEquals(this.${p.name}, other.${p.name})`).join(" && ") || "true";
742
657
  this.w.writeIndentedLine(`equals(other: unknown): boolean {`);
@@ -746,7 +661,6 @@ class CodeGenerator {
746
661
  this.w.popIndent();
747
662
  this.w.writeIndentedLine(`}`);
748
663
  this.w.writeLine();
749
- // toString()
750
664
  const toStringParts = props.map((p) => `${p.name}=\${this.${p.name}}`).join(", ");
751
665
  this.w.writeIndentedLine(`toString(): string {`);
752
666
  this.w.pushIndent();
@@ -754,7 +668,6 @@ class CodeGenerator {
754
668
  this.w.popIndent();
755
669
  this.w.writeIndentedLine(`}`);
756
670
  this.w.writeLine();
757
- // hashCode() — djb2
758
671
  this.w.writeIndentedLine(`hashCode(): number {`);
759
672
  this.w.pushIndent();
760
673
  this.w.writeIndentedLine(`let h = 17;`);
@@ -790,7 +703,6 @@ class CodeGenerator {
790
703
  }
791
704
  }
792
705
  }
793
- // Emit the abstract base class (only non-subtype members in the body)
794
706
  this.w.writeIndentedLine(`${vis}abstract class ${decl.name}${typeParams} {`);
795
707
  this.w.pushIndent();
796
708
  this.w.writeIndentedLine(`abstract readonly __kind: string;`);
@@ -803,7 +715,6 @@ class CodeGenerator {
803
715
  this.w.writeLine();
804
716
  if (subDecls.length === 0)
805
717
  return;
806
- // Emit non-object sub-declarations at module level (before namespace)
807
718
  for (const sub of subDecls) {
808
719
  if (sub.kind === "DataClassDecl") {
809
720
  this.emitDataClassDecl(sub, sub.name);
@@ -814,12 +725,10 @@ class CodeGenerator {
814
725
  this.w.writeLine();
815
726
  }
816
727
  }
817
- // Emit namespace merging so SealedClass.SubType is accessible
818
728
  this.w.writeIndentedLine(`${vis}namespace ${decl.name} {`);
819
729
  this.w.pushIndent();
820
730
  for (const sub of subDecls) {
821
731
  if (sub.kind === "ObjectDecl") {
822
- // Singleton object — emit inline in namespace with __kind discriminant
823
732
  const superStr = sub.superTypes.length > 0
824
733
  ? this.emitSuperTypesStr(sub.superTypes)
825
734
  : ` extends ${decl.name}`;
@@ -835,7 +744,6 @@ class CodeGenerator {
835
744
  this.w.writeIndentedLine(`export const ${sub.name} = new (class${superStr} { ${membersStr} })();`);
836
745
  }
837
746
  else {
838
- // DataClassDecl / ClassDecl — already emitted at module level, re-export
839
747
  this.w.writeIndentedLine(`export { ${sub.name} };`);
840
748
  }
841
749
  }
@@ -846,12 +754,8 @@ class CodeGenerator {
846
754
  emitEnumClassDecl(decl) {
847
755
  const vis = this.exportPrefix(decl.modifiers);
848
756
  const typeParams = this.emitTypeParamsStr(decl.typeParams);
849
- // Emit the enum as a TypeScript class with static singleton instances.
850
- // This preserves the `EnumClass.ENTRY` access pattern and allows
851
- // methods/properties to be attached to entries.
852
757
  this.w.writeIndentedLine(`${vis}class ${decl.name}${typeParams} {`);
853
758
  this.w.pushIndent();
854
- // Private constructor so entries are the only instances
855
759
  if (decl.primaryConstructor && decl.primaryConstructor.params.length > 0) {
856
760
  const ctorParams = decl.primaryConstructor.params
857
761
  .map((p) => {
@@ -866,7 +770,6 @@ class CodeGenerator {
866
770
  this.w.writeIndentedLine(`private constructor(readonly name: string, readonly ordinal: number) {}`);
867
771
  }
868
772
  this.w.writeLine();
869
- // Static entry instances
870
773
  for (let i = 0; i < decl.entries.length; i++) {
871
774
  const entry = decl.entries[i];
872
775
  const args = entry.args.length > 0
@@ -876,7 +779,6 @@ class CodeGenerator {
876
779
  }
877
780
  if (decl.entries.length > 0) {
878
781
  this.w.writeLine();
879
- // values() helper
880
782
  const entryList = decl.entries.map((e) => `${decl.name}.${e.name}`).join(", ");
881
783
  this.w.writeIndentedLine(`static values(): ${decl.name}[] { return [${entryList}]; }`);
882
784
  this.w.writeIndentedLine(`static valueOf(name: string): ${decl.name} {`);
@@ -894,14 +796,12 @@ class CodeGenerator {
894
796
  this.w.popIndent();
895
797
  this.w.writeIndentedLine(`}`);
896
798
  }
897
- // ── destructuring declaration — val (a, b) = expr ─────────────────────────
898
799
  emitDestructuringDecl(decl, _memberOf) {
899
800
  const kw = decl.mutable ? "let" : "const";
900
801
  const names = decl.names.map((n) => n ?? "_").join(", ");
901
802
  const init = this.emitExpr(decl.initializer);
902
803
  this.w.writeIndentedLine(`${kw} [${names}] = ${init};`);
903
804
  }
904
- // ── interface ──────────────────────────────────────────────────────────────
905
805
  emitInterfaceDecl(decl) {
906
806
  this.emitAnnotations(decl.modifiers);
907
807
  const vis = this.exportPrefix(decl.modifiers);
@@ -919,12 +819,9 @@ class CodeGenerator {
919
819
  this.w.popIndent();
920
820
  this.w.writeIndentedLine(`}`);
921
821
  }
922
- // ── object declaration → singleton ────────────────────────────────────────
923
822
  emitObjectDecl(decl) {
924
- if (!decl.name) {
925
- // Anonymous object — emitted inline
823
+ if (!decl.name)
926
824
  return;
927
- }
928
825
  this.emitAnnotations(decl.modifiers);
929
826
  const vis = this.exportPrefix(decl.modifiers);
930
827
  const superTypes = this.emitSuperTypesStr(decl.superTypes);
@@ -934,16 +831,11 @@ class CodeGenerator {
934
831
  this.w.popIndent();
935
832
  this.w.writeIndentedLine(`})();`);
936
833
  }
937
- // ── type alias ─────────────────────────────────────────────────────────────
938
834
  emitTypeAliasDecl(decl) {
939
835
  const vis = this.exportPrefix(decl.modifiers);
940
836
  const typeParams = this.emitTypeParamsStr(decl.typeParams);
941
837
  this.w.writeIndentedLine(`${vis}type ${decl.name}${typeParams} = ${this.emitTypeRef(decl.type)};`);
942
838
  }
943
- // ── extension functions ────────────────────────────────────────────────────
944
- // Emitted as standalone free functions with receiver as explicit first param.
945
- // For class types, also patched onto the prototype for dot-call syntax.
946
- // For primitive types, call sites are rewritten via `primitiveExtensions`.
947
839
  emitExtensionFunDecl(decl) {
948
840
  const isSuspend = AST.isSuspend(decl.modifiers);
949
841
  const asyncKw = isSuspend ? "async " : "";
@@ -954,9 +846,6 @@ class CodeGenerator {
954
846
  ? `: ${this.emitTypeRef(decl.returnType)}`
955
847
  : "";
956
848
  const fnName = `__ext_${receiverType.replace(/[^a-zA-Z0-9_]/g, "_")}_${decl.name}`;
957
- // Emit as a free function: receiver is the first explicit parameter `$receiver`.
958
- // Inside the function body, `this` is rebound to `$receiver` so Jalvin's
959
- // `this.prop` references work correctly.
960
849
  this.w.writeIndentedLine(`${asyncKw}function ${fnName}${typeParams}($receiver: ${receiverType}${params ? ", " + params : ""})${retType} {`);
961
850
  this.w.pushIndent();
962
851
  if (decl.body.kind === "Block") {
@@ -975,14 +864,12 @@ class CodeGenerator {
975
864
  const simpleReceiverName = this.receiverClassName(decl.receiver);
976
865
  if (simpleReceiverName) {
977
866
  if (PRIMITIVE_TYPES.has(simpleReceiverName)) {
978
- // Register for call-site rewriting — cannot patch primitive prototypes
979
867
  if (!this.primitiveExtensions.has(simpleReceiverName)) {
980
868
  this.primitiveExtensions.set(simpleReceiverName, new Map());
981
869
  }
982
870
  this.primitiveExtensions.get(simpleReceiverName).set(decl.name, fnName);
983
871
  }
984
872
  else {
985
- // Class types — prototype monkey-patch for dot-call syntax
986
873
  this.w.writeIndentedLine(`// Extension: ${receiverType}.${decl.name}`);
987
874
  this.w.writeIndentedLine(`(${simpleReceiverName}.prototype as any).${decl.name} = function(this: ${receiverType}, ...args: unknown[]) { return (${fnName} as Function)(this, ...args); };`);
988
875
  }
@@ -995,11 +882,6 @@ class CodeGenerator {
995
882
  return ref.base.name[ref.base.name.length - 1] ?? null;
996
883
  return null;
997
884
  }
998
- /**
999
- * Maps a JType to the primitive receiver type name used as a key
1000
- * in `primitiveExtensions` (e.g. JType `{ tag: "string" }` → `"String"`).
1001
- * Returns null for non-primitive / unknown types.
1002
- */
1003
885
  jTypeToReceiverName(type) {
1004
886
  const tagToReceiverName = {
1005
887
  string: "String",
@@ -1019,19 +901,16 @@ class CodeGenerator {
1019
901
  m.name === "invoke" &&
1020
902
  m.modifiers.modifiers.includes("operator"));
1021
903
  }
1022
- // ── property declaration ───────────────────────────────────────────────────
1023
904
  emitPropertyDecl(decl, member, isLocal = false) {
1024
905
  this.emitAnnotations(decl.modifiers);
1025
906
  const vis = member ? this.visibilityPrefix(decl.modifiers) : isLocal ? "" : this.exportPrefix(decl.modifiers);
1026
907
  const isConst = decl.modifiers.modifiers.includes("const");
1027
908
  const isLateinit = decl.modifiers.modifiers.includes("lateinit");
1028
- // `const val` → `const` at module level; inside classes treated as `static readonly`
1029
909
  const kw = member
1030
910
  ? (decl.mutable ? "" : "readonly ")
1031
911
  : (isConst ? "const " : decl.mutable ? "let " : "const ");
1032
912
  const type = decl.type && this.opts.emitTypes ? `: ${this.emitTypeRef(decl.type)}` : "";
1033
913
  if (decl.delegate) {
1034
- // Delegated property — wrap in a getter/setter pair using the delegate
1035
914
  this.runtimeSymbolsNeeded.add("delegate");
1036
915
  const delegateExpr = this.emitExpr(decl.delegate);
1037
916
  if (member) {
@@ -1041,28 +920,22 @@ class CodeGenerator {
1041
920
  }
1042
921
  }
1043
922
  else {
1044
- // Non-member delegate: resolve via delegate().getValue() so the identifier
1045
- // holds the delegated value, not the raw delegate wrapper object.
1046
923
  this.w.writeIndentedLine(`${kw}${decl.name}${type} = delegate(${delegateExpr}, "${decl.name}", null).getValue();`);
1047
924
  }
1048
925
  return;
1049
926
  }
1050
- const init = decl.initializer ? ` = ${this.emitExpr(decl.initializer)}` : (isLateinit ? "" : "");
927
+ const init = decl.initializer ? ` = ${this.emitExpr(decl.initializer)}` : "";
1051
928
  if (member && !decl.getter) {
1052
- // Skip backing field when a getter is defined — TypeScript forbids both
1053
- // `readonly name: T` and `get name()` with the same name in a class.
1054
929
  const modStr = isConst ? "static readonly " : decl.mutable ? "" : "readonly ";
1055
930
  this.w.writeIndentedLine(`${vis}${modStr}${decl.name}${type}${init};`);
1056
931
  }
1057
932
  else if (!member) {
1058
933
  this.w.writeIndentedLine(`${vis}${kw}${decl.name}${type}${init};`);
1059
934
  }
1060
- if (decl.getter) {
935
+ if (decl.getter)
1061
936
  this.emitPropertyAccessor("get", decl.name, decl.getter, member, type);
1062
- }
1063
- if (decl.setter) {
937
+ if (decl.setter)
1064
938
  this.emitPropertyAccessor("set", decl.name, decl.setter, member, type);
1065
- }
1066
939
  }
1067
940
  emitPropertyAccessor(kind, name, acc, member, type) {
1068
941
  const vis = this.visibilityPrefix(acc.modifiers);
@@ -1082,13 +955,11 @@ class CodeGenerator {
1082
955
  this.w.popIndent();
1083
956
  this.w.writeIndentedLine(`}`);
1084
957
  }
1085
- // ── class body ─────────────────────────────────────────────────────────────
1086
958
  emitClassBody(body) {
1087
959
  const old = this.isInClass;
1088
960
  this.isInClass = true;
1089
961
  try {
1090
962
  for (const member of body.members) {
1091
- // InitBlocks are emitted inside the constructor by emitPrimaryConstructorProps
1092
963
  if (member.kind === "InitBlock")
1093
964
  continue;
1094
965
  this.emitClassMember(member);
@@ -1128,7 +999,6 @@ class CodeGenerator {
1128
999
  case "CompanionObject":
1129
1000
  this.emitCompanionObject(member);
1130
1001
  break;
1131
- case "InitBlock": /* handled in constructor */ break;
1132
1002
  case "SecondaryConstructor":
1133
1003
  this.emitSecondaryConstructor(member);
1134
1004
  break;
@@ -1145,16 +1015,13 @@ class CodeGenerator {
1145
1015
  this.w.writeIndentedLine(`${ro}${p.name}${type};`);
1146
1016
  }
1147
1017
  }
1148
- // Constructor
1149
1018
  const ctorParams = params.map((p) => {
1150
- const ro = p.propertyKind === "val" ? "readonly " : p.propertyKind === "var" ? "" : "";
1151
1019
  const type = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
1152
1020
  const def = p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "";
1153
1021
  return `${p.name}${type}${def}`;
1154
1022
  });
1155
1023
  this.w.writeIndentedLine(`constructor(${ctorParams.join(", ")}) {`);
1156
1024
  this.w.pushIndent();
1157
- // super() call must be the first statement if there is a supertype.
1158
1025
  if (superDelegateArgs !== null && superDelegateArgs !== undefined) {
1159
1026
  const superArgsStr = superDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
1160
1027
  this.w.writeIndentedLine(`super(${superArgsStr});`);
@@ -1164,17 +1031,13 @@ class CodeGenerator {
1164
1031
  this.w.writeIndentedLine(`this.${p.name} = ${p.name};`);
1165
1032
  }
1166
1033
  }
1167
- // Emit init{} blocks inside the constructor, in declaration order
1168
- for (const ib of (initBlocks ?? [])) {
1034
+ for (const ib of (initBlocks ?? []))
1169
1035
  this.emitBlock(ib.body);
1170
- }
1171
1036
  this.w.popIndent();
1172
1037
  this.w.writeIndentedLine(`}`);
1173
1038
  this.w.writeLine();
1174
1039
  }
1175
1040
  emitCompanionObject(co) {
1176
- // Emit companion object members as `static` members of the outer class so that
1177
- // `MyClass.factoryMethod()` works directly (standard semantics).
1178
1041
  for (const member of co.body.members) {
1179
1042
  if (member.kind === "FunDecl") {
1180
1043
  const vis = this.visibilityPrefix(member.modifiers);
@@ -1219,11 +1082,9 @@ class CodeGenerator {
1219
1082
  this.w.popIndent();
1220
1083
  this.w.writeIndentedLine(`}`);
1221
1084
  }
1222
- // ── Statements ─────────────────────────────────────────────────────────────
1223
1085
  emitBlock(block) {
1224
- for (const stmt of block.statements) {
1086
+ for (const stmt of block.statements)
1225
1087
  this.emitStmt(stmt);
1226
- }
1227
1088
  }
1228
1089
  emitStmt(stmt) {
1229
1090
  switch (stmt.kind) {
@@ -1312,8 +1173,6 @@ class CodeGenerator {
1312
1173
  }
1313
1174
  }
1314
1175
  emitWhenStmt(stmt) {
1315
- // when(subject) { is Foo -> ... else -> ... }
1316
- // Compiles to a series of if/else-if chains
1317
1176
  const subjectVar = stmt.subject ? "__when_subject__" : null;
1318
1177
  if (stmt.subject) {
1319
1178
  const binding = stmt.subject.binding ?? subjectVar;
@@ -1330,12 +1189,10 @@ class CodeGenerator {
1330
1189
  }
1331
1190
  first = false;
1332
1191
  this.w.pushIndent();
1333
- if (branch.body.kind === "Block") {
1192
+ if (branch.body.kind === "Block")
1334
1193
  this.emitBlock(branch.body);
1335
- }
1336
- else {
1194
+ else
1337
1195
  this.w.writeIndentedLine(`${this.emitExpr(branch.body)};`);
1338
- }
1339
1196
  this.w.popIndent();
1340
1197
  }
1341
1198
  this.w.writeIndentedLine(`}`);
@@ -1343,8 +1200,6 @@ class CodeGenerator {
1343
1200
  emitWhenCondition(cond, subject) {
1344
1201
  switch (cond.kind) {
1345
1202
  case "WhenIsCondition": {
1346
- // Qualified name (e.g. UiState.Loading) → use __kind discriminant
1347
- // Single name (e.g. BibiError) → use instanceof
1348
1203
  const isQualified = cond.type.kind === "SimpleTypeRef" && cond.type.name.length > 1;
1349
1204
  const check = isQualified
1350
1205
  ? `(${subject} as any).__kind === "${cond.type.kind === "SimpleTypeRef" ? cond.type.name[cond.type.name.length - 1] : ""}"`
@@ -1374,7 +1229,6 @@ class CodeGenerator {
1374
1229
  }
1375
1230
  else {
1376
1231
  const { key, value } = stmt.binding;
1377
- // Use .entries() for JS Map (Map<K,V>), Object.entries() for plain objects
1378
1232
  const iterableType = this.typeMap.get(stmt.iterable);
1379
1233
  const isMap = iterableType?.tag === "class" && iterableType.name === "Map";
1380
1234
  const entries = isMap ? `${iter}.entries()` : `Object.entries(${iter})`;
@@ -1393,9 +1247,6 @@ class CodeGenerator {
1393
1247
  for (const c of stmt.catches) {
1394
1248
  this.w.writeIndentedLine(`} catch (${c.name}: unknown) {`);
1395
1249
  this.w.pushIndent();
1396
- // Narrow type — always emit the guard so catch clauses are type-safe
1397
- // regardless of the emitTypes option (which controls explicit type annotations,
1398
- // not runtime type checks).
1399
1250
  this.w.writeIndentedLine(`if (!(${c.name} instanceof ${this.emitTypeRef(c.type)})) throw ${c.name};`);
1400
1251
  this.emitBlock(c.body);
1401
1252
  this.w.popIndent();
@@ -1408,7 +1259,6 @@ class CodeGenerator {
1408
1259
  }
1409
1260
  this.w.writeIndentedLine(`}`);
1410
1261
  }
1411
- // ── Expressions ────────────────────────────────────────────────────────────
1412
1262
  emitExpr(expr) {
1413
1263
  switch (expr.kind) {
1414
1264
  case "IntLiteralExpr": return String(expr.value);
@@ -1418,17 +1268,12 @@ class CodeGenerator {
1418
1268
  case "BooleanLiteralExpr": return String(expr.value);
1419
1269
  case "NullLiteralExpr": return "null";
1420
1270
  case "StringLiteralExpr":
1421
- return expr.raw
1422
- ? `\`${expr.value}\``
1423
- : JSON.stringify(expr.value);
1271
+ return expr.raw ? `\`${expr.value}\`` : JSON.stringify(expr.value);
1424
1272
  case "StringTemplateExpr":
1425
1273
  return this.emitStringTemplate(expr);
1426
1274
  case "NameExpr":
1427
- // Ensure Int/Long companion objects are imported from the runtime
1428
- if (expr.name === "Int" || expr.name === "Long") {
1275
+ if (expr.name === "Int" || expr.name === "Long")
1429
1276
  this.runtimeSymbolsNeeded.add(expr.name);
1430
- }
1431
- // Unit singleton emits as undefined (void semantics)
1432
1277
  if (expr.name === "Unit")
1433
1278
  return "undefined";
1434
1279
  return expr.name;
@@ -1444,34 +1289,23 @@ class CodeGenerator {
1444
1289
  if (opMethod) {
1445
1290
  const l = this.emitExpr(expr.left);
1446
1291
  const r = this.emitExpr(expr.right);
1447
- // `compareTo` overloads: wrap result in a comparison against 0
1448
- if (opMethod === "compareTo") {
1449
- const cmpOp = expr.op;
1450
- return `(${l}.compareTo(${r}) ${cmpOp} 0)`;
1451
- }
1452
- // `contains` overloads: `element in collection` → `collection.contains(element)`
1453
- if (opMethod === "contains") {
1292
+ if (opMethod === "compareTo")
1293
+ return `(${l}.compareTo(${r}) ${expr.op} 0)`;
1294
+ if (opMethod === "contains")
1454
1295
  return `${r}.contains(${l})`;
1455
- }
1456
- if (opMethod === "!contains") {
1296
+ if (opMethod === "!contains")
1457
1297
  return `!${r}.contains(${l})`;
1458
- }
1459
- // Generic operator overload: emit as method call `left.method(right)`
1460
1298
  return `${l}.${opMethod}(${r})`;
1461
1299
  }
1462
- // `==` → structural equality, `!=` → negation
1463
1300
  if (expr.op === "==" || expr.op === "!=") {
1464
1301
  this.runtimeSymbolsNeeded.add("jalvinEquals");
1465
1302
  const eq = `jalvinEquals(${this.emitExpr(expr.left)}, ${this.emitExpr(expr.right)})`;
1466
1303
  return expr.op === "==" ? eq : `!${eq}`;
1467
1304
  }
1468
- // `in` / `!in` without a user-defined operator: use JS `in` / array includes
1469
- if (expr.op === "in") {
1305
+ if (expr.op === "in")
1470
1306
  return `${this.emitExpr(expr.right)}.includes?.(${this.emitExpr(expr.left)}) ?? (${this.emitExpr(expr.left)} in ${this.emitExpr(expr.right)})`;
1471
- }
1472
- if (expr.op === "!in") {
1307
+ if (expr.op === "!in")
1473
1308
  return `!(${this.emitExpr(expr.right)}.includes?.(${this.emitExpr(expr.left)}) ?? (${this.emitExpr(expr.left)} in ${this.emitExpr(expr.right)}))`;
1474
- }
1475
1309
  return `(${this.emitExpr(expr.left)} ${this.binaryOpStr(expr.op)} ${this.emitExpr(expr.right)})`;
1476
1310
  }
1477
1311
  case "AssignExpr":
@@ -1482,124 +1316,81 @@ class CodeGenerator {
1482
1316
  return expr.prefix
1483
1317
  ? `${expr.op}${this.emitExpr(expr.target)}`
1484
1318
  : `${this.emitExpr(expr.target)}${expr.op}`;
1485
- case "MemberExpr":
1486
- return `${this.emitExpr(expr.target)}.${expr.member}`;
1487
- case "SafeMemberExpr":
1488
- return `${this.emitExpr(expr.target)}?.${expr.member}`;
1489
- case "IndexExpr":
1490
- return `${this.emitExpr(expr.target)}[${this.emitExpr(expr.index)}]`;
1319
+ case "MemberExpr": return `${this.emitExpr(expr.target)}.${expr.member}`;
1320
+ case "SafeMemberExpr": return `${this.emitExpr(expr.target)}?.${expr.member}`;
1321
+ case "IndexExpr": return `${this.emitExpr(expr.target)}[${this.emitExpr(expr.index)}]`;
1491
1322
  case "NotNullExpr":
1492
- // Runtime check
1493
1323
  this.runtimeSymbolsNeeded.add("notNull");
1494
1324
  return `notNull(${this.emitExpr(expr.expr)})`;
1495
1325
  case "SafeCallExpr": {
1496
- // x?.() — safe invocation of a nullable function value
1497
1326
  const callee = this.emitExpr(expr.callee);
1498
- const restArgs = [];
1499
- for (const a of expr.args) {
1500
- restArgs.push(a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
1501
- }
1327
+ const restArgs = expr.args.map((a) => a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
1502
1328
  if (expr.trailingLambda)
1503
1329
  restArgs.push(this.emitLambdaExpr(expr.trailingLambda));
1504
1330
  return `${callee}?.(${restArgs.join(", ")})`;
1505
1331
  }
1506
- case "ElvisExpr": {
1507
- // left ?? right (null coalescing)
1508
- return `(${this.emitExpr(expr.left)} ?? ${this.emitExpr(expr.right)})`;
1509
- }
1510
- case "CallExpr":
1511
- return this.emitCallExpr(expr);
1512
- case "LambdaExpr":
1513
- return this.emitLambdaExpr(expr);
1514
- case "IfExpr":
1515
- return this.emitIfExpr(expr);
1516
- case "WhenExpr":
1517
- return this.emitWhenExpr(expr);
1518
- case "TryCatchExpr":
1519
- return this.emitTryCatchExpr(expr);
1520
- case "TypeCheckExpr":
1521
- return `${expr.negated ? "!(" : ""}${this.emitExpr(expr.expr)} instanceof ${this.emitTypeRef(expr.type)}${expr.negated ? ")" : ""}`;
1522
- case "TypeCastExpr":
1523
- // Unsafe cast — emit as `(expr as Type)` which is stripped at runtime
1524
- return `(${this.emitExpr(expr.expr)} as unknown as ${this.emitTypeRef(expr.type)})`;
1525
- case "SafeCastExpr": {
1332
+ case "ElvisExpr": return `(${this.emitExpr(expr.left)} ?? ${this.emitExpr(expr.right)})`;
1333
+ case "CallExpr": return this.emitCallExpr(expr);
1334
+ case "LambdaExpr": return this.emitLambdaExpr(expr);
1335
+ case "IfExpr": return this.emitIfExpr(expr);
1336
+ case "WhenExpr": return this.emitWhenExpr(expr);
1337
+ case "TryCatchExpr": return this.emitTryCatchExpr(expr);
1338
+ case "TypeCheckExpr": return `${expr.negated ? "!(" : ""}${this.emitExpr(expr.expr)} instanceof ${this.emitTypeRef(expr.type)}${expr.negated ? ")" : ""}`;
1339
+ case "TypeCastExpr": return `(${this.emitExpr(expr.expr)} as unknown as ${this.emitTypeRef(expr.type)})`;
1340
+ case "SafeCastExpr":
1526
1341
  this.runtimeSymbolsNeeded.add("safeCast");
1527
1342
  return `safeCast(${this.emitExpr(expr.expr)}, ${this.emitTypeRef(expr.type)})`;
1528
- }
1529
- case "RangeExpr": {
1343
+ case "RangeExpr":
1530
1344
  this.runtimeSymbolsNeeded.add("range");
1531
1345
  return `range(${this.emitExpr(expr.from)}, ${this.emitExpr(expr.to)}, ${expr.inclusive})`;
1532
- }
1533
1346
  case "LaunchExpr": {
1534
- // fire-and-forget → void IIFE
1535
1347
  const stmts = this.captureBlock(expr.body);
1536
1348
  return `(async () => { ${stmts} })()`;
1537
1349
  }
1538
1350
  case "AsyncExpr": {
1539
- // returns Promise<T>
1540
1351
  const stmts = this.captureBlock(expr.body);
1541
1352
  return `(async () => { ${stmts} })()`;
1542
1353
  }
1543
- case "CollectionLiteralExpr":
1544
- return this.emitCollectionLiteral(expr);
1545
- case "ObjectExpr":
1546
- return this.emitObjectExpr(expr);
1354
+ case "CollectionLiteralExpr": return this.emitCollectionLiteral(expr);
1355
+ case "ObjectExpr": return this.emitObjectExpr(expr);
1547
1356
  case "ReturnExpr": {
1548
1357
  const val = expr.value ? ` ${this.emitExpr(expr.value)}` : "";
1549
1358
  return `((() => { return${val}; })())`;
1550
1359
  }
1551
1360
  case "BreakExpr": return "undefined /* break */";
1552
1361
  case "ContinueExpr": return "undefined /* continue */";
1553
- case "JsxElement":
1554
- return this.emitJsxElement(expr);
1555
- default:
1556
- return "undefined";
1362
+ case "JsxElement": return this.emitJsxElement(expr);
1363
+ default: return "undefined";
1557
1364
  }
1558
1365
  }
1559
- // ── JSX ──────────────────────────────────────────────────────────────────
1560
1366
  emitJsxElement(expr) {
1367
+ this.runtimeSymbolsNeeded.add("jalvinCreateElement");
1561
1368
  const attrsStr = expr.attrs.length > 0
1562
- ? " " + expr.attrs.map((a) => this.emitJsxAttr(a)).join(" ")
1563
- : "";
1564
- if (expr.children.length === 0) {
1565
- return `<${expr.tag}${attrsStr} />`;
1566
- }
1567
- const children = expr.children.map((c) => this.emitJsxChild(c)).join("");
1568
- return `<${expr.tag}${attrsStr}>${children}</${expr.tag}>`;
1369
+ ? "{ " + expr.attrs.map((a) => this.emitJsxAttr(a)).join(", ") + " }"
1370
+ : "{}";
1371
+ const childrenArr = expr.children.length > 0
1372
+ ? "[" + expr.children.map((c) => this.emitJsxChild(c)).join(", ") + "]"
1373
+ : "[]";
1374
+ return `jalvinCreateElement(${JSON.stringify(expr.tag)}, ${attrsStr}, ${childrenArr})`;
1569
1375
  }
1570
1376
  emitJsxAttr(attr) {
1571
- // Map HTML attribute names to React prop names
1572
- const name = attr.name === "class" ? "className"
1573
- : attr.name === "for" ? "htmlFor"
1574
- : attr.name;
1575
- if (attr.value === null)
1576
- return name;
1577
- if (typeof attr.value === "string")
1578
- return `${name}="${attr.value}"`;
1579
- return `${name}={${this.emitExpr(attr.value)}}`;
1377
+ const name = attr.name === "class" ? "className" : attr.name === "for" ? "htmlFor" : attr.name;
1378
+ const val = attr.value === null ? "true" : typeof attr.value === "string" ? JSON.stringify(attr.value) : this.emitExpr(attr.value);
1379
+ return `${name}: ${val}`;
1580
1380
  }
1581
1381
  emitJsxChild(child) {
1582
1382
  switch (child.kind) {
1583
1383
  case "JsxElement": return this.emitJsxElement(child);
1584
- case "JsxExprChild": return `{${this.emitExpr(child.expr)}}`;
1585
- case "JsxTextChild": return child.text;
1384
+ case "JsxExprChild": return this.emitExpr(child.expr);
1385
+ case "JsxTextChild": return `document.createTextNode(${JSON.stringify(child.text)})`;
1586
1386
  }
1587
1387
  }
1588
1388
  emitStringTemplate(expr) {
1589
- const inner = expr.parts.map((p) => {
1590
- if (p.kind === "LiteralPart") {
1591
- return p.value.replace(/`/g, "\\`").replace(/\\/g, "\\\\");
1592
- }
1593
- return `\${${this.emitExpr(p.expr)}}`;
1594
- }).join("");
1389
+ const inner = expr.parts.map((p) => p.kind === "LiteralPart" ? p.value.replace(/`/g, "\\`").replace(/\\/g, "\\\\") : `\${${this.emitExpr(p.expr)}}`).join("");
1595
1390
  return `\`${inner}\``;
1596
1391
  }
1597
1392
  emitCallExpr(expr) {
1598
- // Rewrite infix numeric extension calls that JS numbers don't natively have:
1599
- // `a.downTo(b)` → `downTo(a, b)` `a.until(b)` → `range(a, b, false)`
1600
- // `range.step(n)` → `step(range, n)`
1601
- if (expr.callee.kind === "MemberExpr" &&
1602
- (expr.callee.member === "downTo" || expr.callee.member === "until" || expr.callee.member === "step")) {
1393
+ if (expr.callee.kind === "MemberExpr" && (expr.callee.member === "downTo" || expr.callee.member === "until" || expr.callee.member === "step")) {
1603
1394
  const obj = this.emitExpr(expr.callee.target);
1604
1395
  const arg = expr.args.length > 0 ? this.emitExpr(expr.args[0].value) : "0";
1605
1396
  if (expr.callee.member === "downTo") {
@@ -1615,51 +1406,39 @@ class CodeGenerator {
1615
1406
  return `step(${obj}, ${arg})`;
1616
1407
  }
1617
1408
  }
1618
- // Rewrite calls on primitive-receiver extension functions.
1619
- // `str.truncate(30)` where `String.truncate` is a user extension → `__ext_string_truncate(str, 30)`
1620
1409
  if (expr.callee.kind === "MemberExpr") {
1621
1410
  const scopeMember = expr.callee.member;
1622
- // Scope function call rewriting: `x.let { ... }` → `let_(x, ...)`
1623
- // `x.apply { ... }` `apply(x, function(this:any) { ... })`
1624
- if (scopeMember === "let" || scopeMember === "also" || scopeMember === "apply" || scopeMember === "run" || scopeMember === "takeIf" || scopeMember === "takeUnless") {
1625
- const receiver = this.emitExpr(expr.callee.target);
1626
- const lambda = expr.trailingLambda ??
1627
- (expr.args.length === 1 && expr.args[0].value.kind === "LambdaExpr"
1628
- ? expr.args[0].value
1629
- : null);
1630
- if (lambda) {
1631
- if (scopeMember === "let") {
1632
- this.runtimeSymbolsNeeded.add("let_");
1633
- return `let_(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1634
- }
1635
- if (scopeMember === "also") {
1636
- this.runtimeSymbolsNeeded.add("also");
1637
- return `also(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1638
- }
1639
- if (scopeMember === "apply") {
1640
- this.runtimeSymbolsNeeded.add("apply");
1641
- const body = this.captureBlockStatements(lambda.body);
1642
- return `apply(${receiver}, function(this: any) { ${body} })`;
1643
- }
1644
- if (scopeMember === "run") {
1645
- this.runtimeSymbolsNeeded.add("run_");
1646
- const body = this.captureBlockStatements(lambda.body);
1647
- return `run_(${receiver}, function(this: any) { ${body} })`;
1648
- }
1649
- if (scopeMember === "takeIf") {
1650
- this.runtimeSymbolsNeeded.add("takeIf");
1651
- return `takeIf(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1652
- }
1653
- if (scopeMember === "takeUnless") {
1654
- this.runtimeSymbolsNeeded.add("takeUnless");
1655
- return `takeUnless(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1656
- }
1411
+ const receiver = this.emitExpr(expr.callee.target);
1412
+ const lambda = expr.trailingLambda ?? (expr.args.length === 1 && expr.args[0].value.kind === "LambdaExpr" ? expr.args[0].value : null);
1413
+ if (lambda) {
1414
+ if (scopeMember === "let") {
1415
+ this.runtimeSymbolsNeeded.add("let_");
1416
+ return `let_(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1417
+ }
1418
+ if (scopeMember === "also") {
1419
+ this.runtimeSymbolsNeeded.add("also");
1420
+ return `also(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1421
+ }
1422
+ if (scopeMember === "apply") {
1423
+ this.runtimeSymbolsNeeded.add("apply");
1424
+ const body = this.captureBlockStatements(lambda.body);
1425
+ return `apply(${receiver}, function(this: any) { ${body} })`;
1426
+ }
1427
+ if (scopeMember === "run") {
1428
+ this.runtimeSymbolsNeeded.add("run_");
1429
+ const body = this.captureBlockStatements(lambda.body);
1430
+ return `run_(${receiver}, function(this: any) { ${body} })`;
1431
+ }
1432
+ if (scopeMember === "takeIf") {
1433
+ this.runtimeSymbolsNeeded.add("takeIf");
1434
+ return `takeIf(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1435
+ }
1436
+ if (scopeMember === "takeUnless") {
1437
+ this.runtimeSymbolsNeeded.add("takeUnless");
1438
+ return `takeUnless(${receiver}, ${this.emitLambdaExpr(lambda)})`;
1657
1439
  }
1658
1440
  }
1659
- // `with(x) { ... }` is a free function handled via seedBuiltins; but rewrite if emitted as member
1660
1441
  }
1661
- // Rewrite calls on primitive-receiver extension functions.
1662
- // `str.truncate(30)` where `String.truncate` is a user extension → `__ext_string_truncate(str, 30)`
1663
1442
  if (expr.callee.kind === "MemberExpr") {
1664
1443
  const receiverType = this.typeMap.get(expr.callee.target);
1665
1444
  const memberName = expr.callee.member;
@@ -1679,113 +1458,69 @@ class CodeGenerator {
1679
1458
  }
1680
1459
  }
1681
1460
  const callee = this.emitExpr(expr.callee);
1682
- const typeArgs = expr.typeArgs.length > 0
1683
- ? `<${expr.typeArgs.map((t) => t.star ? "*" : this.emitTypeRef(t.type)).join(", ")}>`
1684
- : "";
1685
- // Rewrite top-level `with(obj) { ... }` → `with_(obj, function(this: any) { ... })`
1686
- if (expr.callee.kind === "NameExpr" && expr.callee.name === "with" &&
1687
- (expr.trailingLambda || (expr.args.length === 2 && expr.args[1].value.kind === "LambdaExpr"))) {
1461
+ const typeArgs = expr.typeArgs.length > 0 ? `<${expr.typeArgs.map((t) => t.star ? "*" : this.emitTypeRef(t.type)).join(", ")}>` : "";
1462
+ if (expr.callee.kind === "NameExpr" && expr.callee.name === "with" && (expr.trailingLambda || (expr.args.length === 2 && expr.args[1].value.kind === "LambdaExpr"))) {
1688
1463
  const obj = expr.args.length > 0 ? this.emitExpr(expr.args[0].value) : "undefined";
1689
- const lambda = expr.trailingLambda ??
1690
- expr.args[1].value;
1464
+ const lambda = expr.trailingLambda ?? expr.args[1].value;
1691
1465
  this.runtimeSymbolsNeeded.add("with_");
1692
1466
  const body = this.captureBlockStatements(lambda.body);
1693
1467
  return `with_(${obj}, function(this: any) { ${body} })`;
1694
1468
  }
1695
- // Rewrite top-level `run { ... }` (no receiver) just call the lambda
1696
- if (expr.callee.kind === "NameExpr" && expr.callee.name === "run" &&
1697
- expr.args.length === 0 && expr.trailingLambda) {
1469
+ if (expr.callee.kind === "NameExpr" && expr.callee.name === "run" && expr.args.length === 0 && expr.trailingLambda) {
1698
1470
  return `(${this.emitLambdaExpr(expr.trailingLambda)})()`;
1699
1471
  }
1700
- // Handle named arguments by reordering them to match positional parameters
1701
1472
  const calleeType = this.typeMap.get(expr.callee);
1702
- // Component call: Column(modifier = ...) { ... } → Column({ modifier: ... }, [children])
1703
- // Also catches star-imported @jalvin/ui primitives (Row, Button, etc.) whose type is T_UNKNOWN
1704
- //
1705
- // Skip the component path when the typechecker resolved a func type with explicit paramNames.
1706
- // That indicates a regular function (fun) or a built-in hook — both use positional calling.
1707
- // component fun declarations intentionally omit paramNames from their func type.
1708
1473
  const isKnownPositionalFunc = calleeType?.tag === "func" && calleeType.paramNames !== undefined;
1709
1474
  let isComponentLike = false;
1710
1475
  if (expr.callee.kind === "NameExpr") {
1711
- isComponentLike = this.allComponentLikeNames.has(expr.callee.name) ||
1712
- (this.hasUiStarImport && (!calleeType || calleeType.tag === "unknown") && /^[A-Z]/.test(expr.callee.name));
1476
+ isComponentLike = this.allComponentLikeNames.has(expr.callee.name) || (this.hasUiStarImport && (!calleeType || calleeType.tag === "unknown") && /^[A-Z]/.test(expr.callee.name));
1713
1477
  }
1714
1478
  else if (expr.callee.kind === "MemberExpr" || expr.callee.kind === "SafeMemberExpr") {
1715
- const member = expr.callee.member;
1716
- isComponentLike = this.allComponentLikeNames.has(member);
1479
+ isComponentLike = this.allComponentLikeNames.has(expr.callee.member);
1717
1480
  }
1718
- if (!isKnownPositionalFunc && isComponentLike) {
1481
+ if (!isKnownPositionalFunc && isComponentLike)
1719
1482
  return this.emitComponentCall(expr);
1720
- }
1721
1483
  let finalArgs = [];
1722
1484
  if (calleeType && calleeType.tag === "func" && calleeType.paramNames) {
1723
1485
  const paramNames = calleeType.paramNames;
1724
1486
  const argsByName = new Map();
1725
1487
  const positionalArgs = [];
1726
1488
  for (const arg of expr.args) {
1727
- if (arg.name) {
1489
+ if (arg.name)
1728
1490
  argsByName.set(arg.name, arg.spread ? `...${this.emitExpr(arg.value)}` : this.emitExpr(arg.value));
1729
- }
1730
- else {
1491
+ else
1731
1492
  positionalArgs.push(arg.spread ? `...${this.emitExpr(arg.value)}` : this.emitExpr(arg.value));
1732
- }
1733
1493
  }
1734
- // Reconstruct args based on paramNames
1735
1494
  for (let i = 0; i < paramNames.length; i++) {
1736
1495
  const name = paramNames[i];
1737
- if (argsByName.has(name)) {
1496
+ if (argsByName.has(name))
1738
1497
  finalArgs.push(argsByName.get(name));
1739
- }
1740
- else if (i < positionalArgs.length) {
1498
+ else if (i < positionalArgs.length)
1741
1499
  finalArgs.push(positionalArgs[i]);
1742
- }
1743
- else {
1744
- // Missing argument - JS default value logic will handle it if we pass undefined
1500
+ else
1745
1501
  finalArgs.push("undefined");
1746
- }
1747
1502
  }
1748
- // Handle trailing positional args if any
1749
- if (positionalArgs.length > paramNames.length) {
1503
+ if (positionalArgs.length > paramNames.length)
1750
1504
  finalArgs.push(...positionalArgs.slice(paramNames.length));
1751
- }
1752
1505
  }
1753
1506
  else {
1754
- // Fallback for types we don't know param names for (like constructors or any)
1755
- finalArgs = expr.args.map((a) => {
1756
- return a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value);
1757
- });
1507
+ finalArgs = expr.args.map((a) => a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
1758
1508
  }
1759
- if (expr.trailingLambda) {
1509
+ if (expr.trailingLambda)
1760
1510
  finalArgs.push(this.emitLambdaExpr(expr.trailingLambda));
1761
- }
1762
- // If the callee is a class instance (not a constructor / function), emit `.invoke(args)`
1763
- if (calleeType &&
1764
- calleeType.tag === "class" &&
1765
- calleeType.decl &&
1766
- this.classHasInvokeOperator(calleeType.decl)) {
1511
+ if (calleeType && calleeType.tag === "class" && calleeType.decl && this.classHasInvokeOperator(calleeType.decl)) {
1767
1512
  return `${callee}.invoke(${finalArgs.join(", ")})`;
1768
1513
  }
1769
- // Constructor calls: class names start with uppercase by convention
1770
- if (this.isConstructorCall(expr.callee)) {
1514
+ if (this.isConstructorCall(expr.callee))
1771
1515
  return `new ${callee}${typeArgs}(${finalArgs.join(", ")})`;
1772
- }
1773
1516
  return `${callee}${typeArgs}(${finalArgs.join(", ")})`;
1774
1517
  }
1775
- /**
1776
- * For each non-scoped local import (e.g. `import src.views.MoveCounterView.MoveCounter`),
1777
- * attempt to load and parse the corresponding .jalvin source file. If the named symbol is
1778
- * a `component fun`, register it in `userComponentNames` and record its param names so that
1779
- * call sites can emit the correct props-object invocation.
1780
- */
1781
1518
  resolveLocalComponentImports(program, sourceRoot) {
1782
1519
  this.localStarExports.clear();
1783
1520
  for (const imp of program.imports) {
1784
- // Skip scoped packages (@org/pkg)
1785
1521
  if (imp.path[0]?.startsWith("@"))
1786
1522
  continue;
1787
1523
  if (imp.star) {
1788
- // Local wildcard import: import src.module.* — load all exports from src/module.jalvin
1789
1524
  if (imp.path.length < 1)
1790
1525
  continue;
1791
1526
  const candidatePath = nodePath.join(sourceRoot, imp.path.join("/") + ".jalvin");
@@ -1823,16 +1558,12 @@ class CodeGenerator {
1823
1558
  }
1824
1559
  }
1825
1560
  }
1826
- if (exportedNames.length > 0) {
1561
+ if (exportedNames.length > 0)
1827
1562
  this.localStarExports.set(moduleSpecifier, exportedNames);
1828
- }
1829
- }
1830
- catch {
1831
- // Silently skip if the file cannot be read or parsed
1832
1563
  }
1564
+ catch { }
1833
1565
  continue;
1834
1566
  }
1835
- // Named import: last path segment is the symbol, preceding segments form the file path
1836
1567
  if (imp.path.length < 2)
1837
1568
  continue;
1838
1569
  const rawSymbolName = imp.path[imp.path.length - 1];
@@ -1850,7 +1581,6 @@ class CodeGenerator {
1850
1581
  const ast = (0, parser_js_1.parse)(tokens, candidatePath, diag, source);
1851
1582
  if (diag.hasErrors)
1852
1583
  continue;
1853
- // Look for a top-level ComponentDecl OR ClassDecl matching the imported symbol name
1854
1584
  const decl = ast.declarations.find((d) => ((d.kind === "ComponentDecl" || d.kind === "ClassDecl") && d.name === rawSymbolName));
1855
1585
  if (decl?.kind === "ComponentDecl") {
1856
1586
  this.userComponentNames.add(symbolName);
@@ -1859,7 +1589,6 @@ class CodeGenerator {
1859
1589
  this.hasComponents = true;
1860
1590
  }
1861
1591
  else if (decl?.kind === "ClassDecl" && decl.body) {
1862
- // If a class was imported, register its member components
1863
1592
  for (const m of decl.body.members) {
1864
1593
  if (m.kind === "ComponentDecl") {
1865
1594
  this.userComponentNames.add(m.name);
@@ -1870,594 +1599,219 @@ class CodeGenerator {
1870
1599
  }
1871
1600
  }
1872
1601
  }
1873
- catch {
1874
- // Silently skip if the file cannot be read or parsed
1875
- }
1602
+ catch { }
1876
1603
  }
1877
1604
  }
1878
- /** Returns true if a call expression's callee looks like a class constructor.
1879
- * In Jalvin, class names always start with an uppercase letter. */
1880
1605
  isConstructorCall(calleeExpr) {
1881
1606
  if (calleeExpr.kind === "NameExpr") {
1882
- // Names in allComponentLikeNames are factory functions (UI primitives, components), not constructors
1883
1607
  if (this.allComponentLikeNames.has(calleeExpr.name))
1884
1608
  return false;
1885
- // Class names start with uppercase; function names start with lowercase
1886
1609
  return /^[A-Z]/.test(calleeExpr.name);
1887
1610
  }
1888
1611
  if (calleeExpr.kind === "MemberExpr") {
1889
- // e.g. UiState.Success(data), Discount.Percentage(0.1)
1890
1612
  if (this.allComponentLikeNames.has(calleeExpr.member))
1891
1613
  return false;
1892
1614
  return /^[A-Z]/.test(calleeExpr.member);
1893
1615
  }
1894
1616
  return false;
1895
1617
  }
1896
- /** Returns true if a name refers to a "true" Jalvin component that needs a React boundary */
1897
- isTrueComponent(name) {
1898
- // 1. Locally declared or imported .jalvin components
1899
- if (this.userComponentNames.has(name))
1900
- return true;
1901
- // 2. Named imports from @jalvin/ui are considered UI primitives (functions)
1902
- // unless they were also identified as components.
1903
- // For now, everything in @jalvin/ui is treated as a UI primitive (function call)
1904
- // to maintain compatibility with the (props, children) signature.
1905
- if (this.uiNamedImports.has(name))
1906
- return false;
1907
- // 3. Fallback for star-imported @jalvin/ui
1908
- return false;
1909
- }
1910
- // ── Component call → props object ──────────────────────────────────────
1911
- /** Emit `Column(modifier = ...) { ... }` as either direct calls or React.createElement depending on jsx option */
1912
1618
  emitComponentCall(expr) {
1913
1619
  const callee = this.emitExpr(expr.callee);
1914
1620
  const tagName = expr.callee.kind === "NameExpr" ? expr.callee.name : expr.callee.member;
1915
1621
  const calleeType = this.typeMap.get(expr.callee);
1916
- // Param names from type checker (for same-file components) or resolved from imported files
1917
- const paramNames = (calleeType?.tag === "func" ? calleeType.paramNames : undefined) ??
1918
- (tagName ? this.componentParamNames.get(tagName) : undefined);
1919
- // Build props object from named/positional args
1622
+ const paramNames = (calleeType?.tag === "func" ? calleeType.paramNames : undefined) ?? (tagName ? this.componentParamNames.get(tagName) : undefined);
1920
1623
  const props = [];
1921
1624
  for (let i = 0; i < expr.args.length; i++) {
1922
1625
  const arg = expr.args[i];
1923
- // Prop name: explicit named arg > known param name > NameExpr variable name (shorthand)
1924
- const propName = arg.name ??
1925
- paramNames?.[i] ??
1926
- (arg.value.kind === "NameExpr" ? arg.value.name : undefined);
1626
+ const propName = arg.name ?? paramNames?.[i] ?? (arg.value.kind === "NameExpr" ? arg.value.name : undefined);
1927
1627
  if (!propName)
1928
1628
  continue;
1929
1629
  const val = this.emitExpr(arg.value);
1930
- // Use JS shorthand `{ key }` when key and value are the same identifier
1931
1630
  props.push(propName === val ? propName : `${propName}: ${val}`);
1932
1631
  }
1933
1632
  const propsStr = props.length > 0 ? `{ ${props.join(", ")} }` : "{}";
1934
- if (this.isJsxMode && tagName && this.isTrueComponent(tagName)) {
1935
- // React.createElement format for JSX output (True Components only)
1936
- if (expr.trailingLambda) {
1937
- const children = this.emitLambdaBodyAsDomChildren(expr.trailingLambda);
1938
- return `React.createElement(${callee}, ${propsStr}, [${children}])`;
1939
- }
1940
- return `React.createElement(${callee}, ${propsStr})`;
1941
- }
1942
- else {
1943
- // Direct function call format for non-JSX output OR UI Primitives
1944
- if (expr.trailingLambda) {
1945
- const children = this.emitLambdaBodyAsDomChildren(expr.trailingLambda);
1946
- return `${callee}(${propsStr}, [${children}])`;
1947
- }
1948
- return `${callee}(${propsStr})`;
1633
+ if (expr.trailingLambda) {
1634
+ const children = this.emitLambdaBodyAsDomChildren(expr.trailingLambda);
1635
+ return `${callee}(${propsStr}, [${children}])`;
1949
1636
  }
1637
+ return `${callee}(${propsStr})`;
1950
1638
  }
1951
- /** Collect each expression statement in a trailing-lambda body as DOM children.
1952
- * For-loop statements are emitted as spread IIFEs so their dynamic output is preserved. */
1953
1639
  emitLambdaBodyAsDomChildren(lambda) {
1954
1640
  const parts = [];
1955
1641
  for (const stmt of lambda.body) {
1956
1642
  if (stmt.kind === "ExprStmt") {
1957
1643
  parts.push(this.emitExpr(stmt.expr));
1958
1644
  }
1645
+ else if (stmt.kind === "IfStmt") {
1646
+ const s = stmt;
1647
+ const cond = this.emitExpr(s.condition);
1648
+ const thenExprs = s.then.statements.filter((st) => st.kind === "ExprStmt").map((st) => this.emitExpr(st.expr));
1649
+ const elseExprs = s.else && s.else.kind === "Block" ? s.else.statements.filter((st) => st.kind === "ExprStmt").map((st) => this.emitExpr(st.expr)) : [];
1650
+ const thenStr = thenExprs.length > 0 ? `...(${cond} ? [${thenExprs.join(", ")}] : [])` : "";
1651
+ const elseStr = elseExprs.length > 0 ? `...(!(${cond}) ? [${elseExprs.join(", ")}] : [])` : "";
1652
+ if (thenStr)
1653
+ parts.push(thenStr);
1654
+ if (elseStr)
1655
+ parts.push(elseStr);
1656
+ }
1959
1657
  else if (stmt.kind === "ForStmt") {
1960
1658
  const s = stmt;
1961
1659
  const iter = this.emitExpr(s.iterable);
1962
- let bindingStr;
1963
- if (typeof s.binding === "string") {
1964
- bindingStr = `const ${s.binding}`;
1965
- }
1966
- else if (s.binding.kind === "TupleDestructure") {
1967
- const names = s.binding.names.map((n) => n ?? "_").join(", ");
1968
- bindingStr = `const [${names}]`;
1969
- }
1970
- else {
1971
- bindingStr = `const [${s.binding.key}, ${s.binding.value}]`;
1972
- }
1973
- const innerExprs = s.body.statements
1974
- .filter((inner) => inner.kind === "ExprStmt")
1975
- .map((inner) => this.emitExpr(inner.expr));
1660
+ let bindingStr = typeof s.binding === "string" ? `const ${s.binding}` : s.binding.kind === "TupleDestructure" ? `const [${s.binding.names.map((n) => n ?? "_").join(", ")}]` : `const [${s.binding.key}, ${s.binding.value}]`;
1661
+ const innerExprs = s.body.statements.filter((inner) => inner.kind === "ExprStmt").map((inner) => this.emitExpr(inner.expr));
1976
1662
  if (innerExprs.length > 0) {
1977
1663
  const pushes = innerExprs.map((e) => `__c.push(${e});`).join(" ");
1978
1664
  parts.push(`...(() => { const __c: any[] = []; for (${bindingStr} of ${iter}) { ${pushes} } return __c; })()`);
1979
1665
  }
1980
1666
  }
1981
- // Other statement kinds (if, while, etc.) are not renderable as UI children inline
1982
1667
  }
1983
1668
  return parts.join(", ");
1984
1669
  }
1985
1670
  emitLambdaExpr(expr) {
1986
- // If no explicit params, emit `it` as the single implicit parameter
1987
- // (Convention for single-argument lambdas)
1988
- const params = expr.params.length === 0
1989
- ? "it"
1990
- : expr.params.map((p) => p.name ?? "_").join(", ");
1991
- const body = expr.body;
1992
- if (body.length === 1 && body[0].kind === "ExprStmt") {
1993
- return `(${params}) => ${this.emitExpr(body[0].expr)}`;
1994
- }
1995
- return `(${params}) => { ${body.map((s) => this.captureStmt(s)).join(" ")} }`;
1671
+ const params = expr.params.length === 0 ? "it" : expr.params.map((p) => p.name ?? "_").join(", ");
1672
+ if (expr.body.length === 1 && expr.body[0].kind === "ExprStmt")
1673
+ return `(${params}) => ${this.emitExpr(expr.body[0].expr)}`;
1674
+ return `(${params}) => { ${expr.body.map((s) => this.captureStmt(s)).join(" ")} }`;
1996
1675
  }
1997
1676
  emitIfExpr(expr) {
1998
1677
  const cond = this.emitExpr(expr.condition);
1999
- const thenStr = expr.then.kind === "Block"
2000
- ? `(() => { ${this.captureBlockStatements(expr.then.statements)} })()`
2001
- : this.emitExpr(expr.then);
2002
- const elseExpr = expr.else;
2003
- const elseStr = elseExpr.kind === "Block"
2004
- ? `(() => { ${this.captureBlockStatements(elseExpr.statements)} })()`
2005
- : elseExpr.kind === "IfExpr"
2006
- ? this.emitIfExpr(elseExpr)
2007
- : this.emitExpr(elseExpr);
1678
+ const thenStr = expr.then.kind === "Block" ? `(() => { ${this.captureBlockStatements(expr.then.statements)} })()` : this.emitExpr(expr.then);
1679
+ const elseStr = expr.else.kind === "Block" ? `(() => { ${this.captureBlockStatements(expr.else.statements)} })()` : expr.else.kind === "IfExpr" ? this.emitIfExpr(expr.else) : this.emitExpr(expr.else);
2008
1680
  return `(${cond} ? ${thenStr} : ${elseStr})`;
2009
1681
  }
2010
1682
  emitWhenExpr(expr) {
2011
- // Compile as an IIFE with if/else chain
2012
1683
  const parts = [];
2013
1684
  const subject = expr.subject ? `const __s = ${this.emitExpr(expr.subject.expr)};` : "";
2014
1685
  const subjectRef = expr.subject ? (expr.subject.binding ?? "__s") : "";
2015
1686
  for (const branch of expr.branches) {
2016
- if (branch.isElse) {
2017
- const body = branch.body.kind === "Block"
2018
- ? this.captureBlock(branch.body)
2019
- : `return ${this.emitExpr(branch.body)};`;
1687
+ const body = branch.body.kind === "Block" ? this.captureBlock(branch.body) : `return ${this.emitExpr(branch.body)};`;
1688
+ if (branch.isElse)
2020
1689
  parts.push(`{ ${body} }`);
2021
- }
2022
- else {
2023
- const cond = branch.conditions.map((c) => this.emitWhenCondition(c, subjectRef)).join(" || ");
2024
- const body = branch.body.kind === "Block"
2025
- ? this.captureBlock(branch.body)
2026
- : `return ${this.emitExpr(branch.body)};`;
2027
- parts.push(`if (${cond}) { ${body} }`);
2028
- }
1690
+ else
1691
+ parts.push(`if (${branch.conditions.map((c) => this.emitWhenCondition(c, subjectRef)).join(" || ")}) { ${body} }`);
2029
1692
  }
2030
1693
  return `(() => { ${subject} ${parts.join(" else ")} })()`;
2031
1694
  }
2032
1695
  emitTryCatchExpr(expr) {
2033
1696
  const body = this.captureBlock(expr.body);
2034
- const catches = expr.catches.map((c) => {
2035
- const cb = this.captureBlock(c.body);
2036
- return `catch (${c.name}) { ${cb} }`;
2037
- }).join(" ");
1697
+ const catches = expr.catches.map((c) => `catch (${c.name}) { ${this.captureBlock(c.body)} }`).join(" ");
2038
1698
  const fin = expr.finally ? `finally { ${this.captureBlock(expr.finally)} }` : "";
2039
1699
  return `(() => { try { ${body} } ${catches} ${fin} })()`;
2040
1700
  }
2041
1701
  emitCollectionLiteral(expr) {
2042
- if (expr.collectionKind === "map") {
2043
- const entries = expr.elements.map((e) => {
2044
- if ("kind" in e && e.kind === "MapEntry") {
2045
- return `[${this.emitExpr(e.key)}, ${this.emitExpr(e.value)}]`;
2046
- }
2047
- return "null";
2048
- });
2049
- return `new Map([${entries.join(", ")}])`;
2050
- }
2051
- if (expr.collectionKind === "set") {
2052
- const items = expr.elements.map((e) => this.emitExpr(e));
2053
- return `new Set([${items.join(", ")}])`;
2054
- }
2055
- const items = expr.elements.map((e) => this.emitExpr(e));
2056
- return `[${items.join(", ")}]`;
1702
+ if (expr.collectionKind === "map")
1703
+ return `new Map([${expr.elements.map((e) => ("kind" in e && e.kind === "MapEntry") ? `[${this.emitExpr(e.key)}, ${this.emitExpr(e.value)}]` : "null").join(", ")}])`;
1704
+ if (expr.collectionKind === "set")
1705
+ return `new Set([${expr.elements.map((e) => this.emitExpr(e)).join(", ")}])`;
1706
+ return `[${expr.elements.map((e) => this.emitExpr(e)).join(", ")}]`;
2057
1707
  }
2058
1708
  emitObjectExpr(expr) {
2059
1709
  const superType = expr.superTypes[0];
2060
1710
  const ext = superType ? ` extends ${this.emitTypeRef(superType.type)}` : "";
2061
- const members = expr.body.members.map((m) => this.captureClassMember(m)).join(" ");
2062
- return `(new (class${ext} { ${members} })())`;
2063
- }
2064
- // ── capture helpers (emit to temp string) ─────────────────────────────────
2065
- captureBlock(block) {
2066
- return block.statements.map((s) => this.captureStmt(s)).join(" ");
2067
- }
2068
- captureBlockStatements(stmts) {
2069
- return stmts.map((s) => this.captureStmt(s)).join(" ");
2070
- }
2071
- captureStmt(stmt) {
2072
- const saved = this.w;
2073
- const tmp = new Writer();
2074
- this.w = tmp;
2075
- this.emitStmt(stmt);
2076
- this.w = saved;
2077
- return tmp.output.trim();
2078
- }
2079
- captureClassMember(member) {
2080
- const saved = this.w;
2081
- const tmp = new Writer();
2082
- this.w = tmp;
2083
- this.emitClassMember(member);
2084
- this.w = saved;
2085
- return tmp.output.trim();
2086
- }
2087
- // ── Type reference emission ────────────────────────────────────────────────
1711
+ return `(new (class${ext} { ${expr.body.members.map((m) => this.captureClassMember(m)).join(" ")} })())`;
1712
+ }
1713
+ captureBlock(block) { return block.statements.map((s) => this.captureStmt(s)).join(" "); }
1714
+ captureBlockStatements(stmts) { return stmts.map((s) => this.captureStmt(s)).join(" "); }
1715
+ captureStmt(stmt) { const saved = this.w; const tmp = new Writer(); this.w = tmp; this.emitStmt(stmt); this.w = saved; return tmp.output.trim(); }
1716
+ captureClassMember(member) { const saved = this.w; const tmp = new Writer(); this.w = tmp; this.emitClassMember(member); this.w = saved; return tmp.output.trim(); }
2088
1717
  emitTypeRef(ref) {
2089
1718
  switch (ref.kind) {
2090
- case "SimpleTypeRef": {
2091
- const name = ref.name.join(".");
2092
- return PRIMITIVE_TYPE_MAP[name] ?? name;
2093
- }
2094
- case "NullableTypeRef":
2095
- return `${this.emitTypeRef(ref.base)} | null | undefined`;
2096
- case "GenericTypeRef": {
2097
- const base = ref.base.name.join(".");
2098
- const mapped = GENERIC_TYPE_MAP[base] ?? base;
2099
- const args = ref.args.map((a) => a.star ? "any" : a.type ? this.emitTypeRef(a.type) : "unknown");
2100
- return `${mapped}<${args.join(", ")}>`;
2101
- }
2102
- case "FunctionTypeRef": {
2103
- const params = ref.params.map((p, i) => `p${i}: ${this.emitTypeRef(p)}`).join(", ");
2104
- const ret = this.emitTypeRef(ref.returnType);
2105
- return `(${params}) => ${ret}`;
2106
- }
2107
- case "StarProjection":
2108
- return "any";
2109
- }
2110
- }
2111
- // ── Utility helpers ────────────────────────────────────────────────────────
2112
- emitTypeParamsStr(params) {
2113
- if (params.length === 0)
2114
- return "";
2115
- const ps = params.map((p) => {
2116
- const bound = p.upperBound ? ` extends ${this.emitTypeRef(p.upperBound)}` : "";
2117
- return `${p.name}${bound}`;
2118
- });
2119
- return `<${ps.join(", ")}>`;
2120
- }
2121
- emitParamsStr(params) {
2122
- return params.map((p) => {
2123
- const spread = p.vararg ? "..." : "";
2124
- // vararg params are rest params in TS: `...name: T[]`
2125
- const type = this.opts.emitTypes
2126
- ? `: ${this.emitTypeRef(p.type)}${p.vararg ? "[]" : ""}`
2127
- : "";
2128
- const def = p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "";
2129
- return `${spread}${p.name}${type}${def}`;
2130
- }).join(", ");
2131
- }
2132
- emitComponentPropsStr(params) {
2133
- if (params.length === 0)
2134
- return "";
2135
- return params.map((p) => {
2136
- const type = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
2137
- return `${p.name}${type}`;
2138
- }).join(", ");
2139
- }
2140
- emitSuperTypesStr(superTypes) {
2141
- if (superTypes.length === 0)
2142
- return "";
2143
- const parts = [];
2144
- let first = true;
2145
- for (const s of superTypes) {
2146
- // TypeScript does NOT allow constructor arguments in the `extends` clause.
2147
- // Delegation args are passed via `super(args)` inside the constructor.
2148
- if (first) {
2149
- parts.push(` extends ${this.emitTypeRef(s.type)}`);
2150
- first = false;
2151
- }
2152
- else {
2153
- parts.push(` implements ${this.emitTypeRef(s.type)}`);
2154
- }
2155
- }
2156
- return parts.join("");
2157
- }
2158
- exportPrefix(mods) {
2159
- if (mods.visibility === "private" || mods.visibility === "internal")
2160
- return "";
2161
- return "export ";
2162
- }
2163
- visibilityPrefix(mods) {
2164
- switch (mods.visibility) {
2165
- case "private": return "private ";
2166
- case "protected": return "protected ";
2167
- case "internal": return "/* internal */ ";
2168
- default: return "";
2169
- }
2170
- }
2171
- unaryOpStr(op) {
2172
- if (op === "not")
2173
- return "!";
2174
- return op;
2175
- }
2176
- binaryOpStr(op) {
2177
- switch (op) {
2178
- case "and": return "&&";
2179
- case "or": return "||";
2180
- case "xor": return "^";
2181
- case "shl": return "<<";
2182
- case "shr": return ">>";
2183
- case "ushr": return ">>>";
2184
- // === / !== are JS reference equality (Jalvin's triple-equals)
2185
- case "===": return "===";
2186
- case "!==": return "!==";
2187
- case "..": return "/* .. */ +"; // handled by range()
2188
- case "..<": return "/* ..< */ +";
2189
- default: return op;
2190
- }
2191
- }
2192
- // ── AST name walkers (for wildcard import resolution) ─────────────────────
2193
- /**
2194
- * Walk the entire program AST and collect all identifier names that are
2195
- * REFERENCED (i.e., used) in the code: NameExpr values and the first name
2196
- * component of SimpleTypeRef / GenericTypeRef nodes.
2197
- *
2198
- * Jalvin primitive type names (String, Boolean, …) that map to TS primitives
2199
- * are excluded from TypeRef collection since they never need an import.
2200
- */
1719
+ case "SimpleTypeRef": return PRIMITIVE_TYPE_MAP[ref.name.join(".")] ?? ref.name.join(".");
1720
+ case "NullableTypeRef": return `${this.emitTypeRef(ref.base)} | null | undefined`;
1721
+ case "GenericTypeRef": return `${GENERIC_TYPE_MAP[ref.base.name.join(".")] ?? ref.base.name.join(".")}<${ref.args.map((a) => a.star ? "any" : a.type ? this.emitTypeRef(a.type) : "unknown").join(", ")}>`;
1722
+ case "FunctionTypeRef": return `(${ref.params.map((p, i) => `p${i}: ${this.emitTypeRef(p)}`).join(", ")}) => ${this.emitTypeRef(ref.returnType)}`;
1723
+ case "StarProjection": return "any";
1724
+ }
1725
+ }
1726
+ emitTypeParamsStr(params) { return params.length === 0 ? "" : `<${params.map((p) => `${p.name}${p.upperBound ? ` extends ${this.emitTypeRef(p.upperBound)}` : ""}`).join(", ")}>`; }
1727
+ emitParamsStr(params) { return params.map((p) => `${p.vararg ? "..." : ""}${p.name}${this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}${p.vararg ? "[]" : ""}` : ""}${p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : ""}`).join(", "); }
1728
+ emitSuperTypesStr(superTypes) { return superTypes.length === 0 ? "" : superTypes.map((s, i) => i === 0 ? ` extends ${this.emitTypeRef(s.type)}` : ` implements ${this.emitTypeRef(s.type)}`).join(""); }
1729
+ exportPrefix(mods) { return (mods.visibility === "private" || mods.visibility === "internal") ? "" : "export "; }
1730
+ visibilityPrefix(mods) { switch (mods.visibility) {
1731
+ case "private": return "private ";
1732
+ case "protected": return "protected ";
1733
+ case "internal": return "/* internal */ ";
1734
+ default: return "";
1735
+ } }
1736
+ unaryOpStr(op) { return op === "not" ? "!" : op; }
1737
+ binaryOpStr(op) { switch (op) {
1738
+ case "and": return "&&";
1739
+ case "or": return "||";
1740
+ case "xor": return "^";
1741
+ case "shl": return "<<";
1742
+ case "shr": return ">>";
1743
+ case "ushr": return ">>>";
1744
+ case "===": return "===";
1745
+ case "!==": return "!==";
1746
+ default: return op;
1747
+ } }
2201
1748
  gatherReferencedNames(program) {
2202
1749
  const names = new Set();
2203
1750
  const visited = new WeakSet();
2204
1751
  const walk = (val) => {
2205
- if (!val || typeof val !== "object")
1752
+ if (!val || typeof val !== "object" || visited.has(val))
2206
1753
  return;
1754
+ visited.add(val);
2207
1755
  if (Array.isArray(val)) {
2208
- for (const item of val)
2209
- walk(item);
1756
+ val.forEach(walk);
2210
1757
  return;
2211
1758
  }
2212
1759
  const obj = val;
2213
- if (visited.has(obj))
2214
- return;
2215
- visited.add(obj);
2216
- const kind = obj["kind"];
2217
- if (kind === "NameExpr") {
2218
- const name = obj["name"];
2219
- if (typeof name === "string")
2220
- names.add(name);
2221
- return; // leaf node
2222
- }
2223
- if (kind === "SimpleTypeRef") {
2224
- const nameArr = obj["name"];
2225
- if (Array.isArray(nameArr) && nameArr.length > 0) {
2226
- const first = nameArr[0];
2227
- // Skip Jalvin primitive type names — they're erased to TS primitives.
2228
- if (!(first in PRIMITIVE_TYPE_MAP))
2229
- names.add(first);
2230
- }
1760
+ if (obj.kind === "NameExpr") {
1761
+ if (typeof obj.name === "string")
1762
+ names.add(obj.name);
2231
1763
  return;
2232
1764
  }
2233
- if (kind === "GenericTypeRef") {
2234
- const base = obj["base"];
2235
- const nameArr = base?.["name"];
2236
- if (Array.isArray(nameArr) && nameArr.length > 0) {
2237
- const first = nameArr[0];
2238
- if (!(first in PRIMITIVE_TYPE_MAP))
2239
- names.add(first);
2240
- }
2241
- // Fall through to recurse into type arguments
1765
+ if (obj.kind === "SimpleTypeRef") {
1766
+ if (obj.name?.[0] && !(obj.name[0] in PRIMITIVE_TYPE_MAP))
1767
+ names.add(obj.name[0]);
1768
+ return;
2242
1769
  }
2243
- for (const propVal of Object.values(obj)) {
2244
- walk(propVal);
1770
+ if (obj.kind === "GenericTypeRef") {
1771
+ if (obj.base?.name?.[0] && !(obj.base.name[0] in PRIMITIVE_TYPE_MAP))
1772
+ names.add(obj.base.name[0]);
2245
1773
  }
1774
+ Object.values(obj).forEach(walk);
2246
1775
  };
2247
- for (const decl of program.declarations)
2248
- walk(decl);
1776
+ program.declarations.forEach(walk);
2249
1777
  return names;
2250
1778
  }
2251
- /**
2252
- * Walk the entire program AST and collect all names that are LOCALLY DEFINED:
2253
- * top-level declaration names, non-star import aliases, function/lambda
2254
- * parameters, local val/var bindings, for-loop variables, catch-clause names,
2255
- * when-subject bindings, and type parameter names.
2256
- */
2257
1779
  gatherAllLocalBindings(program) {
2258
1780
  const names = new Set();
2259
1781
  const visited = new WeakSet();
2260
- // Top-level declaration names
2261
- for (const decl of program.declarations) {
2262
- const d = decl;
2263
- if (typeof d.name === "string" && d.name)
2264
- names.add(d.name);
2265
- }
2266
- // Non-star import aliases
2267
- for (const imp of program.imports) {
2268
- if (!imp.star) {
2269
- const name = imp.alias ?? imp.path[imp.path.length - 1];
2270
- if (name)
2271
- names.add(name);
2272
- }
2273
- }
1782
+ program.declarations.forEach((d) => { if (d.name)
1783
+ names.add(d.name); });
1784
+ program.imports.forEach((i) => { if (!i.star)
1785
+ names.add(i.alias ?? i.path[i.path.length - 1]); });
2274
1786
  const walk = (val) => {
2275
- if (!val || typeof val !== "object")
1787
+ if (!val || typeof val !== "object" || visited.has(val))
2276
1788
  return;
1789
+ visited.add(val);
2277
1790
  if (Array.isArray(val)) {
2278
- for (const item of val)
2279
- walk(item);
1791
+ val.forEach(walk);
2280
1792
  return;
2281
1793
  }
2282
1794
  const obj = val;
2283
- if (visited.has(obj))
2284
- return;
2285
- visited.add(obj);
2286
- const kind = obj["kind"];
2287
- // Collect bound names at their declaration sites
2288
- if (kind === "PropertyDecl" || kind === "DestructuringDecl") {
2289
- if (typeof obj["name"] === "string")
2290
- names.add(obj["name"]);
2291
- const nms = obj["names"];
2292
- if (Array.isArray(nms)) {
2293
- for (const n of nms) {
2294
- if (typeof n === "string")
2295
- names.add(n);
2296
- }
2297
- }
2298
- }
2299
- if (kind === "FunDecl" || kind === "ExtensionFunDecl" ||
2300
- kind === "ComponentDecl" || kind === "ClassDecl" ||
2301
- kind === "DataClassDecl" || kind === "SealedClassDecl" ||
2302
- kind === "EnumClassDecl" || kind === "InterfaceDecl" ||
2303
- kind === "ObjectDecl" || kind === "TypeAliasDecl") {
2304
- if (typeof obj["name"] === "string" && obj["name"])
2305
- names.add(obj["name"]);
2306
- // Type parameters
2307
- const typeParams = obj["typeParams"];
2308
- if (Array.isArray(typeParams)) {
2309
- for (const tp of typeParams) {
2310
- if (tp.name)
2311
- names.add(tp.name);
2312
- }
2313
- }
2314
- // Function/method parameters
2315
- const params = obj["params"];
2316
- if (Array.isArray(params)) {
2317
- for (const p of params) {
2318
- if (p.name)
2319
- names.add(p.name);
2320
- }
2321
- }
2322
- }
2323
- if (kind === "LambdaExpr") {
2324
- const params = obj["params"];
2325
- if (Array.isArray(params)) {
2326
- for (const p of params) {
2327
- if (p.name)
2328
- names.add(p.name);
2329
- }
2330
- }
2331
- }
2332
- if (kind === "ForStmt") {
2333
- const binding = obj["binding"];
2334
- if (typeof binding === "string") {
2335
- names.add(binding);
2336
- }
2337
- else if (binding && typeof binding === "object") {
2338
- const b = binding;
2339
- // TupleDestructure or MapDestructure
2340
- if (Array.isArray(b["names"])) {
2341
- for (const n of b["names"]) {
2342
- if (typeof n === "string")
2343
- names.add(n);
2344
- }
2345
- }
2346
- if (typeof b["key"] === "string")
2347
- names.add(b["key"]);
2348
- if (typeof b["value"] === "string")
2349
- names.add(b["value"]);
2350
- }
2351
- }
2352
- if (kind === "TryCatchStmt") {
2353
- const catches = obj["catches"];
2354
- if (Array.isArray(catches)) {
2355
- for (const c of catches) {
2356
- if (c.name)
2357
- names.add(c.name);
2358
- }
2359
- }
2360
- }
2361
- if (kind === "WhenStmt" || kind === "WhenExpr") {
2362
- const subject = obj["subject"];
2363
- if (typeof subject?.["binding"] === "string")
2364
- names.add(subject["binding"]);
2365
- }
2366
- if (kind === "SecondaryConstructor") {
2367
- const params = obj["params"];
2368
- if (Array.isArray(params)) {
2369
- for (const p of params) {
2370
- if (p.name)
2371
- names.add(p.name);
2372
- }
2373
- }
2374
- }
2375
- for (const propVal of Object.values(obj)) {
2376
- walk(propVal);
2377
- }
1795
+ if (["PropertyDecl", "DestructuringDecl", "FunDecl", "ExtensionFunDecl", "ComponentDecl", "ClassDecl", "DataClassDecl", "SealedClassDecl", "EnumClassDecl", "InterfaceDecl", "ObjectDecl", "TypeAliasDecl", "LambdaExpr"].includes(obj.kind) && obj.name)
1796
+ names.add(obj.name);
1797
+ if (obj.typeParams)
1798
+ obj.typeParams.forEach((tp) => names.add(tp.name));
1799
+ if (obj.params)
1800
+ obj.params.forEach((p) => names.add(p.name));
1801
+ if (obj.kind === "ForStmt" && typeof obj.binding === "string")
1802
+ names.add(obj.binding);
1803
+ if (obj.kind === "TryCatchStmt" && obj.catches)
1804
+ obj.catches.forEach((c) => names.add(c.name));
1805
+ Object.values(obj).forEach(walk);
2378
1806
  };
2379
- for (const decl of program.declarations)
2380
- walk(decl);
1807
+ program.declarations.forEach(walk);
2381
1808
  return names;
2382
1809
  }
2383
1810
  }
2384
1811
  exports.CodeGenerator = CodeGenerator;
2385
- // ---------------------------------------------------------------------------
2386
- // Type name mappings (Jalvin TypeScript)
2387
- // ---------------------------------------------------------------------------
2388
- const PRIMITIVE_TYPE_MAP = {
2389
- Int: "number",
2390
- Long: "bigint",
2391
- Float: "number",
2392
- Double: "number",
2393
- Boolean: "boolean",
2394
- String: "string",
2395
- Char: "string",
2396
- Byte: "number",
2397
- Short: "number",
2398
- Unit: "void",
2399
- Any: "unknown",
2400
- Nothing: "never",
2401
- };
2402
- const GENERIC_TYPE_MAP = {
2403
- List: "ReadonlyArray",
2404
- MutableList: "Array",
2405
- Set: "ReadonlySet",
2406
- MutableSet: "Set",
2407
- Map: "ReadonlyMap",
2408
- MutableMap: "Map",
2409
- Array: "Array",
2410
- Pair: "[", // handled specially
2411
- Triple: "[",
2412
- Deferred: "Promise",
2413
- StateFlow: "StateFlow",
2414
- MutableStateFlow: "MutableStateFlow",
2415
- Flow: "AsyncIterable",
2416
- };
1812
+ const PRIMITIVE_TYPE_MAP = { Int: "number", Long: "bigint", Float: "number", Double: "number", Boolean: "boolean", String: "string", Char: "string", Byte: "number", Short: "number", Unit: "void", Any: "unknown", Nothing: "never" };
1813
+ const GENERIC_TYPE_MAP = { List: "ReadonlyArray", MutableList: "Array", Set: "ReadonlySet", MutableSet: "Set", Map: "ReadonlyMap", MutableMap: "Map", Array: "Array", Deferred: "Promise", StateFlow: "StateFlow", MutableStateFlow: "MutableStateFlow", Flow: "AsyncIterable" };
2417
1814
  const PRIMITIVE_TYPES = new Set(Object.keys(PRIMITIVE_TYPE_MAP));
2418
- // ---------------------------------------------------------------------------
2419
- // Well-known JavaScript / TypeScript global names that must never be emitted
2420
- // as named imports from a wildcard-imported package.
2421
- // ---------------------------------------------------------------------------
2422
- const JS_GLOBAL_NAMES = new Set([
2423
- // JS built-in constructors and objects
2424
- "Array", "Map", "Set", "Object", "String", "Number", "Boolean",
2425
- "Promise", "Error", "TypeError", "RangeError", "SyntaxError", "URIError",
2426
- "EvalError", "ReferenceError",
2427
- "console", "Math", "Date", "JSON", "RegExp", "Symbol", "BigInt",
2428
- "Proxy", "Reflect", "globalThis", "Atomics", "SharedArrayBuffer",
2429
- "ArrayBuffer", "DataView", "Int8Array", "Uint8Array", "Uint8ClampedArray",
2430
- "Int16Array", "Uint16Array", "Int32Array", "Uint32Array",
2431
- "Float32Array", "Float64Array", "BigInt64Array", "BigUint64Array",
2432
- // Global functions
2433
- "isNaN", "isFinite", "parseInt", "parseFloat", "eval",
2434
- "encodeURI", "decodeURI", "encodeURIComponent", "decodeURIComponent",
2435
- // Browser/Node globals
2436
- "setTimeout", "clearTimeout", "setInterval", "clearInterval",
2437
- "queueMicrotask", "requestAnimationFrame", "cancelAnimationFrame",
2438
- "fetch", "URL", "URLSearchParams", "AbortController", "AbortSignal",
2439
- "EventTarget", "Event", "CustomEvent", "FormData", "Headers",
2440
- "Request", "Response", "Blob", "File", "FileReader",
2441
- "Worker", "SharedWorker", "WebSocket",
2442
- "ReadableStream", "WritableStream", "TransformStream",
2443
- "document", "window", "navigator", "location", "history", "screen",
2444
- "performance", "crypto", "indexedDB", "localStorage", "sessionStorage",
2445
- "confirm", "alert", "prompt",
2446
- "process", "Buffer", "global", "require", "module", "exports", "__dirname", "__filename",
2447
- // Special identifiers
2448
- "undefined", "null", "NaN", "Infinity",
2449
- "it", "this", "super", "arguments", "new", "class", "function",
2450
- // TypeScript primitive type names
2451
- "any", "unknown", "never", "void", "string", "number", "boolean", "bigint",
2452
- "object", "symbol",
2453
- // Jalvin type names that map to TS primitives (never need import)
2454
- "String", "Boolean", "Any", "Nothing", "Unit", "Char", "Byte", "Short",
2455
- "Float", "Double",
2456
- ]);
2457
- // ---------------------------------------------------------------------------
2458
- // Public helper
2459
- // ---------------------------------------------------------------------------
2460
- function generate(program, opts, operatorOverloads, typeMap) {
2461
- return new CodeGenerator(opts).generate(program, operatorOverloads, typeMap);
2462
- }
1815
+ const JS_GLOBAL_NAMES = new Set(["Array", "Map", "Set", "Object", "String", "Number", "Boolean", "Promise", "Error", "console", "Math", "Date", "JSON", "setTimeout", "setInterval", "fetch", "document", "window", "undefined", "null", "NaN", "Infinity", "it", "this", "super", "any", "unknown", "never", "void"]);
1816
+ function generate(program, opts, operatorOverloads, typeMap) { return new CodeGenerator(opts).generate(program, operatorOverloads, typeMap); }
2463
1817
  //# sourceMappingURL=codegen.js.map