@jalvin/compiler 2.0.43 → 2.0.45
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 +11 -53
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +344 -839
- package/dist/codegen.js.map +1 -1
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +3 -1
- package/dist/diagnostics.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 +12 -0
- package/dist/typechecker.d.ts.map +1 -1
- package/dist/typechecker.js +244 -152
- package/dist/typechecker.js.map +1 -1
- package/package.json +1 -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,8 +98,10 @@ class CodeGenerator {
|
|
|
99
98
|
opts;
|
|
100
99
|
hasComponents = false;
|
|
101
100
|
runtimeSymbolsNeeded = new Set();
|
|
102
|
-
/** Names of
|
|
103
|
-
|
|
101
|
+
/** Names of true Jalvin components — used to detect functional calls */
|
|
102
|
+
userComponentNames = new Set();
|
|
103
|
+
/** Names of all potential components (including UI primitives) — used to detect props-object calls */
|
|
104
|
+
allComponentLikeNames = new Set();
|
|
104
105
|
/**
|
|
105
106
|
* Param names for components resolved from imported local .jalvin files.
|
|
106
107
|
* Maps component name → ordered list of param names.
|
|
@@ -108,6 +109,8 @@ class CodeGenerator {
|
|
|
108
109
|
componentParamNames = new Map();
|
|
109
110
|
/** True when the program contains `import @jalvin/ui.*` — used to detect UI primitive calls */
|
|
110
111
|
hasUiStarImport = false;
|
|
112
|
+
/** Names of symbols explicitly imported from @jalvin/ui */
|
|
113
|
+
uiNamedImports = new Set();
|
|
111
114
|
/** Operator overload resolutions from the type checker */
|
|
112
115
|
operatorOverloadMap = new Map();
|
|
113
116
|
/** Type map from the type checker */
|
|
@@ -131,6 +134,7 @@ class CodeGenerator {
|
|
|
131
134
|
* Maps module specifier (e.g. "src/views") → sorted list of exported names.
|
|
132
135
|
*/
|
|
133
136
|
localStarExports = new Map();
|
|
137
|
+
isInClass = false;
|
|
134
138
|
constructor(opts = {}) {
|
|
135
139
|
this.opts = { ...exports.DEFAULT_CODEGEN_OPTIONS, ...opts };
|
|
136
140
|
}
|
|
@@ -141,34 +145,40 @@ class CodeGenerator {
|
|
|
141
145
|
this.typeMap = typeMap;
|
|
142
146
|
// Pre-scan for components (local declarations OR @jalvin/ui imports)
|
|
143
147
|
this.hasComponents = program.declarations.some((d) => d.kind === "ComponentDecl" ||
|
|
144
|
-
(d.kind === "ClassDecl" && d.body?.members.some((m) => m.kind === "ComponentDecl"))) || program.imports.some((imp) => imp.path[0] === "@jalvin" && imp.path[1] === "ui")
|
|
148
|
+
(d.kind === "ClassDecl" && d.body?.members.some((m) => m.kind === "ComponentDecl"))) || program.imports.some((imp) => imp.path[0] === "@jalvin" && imp.path[1] === "ui")
|
|
149
|
+
|| this.containsJsx(program);
|
|
145
150
|
// Collect component names for component call detection
|
|
146
|
-
this.
|
|
151
|
+
this.userComponentNames = new Set();
|
|
152
|
+
this.allComponentLikeNames = new Set();
|
|
147
153
|
this.componentParamNames = new Map();
|
|
148
154
|
for (const decl of program.declarations) {
|
|
149
155
|
if (decl.kind === "ComponentDecl") {
|
|
150
|
-
this.
|
|
156
|
+
this.userComponentNames.add(decl.name);
|
|
157
|
+
this.allComponentLikeNames.add(decl.name);
|
|
151
158
|
this.componentParamNames.set(decl.name, decl.params.map((p) => p.name));
|
|
152
159
|
}
|
|
153
160
|
if (decl.kind === "ClassDecl" && decl.body) {
|
|
154
161
|
for (const m of decl.body.members) {
|
|
155
162
|
if (m.kind === "ComponentDecl") {
|
|
156
|
-
this.
|
|
163
|
+
this.userComponentNames.add(m.name);
|
|
164
|
+
this.allComponentLikeNames.add(m.name);
|
|
157
165
|
this.componentParamNames.set(m.name, m.params.map((p) => p.name));
|
|
158
166
|
}
|
|
159
167
|
}
|
|
160
168
|
}
|
|
161
169
|
}
|
|
162
170
|
this.hasUiStarImport = false;
|
|
171
|
+
this.uiNamedImports = new Set();
|
|
163
172
|
for (const imp of program.imports) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
if (imp.path[0] === "@jalvin" && imp.path[1] === "ui") {
|
|
174
|
+
if (imp.star) {
|
|
175
|
+
this.hasUiStarImport = true;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const name = imp.path[imp.path.length - 1];
|
|
179
|
+
this.uiNamedImports.add(name);
|
|
180
|
+
this.allComponentLikeNames.add(name);
|
|
181
|
+
}
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
// Resolve component fun symbols from locally imported .jalvin files
|
|
@@ -202,14 +212,42 @@ class CodeGenerator {
|
|
|
202
212
|
return {
|
|
203
213
|
code,
|
|
204
214
|
lineMap: this.w.lineMap,
|
|
205
|
-
|
|
215
|
+
hasComponents: this.hasComponents,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/** Walk AST to see if it contains any JsxElement nodes */
|
|
219
|
+
containsJsx(program) {
|
|
220
|
+
let found = false;
|
|
221
|
+
const walk = (val) => {
|
|
222
|
+
if (found || !val || typeof val !== "object")
|
|
223
|
+
return;
|
|
224
|
+
if (Array.isArray(val)) {
|
|
225
|
+
for (const item of val)
|
|
226
|
+
walk(item);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const obj = val;
|
|
230
|
+
if (obj["kind"] === "JsxElement") {
|
|
231
|
+
found = true;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
for (const propVal of Object.values(obj)) {
|
|
235
|
+
walk(propVal);
|
|
236
|
+
}
|
|
206
237
|
};
|
|
238
|
+
for (const decl of program.declarations)
|
|
239
|
+
walk(decl);
|
|
240
|
+
return found;
|
|
207
241
|
}
|
|
208
242
|
// ── Preamble & imports ─────────────────────────────────────────────────────
|
|
209
243
|
buildPreamble() {
|
|
210
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("// ─────────────────────────────────────────────────────────────────────────────");
|
|
211
249
|
if (this.hasComponents) {
|
|
212
|
-
|
|
250
|
+
this.runtimeSymbolsNeeded.add("jalvinCreateElement");
|
|
213
251
|
}
|
|
214
252
|
// Emit compiler-injected runtime symbols, but only those NOT already covered
|
|
215
253
|
// by a star-import's named-import expansion (to avoid duplicate imports).
|
|
@@ -226,19 +264,6 @@ class CodeGenerator {
|
|
|
226
264
|
const sourceRoot = this.opts.sourceRoot ?? process.cwd();
|
|
227
265
|
// Re-emit user imports as ES imports
|
|
228
266
|
for (const imp of program.imports) {
|
|
229
|
-
// Build module specifier.
|
|
230
|
-
//
|
|
231
|
-
// Scoped packages (@org/pkg.Symbol): symbol is an export from the package,
|
|
232
|
-
// strip the last segment.
|
|
233
|
-
// import @jalvin/ui.Column → import { Column } from "@jalvin/ui"
|
|
234
|
-
// import @jalvin/runtime.* → import * as runtime from "@jalvin/runtime"
|
|
235
|
-
//
|
|
236
|
-
// Local imports (a.b.C): check whether the preceding segments already
|
|
237
|
-
// resolve to a file on disk.
|
|
238
|
-
// import src.models.Rotation → src/models/Rotation.ts does NOT exist
|
|
239
|
-
// → import { Rotation } from "src/models/Rotation"
|
|
240
|
-
// import src.models.css.Css → src/models/css.ts DOES exist
|
|
241
|
-
// → import { Css } from "src/models/css"
|
|
242
267
|
const isScoped = imp.path[0].startsWith("@");
|
|
243
268
|
let moduleSpecifier;
|
|
244
269
|
if (imp.star) {
|
|
@@ -253,35 +278,28 @@ class CodeGenerator {
|
|
|
253
278
|
moduleSpecifier = moduleParts[0] + "/" + moduleParts.slice(1).join("/");
|
|
254
279
|
}
|
|
255
280
|
else {
|
|
256
|
-
// Non-scoped import: prefer local-file convention
|
|
257
|
-
// an installed npm package path (e.g. cubing/twisty.TwistyPlayer).
|
|
281
|
+
// Non-scoped import: prefer local-file convention
|
|
258
282
|
const precedingParts = imp.path.slice(0, -1);
|
|
259
283
|
const precedingRelPath = precedingParts.join("/");
|
|
260
284
|
const fileExts = [".ts", ".tsx", ".jalvin"];
|
|
261
285
|
const precedingIsFile = precedingParts.length > 0 && fileExts.some((ext) => fs.existsSync(nodePath.join(sourceRoot, precedingRelPath + ext)));
|
|
262
286
|
const looksLikeNpmPackage = this.isLikelyNodeModuleImport(imp.path, sourceRoot);
|
|
263
287
|
moduleSpecifier = precedingIsFile
|
|
264
|
-
? precedingRelPath
|
|
288
|
+
? precedingRelPath
|
|
265
289
|
: looksLikeNpmPackage
|
|
266
|
-
? precedingRelPath
|
|
267
|
-
: imp.path.join("/");
|
|
290
|
+
? precedingRelPath
|
|
291
|
+
: imp.path.join("/");
|
|
268
292
|
}
|
|
269
293
|
if (imp.star && isScoped && this.externalStarCandidates.size > 0) {
|
|
270
|
-
// Named-import expansion of a scoped star import.
|
|
271
|
-
// Emit explicit named imports for each external symbol used in the file so
|
|
272
|
-
// that symbols like `ViewModel`, `mutableStateOf` are directly in scope.
|
|
273
294
|
const symbols = [...this.externalStarCandidates].sort();
|
|
274
295
|
this.w.writeIndentedLine(`import { ${symbols.join(", ")} } from "${moduleSpecifier}";`);
|
|
275
296
|
this.handledStarImportModules.add(moduleSpecifier);
|
|
276
297
|
}
|
|
277
298
|
else if (imp.star && !isScoped && this.localStarExports.has(moduleSpecifier)) {
|
|
278
|
-
// Local wildcard import: import src.module.* — expand to named imports
|
|
279
|
-
// so all exported symbols are directly in scope (no namespace qualifier needed).
|
|
280
299
|
const symbols = this.localStarExports.get(moduleSpecifier).slice().sort();
|
|
281
300
|
this.w.writeIndentedLine(`import { ${symbols.join(", ")} } from "${moduleSpecifier}";`);
|
|
282
301
|
}
|
|
283
302
|
else if (imp.star) {
|
|
284
|
-
// Fallback: namespace import (multiple scoped star imports or no candidates).
|
|
285
303
|
this.w.writeIndentedLine(`import * as ${imp.path[imp.path.length - 1]} from "${moduleSpecifier}";`);
|
|
286
304
|
}
|
|
287
305
|
else if (imp.alias) {
|
|
@@ -295,11 +313,6 @@ class CodeGenerator {
|
|
|
295
313
|
if (program.imports.length > 0)
|
|
296
314
|
this.w.writeLine();
|
|
297
315
|
}
|
|
298
|
-
/**
|
|
299
|
-
* Heuristic for non-scoped imports: if the first path segment resolves to an
|
|
300
|
-
* installed package in node_modules, treat the import as npm/ESM and strip
|
|
301
|
-
* the trailing symbol name from the module specifier.
|
|
302
|
-
*/
|
|
303
316
|
isLikelyNodeModuleImport(pathParts, sourceRoot) {
|
|
304
317
|
if (pathParts.length < 2)
|
|
305
318
|
return false;
|
|
@@ -317,14 +330,12 @@ class CodeGenerator {
|
|
|
317
330
|
return false;
|
|
318
331
|
}
|
|
319
332
|
// ── Top-level declarations ─────────────────────────────────────────────────
|
|
320
|
-
/** Emit a @deprecated JSDoc block if the declaration has @Nuked. */
|
|
321
333
|
emitAnnotations(mods) {
|
|
322
334
|
for (const ann of mods.annotations) {
|
|
323
335
|
if (ann.name === "Nuked") {
|
|
324
336
|
const reason = ann.args ? ` ${ann.args.replace(/^["']|["']$/g, "")}` : "";
|
|
325
337
|
this.w.writeIndentedLine(`/** @deprecated${reason} */`);
|
|
326
338
|
}
|
|
327
|
-
// Other annotations are silently ignored for now (pass-through model)
|
|
328
339
|
}
|
|
329
340
|
}
|
|
330
341
|
emitTopLevelDecl(decl) {
|
|
@@ -379,7 +390,6 @@ class CodeGenerator {
|
|
|
379
390
|
? `: ${this.emitTypeRef(decl.returnType)}`
|
|
380
391
|
: "";
|
|
381
392
|
if (!decl.body) {
|
|
382
|
-
// Abstract / interface method
|
|
383
393
|
this.w.writeIndentedLine(`${vis}${asyncKw}${decl.name}${typeParams}(${params})${retType};`);
|
|
384
394
|
return;
|
|
385
395
|
}
|
|
@@ -396,7 +406,6 @@ class CodeGenerator {
|
|
|
396
406
|
this.w.writeIndentedLine(`}`);
|
|
397
407
|
}
|
|
398
408
|
else {
|
|
399
|
-
// Expression body
|
|
400
409
|
if (memberOf) {
|
|
401
410
|
this.w.writeIndentedLine(`${vis}${asyncKw}${decl.name}${typeParams}(${params})${retType} {`);
|
|
402
411
|
}
|
|
@@ -437,21 +446,19 @@ class CodeGenerator {
|
|
|
437
446
|
this.w.writeIndentedLine(`}`);
|
|
438
447
|
}
|
|
439
448
|
}
|
|
440
|
-
// ── component declarations →
|
|
449
|
+
// ── component declarations → Functional components ────────────────────
|
|
441
450
|
emitComponentDecl(decl) {
|
|
442
451
|
this.emitAnnotations(decl.modifiers);
|
|
443
452
|
this.hasComponents = true;
|
|
444
|
-
const vis = this.exportPrefix(decl.modifiers);
|
|
445
|
-
// "children" is passed as the second positional argument by emitComponentCall,
|
|
446
|
-
// so it must NOT be destructured from the props object.
|
|
447
|
-
const propsParams = decl.params.filter((p) => p.name !== "children");
|
|
453
|
+
const vis = this.isInClass ? this.visibilityPrefix(decl.modifiers) : this.exportPrefix(decl.modifiers);
|
|
448
454
|
const hasChildren = decl.params.some((p) => p.name === "children");
|
|
455
|
+
const propsParams = decl.params.filter((p) => p.name !== "children");
|
|
449
456
|
// Props interface
|
|
450
|
-
if (
|
|
457
|
+
if (!this.isInClass && propsParams.length > 0) {
|
|
451
458
|
this.w.writeIndentedLine(`interface ${decl.name}Props {`);
|
|
452
459
|
this.w.pushIndent();
|
|
453
|
-
for (const p of
|
|
454
|
-
const optional = p.defaultValue
|
|
460
|
+
for (const p of propsParams) {
|
|
461
|
+
const optional = p.defaultValue ? "?" : "";
|
|
455
462
|
const typeStr = this.opts.emitTypes ? this.emitTypeRef(p.type) : "any";
|
|
456
463
|
this.w.writeIndentedLine(`readonly ${p.name}${optional}: ${typeStr};`);
|
|
457
464
|
}
|
|
@@ -459,21 +466,32 @@ class CodeGenerator {
|
|
|
459
466
|
this.w.writeIndentedLine(`}`);
|
|
460
467
|
this.w.writeLine();
|
|
461
468
|
}
|
|
462
|
-
const
|
|
463
|
-
? `{ ${
|
|
464
|
-
|
|
465
|
-
|
|
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} {`);
|
|
466
490
|
this.w.pushIndent();
|
|
467
491
|
this.emitComponentBlock(decl.body);
|
|
468
492
|
this.w.popIndent();
|
|
469
493
|
this.w.writeIndentedLine(`}`);
|
|
470
494
|
}
|
|
471
|
-
/**
|
|
472
|
-
* Emit the body of a `component fun` block.
|
|
473
|
-
* The last statement is emitted in tail position — if it is an expression,
|
|
474
|
-
* an if/else chain, or a when block, the innermost UI call is implicitly
|
|
475
|
-
* returned (implicit return of the last expression).
|
|
476
|
-
*/
|
|
477
495
|
emitComponentBlock(block) {
|
|
478
496
|
const stmts = block.statements;
|
|
479
497
|
for (let i = 0; i < stmts.length; i++) {
|
|
@@ -486,10 +504,8 @@ class CodeGenerator {
|
|
|
486
504
|
}
|
|
487
505
|
}
|
|
488
506
|
}
|
|
489
|
-
/** Emit a statement in tail position, adding implicit return where applicable. */
|
|
490
507
|
emitTailStmt(stmt) {
|
|
491
508
|
if (stmt.kind === "ExprStmt") {
|
|
492
|
-
// Implicit return: last expression in a component block
|
|
493
509
|
this.w.writeIndentedLine(`return ${this.emitExpr(stmt.expr)};`);
|
|
494
510
|
}
|
|
495
511
|
else if (stmt.kind === "IfStmt") {
|
|
@@ -502,7 +518,6 @@ class CodeGenerator {
|
|
|
502
518
|
this.emitStmt(stmt);
|
|
503
519
|
}
|
|
504
520
|
}
|
|
505
|
-
/** Emit a block in tail position, treating its last statement as a tail expression. */
|
|
506
521
|
emitTailBlock(block) {
|
|
507
522
|
const stmts = block.statements;
|
|
508
523
|
for (let i = 0; i < stmts.length; i++) {
|
|
@@ -515,7 +530,6 @@ class CodeGenerator {
|
|
|
515
530
|
}
|
|
516
531
|
}
|
|
517
532
|
}
|
|
518
|
-
/** Like emitIfStmt but with tail-return applied recursively to each branch body. */
|
|
519
533
|
emitIfStmtWithTailReturn(stmt) {
|
|
520
534
|
this.w.writeIndentedLine(`if (${this.emitExpr(stmt.condition)}) {`);
|
|
521
535
|
this.w.pushIndent();
|
|
@@ -538,7 +552,6 @@ class CodeGenerator {
|
|
|
538
552
|
this.w.writeIndentedLine(`}`);
|
|
539
553
|
}
|
|
540
554
|
}
|
|
541
|
-
/** Like emitWhenStmt but with tail-return applied recursively to each branch body. */
|
|
542
555
|
emitWhenStmtWithTailReturn(stmt) {
|
|
543
556
|
const subjectVar = stmt.subject ? "__when_subject__" : null;
|
|
544
557
|
if (stmt.subject) {
|
|
@@ -568,6 +581,8 @@ class CodeGenerator {
|
|
|
568
581
|
}
|
|
569
582
|
// ── regular class ──────────────────────────────────────────────────────────
|
|
570
583
|
emitClassDecl(decl) {
|
|
584
|
+
if (decl.modifiers.modifiers.includes("external"))
|
|
585
|
+
return;
|
|
571
586
|
this.emitAnnotations(decl.modifiers);
|
|
572
587
|
const vis = this.exportPrefix(decl.modifiers);
|
|
573
588
|
const abstract = decl.modifiers.modifiers.includes("abstract") ? "abstract " : "";
|
|
@@ -575,15 +590,12 @@ class CodeGenerator {
|
|
|
575
590
|
const superTypes = this.emitSuperTypesStr(decl.superTypes);
|
|
576
591
|
this.w.writeIndentedLine(`${vis}${abstract}class ${decl.name}${typeParams}${superTypes} {`);
|
|
577
592
|
this.w.pushIndent();
|
|
578
|
-
// The first supertype's delegateArgs become the `super(args)` call inside the constructor.
|
|
579
593
|
const superDelegateArgs = decl.superTypes[0]?.delegateArgs ?? null;
|
|
580
594
|
if (decl.primaryConstructor) {
|
|
581
595
|
const initBlocks = decl.body?.members.filter((m) => m.kind === "InitBlock") ?? [];
|
|
582
596
|
this.emitPrimaryConstructorProps(decl.primaryConstructor.params, initBlocks, superDelegateArgs);
|
|
583
597
|
}
|
|
584
598
|
else if (superDelegateArgs !== null) {
|
|
585
|
-
// No primary constructor but the supertype has explicit delegation args —
|
|
586
|
-
// emit a minimal constructor that forwards them to super().
|
|
587
599
|
const superArgsStr = superDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
|
|
588
600
|
this.w.writeIndentedLine(`constructor() {`);
|
|
589
601
|
this.w.pushIndent();
|
|
@@ -606,24 +618,20 @@ class CodeGenerator {
|
|
|
606
618
|
const props = decl.primaryConstructor.params;
|
|
607
619
|
this.w.writeIndentedLine(`${vis}class ${decl.name}${typeParams}${superTypes} {`);
|
|
608
620
|
this.w.pushIndent();
|
|
609
|
-
// Discriminant for sealed class sub-types
|
|
610
621
|
if (kindName) {
|
|
611
622
|
this.w.writeIndentedLine(`readonly __kind = "${kindName}" as const;`);
|
|
612
623
|
}
|
|
613
|
-
// Constructor params → readonly properties
|
|
614
624
|
for (const p of props) {
|
|
615
625
|
const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
616
626
|
this.w.writeIndentedLine(`readonly ${p.name}${t};`);
|
|
617
627
|
}
|
|
618
628
|
this.w.writeLine();
|
|
619
|
-
// Constructor
|
|
620
629
|
const ctorParams = props.map((p) => {
|
|
621
630
|
const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
622
631
|
return `${p.name}${t}`;
|
|
623
632
|
}).join(", ");
|
|
624
633
|
this.w.writeIndentedLine(`constructor(${ctorParams}) {`);
|
|
625
634
|
this.w.pushIndent();
|
|
626
|
-
// Emit super() with actual delegation args (or empty call if delegateArgs is []).
|
|
627
635
|
const dataSuperDelegateArgs = decl.superTypes[0]?.delegateArgs ?? null;
|
|
628
636
|
if (dataSuperDelegateArgs !== null) {
|
|
629
637
|
const superArgsStr = dataSuperDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
|
|
@@ -635,7 +643,6 @@ class CodeGenerator {
|
|
|
635
643
|
this.w.popIndent();
|
|
636
644
|
this.w.writeIndentedLine(`}`);
|
|
637
645
|
this.w.writeLine();
|
|
638
|
-
// copy()
|
|
639
646
|
const copyParams = props.map((p) => {
|
|
640
647
|
const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
641
648
|
return `${p.name}${t} = this.${p.name}`;
|
|
@@ -647,7 +654,6 @@ class CodeGenerator {
|
|
|
647
654
|
this.w.popIndent();
|
|
648
655
|
this.w.writeIndentedLine(`}`);
|
|
649
656
|
this.w.writeLine();
|
|
650
|
-
// equals()
|
|
651
657
|
this.runtimeSymbolsNeeded.add("jalvinEquals");
|
|
652
658
|
const eqChecks = props.map((p) => `jalvinEquals(this.${p.name}, other.${p.name})`).join(" && ") || "true";
|
|
653
659
|
this.w.writeIndentedLine(`equals(other: unknown): boolean {`);
|
|
@@ -657,7 +663,6 @@ class CodeGenerator {
|
|
|
657
663
|
this.w.popIndent();
|
|
658
664
|
this.w.writeIndentedLine(`}`);
|
|
659
665
|
this.w.writeLine();
|
|
660
|
-
// toString()
|
|
661
666
|
const toStringParts = props.map((p) => `${p.name}=\${this.${p.name}}`).join(", ");
|
|
662
667
|
this.w.writeIndentedLine(`toString(): string {`);
|
|
663
668
|
this.w.pushIndent();
|
|
@@ -665,7 +670,6 @@ class CodeGenerator {
|
|
|
665
670
|
this.w.popIndent();
|
|
666
671
|
this.w.writeIndentedLine(`}`);
|
|
667
672
|
this.w.writeLine();
|
|
668
|
-
// hashCode() — djb2
|
|
669
673
|
this.w.writeIndentedLine(`hashCode(): number {`);
|
|
670
674
|
this.w.pushIndent();
|
|
671
675
|
this.w.writeIndentedLine(`let h = 17;`);
|
|
@@ -701,7 +705,6 @@ class CodeGenerator {
|
|
|
701
705
|
}
|
|
702
706
|
}
|
|
703
707
|
}
|
|
704
|
-
// Emit the abstract base class (only non-subtype members in the body)
|
|
705
708
|
this.w.writeIndentedLine(`${vis}abstract class ${decl.name}${typeParams} {`);
|
|
706
709
|
this.w.pushIndent();
|
|
707
710
|
this.w.writeIndentedLine(`abstract readonly __kind: string;`);
|
|
@@ -714,7 +717,6 @@ class CodeGenerator {
|
|
|
714
717
|
this.w.writeLine();
|
|
715
718
|
if (subDecls.length === 0)
|
|
716
719
|
return;
|
|
717
|
-
// Emit non-object sub-declarations at module level (before namespace)
|
|
718
720
|
for (const sub of subDecls) {
|
|
719
721
|
if (sub.kind === "DataClassDecl") {
|
|
720
722
|
this.emitDataClassDecl(sub, sub.name);
|
|
@@ -725,12 +727,10 @@ class CodeGenerator {
|
|
|
725
727
|
this.w.writeLine();
|
|
726
728
|
}
|
|
727
729
|
}
|
|
728
|
-
// Emit namespace merging so SealedClass.SubType is accessible
|
|
729
730
|
this.w.writeIndentedLine(`${vis}namespace ${decl.name} {`);
|
|
730
731
|
this.w.pushIndent();
|
|
731
732
|
for (const sub of subDecls) {
|
|
732
733
|
if (sub.kind === "ObjectDecl") {
|
|
733
|
-
// Singleton object — emit inline in namespace with __kind discriminant
|
|
734
734
|
const superStr = sub.superTypes.length > 0
|
|
735
735
|
? this.emitSuperTypesStr(sub.superTypes)
|
|
736
736
|
: ` extends ${decl.name}`;
|
|
@@ -746,7 +746,6 @@ class CodeGenerator {
|
|
|
746
746
|
this.w.writeIndentedLine(`export const ${sub.name} = new (class${superStr} { ${membersStr} })();`);
|
|
747
747
|
}
|
|
748
748
|
else {
|
|
749
|
-
// DataClassDecl / ClassDecl — already emitted at module level, re-export
|
|
750
749
|
this.w.writeIndentedLine(`export { ${sub.name} };`);
|
|
751
750
|
}
|
|
752
751
|
}
|
|
@@ -757,12 +756,8 @@ class CodeGenerator {
|
|
|
757
756
|
emitEnumClassDecl(decl) {
|
|
758
757
|
const vis = this.exportPrefix(decl.modifiers);
|
|
759
758
|
const typeParams = this.emitTypeParamsStr(decl.typeParams);
|
|
760
|
-
// Emit the enum as a TypeScript class with static singleton instances.
|
|
761
|
-
// This preserves the `EnumClass.ENTRY` access pattern and allows
|
|
762
|
-
// methods/properties to be attached to entries.
|
|
763
759
|
this.w.writeIndentedLine(`${vis}class ${decl.name}${typeParams} {`);
|
|
764
760
|
this.w.pushIndent();
|
|
765
|
-
// Private constructor so entries are the only instances
|
|
766
761
|
if (decl.primaryConstructor && decl.primaryConstructor.params.length > 0) {
|
|
767
762
|
const ctorParams = decl.primaryConstructor.params
|
|
768
763
|
.map((p) => {
|
|
@@ -777,7 +772,6 @@ class CodeGenerator {
|
|
|
777
772
|
this.w.writeIndentedLine(`private constructor(readonly name: string, readonly ordinal: number) {}`);
|
|
778
773
|
}
|
|
779
774
|
this.w.writeLine();
|
|
780
|
-
// Static entry instances
|
|
781
775
|
for (let i = 0; i < decl.entries.length; i++) {
|
|
782
776
|
const entry = decl.entries[i];
|
|
783
777
|
const args = entry.args.length > 0
|
|
@@ -787,7 +781,6 @@ class CodeGenerator {
|
|
|
787
781
|
}
|
|
788
782
|
if (decl.entries.length > 0) {
|
|
789
783
|
this.w.writeLine();
|
|
790
|
-
// values() helper
|
|
791
784
|
const entryList = decl.entries.map((e) => `${decl.name}.${e.name}`).join(", ");
|
|
792
785
|
this.w.writeIndentedLine(`static values(): ${decl.name}[] { return [${entryList}]; }`);
|
|
793
786
|
this.w.writeIndentedLine(`static valueOf(name: string): ${decl.name} {`);
|
|
@@ -805,14 +798,12 @@ class CodeGenerator {
|
|
|
805
798
|
this.w.popIndent();
|
|
806
799
|
this.w.writeIndentedLine(`}`);
|
|
807
800
|
}
|
|
808
|
-
// ── destructuring declaration — val (a, b) = expr ─────────────────────────
|
|
809
801
|
emitDestructuringDecl(decl, _memberOf) {
|
|
810
802
|
const kw = decl.mutable ? "let" : "const";
|
|
811
803
|
const names = decl.names.map((n) => n ?? "_").join(", ");
|
|
812
804
|
const init = this.emitExpr(decl.initializer);
|
|
813
805
|
this.w.writeIndentedLine(`${kw} [${names}] = ${init};`);
|
|
814
806
|
}
|
|
815
|
-
// ── interface ──────────────────────────────────────────────────────────────
|
|
816
807
|
emitInterfaceDecl(decl) {
|
|
817
808
|
this.emitAnnotations(decl.modifiers);
|
|
818
809
|
const vis = this.exportPrefix(decl.modifiers);
|
|
@@ -830,12 +821,9 @@ class CodeGenerator {
|
|
|
830
821
|
this.w.popIndent();
|
|
831
822
|
this.w.writeIndentedLine(`}`);
|
|
832
823
|
}
|
|
833
|
-
// ── object declaration → singleton ────────────────────────────────────────
|
|
834
824
|
emitObjectDecl(decl) {
|
|
835
|
-
if (!decl.name)
|
|
836
|
-
// Anonymous object — emitted inline
|
|
825
|
+
if (!decl.name)
|
|
837
826
|
return;
|
|
838
|
-
}
|
|
839
827
|
this.emitAnnotations(decl.modifiers);
|
|
840
828
|
const vis = this.exportPrefix(decl.modifiers);
|
|
841
829
|
const superTypes = this.emitSuperTypesStr(decl.superTypes);
|
|
@@ -845,16 +833,11 @@ class CodeGenerator {
|
|
|
845
833
|
this.w.popIndent();
|
|
846
834
|
this.w.writeIndentedLine(`})();`);
|
|
847
835
|
}
|
|
848
|
-
// ── type alias ─────────────────────────────────────────────────────────────
|
|
849
836
|
emitTypeAliasDecl(decl) {
|
|
850
837
|
const vis = this.exportPrefix(decl.modifiers);
|
|
851
838
|
const typeParams = this.emitTypeParamsStr(decl.typeParams);
|
|
852
839
|
this.w.writeIndentedLine(`${vis}type ${decl.name}${typeParams} = ${this.emitTypeRef(decl.type)};`);
|
|
853
840
|
}
|
|
854
|
-
// ── extension functions ────────────────────────────────────────────────────
|
|
855
|
-
// Emitted as standalone free functions with receiver as explicit first param.
|
|
856
|
-
// For class types, also patched onto the prototype for dot-call syntax.
|
|
857
|
-
// For primitive types, call sites are rewritten via `primitiveExtensions`.
|
|
858
841
|
emitExtensionFunDecl(decl) {
|
|
859
842
|
const isSuspend = AST.isSuspend(decl.modifiers);
|
|
860
843
|
const asyncKw = isSuspend ? "async " : "";
|
|
@@ -865,9 +848,6 @@ class CodeGenerator {
|
|
|
865
848
|
? `: ${this.emitTypeRef(decl.returnType)}`
|
|
866
849
|
: "";
|
|
867
850
|
const fnName = `__ext_${receiverType.replace(/[^a-zA-Z0-9_]/g, "_")}_${decl.name}`;
|
|
868
|
-
// Emit as a free function: receiver is the first explicit parameter `$receiver`.
|
|
869
|
-
// Inside the function body, `this` is rebound to `$receiver` so Jalvin's
|
|
870
|
-
// `this.prop` references work correctly.
|
|
871
851
|
this.w.writeIndentedLine(`${asyncKw}function ${fnName}${typeParams}($receiver: ${receiverType}${params ? ", " + params : ""})${retType} {`);
|
|
872
852
|
this.w.pushIndent();
|
|
873
853
|
if (decl.body.kind === "Block") {
|
|
@@ -886,14 +866,12 @@ class CodeGenerator {
|
|
|
886
866
|
const simpleReceiverName = this.receiverClassName(decl.receiver);
|
|
887
867
|
if (simpleReceiverName) {
|
|
888
868
|
if (PRIMITIVE_TYPES.has(simpleReceiverName)) {
|
|
889
|
-
// Register for call-site rewriting — cannot patch primitive prototypes
|
|
890
869
|
if (!this.primitiveExtensions.has(simpleReceiverName)) {
|
|
891
870
|
this.primitiveExtensions.set(simpleReceiverName, new Map());
|
|
892
871
|
}
|
|
893
872
|
this.primitiveExtensions.get(simpleReceiverName).set(decl.name, fnName);
|
|
894
873
|
}
|
|
895
874
|
else {
|
|
896
|
-
// Class types — prototype monkey-patch for dot-call syntax
|
|
897
875
|
this.w.writeIndentedLine(`// Extension: ${receiverType}.${decl.name}`);
|
|
898
876
|
this.w.writeIndentedLine(`(${simpleReceiverName}.prototype as any).${decl.name} = function(this: ${receiverType}, ...args: unknown[]) { return (${fnName} as Function)(this, ...args); };`);
|
|
899
877
|
}
|
|
@@ -906,11 +884,6 @@ class CodeGenerator {
|
|
|
906
884
|
return ref.base.name[ref.base.name.length - 1] ?? null;
|
|
907
885
|
return null;
|
|
908
886
|
}
|
|
909
|
-
/**
|
|
910
|
-
* Maps a JType to the primitive receiver type name used as a key
|
|
911
|
-
* in `primitiveExtensions` (e.g. JType `{ tag: "string" }` → `"String"`).
|
|
912
|
-
* Returns null for non-primitive / unknown types.
|
|
913
|
-
*/
|
|
914
887
|
jTypeToReceiverName(type) {
|
|
915
888
|
const tagToReceiverName = {
|
|
916
889
|
string: "String",
|
|
@@ -930,19 +903,16 @@ class CodeGenerator {
|
|
|
930
903
|
m.name === "invoke" &&
|
|
931
904
|
m.modifiers.modifiers.includes("operator"));
|
|
932
905
|
}
|
|
933
|
-
// ── property declaration ───────────────────────────────────────────────────
|
|
934
906
|
emitPropertyDecl(decl, member, isLocal = false) {
|
|
935
907
|
this.emitAnnotations(decl.modifiers);
|
|
936
908
|
const vis = member ? this.visibilityPrefix(decl.modifiers) : isLocal ? "" : this.exportPrefix(decl.modifiers);
|
|
937
909
|
const isConst = decl.modifiers.modifiers.includes("const");
|
|
938
910
|
const isLateinit = decl.modifiers.modifiers.includes("lateinit");
|
|
939
|
-
// `const val` → `const` at module level; inside classes treated as `static readonly`
|
|
940
911
|
const kw = member
|
|
941
912
|
? (decl.mutable ? "" : "readonly ")
|
|
942
913
|
: (isConst ? "const " : decl.mutable ? "let " : "const ");
|
|
943
914
|
const type = decl.type && this.opts.emitTypes ? `: ${this.emitTypeRef(decl.type)}` : "";
|
|
944
915
|
if (decl.delegate) {
|
|
945
|
-
// Delegated property — wrap in a getter/setter pair using the delegate
|
|
946
916
|
this.runtimeSymbolsNeeded.add("delegate");
|
|
947
917
|
const delegateExpr = this.emitExpr(decl.delegate);
|
|
948
918
|
if (member) {
|
|
@@ -952,28 +922,22 @@ class CodeGenerator {
|
|
|
952
922
|
}
|
|
953
923
|
}
|
|
954
924
|
else {
|
|
955
|
-
// Non-member delegate: resolve via delegate().getValue() so the identifier
|
|
956
|
-
// holds the delegated value, not the raw delegate wrapper object.
|
|
957
925
|
this.w.writeIndentedLine(`${kw}${decl.name}${type} = delegate(${delegateExpr}, "${decl.name}", null).getValue();`);
|
|
958
926
|
}
|
|
959
927
|
return;
|
|
960
928
|
}
|
|
961
|
-
const init = decl.initializer ? ` = ${this.emitExpr(decl.initializer)}` :
|
|
929
|
+
const init = decl.initializer ? ` = ${this.emitExpr(decl.initializer)}` : "";
|
|
962
930
|
if (member && !decl.getter) {
|
|
963
|
-
// Skip backing field when a getter is defined — TypeScript forbids both
|
|
964
|
-
// `readonly name: T` and `get name()` with the same name in a class.
|
|
965
931
|
const modStr = isConst ? "static readonly " : decl.mutable ? "" : "readonly ";
|
|
966
932
|
this.w.writeIndentedLine(`${vis}${modStr}${decl.name}${type}${init};`);
|
|
967
933
|
}
|
|
968
934
|
else if (!member) {
|
|
969
935
|
this.w.writeIndentedLine(`${vis}${kw}${decl.name}${type}${init};`);
|
|
970
936
|
}
|
|
971
|
-
if (decl.getter)
|
|
937
|
+
if (decl.getter)
|
|
972
938
|
this.emitPropertyAccessor("get", decl.name, decl.getter, member, type);
|
|
973
|
-
|
|
974
|
-
if (decl.setter) {
|
|
939
|
+
if (decl.setter)
|
|
975
940
|
this.emitPropertyAccessor("set", decl.name, decl.setter, member, type);
|
|
976
|
-
}
|
|
977
941
|
}
|
|
978
942
|
emitPropertyAccessor(kind, name, acc, member, type) {
|
|
979
943
|
const vis = this.visibilityPrefix(acc.modifiers);
|
|
@@ -993,14 +957,19 @@ class CodeGenerator {
|
|
|
993
957
|
this.w.popIndent();
|
|
994
958
|
this.w.writeIndentedLine(`}`);
|
|
995
959
|
}
|
|
996
|
-
// ── class body ─────────────────────────────────────────────────────────────
|
|
997
960
|
emitClassBody(body) {
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
961
|
+
const old = this.isInClass;
|
|
962
|
+
this.isInClass = true;
|
|
963
|
+
try {
|
|
964
|
+
for (const member of body.members) {
|
|
965
|
+
if (member.kind === "InitBlock")
|
|
966
|
+
continue;
|
|
967
|
+
this.emitClassMember(member);
|
|
968
|
+
this.w.writeLine();
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
finally {
|
|
972
|
+
this.isInClass = old;
|
|
1004
973
|
}
|
|
1005
974
|
}
|
|
1006
975
|
emitClassMember(member) {
|
|
@@ -1032,7 +1001,6 @@ class CodeGenerator {
|
|
|
1032
1001
|
case "CompanionObject":
|
|
1033
1002
|
this.emitCompanionObject(member);
|
|
1034
1003
|
break;
|
|
1035
|
-
case "InitBlock": /* handled in constructor */ break;
|
|
1036
1004
|
case "SecondaryConstructor":
|
|
1037
1005
|
this.emitSecondaryConstructor(member);
|
|
1038
1006
|
break;
|
|
@@ -1049,16 +1017,13 @@ class CodeGenerator {
|
|
|
1049
1017
|
this.w.writeIndentedLine(`${ro}${p.name}${type};`);
|
|
1050
1018
|
}
|
|
1051
1019
|
}
|
|
1052
|
-
// Constructor
|
|
1053
1020
|
const ctorParams = params.map((p) => {
|
|
1054
|
-
const ro = p.propertyKind === "val" ? "readonly " : p.propertyKind === "var" ? "" : "";
|
|
1055
1021
|
const type = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
1056
1022
|
const def = p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "";
|
|
1057
1023
|
return `${p.name}${type}${def}`;
|
|
1058
1024
|
});
|
|
1059
1025
|
this.w.writeIndentedLine(`constructor(${ctorParams.join(", ")}) {`);
|
|
1060
1026
|
this.w.pushIndent();
|
|
1061
|
-
// super() call must be the first statement if there is a supertype.
|
|
1062
1027
|
if (superDelegateArgs !== null && superDelegateArgs !== undefined) {
|
|
1063
1028
|
const superArgsStr = superDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
|
|
1064
1029
|
this.w.writeIndentedLine(`super(${superArgsStr});`);
|
|
@@ -1068,17 +1033,13 @@ class CodeGenerator {
|
|
|
1068
1033
|
this.w.writeIndentedLine(`this.${p.name} = ${p.name};`);
|
|
1069
1034
|
}
|
|
1070
1035
|
}
|
|
1071
|
-
|
|
1072
|
-
for (const ib of (initBlocks ?? [])) {
|
|
1036
|
+
for (const ib of (initBlocks ?? []))
|
|
1073
1037
|
this.emitBlock(ib.body);
|
|
1074
|
-
}
|
|
1075
1038
|
this.w.popIndent();
|
|
1076
1039
|
this.w.writeIndentedLine(`}`);
|
|
1077
1040
|
this.w.writeLine();
|
|
1078
1041
|
}
|
|
1079
1042
|
emitCompanionObject(co) {
|
|
1080
|
-
// Emit companion object members as `static` members of the outer class so that
|
|
1081
|
-
// `MyClass.factoryMethod()` works directly (standard semantics).
|
|
1082
1043
|
for (const member of co.body.members) {
|
|
1083
1044
|
if (member.kind === "FunDecl") {
|
|
1084
1045
|
const vis = this.visibilityPrefix(member.modifiers);
|
|
@@ -1123,11 +1084,9 @@ class CodeGenerator {
|
|
|
1123
1084
|
this.w.popIndent();
|
|
1124
1085
|
this.w.writeIndentedLine(`}`);
|
|
1125
1086
|
}
|
|
1126
|
-
// ── Statements ─────────────────────────────────────────────────────────────
|
|
1127
1087
|
emitBlock(block) {
|
|
1128
|
-
for (const stmt of block.statements)
|
|
1088
|
+
for (const stmt of block.statements)
|
|
1129
1089
|
this.emitStmt(stmt);
|
|
1130
|
-
}
|
|
1131
1090
|
}
|
|
1132
1091
|
emitStmt(stmt) {
|
|
1133
1092
|
switch (stmt.kind) {
|
|
@@ -1216,8 +1175,6 @@ class CodeGenerator {
|
|
|
1216
1175
|
}
|
|
1217
1176
|
}
|
|
1218
1177
|
emitWhenStmt(stmt) {
|
|
1219
|
-
// when(subject) { is Foo -> ... else -> ... }
|
|
1220
|
-
// Compiles to a series of if/else-if chains
|
|
1221
1178
|
const subjectVar = stmt.subject ? "__when_subject__" : null;
|
|
1222
1179
|
if (stmt.subject) {
|
|
1223
1180
|
const binding = stmt.subject.binding ?? subjectVar;
|
|
@@ -1234,12 +1191,10 @@ class CodeGenerator {
|
|
|
1234
1191
|
}
|
|
1235
1192
|
first = false;
|
|
1236
1193
|
this.w.pushIndent();
|
|
1237
|
-
if (branch.body.kind === "Block")
|
|
1194
|
+
if (branch.body.kind === "Block")
|
|
1238
1195
|
this.emitBlock(branch.body);
|
|
1239
|
-
|
|
1240
|
-
else {
|
|
1196
|
+
else
|
|
1241
1197
|
this.w.writeIndentedLine(`${this.emitExpr(branch.body)};`);
|
|
1242
|
-
}
|
|
1243
1198
|
this.w.popIndent();
|
|
1244
1199
|
}
|
|
1245
1200
|
this.w.writeIndentedLine(`}`);
|
|
@@ -1247,8 +1202,6 @@ class CodeGenerator {
|
|
|
1247
1202
|
emitWhenCondition(cond, subject) {
|
|
1248
1203
|
switch (cond.kind) {
|
|
1249
1204
|
case "WhenIsCondition": {
|
|
1250
|
-
// Qualified name (e.g. UiState.Loading) → use __kind discriminant
|
|
1251
|
-
// Single name (e.g. BibiError) → use instanceof
|
|
1252
1205
|
const isQualified = cond.type.kind === "SimpleTypeRef" && cond.type.name.length > 1;
|
|
1253
1206
|
const check = isQualified
|
|
1254
1207
|
? `(${subject} as any).__kind === "${cond.type.kind === "SimpleTypeRef" ? cond.type.name[cond.type.name.length - 1] : ""}"`
|
|
@@ -1278,7 +1231,6 @@ class CodeGenerator {
|
|
|
1278
1231
|
}
|
|
1279
1232
|
else {
|
|
1280
1233
|
const { key, value } = stmt.binding;
|
|
1281
|
-
// Use .entries() for JS Map (Map<K,V>), Object.entries() for plain objects
|
|
1282
1234
|
const iterableType = this.typeMap.get(stmt.iterable);
|
|
1283
1235
|
const isMap = iterableType?.tag === "class" && iterableType.name === "Map";
|
|
1284
1236
|
const entries = isMap ? `${iter}.entries()` : `Object.entries(${iter})`;
|
|
@@ -1297,9 +1249,6 @@ class CodeGenerator {
|
|
|
1297
1249
|
for (const c of stmt.catches) {
|
|
1298
1250
|
this.w.writeIndentedLine(`} catch (${c.name}: unknown) {`);
|
|
1299
1251
|
this.w.pushIndent();
|
|
1300
|
-
// Narrow type — always emit the guard so catch clauses are type-safe
|
|
1301
|
-
// regardless of the emitTypes option (which controls explicit type annotations,
|
|
1302
|
-
// not runtime type checks).
|
|
1303
1252
|
this.w.writeIndentedLine(`if (!(${c.name} instanceof ${this.emitTypeRef(c.type)})) throw ${c.name};`);
|
|
1304
1253
|
this.emitBlock(c.body);
|
|
1305
1254
|
this.w.popIndent();
|
|
@@ -1312,7 +1261,6 @@ class CodeGenerator {
|
|
|
1312
1261
|
}
|
|
1313
1262
|
this.w.writeIndentedLine(`}`);
|
|
1314
1263
|
}
|
|
1315
|
-
// ── Expressions ────────────────────────────────────────────────────────────
|
|
1316
1264
|
emitExpr(expr) {
|
|
1317
1265
|
switch (expr.kind) {
|
|
1318
1266
|
case "IntLiteralExpr": return String(expr.value);
|
|
@@ -1322,17 +1270,12 @@ class CodeGenerator {
|
|
|
1322
1270
|
case "BooleanLiteralExpr": return String(expr.value);
|
|
1323
1271
|
case "NullLiteralExpr": return "null";
|
|
1324
1272
|
case "StringLiteralExpr":
|
|
1325
|
-
return expr.raw
|
|
1326
|
-
? `\`${expr.value}\``
|
|
1327
|
-
: JSON.stringify(expr.value);
|
|
1273
|
+
return expr.raw ? `\`${expr.value}\`` : JSON.stringify(expr.value);
|
|
1328
1274
|
case "StringTemplateExpr":
|
|
1329
1275
|
return this.emitStringTemplate(expr);
|
|
1330
1276
|
case "NameExpr":
|
|
1331
|
-
|
|
1332
|
-
if (expr.name === "Int" || expr.name === "Long") {
|
|
1277
|
+
if (expr.name === "Int" || expr.name === "Long")
|
|
1333
1278
|
this.runtimeSymbolsNeeded.add(expr.name);
|
|
1334
|
-
}
|
|
1335
|
-
// Unit singleton emits as undefined (void semantics)
|
|
1336
1279
|
if (expr.name === "Unit")
|
|
1337
1280
|
return "undefined";
|
|
1338
1281
|
return expr.name;
|
|
@@ -1348,34 +1291,23 @@ class CodeGenerator {
|
|
|
1348
1291
|
if (opMethod) {
|
|
1349
1292
|
const l = this.emitExpr(expr.left);
|
|
1350
1293
|
const r = this.emitExpr(expr.right);
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
return `(${l}.compareTo(${r}) ${cmpOp} 0)`;
|
|
1355
|
-
}
|
|
1356
|
-
// `contains` overloads: `element in collection` → `collection.contains(element)`
|
|
1357
|
-
if (opMethod === "contains") {
|
|
1294
|
+
if (opMethod === "compareTo")
|
|
1295
|
+
return `(${l}.compareTo(${r}) ${expr.op} 0)`;
|
|
1296
|
+
if (opMethod === "contains")
|
|
1358
1297
|
return `${r}.contains(${l})`;
|
|
1359
|
-
|
|
1360
|
-
if (opMethod === "!contains") {
|
|
1298
|
+
if (opMethod === "!contains")
|
|
1361
1299
|
return `!${r}.contains(${l})`;
|
|
1362
|
-
}
|
|
1363
|
-
// Generic operator overload: emit as method call `left.method(right)`
|
|
1364
1300
|
return `${l}.${opMethod}(${r})`;
|
|
1365
1301
|
}
|
|
1366
|
-
// `==` → structural equality, `!=` → negation
|
|
1367
1302
|
if (expr.op === "==" || expr.op === "!=") {
|
|
1368
1303
|
this.runtimeSymbolsNeeded.add("jalvinEquals");
|
|
1369
1304
|
const eq = `jalvinEquals(${this.emitExpr(expr.left)}, ${this.emitExpr(expr.right)})`;
|
|
1370
1305
|
return expr.op === "==" ? eq : `!${eq}`;
|
|
1371
1306
|
}
|
|
1372
|
-
|
|
1373
|
-
if (expr.op === "in") {
|
|
1307
|
+
if (expr.op === "in")
|
|
1374
1308
|
return `${this.emitExpr(expr.right)}.includes?.(${this.emitExpr(expr.left)}) ?? (${this.emitExpr(expr.left)} in ${this.emitExpr(expr.right)})`;
|
|
1375
|
-
|
|
1376
|
-
if (expr.op === "!in") {
|
|
1309
|
+
if (expr.op === "!in")
|
|
1377
1310
|
return `!(${this.emitExpr(expr.right)}.includes?.(${this.emitExpr(expr.left)}) ?? (${this.emitExpr(expr.left)} in ${this.emitExpr(expr.right)}))`;
|
|
1378
|
-
}
|
|
1379
1311
|
return `(${this.emitExpr(expr.left)} ${this.binaryOpStr(expr.op)} ${this.emitExpr(expr.right)})`;
|
|
1380
1312
|
}
|
|
1381
1313
|
case "AssignExpr":
|
|
@@ -1386,124 +1318,81 @@ class CodeGenerator {
|
|
|
1386
1318
|
return expr.prefix
|
|
1387
1319
|
? `${expr.op}${this.emitExpr(expr.target)}`
|
|
1388
1320
|
: `${this.emitExpr(expr.target)}${expr.op}`;
|
|
1389
|
-
case "MemberExpr":
|
|
1390
|
-
|
|
1391
|
-
case "
|
|
1392
|
-
return `${this.emitExpr(expr.target)}?.${expr.member}`;
|
|
1393
|
-
case "IndexExpr":
|
|
1394
|
-
return `${this.emitExpr(expr.target)}[${this.emitExpr(expr.index)}]`;
|
|
1321
|
+
case "MemberExpr": return `${this.emitExpr(expr.target)}.${expr.member}`;
|
|
1322
|
+
case "SafeMemberExpr": return `${this.emitExpr(expr.target)}?.${expr.member}`;
|
|
1323
|
+
case "IndexExpr": return `${this.emitExpr(expr.target)}[${this.emitExpr(expr.index)}]`;
|
|
1395
1324
|
case "NotNullExpr":
|
|
1396
|
-
// Runtime check
|
|
1397
1325
|
this.runtimeSymbolsNeeded.add("notNull");
|
|
1398
1326
|
return `notNull(${this.emitExpr(expr.expr)})`;
|
|
1399
1327
|
case "SafeCallExpr": {
|
|
1400
|
-
// x?.() — safe invocation of a nullable function value
|
|
1401
1328
|
const callee = this.emitExpr(expr.callee);
|
|
1402
|
-
const restArgs =
|
|
1403
|
-
for (const a of expr.args) {
|
|
1404
|
-
restArgs.push(a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
|
|
1405
|
-
}
|
|
1329
|
+
const restArgs = expr.args.map((a) => a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
|
|
1406
1330
|
if (expr.trailingLambda)
|
|
1407
1331
|
restArgs.push(this.emitLambdaExpr(expr.trailingLambda));
|
|
1408
1332
|
return `${callee}?.(${restArgs.join(", ")})`;
|
|
1409
1333
|
}
|
|
1410
|
-
case "ElvisExpr": {
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
case "
|
|
1415
|
-
|
|
1416
|
-
case "
|
|
1417
|
-
|
|
1418
|
-
case "
|
|
1419
|
-
return this.emitIfExpr(expr);
|
|
1420
|
-
case "WhenExpr":
|
|
1421
|
-
return this.emitWhenExpr(expr);
|
|
1422
|
-
case "TryCatchExpr":
|
|
1423
|
-
return this.emitTryCatchExpr(expr);
|
|
1424
|
-
case "TypeCheckExpr":
|
|
1425
|
-
return `${expr.negated ? "!(" : ""}${this.emitExpr(expr.expr)} instanceof ${this.emitTypeRef(expr.type)}${expr.negated ? ")" : ""}`;
|
|
1426
|
-
case "TypeCastExpr":
|
|
1427
|
-
// Unsafe cast — emit as `(expr as Type)` which is stripped at runtime
|
|
1428
|
-
return `(${this.emitExpr(expr.expr)} as unknown as ${this.emitTypeRef(expr.type)})`;
|
|
1429
|
-
case "SafeCastExpr": {
|
|
1334
|
+
case "ElvisExpr": return `(${this.emitExpr(expr.left)} ?? ${this.emitExpr(expr.right)})`;
|
|
1335
|
+
case "CallExpr": return this.emitCallExpr(expr);
|
|
1336
|
+
case "LambdaExpr": return this.emitLambdaExpr(expr);
|
|
1337
|
+
case "IfExpr": return this.emitIfExpr(expr);
|
|
1338
|
+
case "WhenExpr": return this.emitWhenExpr(expr);
|
|
1339
|
+
case "TryCatchExpr": return this.emitTryCatchExpr(expr);
|
|
1340
|
+
case "TypeCheckExpr": return `${expr.negated ? "!(" : ""}${this.emitExpr(expr.expr)} instanceof ${this.emitTypeRef(expr.type)}${expr.negated ? ")" : ""}`;
|
|
1341
|
+
case "TypeCastExpr": return `(${this.emitExpr(expr.expr)} as unknown as ${this.emitTypeRef(expr.type)})`;
|
|
1342
|
+
case "SafeCastExpr":
|
|
1430
1343
|
this.runtimeSymbolsNeeded.add("safeCast");
|
|
1431
1344
|
return `safeCast(${this.emitExpr(expr.expr)}, ${this.emitTypeRef(expr.type)})`;
|
|
1432
|
-
|
|
1433
|
-
case "RangeExpr": {
|
|
1345
|
+
case "RangeExpr":
|
|
1434
1346
|
this.runtimeSymbolsNeeded.add("range");
|
|
1435
1347
|
return `range(${this.emitExpr(expr.from)}, ${this.emitExpr(expr.to)}, ${expr.inclusive})`;
|
|
1436
|
-
}
|
|
1437
1348
|
case "LaunchExpr": {
|
|
1438
|
-
// fire-and-forget → void IIFE
|
|
1439
1349
|
const stmts = this.captureBlock(expr.body);
|
|
1440
1350
|
return `(async () => { ${stmts} })()`;
|
|
1441
1351
|
}
|
|
1442
1352
|
case "AsyncExpr": {
|
|
1443
|
-
// returns Promise<T>
|
|
1444
1353
|
const stmts = this.captureBlock(expr.body);
|
|
1445
1354
|
return `(async () => { ${stmts} })()`;
|
|
1446
1355
|
}
|
|
1447
|
-
case "CollectionLiteralExpr":
|
|
1448
|
-
|
|
1449
|
-
case "ObjectExpr":
|
|
1450
|
-
return this.emitObjectExpr(expr);
|
|
1356
|
+
case "CollectionLiteralExpr": return this.emitCollectionLiteral(expr);
|
|
1357
|
+
case "ObjectExpr": return this.emitObjectExpr(expr);
|
|
1451
1358
|
case "ReturnExpr": {
|
|
1452
1359
|
const val = expr.value ? ` ${this.emitExpr(expr.value)}` : "";
|
|
1453
1360
|
return `((() => { return${val}; })())`;
|
|
1454
1361
|
}
|
|
1455
1362
|
case "BreakExpr": return "undefined /* break */";
|
|
1456
1363
|
case "ContinueExpr": return "undefined /* continue */";
|
|
1457
|
-
case "JsxElement":
|
|
1458
|
-
|
|
1459
|
-
default:
|
|
1460
|
-
return "undefined";
|
|
1364
|
+
case "JsxElement": return this.emitJsxElement(expr);
|
|
1365
|
+
default: return "undefined";
|
|
1461
1366
|
}
|
|
1462
1367
|
}
|
|
1463
|
-
// ── JSX ──────────────────────────────────────────────────────────────────
|
|
1464
1368
|
emitJsxElement(expr) {
|
|
1369
|
+
this.runtimeSymbolsNeeded.add("jalvinCreateElement");
|
|
1465
1370
|
const attrsStr = expr.attrs.length > 0
|
|
1466
|
-
? " " + expr.attrs.map((a) => this.emitJsxAttr(a)).join(" ")
|
|
1467
|
-
: "";
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
return `<${expr.tag}${attrsStr}>${children}</${expr.tag}>`;
|
|
1371
|
+
? "{ " + expr.attrs.map((a) => this.emitJsxAttr(a)).join(", ") + " }"
|
|
1372
|
+
: "{}";
|
|
1373
|
+
const childrenArr = expr.children.length > 0
|
|
1374
|
+
? "[" + expr.children.map((c) => this.emitJsxChild(c)).join(", ") + "]"
|
|
1375
|
+
: "[]";
|
|
1376
|
+
return `jalvinCreateElement(${JSON.stringify(expr.tag)}, ${attrsStr}, ${childrenArr})`;
|
|
1473
1377
|
}
|
|
1474
1378
|
emitJsxAttr(attr) {
|
|
1475
|
-
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
: attr.name;
|
|
1479
|
-
if (attr.value === null)
|
|
1480
|
-
return name;
|
|
1481
|
-
if (typeof attr.value === "string")
|
|
1482
|
-
return `${name}="${attr.value}"`;
|
|
1483
|
-
return `${name}={${this.emitExpr(attr.value)}}`;
|
|
1379
|
+
const name = attr.name === "class" ? "className" : attr.name === "for" ? "htmlFor" : attr.name;
|
|
1380
|
+
const val = attr.value === null ? "true" : typeof attr.value === "string" ? JSON.stringify(attr.value) : this.emitExpr(attr.value);
|
|
1381
|
+
return `${name}: ${val}`;
|
|
1484
1382
|
}
|
|
1485
1383
|
emitJsxChild(child) {
|
|
1486
1384
|
switch (child.kind) {
|
|
1487
1385
|
case "JsxElement": return this.emitJsxElement(child);
|
|
1488
|
-
case "JsxExprChild": return
|
|
1489
|
-
case "JsxTextChild": return child.text
|
|
1386
|
+
case "JsxExprChild": return this.emitExpr(child.expr);
|
|
1387
|
+
case "JsxTextChild": return `document.createTextNode(${JSON.stringify(child.text)})`;
|
|
1490
1388
|
}
|
|
1491
1389
|
}
|
|
1492
1390
|
emitStringTemplate(expr) {
|
|
1493
|
-
const inner = expr.parts.map((p) => {
|
|
1494
|
-
if (p.kind === "LiteralPart") {
|
|
1495
|
-
return p.value.replace(/`/g, "\\`").replace(/\\/g, "\\\\");
|
|
1496
|
-
}
|
|
1497
|
-
return `\${${this.emitExpr(p.expr)}}`;
|
|
1498
|
-
}).join("");
|
|
1391
|
+
const inner = expr.parts.map((p) => p.kind === "LiteralPart" ? p.value.replace(/`/g, "\\`").replace(/\\/g, "\\\\") : `\${${this.emitExpr(p.expr)}}`).join("");
|
|
1499
1392
|
return `\`${inner}\``;
|
|
1500
1393
|
}
|
|
1501
1394
|
emitCallExpr(expr) {
|
|
1502
|
-
|
|
1503
|
-
// `a.downTo(b)` → `downTo(a, b)` `a.until(b)` → `range(a, b, false)`
|
|
1504
|
-
// `range.step(n)` → `step(range, n)`
|
|
1505
|
-
if (expr.callee.kind === "MemberExpr" &&
|
|
1506
|
-
(expr.callee.member === "downTo" || expr.callee.member === "until" || expr.callee.member === "step")) {
|
|
1395
|
+
if (expr.callee.kind === "MemberExpr" && (expr.callee.member === "downTo" || expr.callee.member === "until" || expr.callee.member === "step")) {
|
|
1507
1396
|
const obj = this.emitExpr(expr.callee.target);
|
|
1508
1397
|
const arg = expr.args.length > 0 ? this.emitExpr(expr.args[0].value) : "0";
|
|
1509
1398
|
if (expr.callee.member === "downTo") {
|
|
@@ -1519,51 +1408,39 @@ class CodeGenerator {
|
|
|
1519
1408
|
return `step(${obj}, ${arg})`;
|
|
1520
1409
|
}
|
|
1521
1410
|
}
|
|
1522
|
-
// Rewrite calls on primitive-receiver extension functions.
|
|
1523
|
-
// `str.truncate(30)` where `String.truncate` is a user extension → `__ext_string_truncate(str, 30)`
|
|
1524
1411
|
if (expr.callee.kind === "MemberExpr") {
|
|
1525
1412
|
const scopeMember = expr.callee.member;
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
if (
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
(
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
this.runtimeSymbolsNeeded.add("takeIf");
|
|
1555
|
-
return `takeIf(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1556
|
-
}
|
|
1557
|
-
if (scopeMember === "takeUnless") {
|
|
1558
|
-
this.runtimeSymbolsNeeded.add("takeUnless");
|
|
1559
|
-
return `takeUnless(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1560
|
-
}
|
|
1413
|
+
const receiver = this.emitExpr(expr.callee.target);
|
|
1414
|
+
const lambda = expr.trailingLambda ?? (expr.args.length === 1 && expr.args[0].value.kind === "LambdaExpr" ? expr.args[0].value : null);
|
|
1415
|
+
if (lambda) {
|
|
1416
|
+
if (scopeMember === "let") {
|
|
1417
|
+
this.runtimeSymbolsNeeded.add("let_");
|
|
1418
|
+
return `let_(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1419
|
+
}
|
|
1420
|
+
if (scopeMember === "also") {
|
|
1421
|
+
this.runtimeSymbolsNeeded.add("also");
|
|
1422
|
+
return `also(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1423
|
+
}
|
|
1424
|
+
if (scopeMember === "apply") {
|
|
1425
|
+
this.runtimeSymbolsNeeded.add("apply");
|
|
1426
|
+
const body = this.captureBlockStatements(lambda.body);
|
|
1427
|
+
return `apply(${receiver}, function(this: any) { ${body} })`;
|
|
1428
|
+
}
|
|
1429
|
+
if (scopeMember === "run") {
|
|
1430
|
+
this.runtimeSymbolsNeeded.add("run_");
|
|
1431
|
+
const body = this.captureBlockStatements(lambda.body);
|
|
1432
|
+
return `run_(${receiver}, function(this: any) { ${body} })`;
|
|
1433
|
+
}
|
|
1434
|
+
if (scopeMember === "takeIf") {
|
|
1435
|
+
this.runtimeSymbolsNeeded.add("takeIf");
|
|
1436
|
+
return `takeIf(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1437
|
+
}
|
|
1438
|
+
if (scopeMember === "takeUnless") {
|
|
1439
|
+
this.runtimeSymbolsNeeded.add("takeUnless");
|
|
1440
|
+
return `takeUnless(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1561
1441
|
}
|
|
1562
1442
|
}
|
|
1563
|
-
// `with(x) { ... }` is a free function handled via seedBuiltins; but rewrite if emitted as member
|
|
1564
1443
|
}
|
|
1565
|
-
// Rewrite calls on primitive-receiver extension functions.
|
|
1566
|
-
// `str.truncate(30)` where `String.truncate` is a user extension → `__ext_string_truncate(str, 30)`
|
|
1567
1444
|
if (expr.callee.kind === "MemberExpr") {
|
|
1568
1445
|
const receiverType = this.typeMap.get(expr.callee.target);
|
|
1569
1446
|
const memberName = expr.callee.member;
|
|
@@ -1583,105 +1460,69 @@ class CodeGenerator {
|
|
|
1583
1460
|
}
|
|
1584
1461
|
}
|
|
1585
1462
|
const callee = this.emitExpr(expr.callee);
|
|
1586
|
-
const typeArgs = expr.typeArgs.length > 0
|
|
1587
|
-
|
|
1588
|
-
: "";
|
|
1589
|
-
// Rewrite top-level `with(obj) { ... }` → `with_(obj, function(this: any) { ... })`
|
|
1590
|
-
if (expr.callee.kind === "NameExpr" && expr.callee.name === "with" &&
|
|
1591
|
-
(expr.trailingLambda || (expr.args.length === 2 && expr.args[1].value.kind === "LambdaExpr"))) {
|
|
1463
|
+
const typeArgs = expr.typeArgs.length > 0 ? `<${expr.typeArgs.map((t) => t.star ? "*" : this.emitTypeRef(t.type)).join(", ")}>` : "";
|
|
1464
|
+
if (expr.callee.kind === "NameExpr" && expr.callee.name === "with" && (expr.trailingLambda || (expr.args.length === 2 && expr.args[1].value.kind === "LambdaExpr"))) {
|
|
1592
1465
|
const obj = expr.args.length > 0 ? this.emitExpr(expr.args[0].value) : "undefined";
|
|
1593
|
-
const lambda = expr.trailingLambda ??
|
|
1594
|
-
expr.args[1].value;
|
|
1466
|
+
const lambda = expr.trailingLambda ?? expr.args[1].value;
|
|
1595
1467
|
this.runtimeSymbolsNeeded.add("with_");
|
|
1596
1468
|
const body = this.captureBlockStatements(lambda.body);
|
|
1597
1469
|
return `with_(${obj}, function(this: any) { ${body} })`;
|
|
1598
1470
|
}
|
|
1599
|
-
|
|
1600
|
-
if (expr.callee.kind === "NameExpr" && expr.callee.name === "run" &&
|
|
1601
|
-
expr.args.length === 0 && expr.trailingLambda) {
|
|
1471
|
+
if (expr.callee.kind === "NameExpr" && expr.callee.name === "run" && expr.args.length === 0 && expr.trailingLambda) {
|
|
1602
1472
|
return `(${this.emitLambdaExpr(expr.trailingLambda)})()`;
|
|
1603
1473
|
}
|
|
1604
|
-
// Handle named arguments by reordering them to match positional parameters
|
|
1605
1474
|
const calleeType = this.typeMap.get(expr.callee);
|
|
1606
|
-
// Component call: Column(modifier = ...) { ... } → Column({ modifier: ... }, [children])
|
|
1607
|
-
// Also catches star-imported @jalvin/ui primitives (Row, Button, etc.) whose type is T_UNKNOWN
|
|
1608
|
-
//
|
|
1609
|
-
// Skip the component path when the typechecker resolved a func type with explicit paramNames.
|
|
1610
|
-
// That indicates a regular function (fun) or a built-in hook — both use positional calling.
|
|
1611
|
-
// component fun declarations intentionally omit paramNames from their func type.
|
|
1612
1475
|
const isKnownPositionalFunc = calleeType?.tag === "func" && calleeType.paramNames !== undefined;
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1476
|
+
let isComponentLike = false;
|
|
1477
|
+
if (expr.callee.kind === "NameExpr") {
|
|
1478
|
+
isComponentLike = this.allComponentLikeNames.has(expr.callee.name) || (this.hasUiStarImport && (!calleeType || calleeType.tag === "unknown") && /^[A-Z]/.test(expr.callee.name));
|
|
1479
|
+
}
|
|
1480
|
+
else if (expr.callee.kind === "MemberExpr" || expr.callee.kind === "SafeMemberExpr") {
|
|
1481
|
+
isComponentLike = this.allComponentLikeNames.has(expr.callee.member);
|
|
1616
1482
|
}
|
|
1483
|
+
if (!isKnownPositionalFunc && isComponentLike)
|
|
1484
|
+
return this.emitComponentCall(expr);
|
|
1617
1485
|
let finalArgs = [];
|
|
1618
1486
|
if (calleeType && calleeType.tag === "func" && calleeType.paramNames) {
|
|
1619
1487
|
const paramNames = calleeType.paramNames;
|
|
1620
1488
|
const argsByName = new Map();
|
|
1621
1489
|
const positionalArgs = [];
|
|
1622
1490
|
for (const arg of expr.args) {
|
|
1623
|
-
if (arg.name)
|
|
1491
|
+
if (arg.name)
|
|
1624
1492
|
argsByName.set(arg.name, arg.spread ? `...${this.emitExpr(arg.value)}` : this.emitExpr(arg.value));
|
|
1625
|
-
|
|
1626
|
-
else {
|
|
1493
|
+
else
|
|
1627
1494
|
positionalArgs.push(arg.spread ? `...${this.emitExpr(arg.value)}` : this.emitExpr(arg.value));
|
|
1628
|
-
}
|
|
1629
1495
|
}
|
|
1630
|
-
// Reconstruct args based on paramNames
|
|
1631
1496
|
for (let i = 0; i < paramNames.length; i++) {
|
|
1632
1497
|
const name = paramNames[i];
|
|
1633
|
-
if (argsByName.has(name))
|
|
1498
|
+
if (argsByName.has(name))
|
|
1634
1499
|
finalArgs.push(argsByName.get(name));
|
|
1635
|
-
|
|
1636
|
-
else if (i < positionalArgs.length) {
|
|
1500
|
+
else if (i < positionalArgs.length)
|
|
1637
1501
|
finalArgs.push(positionalArgs[i]);
|
|
1638
|
-
|
|
1639
|
-
else {
|
|
1640
|
-
// Missing argument - JS default value logic will handle it if we pass undefined
|
|
1502
|
+
else
|
|
1641
1503
|
finalArgs.push("undefined");
|
|
1642
|
-
}
|
|
1643
1504
|
}
|
|
1644
|
-
|
|
1645
|
-
if (positionalArgs.length > paramNames.length) {
|
|
1505
|
+
if (positionalArgs.length > paramNames.length)
|
|
1646
1506
|
finalArgs.push(...positionalArgs.slice(paramNames.length));
|
|
1647
|
-
}
|
|
1648
1507
|
}
|
|
1649
1508
|
else {
|
|
1650
|
-
|
|
1651
|
-
finalArgs = expr.args.map((a) => {
|
|
1652
|
-
return a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value);
|
|
1653
|
-
});
|
|
1509
|
+
finalArgs = expr.args.map((a) => a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
|
|
1654
1510
|
}
|
|
1655
|
-
if (expr.trailingLambda)
|
|
1511
|
+
if (expr.trailingLambda)
|
|
1656
1512
|
finalArgs.push(this.emitLambdaExpr(expr.trailingLambda));
|
|
1657
|
-
|
|
1658
|
-
// If the callee is a class instance (not a constructor / function), emit `.invoke(args)`
|
|
1659
|
-
if (calleeType &&
|
|
1660
|
-
calleeType.tag === "class" &&
|
|
1661
|
-
calleeType.decl &&
|
|
1662
|
-
this.classHasInvokeOperator(calleeType.decl)) {
|
|
1513
|
+
if (calleeType && calleeType.tag === "class" && calleeType.decl && this.classHasInvokeOperator(calleeType.decl)) {
|
|
1663
1514
|
return `${callee}.invoke(${finalArgs.join(", ")})`;
|
|
1664
1515
|
}
|
|
1665
|
-
|
|
1666
|
-
if (this.isConstructorCall(expr.callee)) {
|
|
1516
|
+
if (this.isConstructorCall(expr.callee))
|
|
1667
1517
|
return `new ${callee}${typeArgs}(${finalArgs.join(", ")})`;
|
|
1668
|
-
}
|
|
1669
1518
|
return `${callee}${typeArgs}(${finalArgs.join(", ")})`;
|
|
1670
1519
|
}
|
|
1671
|
-
/**
|
|
1672
|
-
* For each non-scoped local import (e.g. `import src.views.MoveCounterView.MoveCounter`),
|
|
1673
|
-
* attempt to load and parse the corresponding .jalvin source file. If the named symbol is
|
|
1674
|
-
* a `component fun`, register it in `componentNames` and record its param names so that
|
|
1675
|
-
* call sites can emit the correct props-object invocation.
|
|
1676
|
-
*/
|
|
1677
1520
|
resolveLocalComponentImports(program, sourceRoot) {
|
|
1678
1521
|
this.localStarExports.clear();
|
|
1679
1522
|
for (const imp of program.imports) {
|
|
1680
|
-
// Skip scoped packages (@org/pkg)
|
|
1681
1523
|
if (imp.path[0]?.startsWith("@"))
|
|
1682
1524
|
continue;
|
|
1683
1525
|
if (imp.star) {
|
|
1684
|
-
// Local wildcard import: import src.module.* — load all exports from src/module.jalvin
|
|
1685
1526
|
if (imp.path.length < 1)
|
|
1686
1527
|
continue;
|
|
1687
1528
|
const candidatePath = nodePath.join(sourceRoot, imp.path.join("/") + ".jalvin");
|
|
@@ -1704,21 +1545,27 @@ class CodeGenerator {
|
|
|
1704
1545
|
continue;
|
|
1705
1546
|
exportedNames.push(name);
|
|
1706
1547
|
if (d.kind === "ComponentDecl") {
|
|
1707
|
-
this.
|
|
1548
|
+
this.userComponentNames.add(name);
|
|
1549
|
+
this.allComponentLikeNames.add(name);
|
|
1708
1550
|
this.componentParamNames.set(name, d.params.map((p) => p.name));
|
|
1709
1551
|
this.hasComponents = true;
|
|
1710
1552
|
}
|
|
1553
|
+
if (d.kind === "ClassDecl" && d.body) {
|
|
1554
|
+
for (const m of d.body.members) {
|
|
1555
|
+
if (m.kind === "ComponentDecl") {
|
|
1556
|
+
this.allComponentLikeNames.add(m.name);
|
|
1557
|
+
this.componentParamNames.set(m.name, m.params.map((p) => p.name));
|
|
1558
|
+
this.hasComponents = true;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1711
1562
|
}
|
|
1712
|
-
if (exportedNames.length > 0)
|
|
1563
|
+
if (exportedNames.length > 0)
|
|
1713
1564
|
this.localStarExports.set(moduleSpecifier, exportedNames);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
catch {
|
|
1717
|
-
// Silently skip if the file cannot be read or parsed
|
|
1718
1565
|
}
|
|
1566
|
+
catch { }
|
|
1719
1567
|
continue;
|
|
1720
1568
|
}
|
|
1721
|
-
// Named import: last path segment is the symbol, preceding segments form the file path
|
|
1722
1569
|
if (imp.path.length < 2)
|
|
1723
1570
|
continue;
|
|
1724
1571
|
const rawSymbolName = imp.path[imp.path.length - 1];
|
|
@@ -1736,574 +1583,232 @@ class CodeGenerator {
|
|
|
1736
1583
|
const ast = (0, parser_js_1.parse)(tokens, candidatePath, diag, source);
|
|
1737
1584
|
if (diag.hasErrors)
|
|
1738
1585
|
continue;
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
this.
|
|
1743
|
-
this.componentParamNames.set(symbolName,
|
|
1586
|
+
const decl = ast.declarations.find((d) => ((d.kind === "ComponentDecl" || d.kind === "ClassDecl") && d.name === rawSymbolName));
|
|
1587
|
+
if (decl?.kind === "ComponentDecl") {
|
|
1588
|
+
this.userComponentNames.add(symbolName);
|
|
1589
|
+
this.allComponentLikeNames.add(symbolName);
|
|
1590
|
+
this.componentParamNames.set(symbolName, decl.params.map((p) => p.name));
|
|
1744
1591
|
this.hasComponents = true;
|
|
1745
1592
|
}
|
|
1593
|
+
else if (decl?.kind === "ClassDecl" && decl.body) {
|
|
1594
|
+
for (const m of decl.body.members) {
|
|
1595
|
+
if (m.kind === "ComponentDecl") {
|
|
1596
|
+
this.userComponentNames.add(m.name);
|
|
1597
|
+
this.allComponentLikeNames.add(m.name);
|
|
1598
|
+
this.componentParamNames.set(m.name, m.params.map((p) => p.name));
|
|
1599
|
+
this.hasComponents = true;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1746
1603
|
}
|
|
1747
|
-
catch {
|
|
1748
|
-
// Silently skip if the file cannot be read or parsed
|
|
1749
|
-
}
|
|
1604
|
+
catch { }
|
|
1750
1605
|
}
|
|
1751
1606
|
}
|
|
1752
|
-
/** Returns true if a call expression's callee looks like a class constructor.
|
|
1753
|
-
* In Jalvin, class names always start with an uppercase letter. */
|
|
1754
1607
|
isConstructorCall(calleeExpr) {
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
}
|
|
1762
|
-
if (calleeExpr.kind === "MemberExpr") {
|
|
1763
|
-
// e.g. UiState.Success(data), Discount.Percentage(0.1)
|
|
1764
|
-
return /^[A-Z]/.test(calleeExpr.member);
|
|
1765
|
-
}
|
|
1766
|
-
return false;
|
|
1608
|
+
const name = calleeExpr.kind === "NameExpr" ? calleeExpr.name :
|
|
1609
|
+
calleeExpr.kind === "MemberExpr" ? calleeExpr.member :
|
|
1610
|
+
null;
|
|
1611
|
+
if (name !== null && this.allComponentLikeNames.has(name))
|
|
1612
|
+
return false;
|
|
1613
|
+
return this.typeMap.get(calleeExpr)?.tag === "class";
|
|
1767
1614
|
}
|
|
1768
|
-
// ── Component call → props object ──────────────────────────────────────
|
|
1769
|
-
/** Emit `Column(modifier = ...) { ... }` as `React.createElement(Column, { modifier: ... }, [children])` */
|
|
1770
1615
|
emitComponentCall(expr) {
|
|
1771
|
-
const
|
|
1616
|
+
const callee = this.emitExpr(expr.callee);
|
|
1617
|
+
const tagName = expr.callee.kind === "NameExpr" ? expr.callee.name : expr.callee.member;
|
|
1772
1618
|
const calleeType = this.typeMap.get(expr.callee);
|
|
1773
|
-
|
|
1774
|
-
const paramNames = (calleeType?.tag === "func" ? calleeType.paramNames : undefined) ??
|
|
1775
|
-
this.componentParamNames.get(tag);
|
|
1776
|
-
// Build props object from named/positional args
|
|
1619
|
+
const paramNames = (calleeType?.tag === "func" ? calleeType.paramNames : undefined) ?? (tagName ? this.componentParamNames.get(tagName) : undefined);
|
|
1777
1620
|
const props = [];
|
|
1778
1621
|
for (let i = 0; i < expr.args.length; i++) {
|
|
1779
1622
|
const arg = expr.args[i];
|
|
1780
|
-
|
|
1781
|
-
const propName = arg.name ??
|
|
1782
|
-
paramNames?.[i] ??
|
|
1783
|
-
(arg.value.kind === "NameExpr" ? arg.value.name : undefined);
|
|
1623
|
+
const propName = arg.name ?? paramNames?.[i] ?? (arg.value.kind === "NameExpr" ? arg.value.name : undefined);
|
|
1784
1624
|
if (!propName)
|
|
1785
1625
|
continue;
|
|
1786
1626
|
const val = this.emitExpr(arg.value);
|
|
1787
|
-
// Use JS shorthand `{ key }` when key and value are the same identifier
|
|
1788
1627
|
props.push(propName === val ? propName : `${propName}: ${val}`);
|
|
1789
1628
|
}
|
|
1790
1629
|
const propsStr = props.length > 0 ? `{ ${props.join(", ")} }` : "{}";
|
|
1791
1630
|
if (expr.trailingLambda) {
|
|
1792
1631
|
const children = this.emitLambdaBodyAsDomChildren(expr.trailingLambda);
|
|
1793
|
-
return
|
|
1632
|
+
return `${callee}(${propsStr}, [${children}])`;
|
|
1794
1633
|
}
|
|
1795
|
-
return
|
|
1634
|
+
return `${callee}(${propsStr})`;
|
|
1796
1635
|
}
|
|
1797
|
-
/** Collect each expression statement in a trailing-lambda body as DOM children.
|
|
1798
|
-
* For-loop statements are emitted as spread IIFEs so their dynamic output is preserved. */
|
|
1799
1636
|
emitLambdaBodyAsDomChildren(lambda) {
|
|
1800
1637
|
const parts = [];
|
|
1801
1638
|
for (const stmt of lambda.body) {
|
|
1802
1639
|
if (stmt.kind === "ExprStmt") {
|
|
1803
1640
|
parts.push(this.emitExpr(stmt.expr));
|
|
1804
1641
|
}
|
|
1642
|
+
else if (stmt.kind === "IfStmt") {
|
|
1643
|
+
const s = stmt;
|
|
1644
|
+
const cond = this.emitExpr(s.condition);
|
|
1645
|
+
const thenExprs = s.then.statements.filter((st) => st.kind === "ExprStmt").map((st) => this.emitExpr(st.expr));
|
|
1646
|
+
const elseExprs = s.else && s.else.kind === "Block" ? s.else.statements.filter((st) => st.kind === "ExprStmt").map((st) => this.emitExpr(st.expr)) : [];
|
|
1647
|
+
const thenStr = thenExprs.length > 0 ? `...(${cond} ? [${thenExprs.join(", ")}] : [])` : "";
|
|
1648
|
+
const elseStr = elseExprs.length > 0 ? `...(!(${cond}) ? [${elseExprs.join(", ")}] : [])` : "";
|
|
1649
|
+
if (thenStr)
|
|
1650
|
+
parts.push(thenStr);
|
|
1651
|
+
if (elseStr)
|
|
1652
|
+
parts.push(elseStr);
|
|
1653
|
+
}
|
|
1805
1654
|
else if (stmt.kind === "ForStmt") {
|
|
1806
1655
|
const s = stmt;
|
|
1807
1656
|
const iter = this.emitExpr(s.iterable);
|
|
1808
|
-
let bindingStr
|
|
1809
|
-
|
|
1810
|
-
bindingStr = `const ${s.binding}`;
|
|
1811
|
-
}
|
|
1812
|
-
else if (s.binding.kind === "TupleDestructure") {
|
|
1813
|
-
const names = s.binding.names.map((n) => n ?? "_").join(", ");
|
|
1814
|
-
bindingStr = `const [${names}]`;
|
|
1815
|
-
}
|
|
1816
|
-
else {
|
|
1817
|
-
bindingStr = `const [${s.binding.key}, ${s.binding.value}]`;
|
|
1818
|
-
}
|
|
1819
|
-
const innerExprs = s.body.statements
|
|
1820
|
-
.filter((inner) => inner.kind === "ExprStmt")
|
|
1821
|
-
.map((inner) => this.emitExpr(inner.expr));
|
|
1657
|
+
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}]`;
|
|
1658
|
+
const innerExprs = s.body.statements.filter((inner) => inner.kind === "ExprStmt").map((inner) => this.emitExpr(inner.expr));
|
|
1822
1659
|
if (innerExprs.length > 0) {
|
|
1823
1660
|
const pushes = innerExprs.map((e) => `__c.push(${e});`).join(" ");
|
|
1824
1661
|
parts.push(`...(() => { const __c: any[] = []; for (${bindingStr} of ${iter}) { ${pushes} } return __c; })()`);
|
|
1825
1662
|
}
|
|
1826
1663
|
}
|
|
1827
|
-
// Other statement kinds (if, while, etc.) are not renderable as UI children inline
|
|
1828
1664
|
}
|
|
1829
1665
|
return parts.join(", ");
|
|
1830
1666
|
}
|
|
1831
1667
|
emitLambdaExpr(expr) {
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
: expr.params.map((p) => p.name ?? "_").join(", ");
|
|
1837
|
-
const body = expr.body;
|
|
1838
|
-
if (body.length === 1 && body[0].kind === "ExprStmt") {
|
|
1839
|
-
return `(${params}) => ${this.emitExpr(body[0].expr)}`;
|
|
1840
|
-
}
|
|
1841
|
-
return `(${params}) => { ${body.map((s) => this.captureStmt(s)).join(" ")} }`;
|
|
1668
|
+
const params = expr.params.length === 0 ? "it" : expr.params.map((p) => p.name ?? "_").join(", ");
|
|
1669
|
+
if (expr.body.length === 1 && expr.body[0].kind === "ExprStmt")
|
|
1670
|
+
return `(${params}) => ${this.emitExpr(expr.body[0].expr)}`;
|
|
1671
|
+
return `(${params}) => { ${expr.body.map((s) => this.captureStmt(s)).join(" ")} }`;
|
|
1842
1672
|
}
|
|
1843
1673
|
emitIfExpr(expr) {
|
|
1844
1674
|
const cond = this.emitExpr(expr.condition);
|
|
1845
|
-
const thenStr = expr.then.kind === "Block"
|
|
1846
|
-
|
|
1847
|
-
: this.emitExpr(expr.then);
|
|
1848
|
-
const elseExpr = expr.else;
|
|
1849
|
-
const elseStr = elseExpr.kind === "Block"
|
|
1850
|
-
? `(() => { ${this.captureBlockStatements(elseExpr.statements)} })()`
|
|
1851
|
-
: elseExpr.kind === "IfExpr"
|
|
1852
|
-
? this.emitIfExpr(elseExpr)
|
|
1853
|
-
: this.emitExpr(elseExpr);
|
|
1675
|
+
const thenStr = expr.then.kind === "Block" ? `(() => { ${this.captureBlockStatements(expr.then.statements)} })()` : this.emitExpr(expr.then);
|
|
1676
|
+
const elseStr = expr.else.kind === "Block" ? `(() => { ${this.captureBlockStatements(expr.else.statements)} })()` : expr.else.kind === "IfExpr" ? this.emitIfExpr(expr.else) : this.emitExpr(expr.else);
|
|
1854
1677
|
return `(${cond} ? ${thenStr} : ${elseStr})`;
|
|
1855
1678
|
}
|
|
1856
1679
|
emitWhenExpr(expr) {
|
|
1857
|
-
// Compile as an IIFE with if/else chain
|
|
1858
1680
|
const parts = [];
|
|
1859
1681
|
const subject = expr.subject ? `const __s = ${this.emitExpr(expr.subject.expr)};` : "";
|
|
1860
1682
|
const subjectRef = expr.subject ? (expr.subject.binding ?? "__s") : "";
|
|
1861
1683
|
for (const branch of expr.branches) {
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
? this.captureBlock(branch.body)
|
|
1865
|
-
: `return ${this.emitExpr(branch.body)};`;
|
|
1684
|
+
const body = branch.body.kind === "Block" ? this.captureBlock(branch.body) : `return ${this.emitExpr(branch.body)};`;
|
|
1685
|
+
if (branch.isElse)
|
|
1866
1686
|
parts.push(`{ ${body} }`);
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
const cond = branch.conditions.map((c) => this.emitWhenCondition(c, subjectRef)).join(" || ");
|
|
1870
|
-
const body = branch.body.kind === "Block"
|
|
1871
|
-
? this.captureBlock(branch.body)
|
|
1872
|
-
: `return ${this.emitExpr(branch.body)};`;
|
|
1873
|
-
parts.push(`if (${cond}) { ${body} }`);
|
|
1874
|
-
}
|
|
1687
|
+
else
|
|
1688
|
+
parts.push(`if (${branch.conditions.map((c) => this.emitWhenCondition(c, subjectRef)).join(" || ")}) { ${body} }`);
|
|
1875
1689
|
}
|
|
1876
1690
|
return `(() => { ${subject} ${parts.join(" else ")} })()`;
|
|
1877
1691
|
}
|
|
1878
1692
|
emitTryCatchExpr(expr) {
|
|
1879
1693
|
const body = this.captureBlock(expr.body);
|
|
1880
|
-
const catches = expr.catches.map((c) => {
|
|
1881
|
-
const cb = this.captureBlock(c.body);
|
|
1882
|
-
return `catch (${c.name}) { ${cb} }`;
|
|
1883
|
-
}).join(" ");
|
|
1694
|
+
const catches = expr.catches.map((c) => `catch (${c.name}) { ${this.captureBlock(c.body)} }`).join(" ");
|
|
1884
1695
|
const fin = expr.finally ? `finally { ${this.captureBlock(expr.finally)} }` : "";
|
|
1885
1696
|
return `(() => { try { ${body} } ${catches} ${fin} })()`;
|
|
1886
1697
|
}
|
|
1887
1698
|
emitCollectionLiteral(expr) {
|
|
1888
|
-
if (expr.collectionKind === "map")
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
return "null";
|
|
1894
|
-
});
|
|
1895
|
-
return `new Map([${entries.join(", ")}])`;
|
|
1896
|
-
}
|
|
1897
|
-
if (expr.collectionKind === "set") {
|
|
1898
|
-
const items = expr.elements.map((e) => this.emitExpr(e));
|
|
1899
|
-
return `new Set([${items.join(", ")}])`;
|
|
1900
|
-
}
|
|
1901
|
-
const items = expr.elements.map((e) => this.emitExpr(e));
|
|
1902
|
-
return `[${items.join(", ")}]`;
|
|
1699
|
+
if (expr.collectionKind === "map")
|
|
1700
|
+
return `new Map([${expr.elements.map((e) => ("kind" in e && e.kind === "MapEntry") ? `[${this.emitExpr(e.key)}, ${this.emitExpr(e.value)}]` : "null").join(", ")}])`;
|
|
1701
|
+
if (expr.collectionKind === "set")
|
|
1702
|
+
return `new Set([${expr.elements.map((e) => this.emitExpr(e)).join(", ")}])`;
|
|
1703
|
+
return `[${expr.elements.map((e) => this.emitExpr(e)).join(", ")}]`;
|
|
1903
1704
|
}
|
|
1904
1705
|
emitObjectExpr(expr) {
|
|
1905
1706
|
const superType = expr.superTypes[0];
|
|
1906
1707
|
const ext = superType ? ` extends ${this.emitTypeRef(superType.type)}` : "";
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
}
|
|
1914
|
-
captureBlockStatements(stmts) {
|
|
1915
|
-
return stmts.map((s) => this.captureStmt(s)).join(" ");
|
|
1916
|
-
}
|
|
1917
|
-
captureStmt(stmt) {
|
|
1918
|
-
const saved = this.w;
|
|
1919
|
-
const tmp = new Writer();
|
|
1920
|
-
this.w = tmp;
|
|
1921
|
-
this.emitStmt(stmt);
|
|
1922
|
-
this.w = saved;
|
|
1923
|
-
return tmp.output.trim();
|
|
1924
|
-
}
|
|
1925
|
-
captureClassMember(member) {
|
|
1926
|
-
const saved = this.w;
|
|
1927
|
-
const tmp = new Writer();
|
|
1928
|
-
this.w = tmp;
|
|
1929
|
-
this.emitClassMember(member);
|
|
1930
|
-
this.w = saved;
|
|
1931
|
-
return tmp.output.trim();
|
|
1932
|
-
}
|
|
1933
|
-
// ── Type reference emission ────────────────────────────────────────────────
|
|
1708
|
+
return `(new (class${ext} { ${expr.body.members.map((m) => this.captureClassMember(m)).join(" ")} })())`;
|
|
1709
|
+
}
|
|
1710
|
+
captureBlock(block) { return block.statements.map((s) => this.captureStmt(s)).join(" "); }
|
|
1711
|
+
captureBlockStatements(stmts) { return stmts.map((s) => this.captureStmt(s)).join(" "); }
|
|
1712
|
+
captureStmt(stmt) { const saved = this.w; const tmp = new Writer(); this.w = tmp; this.emitStmt(stmt); this.w = saved; return tmp.output.trim(); }
|
|
1713
|
+
captureClassMember(member) { const saved = this.w; const tmp = new Writer(); this.w = tmp; this.emitClassMember(member); this.w = saved; return tmp.output.trim(); }
|
|
1934
1714
|
emitTypeRef(ref) {
|
|
1935
1715
|
switch (ref.kind) {
|
|
1936
|
-
case "SimpleTypeRef":
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}
|
|
1940
|
-
case "
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
return `<${ps.join(", ")}>`;
|
|
1966
|
-
}
|
|
1967
|
-
emitParamsStr(params) {
|
|
1968
|
-
return params.map((p) => {
|
|
1969
|
-
const spread = p.vararg ? "..." : "";
|
|
1970
|
-
// vararg params are rest params in TS: `...name: T[]`
|
|
1971
|
-
const type = this.opts.emitTypes
|
|
1972
|
-
? `: ${this.emitTypeRef(p.type)}${p.vararg ? "[]" : ""}`
|
|
1973
|
-
: "";
|
|
1974
|
-
const def = p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "";
|
|
1975
|
-
return `${spread}${p.name}${type}${def}`;
|
|
1976
|
-
}).join(", ");
|
|
1977
|
-
}
|
|
1978
|
-
emitComponentPropsStr(params) {
|
|
1979
|
-
if (params.length === 0)
|
|
1980
|
-
return "";
|
|
1981
|
-
return params.map((p) => {
|
|
1982
|
-
const type = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
1983
|
-
return `${p.name}${type}`;
|
|
1984
|
-
}).join(", ");
|
|
1985
|
-
}
|
|
1986
|
-
emitSuperTypesStr(superTypes) {
|
|
1987
|
-
if (superTypes.length === 0)
|
|
1988
|
-
return "";
|
|
1989
|
-
const parts = [];
|
|
1990
|
-
let first = true;
|
|
1991
|
-
for (const s of superTypes) {
|
|
1992
|
-
// TypeScript does NOT allow constructor arguments in the `extends` clause.
|
|
1993
|
-
// Delegation args are passed via `super(args)` inside the constructor.
|
|
1994
|
-
if (first) {
|
|
1995
|
-
parts.push(` extends ${this.emitTypeRef(s.type)}`);
|
|
1996
|
-
first = false;
|
|
1997
|
-
}
|
|
1998
|
-
else {
|
|
1999
|
-
parts.push(` implements ${this.emitTypeRef(s.type)}`);
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
return parts.join("");
|
|
2003
|
-
}
|
|
2004
|
-
exportPrefix(mods) {
|
|
2005
|
-
if (mods.visibility === "private" || mods.visibility === "internal")
|
|
2006
|
-
return "";
|
|
2007
|
-
return "export ";
|
|
2008
|
-
}
|
|
2009
|
-
visibilityPrefix(mods) {
|
|
2010
|
-
switch (mods.visibility) {
|
|
2011
|
-
case "private": return "private ";
|
|
2012
|
-
case "protected": return "protected ";
|
|
2013
|
-
case "internal": return "/* internal */ ";
|
|
2014
|
-
default: return "";
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
unaryOpStr(op) {
|
|
2018
|
-
if (op === "not")
|
|
2019
|
-
return "!";
|
|
2020
|
-
return op;
|
|
2021
|
-
}
|
|
2022
|
-
binaryOpStr(op) {
|
|
2023
|
-
switch (op) {
|
|
2024
|
-
case "and": return "&&";
|
|
2025
|
-
case "or": return "||";
|
|
2026
|
-
case "xor": return "^";
|
|
2027
|
-
case "shl": return "<<";
|
|
2028
|
-
case "shr": return ">>";
|
|
2029
|
-
case "ushr": return ">>>";
|
|
2030
|
-
// === / !== are JS reference equality (Jalvin's triple-equals)
|
|
2031
|
-
case "===": return "===";
|
|
2032
|
-
case "!==": return "!==";
|
|
2033
|
-
case "..": return "/* .. */ +"; // handled by range()
|
|
2034
|
-
case "..<": return "/* ..< */ +";
|
|
2035
|
-
default: return op;
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
// ── AST name walkers (for wildcard import resolution) ─────────────────────
|
|
2039
|
-
/**
|
|
2040
|
-
* Walk the entire program AST and collect all identifier names that are
|
|
2041
|
-
* REFERENCED (i.e., used) in the code: NameExpr values and the first name
|
|
2042
|
-
* component of SimpleTypeRef / GenericTypeRef nodes.
|
|
2043
|
-
*
|
|
2044
|
-
* Jalvin primitive type names (String, Boolean, …) that map to TS primitives
|
|
2045
|
-
* are excluded from TypeRef collection since they never need an import.
|
|
2046
|
-
*/
|
|
1716
|
+
case "SimpleTypeRef": return PRIMITIVE_TYPE_MAP[ref.name.join(".")] ?? ref.name.join(".");
|
|
1717
|
+
case "NullableTypeRef": return `${this.emitTypeRef(ref.base)} | null | undefined`;
|
|
1718
|
+
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(", ")}>`;
|
|
1719
|
+
case "FunctionTypeRef": return `(${ref.params.map((p, i) => `p${i}: ${this.emitTypeRef(p)}`).join(", ")}) => ${this.emitTypeRef(ref.returnType)}`;
|
|
1720
|
+
case "StarProjection": return "any";
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
emitTypeParamsStr(params) { return params.length === 0 ? "" : `<${params.map((p) => `${p.name}${p.upperBound ? ` extends ${this.emitTypeRef(p.upperBound)}` : ""}`).join(", ")}>`; }
|
|
1724
|
+
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(", "); }
|
|
1725
|
+
emitSuperTypesStr(superTypes) { return superTypes.length === 0 ? "" : superTypes.map((s, i) => i === 0 ? ` extends ${this.emitTypeRef(s.type)}` : ` implements ${this.emitTypeRef(s.type)}`).join(""); }
|
|
1726
|
+
exportPrefix(mods) { return (mods.visibility === "private" || mods.visibility === "internal") ? "" : "export "; }
|
|
1727
|
+
visibilityPrefix(mods) { switch (mods.visibility) {
|
|
1728
|
+
case "private": return "private ";
|
|
1729
|
+
case "protected": return "protected ";
|
|
1730
|
+
case "internal": return "/* internal */ ";
|
|
1731
|
+
default: return "";
|
|
1732
|
+
} }
|
|
1733
|
+
unaryOpStr(op) { return op === "not" ? "!" : op; }
|
|
1734
|
+
binaryOpStr(op) { switch (op) {
|
|
1735
|
+
case "and": return "&&";
|
|
1736
|
+
case "or": return "||";
|
|
1737
|
+
case "xor": return "^";
|
|
1738
|
+
case "shl": return "<<";
|
|
1739
|
+
case "shr": return ">>";
|
|
1740
|
+
case "ushr": return ">>>";
|
|
1741
|
+
case "===": return "===";
|
|
1742
|
+
case "!==": return "!==";
|
|
1743
|
+
default: return op;
|
|
1744
|
+
} }
|
|
2047
1745
|
gatherReferencedNames(program) {
|
|
2048
1746
|
const names = new Set();
|
|
2049
1747
|
const visited = new WeakSet();
|
|
2050
1748
|
const walk = (val) => {
|
|
2051
|
-
if (!val || typeof val !== "object")
|
|
1749
|
+
if (!val || typeof val !== "object" || visited.has(val))
|
|
2052
1750
|
return;
|
|
1751
|
+
visited.add(val);
|
|
2053
1752
|
if (Array.isArray(val)) {
|
|
2054
|
-
|
|
2055
|
-
walk(item);
|
|
1753
|
+
val.forEach(walk);
|
|
2056
1754
|
return;
|
|
2057
1755
|
}
|
|
2058
1756
|
const obj = val;
|
|
2059
|
-
if (
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
const kind = obj["kind"];
|
|
2063
|
-
if (kind === "NameExpr") {
|
|
2064
|
-
const name = obj["name"];
|
|
2065
|
-
if (typeof name === "string")
|
|
2066
|
-
names.add(name);
|
|
2067
|
-
return; // leaf node
|
|
2068
|
-
}
|
|
2069
|
-
if (kind === "SimpleTypeRef") {
|
|
2070
|
-
const nameArr = obj["name"];
|
|
2071
|
-
if (Array.isArray(nameArr) && nameArr.length > 0) {
|
|
2072
|
-
const first = nameArr[0];
|
|
2073
|
-
// Skip Jalvin primitive type names — they're erased to TS primitives.
|
|
2074
|
-
if (!(first in PRIMITIVE_TYPE_MAP))
|
|
2075
|
-
names.add(first);
|
|
2076
|
-
}
|
|
1757
|
+
if (obj.kind === "NameExpr") {
|
|
1758
|
+
if (typeof obj.name === "string")
|
|
1759
|
+
names.add(obj.name);
|
|
2077
1760
|
return;
|
|
2078
1761
|
}
|
|
2079
|
-
if (kind === "
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
const first = nameArr[0];
|
|
2084
|
-
if (!(first in PRIMITIVE_TYPE_MAP))
|
|
2085
|
-
names.add(first);
|
|
2086
|
-
}
|
|
2087
|
-
// Fall through to recurse into type arguments
|
|
1762
|
+
if (obj.kind === "SimpleTypeRef") {
|
|
1763
|
+
if (obj.name?.[0] && !(obj.name[0] in PRIMITIVE_TYPE_MAP))
|
|
1764
|
+
names.add(obj.name[0]);
|
|
1765
|
+
return;
|
|
2088
1766
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
1767
|
+
if (obj.kind === "GenericTypeRef") {
|
|
1768
|
+
if (obj.base?.name?.[0] && !(obj.base.name[0] in PRIMITIVE_TYPE_MAP))
|
|
1769
|
+
names.add(obj.base.name[0]);
|
|
2091
1770
|
}
|
|
1771
|
+
Object.values(obj).forEach(walk);
|
|
2092
1772
|
};
|
|
2093
|
-
|
|
2094
|
-
walk(decl);
|
|
1773
|
+
program.declarations.forEach(walk);
|
|
2095
1774
|
return names;
|
|
2096
1775
|
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Walk the entire program AST and collect all names that are LOCALLY DEFINED:
|
|
2099
|
-
* top-level declaration names, non-star import aliases, function/lambda
|
|
2100
|
-
* parameters, local val/var bindings, for-loop variables, catch-clause names,
|
|
2101
|
-
* when-subject bindings, and type parameter names.
|
|
2102
|
-
*/
|
|
2103
1776
|
gatherAllLocalBindings(program) {
|
|
2104
1777
|
const names = new Set();
|
|
2105
1778
|
const visited = new WeakSet();
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
names.add(d.name);
|
|
2111
|
-
}
|
|
2112
|
-
// Non-star import aliases
|
|
2113
|
-
for (const imp of program.imports) {
|
|
2114
|
-
if (!imp.star) {
|
|
2115
|
-
const name = imp.alias ?? imp.path[imp.path.length - 1];
|
|
2116
|
-
if (name)
|
|
2117
|
-
names.add(name);
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
1779
|
+
program.declarations.forEach((d) => { if (d.name)
|
|
1780
|
+
names.add(d.name); });
|
|
1781
|
+
program.imports.forEach((i) => { if (!i.star)
|
|
1782
|
+
names.add(i.alias ?? i.path[i.path.length - 1]); });
|
|
2120
1783
|
const walk = (val) => {
|
|
2121
|
-
if (!val || typeof val !== "object")
|
|
1784
|
+
if (!val || typeof val !== "object" || visited.has(val))
|
|
2122
1785
|
return;
|
|
1786
|
+
visited.add(val);
|
|
2123
1787
|
if (Array.isArray(val)) {
|
|
2124
|
-
|
|
2125
|
-
walk(item);
|
|
1788
|
+
val.forEach(walk);
|
|
2126
1789
|
return;
|
|
2127
1790
|
}
|
|
2128
1791
|
const obj = val;
|
|
2129
|
-
if (
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
if (typeof n === "string")
|
|
2141
|
-
names.add(n);
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2145
|
-
if (kind === "FunDecl" || kind === "ExtensionFunDecl" ||
|
|
2146
|
-
kind === "ComponentDecl" || kind === "ClassDecl" ||
|
|
2147
|
-
kind === "DataClassDecl" || kind === "SealedClassDecl" ||
|
|
2148
|
-
kind === "EnumClassDecl" || kind === "InterfaceDecl" ||
|
|
2149
|
-
kind === "ObjectDecl" || kind === "TypeAliasDecl") {
|
|
2150
|
-
if (typeof obj["name"] === "string" && obj["name"])
|
|
2151
|
-
names.add(obj["name"]);
|
|
2152
|
-
// Type parameters
|
|
2153
|
-
const typeParams = obj["typeParams"];
|
|
2154
|
-
if (Array.isArray(typeParams)) {
|
|
2155
|
-
for (const tp of typeParams) {
|
|
2156
|
-
if (tp.name)
|
|
2157
|
-
names.add(tp.name);
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
// Function/method parameters
|
|
2161
|
-
const params = obj["params"];
|
|
2162
|
-
if (Array.isArray(params)) {
|
|
2163
|
-
for (const p of params) {
|
|
2164
|
-
if (p.name)
|
|
2165
|
-
names.add(p.name);
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
if (kind === "LambdaExpr") {
|
|
2170
|
-
const params = obj["params"];
|
|
2171
|
-
if (Array.isArray(params)) {
|
|
2172
|
-
for (const p of params) {
|
|
2173
|
-
if (p.name)
|
|
2174
|
-
names.add(p.name);
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
if (kind === "ForStmt") {
|
|
2179
|
-
const binding = obj["binding"];
|
|
2180
|
-
if (typeof binding === "string") {
|
|
2181
|
-
names.add(binding);
|
|
2182
|
-
}
|
|
2183
|
-
else if (binding && typeof binding === "object") {
|
|
2184
|
-
const b = binding;
|
|
2185
|
-
// TupleDestructure or MapDestructure
|
|
2186
|
-
if (Array.isArray(b["names"])) {
|
|
2187
|
-
for (const n of b["names"]) {
|
|
2188
|
-
if (typeof n === "string")
|
|
2189
|
-
names.add(n);
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
if (typeof b["key"] === "string")
|
|
2193
|
-
names.add(b["key"]);
|
|
2194
|
-
if (typeof b["value"] === "string")
|
|
2195
|
-
names.add(b["value"]);
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
if (kind === "TryCatchStmt") {
|
|
2199
|
-
const catches = obj["catches"];
|
|
2200
|
-
if (Array.isArray(catches)) {
|
|
2201
|
-
for (const c of catches) {
|
|
2202
|
-
if (c.name)
|
|
2203
|
-
names.add(c.name);
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
if (kind === "WhenStmt" || kind === "WhenExpr") {
|
|
2208
|
-
const subject = obj["subject"];
|
|
2209
|
-
if (typeof subject?.["binding"] === "string")
|
|
2210
|
-
names.add(subject["binding"]);
|
|
2211
|
-
}
|
|
2212
|
-
if (kind === "SecondaryConstructor") {
|
|
2213
|
-
const params = obj["params"];
|
|
2214
|
-
if (Array.isArray(params)) {
|
|
2215
|
-
for (const p of params) {
|
|
2216
|
-
if (p.name)
|
|
2217
|
-
names.add(p.name);
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
for (const propVal of Object.values(obj)) {
|
|
2222
|
-
walk(propVal);
|
|
2223
|
-
}
|
|
1792
|
+
if (["PropertyDecl", "DestructuringDecl", "FunDecl", "ExtensionFunDecl", "ComponentDecl", "ClassDecl", "DataClassDecl", "SealedClassDecl", "EnumClassDecl", "InterfaceDecl", "ObjectDecl", "TypeAliasDecl", "LambdaExpr"].includes(obj.kind) && obj.name)
|
|
1793
|
+
names.add(obj.name);
|
|
1794
|
+
if (obj.typeParams)
|
|
1795
|
+
obj.typeParams.forEach((tp) => names.add(tp.name));
|
|
1796
|
+
if (obj.params)
|
|
1797
|
+
obj.params.forEach((p) => names.add(p.name));
|
|
1798
|
+
if (obj.kind === "ForStmt" && typeof obj.binding === "string")
|
|
1799
|
+
names.add(obj.binding);
|
|
1800
|
+
if (obj.kind === "TryCatchStmt" && obj.catches)
|
|
1801
|
+
obj.catches.forEach((c) => names.add(c.name));
|
|
1802
|
+
Object.values(obj).forEach(walk);
|
|
2224
1803
|
};
|
|
2225
|
-
|
|
2226
|
-
walk(decl);
|
|
1804
|
+
program.declarations.forEach(walk);
|
|
2227
1805
|
return names;
|
|
2228
1806
|
}
|
|
2229
1807
|
}
|
|
2230
1808
|
exports.CodeGenerator = CodeGenerator;
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
// ---------------------------------------------------------------------------
|
|
2234
|
-
const PRIMITIVE_TYPE_MAP = {
|
|
2235
|
-
Int: "number",
|
|
2236
|
-
Long: "bigint",
|
|
2237
|
-
Float: "number",
|
|
2238
|
-
Double: "number",
|
|
2239
|
-
Boolean: "boolean",
|
|
2240
|
-
String: "string",
|
|
2241
|
-
Char: "string",
|
|
2242
|
-
Byte: "number",
|
|
2243
|
-
Short: "number",
|
|
2244
|
-
Unit: "void",
|
|
2245
|
-
Any: "unknown",
|
|
2246
|
-
Nothing: "never",
|
|
2247
|
-
};
|
|
2248
|
-
const GENERIC_TYPE_MAP = {
|
|
2249
|
-
List: "ReadonlyArray",
|
|
2250
|
-
MutableList: "Array",
|
|
2251
|
-
Set: "ReadonlySet",
|
|
2252
|
-
MutableSet: "Set",
|
|
2253
|
-
Map: "ReadonlyMap",
|
|
2254
|
-
MutableMap: "Map",
|
|
2255
|
-
Array: "Array",
|
|
2256
|
-
Pair: "[", // handled specially
|
|
2257
|
-
Triple: "[",
|
|
2258
|
-
Deferred: "Promise",
|
|
2259
|
-
StateFlow: "StateFlow",
|
|
2260
|
-
MutableStateFlow: "MutableStateFlow",
|
|
2261
|
-
Flow: "AsyncIterable",
|
|
2262
|
-
};
|
|
1809
|
+
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" };
|
|
1810
|
+
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" };
|
|
2263
1811
|
const PRIMITIVE_TYPES = new Set(Object.keys(PRIMITIVE_TYPE_MAP));
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
// as named imports from a wildcard-imported package.
|
|
2267
|
-
// ---------------------------------------------------------------------------
|
|
2268
|
-
const JS_GLOBAL_NAMES = new Set([
|
|
2269
|
-
// JS built-in constructors and objects
|
|
2270
|
-
"Array", "Map", "Set", "Object", "String", "Number", "Boolean",
|
|
2271
|
-
"Promise", "Error", "TypeError", "RangeError", "SyntaxError", "URIError",
|
|
2272
|
-
"EvalError", "ReferenceError",
|
|
2273
|
-
"console", "Math", "Date", "JSON", "RegExp", "Symbol", "BigInt",
|
|
2274
|
-
"Proxy", "Reflect", "globalThis", "Atomics", "SharedArrayBuffer",
|
|
2275
|
-
"ArrayBuffer", "DataView", "Int8Array", "Uint8Array", "Uint8ClampedArray",
|
|
2276
|
-
"Int16Array", "Uint16Array", "Int32Array", "Uint32Array",
|
|
2277
|
-
"Float32Array", "Float64Array", "BigInt64Array", "BigUint64Array",
|
|
2278
|
-
// Global functions
|
|
2279
|
-
"isNaN", "isFinite", "parseInt", "parseFloat", "eval",
|
|
2280
|
-
"encodeURI", "decodeURI", "encodeURIComponent", "decodeURIComponent",
|
|
2281
|
-
// Browser/Node globals
|
|
2282
|
-
"setTimeout", "clearTimeout", "setInterval", "clearInterval",
|
|
2283
|
-
"queueMicrotask", "requestAnimationFrame", "cancelAnimationFrame",
|
|
2284
|
-
"fetch", "URL", "URLSearchParams", "AbortController", "AbortSignal",
|
|
2285
|
-
"EventTarget", "Event", "CustomEvent", "FormData", "Headers",
|
|
2286
|
-
"Request", "Response", "Blob", "File", "FileReader",
|
|
2287
|
-
"Worker", "SharedWorker", "WebSocket",
|
|
2288
|
-
"ReadableStream", "WritableStream", "TransformStream",
|
|
2289
|
-
"document", "window", "navigator", "location", "history", "screen",
|
|
2290
|
-
"performance", "crypto", "indexedDB", "localStorage", "sessionStorage",
|
|
2291
|
-
"confirm", "alert", "prompt",
|
|
2292
|
-
"process", "Buffer", "global", "require", "module", "exports", "__dirname", "__filename",
|
|
2293
|
-
// Special identifiers
|
|
2294
|
-
"undefined", "null", "NaN", "Infinity",
|
|
2295
|
-
"it", "this", "super", "arguments", "new", "class", "function",
|
|
2296
|
-
// TypeScript primitive type names
|
|
2297
|
-
"any", "unknown", "never", "void", "string", "number", "boolean", "bigint",
|
|
2298
|
-
"object", "symbol",
|
|
2299
|
-
// Jalvin type names that map to TS primitives (never need import)
|
|
2300
|
-
"String", "Boolean", "Any", "Nothing", "Unit", "Char", "Byte", "Short",
|
|
2301
|
-
"Float", "Double",
|
|
2302
|
-
]);
|
|
2303
|
-
// ---------------------------------------------------------------------------
|
|
2304
|
-
// Public helper
|
|
2305
|
-
// ---------------------------------------------------------------------------
|
|
2306
|
-
function generate(program, opts, operatorOverloads, typeMap) {
|
|
2307
|
-
return new CodeGenerator(opts).generate(program, operatorOverloads, typeMap);
|
|
2308
|
-
}
|
|
1812
|
+
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"]);
|
|
1813
|
+
function generate(program, opts, operatorOverloads, typeMap) { return new CodeGenerator(opts).generate(program, operatorOverloads, typeMap); }
|
|
2309
1814
|
//# sourceMappingURL=codegen.js.map
|