@jalvin/compiler 2.0.38 → 2.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codegen.d.ts +3 -56
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +252 -898
- package/dist/codegen.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/typechecker.d.ts.map +1 -1
- package/dist/typechecker.js +1 -3
- package/dist/typechecker.js.map +1 -1
- package/package.json +1 -1
- package/dist/codegen/index.d.ts +0 -179
- package/dist/codegen/index.d.ts.map +0 -1
- package/dist/codegen/index.js +0 -2207
- package/dist/codegen/index.js.map +0 -1
- package/dist/codegen_freestanding_c.d.ts +0 -9
- package/dist/codegen_freestanding_c.d.ts.map +0 -1
- package/dist/codegen_freestanding_c.js +0 -321
- package/dist/codegen_freestanding_c.js.map +0 -1
- package/dist/lexer/chars.d.ts +0 -5
- package/dist/lexer/chars.d.ts.map +0 -1
- package/dist/lexer/chars.js +0 -20
- package/dist/lexer/chars.js.map +0 -1
- package/dist/lexer/index.d.ts +0 -37
- package/dist/lexer/index.d.ts.map +0 -1
- package/dist/lexer/index.js +0 -630
- package/dist/lexer/index.js.map +0 -1
- package/dist/lexer/tokens.d.ts +0 -135
- package/dist/lexer/tokens.d.ts.map +0 -1
- package/dist/lexer/tokens.js +0 -89
- package/dist/lexer/tokens.js.map +0 -1
- package/dist/parser/constants.d.ts +0 -3
- package/dist/parser/constants.d.ts.map +0 -1
- package/dist/parser/constants.js +0 -7
- package/dist/parser/constants.js.map +0 -1
- package/dist/parser/helpers.d.ts +0 -2
- package/dist/parser/helpers.d.ts.map +0 -1
- package/dist/parser/helpers.js +0 -9
- package/dist/parser/helpers.js.map +0 -1
- package/dist/parser/index.d.ts +0 -118
- package/dist/parser/index.d.ts.map +0 -1
- package/dist/parser/index.js +0 -2387
- package/dist/parser/index.js.map +0 -1
- package/dist/typechecker/globals.d.ts +0 -2
- package/dist/typechecker/globals.d.ts.map +0 -1
- package/dist/typechecker/globals.js +0 -15
- package/dist/typechecker/globals.js.map +0 -1
- package/dist/typechecker/index.d.ts +0 -146
- package/dist/typechecker/index.d.ts.map +0 -1
- package/dist/typechecker/index.js +0 -2288
- package/dist/typechecker/index.js.map +0 -1
- package/dist/typechecker/types.d.ts +0 -66
- package/dist/typechecker/types.d.ts.map +0 -1
- package/dist/typechecker/types.js +0 -39
- 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
|
|
3
|
+
// Jalvin Code Generator — AST → TypeScript
|
|
4
4
|
//
|
|
5
5
|
// Design principles:
|
|
6
|
-
// • component declarations →
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
288
|
+
? precedingRelPath
|
|
304
289
|
: looksLikeNpmPackage
|
|
305
|
-
? precedingRelPath
|
|
306
|
-
: imp.path.join("/");
|
|
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 →
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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)}` :
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1487
|
-
case "
|
|
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
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
case "
|
|
1511
|
-
|
|
1512
|
-
case "
|
|
1513
|
-
|
|
1514
|
-
case "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
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
|
-
|
|
1572
|
-
const
|
|
1573
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1623
|
-
|
|
1624
|
-
if (
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
(
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2017
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
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
|
-
|
|
2062
|
-
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
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
|
-
|
|
2092
|
-
|
|
2093
|
-
}
|
|
2094
|
-
case "
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
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
|
-
|
|
2209
|
-
walk(item);
|
|
1756
|
+
val.forEach(walk);
|
|
2210
1757
|
return;
|
|
2211
1758
|
}
|
|
2212
1759
|
const obj = val;
|
|
2213
|
-
if (
|
|
2214
|
-
|
|
2215
|
-
|
|
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 === "
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
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
|
-
|
|
2244
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
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
|
-
|
|
2279
|
-
walk(item);
|
|
1791
|
+
val.forEach(walk);
|
|
2280
1792
|
return;
|
|
2281
1793
|
}
|
|
2282
1794
|
const obj = val;
|
|
2283
|
-
if (
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
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
|
-
|
|
2380
|
-
walk(decl);
|
|
1807
|
+
program.declarations.forEach(walk);
|
|
2381
1808
|
return names;
|
|
2382
1809
|
}
|
|
2383
1810
|
}
|
|
2384
1811
|
exports.CodeGenerator = CodeGenerator;
|
|
2385
|
-
|
|
2386
|
-
|
|
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
|
-
|
|
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
|