@jalvin/compiler 2.0.43 → 2.0.44
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 +339 -831
- 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) {
|
|
@@ -575,15 +588,12 @@ class CodeGenerator {
|
|
|
575
588
|
const superTypes = this.emitSuperTypesStr(decl.superTypes);
|
|
576
589
|
this.w.writeIndentedLine(`${vis}${abstract}class ${decl.name}${typeParams}${superTypes} {`);
|
|
577
590
|
this.w.pushIndent();
|
|
578
|
-
// The first supertype's delegateArgs become the `super(args)` call inside the constructor.
|
|
579
591
|
const superDelegateArgs = decl.superTypes[0]?.delegateArgs ?? null;
|
|
580
592
|
if (decl.primaryConstructor) {
|
|
581
593
|
const initBlocks = decl.body?.members.filter((m) => m.kind === "InitBlock") ?? [];
|
|
582
594
|
this.emitPrimaryConstructorProps(decl.primaryConstructor.params, initBlocks, superDelegateArgs);
|
|
583
595
|
}
|
|
584
596
|
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
597
|
const superArgsStr = superDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
|
|
588
598
|
this.w.writeIndentedLine(`constructor() {`);
|
|
589
599
|
this.w.pushIndent();
|
|
@@ -606,24 +616,20 @@ class CodeGenerator {
|
|
|
606
616
|
const props = decl.primaryConstructor.params;
|
|
607
617
|
this.w.writeIndentedLine(`${vis}class ${decl.name}${typeParams}${superTypes} {`);
|
|
608
618
|
this.w.pushIndent();
|
|
609
|
-
// Discriminant for sealed class sub-types
|
|
610
619
|
if (kindName) {
|
|
611
620
|
this.w.writeIndentedLine(`readonly __kind = "${kindName}" as const;`);
|
|
612
621
|
}
|
|
613
|
-
// Constructor params → readonly properties
|
|
614
622
|
for (const p of props) {
|
|
615
623
|
const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
616
624
|
this.w.writeIndentedLine(`readonly ${p.name}${t};`);
|
|
617
625
|
}
|
|
618
626
|
this.w.writeLine();
|
|
619
|
-
// Constructor
|
|
620
627
|
const ctorParams = props.map((p) => {
|
|
621
628
|
const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
622
629
|
return `${p.name}${t}`;
|
|
623
630
|
}).join(", ");
|
|
624
631
|
this.w.writeIndentedLine(`constructor(${ctorParams}) {`);
|
|
625
632
|
this.w.pushIndent();
|
|
626
|
-
// Emit super() with actual delegation args (or empty call if delegateArgs is []).
|
|
627
633
|
const dataSuperDelegateArgs = decl.superTypes[0]?.delegateArgs ?? null;
|
|
628
634
|
if (dataSuperDelegateArgs !== null) {
|
|
629
635
|
const superArgsStr = dataSuperDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
|
|
@@ -635,7 +641,6 @@ class CodeGenerator {
|
|
|
635
641
|
this.w.popIndent();
|
|
636
642
|
this.w.writeIndentedLine(`}`);
|
|
637
643
|
this.w.writeLine();
|
|
638
|
-
// copy()
|
|
639
644
|
const copyParams = props.map((p) => {
|
|
640
645
|
const t = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
641
646
|
return `${p.name}${t} = this.${p.name}`;
|
|
@@ -647,7 +652,6 @@ class CodeGenerator {
|
|
|
647
652
|
this.w.popIndent();
|
|
648
653
|
this.w.writeIndentedLine(`}`);
|
|
649
654
|
this.w.writeLine();
|
|
650
|
-
// equals()
|
|
651
655
|
this.runtimeSymbolsNeeded.add("jalvinEquals");
|
|
652
656
|
const eqChecks = props.map((p) => `jalvinEquals(this.${p.name}, other.${p.name})`).join(" && ") || "true";
|
|
653
657
|
this.w.writeIndentedLine(`equals(other: unknown): boolean {`);
|
|
@@ -657,7 +661,6 @@ class CodeGenerator {
|
|
|
657
661
|
this.w.popIndent();
|
|
658
662
|
this.w.writeIndentedLine(`}`);
|
|
659
663
|
this.w.writeLine();
|
|
660
|
-
// toString()
|
|
661
664
|
const toStringParts = props.map((p) => `${p.name}=\${this.${p.name}}`).join(", ");
|
|
662
665
|
this.w.writeIndentedLine(`toString(): string {`);
|
|
663
666
|
this.w.pushIndent();
|
|
@@ -665,7 +668,6 @@ class CodeGenerator {
|
|
|
665
668
|
this.w.popIndent();
|
|
666
669
|
this.w.writeIndentedLine(`}`);
|
|
667
670
|
this.w.writeLine();
|
|
668
|
-
// hashCode() — djb2
|
|
669
671
|
this.w.writeIndentedLine(`hashCode(): number {`);
|
|
670
672
|
this.w.pushIndent();
|
|
671
673
|
this.w.writeIndentedLine(`let h = 17;`);
|
|
@@ -701,7 +703,6 @@ class CodeGenerator {
|
|
|
701
703
|
}
|
|
702
704
|
}
|
|
703
705
|
}
|
|
704
|
-
// Emit the abstract base class (only non-subtype members in the body)
|
|
705
706
|
this.w.writeIndentedLine(`${vis}abstract class ${decl.name}${typeParams} {`);
|
|
706
707
|
this.w.pushIndent();
|
|
707
708
|
this.w.writeIndentedLine(`abstract readonly __kind: string;`);
|
|
@@ -714,7 +715,6 @@ class CodeGenerator {
|
|
|
714
715
|
this.w.writeLine();
|
|
715
716
|
if (subDecls.length === 0)
|
|
716
717
|
return;
|
|
717
|
-
// Emit non-object sub-declarations at module level (before namespace)
|
|
718
718
|
for (const sub of subDecls) {
|
|
719
719
|
if (sub.kind === "DataClassDecl") {
|
|
720
720
|
this.emitDataClassDecl(sub, sub.name);
|
|
@@ -725,12 +725,10 @@ class CodeGenerator {
|
|
|
725
725
|
this.w.writeLine();
|
|
726
726
|
}
|
|
727
727
|
}
|
|
728
|
-
// Emit namespace merging so SealedClass.SubType is accessible
|
|
729
728
|
this.w.writeIndentedLine(`${vis}namespace ${decl.name} {`);
|
|
730
729
|
this.w.pushIndent();
|
|
731
730
|
for (const sub of subDecls) {
|
|
732
731
|
if (sub.kind === "ObjectDecl") {
|
|
733
|
-
// Singleton object — emit inline in namespace with __kind discriminant
|
|
734
732
|
const superStr = sub.superTypes.length > 0
|
|
735
733
|
? this.emitSuperTypesStr(sub.superTypes)
|
|
736
734
|
: ` extends ${decl.name}`;
|
|
@@ -746,7 +744,6 @@ class CodeGenerator {
|
|
|
746
744
|
this.w.writeIndentedLine(`export const ${sub.name} = new (class${superStr} { ${membersStr} })();`);
|
|
747
745
|
}
|
|
748
746
|
else {
|
|
749
|
-
// DataClassDecl / ClassDecl — already emitted at module level, re-export
|
|
750
747
|
this.w.writeIndentedLine(`export { ${sub.name} };`);
|
|
751
748
|
}
|
|
752
749
|
}
|
|
@@ -757,12 +754,8 @@ class CodeGenerator {
|
|
|
757
754
|
emitEnumClassDecl(decl) {
|
|
758
755
|
const vis = this.exportPrefix(decl.modifiers);
|
|
759
756
|
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
757
|
this.w.writeIndentedLine(`${vis}class ${decl.name}${typeParams} {`);
|
|
764
758
|
this.w.pushIndent();
|
|
765
|
-
// Private constructor so entries are the only instances
|
|
766
759
|
if (decl.primaryConstructor && decl.primaryConstructor.params.length > 0) {
|
|
767
760
|
const ctorParams = decl.primaryConstructor.params
|
|
768
761
|
.map((p) => {
|
|
@@ -777,7 +770,6 @@ class CodeGenerator {
|
|
|
777
770
|
this.w.writeIndentedLine(`private constructor(readonly name: string, readonly ordinal: number) {}`);
|
|
778
771
|
}
|
|
779
772
|
this.w.writeLine();
|
|
780
|
-
// Static entry instances
|
|
781
773
|
for (let i = 0; i < decl.entries.length; i++) {
|
|
782
774
|
const entry = decl.entries[i];
|
|
783
775
|
const args = entry.args.length > 0
|
|
@@ -787,7 +779,6 @@ class CodeGenerator {
|
|
|
787
779
|
}
|
|
788
780
|
if (decl.entries.length > 0) {
|
|
789
781
|
this.w.writeLine();
|
|
790
|
-
// values() helper
|
|
791
782
|
const entryList = decl.entries.map((e) => `${decl.name}.${e.name}`).join(", ");
|
|
792
783
|
this.w.writeIndentedLine(`static values(): ${decl.name}[] { return [${entryList}]; }`);
|
|
793
784
|
this.w.writeIndentedLine(`static valueOf(name: string): ${decl.name} {`);
|
|
@@ -805,14 +796,12 @@ class CodeGenerator {
|
|
|
805
796
|
this.w.popIndent();
|
|
806
797
|
this.w.writeIndentedLine(`}`);
|
|
807
798
|
}
|
|
808
|
-
// ── destructuring declaration — val (a, b) = expr ─────────────────────────
|
|
809
799
|
emitDestructuringDecl(decl, _memberOf) {
|
|
810
800
|
const kw = decl.mutable ? "let" : "const";
|
|
811
801
|
const names = decl.names.map((n) => n ?? "_").join(", ");
|
|
812
802
|
const init = this.emitExpr(decl.initializer);
|
|
813
803
|
this.w.writeIndentedLine(`${kw} [${names}] = ${init};`);
|
|
814
804
|
}
|
|
815
|
-
// ── interface ──────────────────────────────────────────────────────────────
|
|
816
805
|
emitInterfaceDecl(decl) {
|
|
817
806
|
this.emitAnnotations(decl.modifiers);
|
|
818
807
|
const vis = this.exportPrefix(decl.modifiers);
|
|
@@ -830,12 +819,9 @@ class CodeGenerator {
|
|
|
830
819
|
this.w.popIndent();
|
|
831
820
|
this.w.writeIndentedLine(`}`);
|
|
832
821
|
}
|
|
833
|
-
// ── object declaration → singleton ────────────────────────────────────────
|
|
834
822
|
emitObjectDecl(decl) {
|
|
835
|
-
if (!decl.name)
|
|
836
|
-
// Anonymous object — emitted inline
|
|
823
|
+
if (!decl.name)
|
|
837
824
|
return;
|
|
838
|
-
}
|
|
839
825
|
this.emitAnnotations(decl.modifiers);
|
|
840
826
|
const vis = this.exportPrefix(decl.modifiers);
|
|
841
827
|
const superTypes = this.emitSuperTypesStr(decl.superTypes);
|
|
@@ -845,16 +831,11 @@ class CodeGenerator {
|
|
|
845
831
|
this.w.popIndent();
|
|
846
832
|
this.w.writeIndentedLine(`})();`);
|
|
847
833
|
}
|
|
848
|
-
// ── type alias ─────────────────────────────────────────────────────────────
|
|
849
834
|
emitTypeAliasDecl(decl) {
|
|
850
835
|
const vis = this.exportPrefix(decl.modifiers);
|
|
851
836
|
const typeParams = this.emitTypeParamsStr(decl.typeParams);
|
|
852
837
|
this.w.writeIndentedLine(`${vis}type ${decl.name}${typeParams} = ${this.emitTypeRef(decl.type)};`);
|
|
853
838
|
}
|
|
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
839
|
emitExtensionFunDecl(decl) {
|
|
859
840
|
const isSuspend = AST.isSuspend(decl.modifiers);
|
|
860
841
|
const asyncKw = isSuspend ? "async " : "";
|
|
@@ -865,9 +846,6 @@ class CodeGenerator {
|
|
|
865
846
|
? `: ${this.emitTypeRef(decl.returnType)}`
|
|
866
847
|
: "";
|
|
867
848
|
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
849
|
this.w.writeIndentedLine(`${asyncKw}function ${fnName}${typeParams}($receiver: ${receiverType}${params ? ", " + params : ""})${retType} {`);
|
|
872
850
|
this.w.pushIndent();
|
|
873
851
|
if (decl.body.kind === "Block") {
|
|
@@ -886,14 +864,12 @@ class CodeGenerator {
|
|
|
886
864
|
const simpleReceiverName = this.receiverClassName(decl.receiver);
|
|
887
865
|
if (simpleReceiverName) {
|
|
888
866
|
if (PRIMITIVE_TYPES.has(simpleReceiverName)) {
|
|
889
|
-
// Register for call-site rewriting — cannot patch primitive prototypes
|
|
890
867
|
if (!this.primitiveExtensions.has(simpleReceiverName)) {
|
|
891
868
|
this.primitiveExtensions.set(simpleReceiverName, new Map());
|
|
892
869
|
}
|
|
893
870
|
this.primitiveExtensions.get(simpleReceiverName).set(decl.name, fnName);
|
|
894
871
|
}
|
|
895
872
|
else {
|
|
896
|
-
// Class types — prototype monkey-patch for dot-call syntax
|
|
897
873
|
this.w.writeIndentedLine(`// Extension: ${receiverType}.${decl.name}`);
|
|
898
874
|
this.w.writeIndentedLine(`(${simpleReceiverName}.prototype as any).${decl.name} = function(this: ${receiverType}, ...args: unknown[]) { return (${fnName} as Function)(this, ...args); };`);
|
|
899
875
|
}
|
|
@@ -906,11 +882,6 @@ class CodeGenerator {
|
|
|
906
882
|
return ref.base.name[ref.base.name.length - 1] ?? null;
|
|
907
883
|
return null;
|
|
908
884
|
}
|
|
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
885
|
jTypeToReceiverName(type) {
|
|
915
886
|
const tagToReceiverName = {
|
|
916
887
|
string: "String",
|
|
@@ -930,19 +901,16 @@ class CodeGenerator {
|
|
|
930
901
|
m.name === "invoke" &&
|
|
931
902
|
m.modifiers.modifiers.includes("operator"));
|
|
932
903
|
}
|
|
933
|
-
// ── property declaration ───────────────────────────────────────────────────
|
|
934
904
|
emitPropertyDecl(decl, member, isLocal = false) {
|
|
935
905
|
this.emitAnnotations(decl.modifiers);
|
|
936
906
|
const vis = member ? this.visibilityPrefix(decl.modifiers) : isLocal ? "" : this.exportPrefix(decl.modifiers);
|
|
937
907
|
const isConst = decl.modifiers.modifiers.includes("const");
|
|
938
908
|
const isLateinit = decl.modifiers.modifiers.includes("lateinit");
|
|
939
|
-
// `const val` → `const` at module level; inside classes treated as `static readonly`
|
|
940
909
|
const kw = member
|
|
941
910
|
? (decl.mutable ? "" : "readonly ")
|
|
942
911
|
: (isConst ? "const " : decl.mutable ? "let " : "const ");
|
|
943
912
|
const type = decl.type && this.opts.emitTypes ? `: ${this.emitTypeRef(decl.type)}` : "";
|
|
944
913
|
if (decl.delegate) {
|
|
945
|
-
// Delegated property — wrap in a getter/setter pair using the delegate
|
|
946
914
|
this.runtimeSymbolsNeeded.add("delegate");
|
|
947
915
|
const delegateExpr = this.emitExpr(decl.delegate);
|
|
948
916
|
if (member) {
|
|
@@ -952,28 +920,22 @@ class CodeGenerator {
|
|
|
952
920
|
}
|
|
953
921
|
}
|
|
954
922
|
else {
|
|
955
|
-
// Non-member delegate: resolve via delegate().getValue() so the identifier
|
|
956
|
-
// holds the delegated value, not the raw delegate wrapper object.
|
|
957
923
|
this.w.writeIndentedLine(`${kw}${decl.name}${type} = delegate(${delegateExpr}, "${decl.name}", null).getValue();`);
|
|
958
924
|
}
|
|
959
925
|
return;
|
|
960
926
|
}
|
|
961
|
-
const init = decl.initializer ? ` = ${this.emitExpr(decl.initializer)}` :
|
|
927
|
+
const init = decl.initializer ? ` = ${this.emitExpr(decl.initializer)}` : "";
|
|
962
928
|
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
929
|
const modStr = isConst ? "static readonly " : decl.mutable ? "" : "readonly ";
|
|
966
930
|
this.w.writeIndentedLine(`${vis}${modStr}${decl.name}${type}${init};`);
|
|
967
931
|
}
|
|
968
932
|
else if (!member) {
|
|
969
933
|
this.w.writeIndentedLine(`${vis}${kw}${decl.name}${type}${init};`);
|
|
970
934
|
}
|
|
971
|
-
if (decl.getter)
|
|
935
|
+
if (decl.getter)
|
|
972
936
|
this.emitPropertyAccessor("get", decl.name, decl.getter, member, type);
|
|
973
|
-
|
|
974
|
-
if (decl.setter) {
|
|
937
|
+
if (decl.setter)
|
|
975
938
|
this.emitPropertyAccessor("set", decl.name, decl.setter, member, type);
|
|
976
|
-
}
|
|
977
939
|
}
|
|
978
940
|
emitPropertyAccessor(kind, name, acc, member, type) {
|
|
979
941
|
const vis = this.visibilityPrefix(acc.modifiers);
|
|
@@ -993,14 +955,19 @@ class CodeGenerator {
|
|
|
993
955
|
this.w.popIndent();
|
|
994
956
|
this.w.writeIndentedLine(`}`);
|
|
995
957
|
}
|
|
996
|
-
// ── class body ─────────────────────────────────────────────────────────────
|
|
997
958
|
emitClassBody(body) {
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
959
|
+
const old = this.isInClass;
|
|
960
|
+
this.isInClass = true;
|
|
961
|
+
try {
|
|
962
|
+
for (const member of body.members) {
|
|
963
|
+
if (member.kind === "InitBlock")
|
|
964
|
+
continue;
|
|
965
|
+
this.emitClassMember(member);
|
|
966
|
+
this.w.writeLine();
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
finally {
|
|
970
|
+
this.isInClass = old;
|
|
1004
971
|
}
|
|
1005
972
|
}
|
|
1006
973
|
emitClassMember(member) {
|
|
@@ -1032,7 +999,6 @@ class CodeGenerator {
|
|
|
1032
999
|
case "CompanionObject":
|
|
1033
1000
|
this.emitCompanionObject(member);
|
|
1034
1001
|
break;
|
|
1035
|
-
case "InitBlock": /* handled in constructor */ break;
|
|
1036
1002
|
case "SecondaryConstructor":
|
|
1037
1003
|
this.emitSecondaryConstructor(member);
|
|
1038
1004
|
break;
|
|
@@ -1049,16 +1015,13 @@ class CodeGenerator {
|
|
|
1049
1015
|
this.w.writeIndentedLine(`${ro}${p.name}${type};`);
|
|
1050
1016
|
}
|
|
1051
1017
|
}
|
|
1052
|
-
// Constructor
|
|
1053
1018
|
const ctorParams = params.map((p) => {
|
|
1054
|
-
const ro = p.propertyKind === "val" ? "readonly " : p.propertyKind === "var" ? "" : "";
|
|
1055
1019
|
const type = this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}` : "";
|
|
1056
1020
|
const def = p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : "";
|
|
1057
1021
|
return `${p.name}${type}${def}`;
|
|
1058
1022
|
});
|
|
1059
1023
|
this.w.writeIndentedLine(`constructor(${ctorParams.join(", ")}) {`);
|
|
1060
1024
|
this.w.pushIndent();
|
|
1061
|
-
// super() call must be the first statement if there is a supertype.
|
|
1062
1025
|
if (superDelegateArgs !== null && superDelegateArgs !== undefined) {
|
|
1063
1026
|
const superArgsStr = superDelegateArgs.map((a) => this.emitExpr(a.value)).join(", ");
|
|
1064
1027
|
this.w.writeIndentedLine(`super(${superArgsStr});`);
|
|
@@ -1068,17 +1031,13 @@ class CodeGenerator {
|
|
|
1068
1031
|
this.w.writeIndentedLine(`this.${p.name} = ${p.name};`);
|
|
1069
1032
|
}
|
|
1070
1033
|
}
|
|
1071
|
-
|
|
1072
|
-
for (const ib of (initBlocks ?? [])) {
|
|
1034
|
+
for (const ib of (initBlocks ?? []))
|
|
1073
1035
|
this.emitBlock(ib.body);
|
|
1074
|
-
}
|
|
1075
1036
|
this.w.popIndent();
|
|
1076
1037
|
this.w.writeIndentedLine(`}`);
|
|
1077
1038
|
this.w.writeLine();
|
|
1078
1039
|
}
|
|
1079
1040
|
emitCompanionObject(co) {
|
|
1080
|
-
// Emit companion object members as `static` members of the outer class so that
|
|
1081
|
-
// `MyClass.factoryMethod()` works directly (standard semantics).
|
|
1082
1041
|
for (const member of co.body.members) {
|
|
1083
1042
|
if (member.kind === "FunDecl") {
|
|
1084
1043
|
const vis = this.visibilityPrefix(member.modifiers);
|
|
@@ -1123,11 +1082,9 @@ class CodeGenerator {
|
|
|
1123
1082
|
this.w.popIndent();
|
|
1124
1083
|
this.w.writeIndentedLine(`}`);
|
|
1125
1084
|
}
|
|
1126
|
-
// ── Statements ─────────────────────────────────────────────────────────────
|
|
1127
1085
|
emitBlock(block) {
|
|
1128
|
-
for (const stmt of block.statements)
|
|
1086
|
+
for (const stmt of block.statements)
|
|
1129
1087
|
this.emitStmt(stmt);
|
|
1130
|
-
}
|
|
1131
1088
|
}
|
|
1132
1089
|
emitStmt(stmt) {
|
|
1133
1090
|
switch (stmt.kind) {
|
|
@@ -1216,8 +1173,6 @@ class CodeGenerator {
|
|
|
1216
1173
|
}
|
|
1217
1174
|
}
|
|
1218
1175
|
emitWhenStmt(stmt) {
|
|
1219
|
-
// when(subject) { is Foo -> ... else -> ... }
|
|
1220
|
-
// Compiles to a series of if/else-if chains
|
|
1221
1176
|
const subjectVar = stmt.subject ? "__when_subject__" : null;
|
|
1222
1177
|
if (stmt.subject) {
|
|
1223
1178
|
const binding = stmt.subject.binding ?? subjectVar;
|
|
@@ -1234,12 +1189,10 @@ class CodeGenerator {
|
|
|
1234
1189
|
}
|
|
1235
1190
|
first = false;
|
|
1236
1191
|
this.w.pushIndent();
|
|
1237
|
-
if (branch.body.kind === "Block")
|
|
1192
|
+
if (branch.body.kind === "Block")
|
|
1238
1193
|
this.emitBlock(branch.body);
|
|
1239
|
-
|
|
1240
|
-
else {
|
|
1194
|
+
else
|
|
1241
1195
|
this.w.writeIndentedLine(`${this.emitExpr(branch.body)};`);
|
|
1242
|
-
}
|
|
1243
1196
|
this.w.popIndent();
|
|
1244
1197
|
}
|
|
1245
1198
|
this.w.writeIndentedLine(`}`);
|
|
@@ -1247,8 +1200,6 @@ class CodeGenerator {
|
|
|
1247
1200
|
emitWhenCondition(cond, subject) {
|
|
1248
1201
|
switch (cond.kind) {
|
|
1249
1202
|
case "WhenIsCondition": {
|
|
1250
|
-
// Qualified name (e.g. UiState.Loading) → use __kind discriminant
|
|
1251
|
-
// Single name (e.g. BibiError) → use instanceof
|
|
1252
1203
|
const isQualified = cond.type.kind === "SimpleTypeRef" && cond.type.name.length > 1;
|
|
1253
1204
|
const check = isQualified
|
|
1254
1205
|
? `(${subject} as any).__kind === "${cond.type.kind === "SimpleTypeRef" ? cond.type.name[cond.type.name.length - 1] : ""}"`
|
|
@@ -1278,7 +1229,6 @@ class CodeGenerator {
|
|
|
1278
1229
|
}
|
|
1279
1230
|
else {
|
|
1280
1231
|
const { key, value } = stmt.binding;
|
|
1281
|
-
// Use .entries() for JS Map (Map<K,V>), Object.entries() for plain objects
|
|
1282
1232
|
const iterableType = this.typeMap.get(stmt.iterable);
|
|
1283
1233
|
const isMap = iterableType?.tag === "class" && iterableType.name === "Map";
|
|
1284
1234
|
const entries = isMap ? `${iter}.entries()` : `Object.entries(${iter})`;
|
|
@@ -1297,9 +1247,6 @@ class CodeGenerator {
|
|
|
1297
1247
|
for (const c of stmt.catches) {
|
|
1298
1248
|
this.w.writeIndentedLine(`} catch (${c.name}: unknown) {`);
|
|
1299
1249
|
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
1250
|
this.w.writeIndentedLine(`if (!(${c.name} instanceof ${this.emitTypeRef(c.type)})) throw ${c.name};`);
|
|
1304
1251
|
this.emitBlock(c.body);
|
|
1305
1252
|
this.w.popIndent();
|
|
@@ -1312,7 +1259,6 @@ class CodeGenerator {
|
|
|
1312
1259
|
}
|
|
1313
1260
|
this.w.writeIndentedLine(`}`);
|
|
1314
1261
|
}
|
|
1315
|
-
// ── Expressions ────────────────────────────────────────────────────────────
|
|
1316
1262
|
emitExpr(expr) {
|
|
1317
1263
|
switch (expr.kind) {
|
|
1318
1264
|
case "IntLiteralExpr": return String(expr.value);
|
|
@@ -1322,17 +1268,12 @@ class CodeGenerator {
|
|
|
1322
1268
|
case "BooleanLiteralExpr": return String(expr.value);
|
|
1323
1269
|
case "NullLiteralExpr": return "null";
|
|
1324
1270
|
case "StringLiteralExpr":
|
|
1325
|
-
return expr.raw
|
|
1326
|
-
? `\`${expr.value}\``
|
|
1327
|
-
: JSON.stringify(expr.value);
|
|
1271
|
+
return expr.raw ? `\`${expr.value}\`` : JSON.stringify(expr.value);
|
|
1328
1272
|
case "StringTemplateExpr":
|
|
1329
1273
|
return this.emitStringTemplate(expr);
|
|
1330
1274
|
case "NameExpr":
|
|
1331
|
-
|
|
1332
|
-
if (expr.name === "Int" || expr.name === "Long") {
|
|
1275
|
+
if (expr.name === "Int" || expr.name === "Long")
|
|
1333
1276
|
this.runtimeSymbolsNeeded.add(expr.name);
|
|
1334
|
-
}
|
|
1335
|
-
// Unit singleton emits as undefined (void semantics)
|
|
1336
1277
|
if (expr.name === "Unit")
|
|
1337
1278
|
return "undefined";
|
|
1338
1279
|
return expr.name;
|
|
@@ -1348,34 +1289,23 @@ class CodeGenerator {
|
|
|
1348
1289
|
if (opMethod) {
|
|
1349
1290
|
const l = this.emitExpr(expr.left);
|
|
1350
1291
|
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") {
|
|
1292
|
+
if (opMethod === "compareTo")
|
|
1293
|
+
return `(${l}.compareTo(${r}) ${expr.op} 0)`;
|
|
1294
|
+
if (opMethod === "contains")
|
|
1358
1295
|
return `${r}.contains(${l})`;
|
|
1359
|
-
|
|
1360
|
-
if (opMethod === "!contains") {
|
|
1296
|
+
if (opMethod === "!contains")
|
|
1361
1297
|
return `!${r}.contains(${l})`;
|
|
1362
|
-
}
|
|
1363
|
-
// Generic operator overload: emit as method call `left.method(right)`
|
|
1364
1298
|
return `${l}.${opMethod}(${r})`;
|
|
1365
1299
|
}
|
|
1366
|
-
// `==` → structural equality, `!=` → negation
|
|
1367
1300
|
if (expr.op === "==" || expr.op === "!=") {
|
|
1368
1301
|
this.runtimeSymbolsNeeded.add("jalvinEquals");
|
|
1369
1302
|
const eq = `jalvinEquals(${this.emitExpr(expr.left)}, ${this.emitExpr(expr.right)})`;
|
|
1370
1303
|
return expr.op === "==" ? eq : `!${eq}`;
|
|
1371
1304
|
}
|
|
1372
|
-
|
|
1373
|
-
if (expr.op === "in") {
|
|
1305
|
+
if (expr.op === "in")
|
|
1374
1306
|
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") {
|
|
1307
|
+
if (expr.op === "!in")
|
|
1377
1308
|
return `!(${this.emitExpr(expr.right)}.includes?.(${this.emitExpr(expr.left)}) ?? (${this.emitExpr(expr.left)} in ${this.emitExpr(expr.right)}))`;
|
|
1378
|
-
}
|
|
1379
1309
|
return `(${this.emitExpr(expr.left)} ${this.binaryOpStr(expr.op)} ${this.emitExpr(expr.right)})`;
|
|
1380
1310
|
}
|
|
1381
1311
|
case "AssignExpr":
|
|
@@ -1386,124 +1316,81 @@ class CodeGenerator {
|
|
|
1386
1316
|
return expr.prefix
|
|
1387
1317
|
? `${expr.op}${this.emitExpr(expr.target)}`
|
|
1388
1318
|
: `${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)}]`;
|
|
1319
|
+
case "MemberExpr": return `${this.emitExpr(expr.target)}.${expr.member}`;
|
|
1320
|
+
case "SafeMemberExpr": return `${this.emitExpr(expr.target)}?.${expr.member}`;
|
|
1321
|
+
case "IndexExpr": return `${this.emitExpr(expr.target)}[${this.emitExpr(expr.index)}]`;
|
|
1395
1322
|
case "NotNullExpr":
|
|
1396
|
-
// Runtime check
|
|
1397
1323
|
this.runtimeSymbolsNeeded.add("notNull");
|
|
1398
1324
|
return `notNull(${this.emitExpr(expr.expr)})`;
|
|
1399
1325
|
case "SafeCallExpr": {
|
|
1400
|
-
// x?.() — safe invocation of a nullable function value
|
|
1401
1326
|
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
|
-
}
|
|
1327
|
+
const restArgs = expr.args.map((a) => a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
|
|
1406
1328
|
if (expr.trailingLambda)
|
|
1407
1329
|
restArgs.push(this.emitLambdaExpr(expr.trailingLambda));
|
|
1408
1330
|
return `${callee}?.(${restArgs.join(", ")})`;
|
|
1409
1331
|
}
|
|
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": {
|
|
1332
|
+
case "ElvisExpr": return `(${this.emitExpr(expr.left)} ?? ${this.emitExpr(expr.right)})`;
|
|
1333
|
+
case "CallExpr": return this.emitCallExpr(expr);
|
|
1334
|
+
case "LambdaExpr": return this.emitLambdaExpr(expr);
|
|
1335
|
+
case "IfExpr": return this.emitIfExpr(expr);
|
|
1336
|
+
case "WhenExpr": return this.emitWhenExpr(expr);
|
|
1337
|
+
case "TryCatchExpr": return this.emitTryCatchExpr(expr);
|
|
1338
|
+
case "TypeCheckExpr": return `${expr.negated ? "!(" : ""}${this.emitExpr(expr.expr)} instanceof ${this.emitTypeRef(expr.type)}${expr.negated ? ")" : ""}`;
|
|
1339
|
+
case "TypeCastExpr": return `(${this.emitExpr(expr.expr)} as unknown as ${this.emitTypeRef(expr.type)})`;
|
|
1340
|
+
case "SafeCastExpr":
|
|
1430
1341
|
this.runtimeSymbolsNeeded.add("safeCast");
|
|
1431
1342
|
return `safeCast(${this.emitExpr(expr.expr)}, ${this.emitTypeRef(expr.type)})`;
|
|
1432
|
-
|
|
1433
|
-
case "RangeExpr": {
|
|
1343
|
+
case "RangeExpr":
|
|
1434
1344
|
this.runtimeSymbolsNeeded.add("range");
|
|
1435
1345
|
return `range(${this.emitExpr(expr.from)}, ${this.emitExpr(expr.to)}, ${expr.inclusive})`;
|
|
1436
|
-
}
|
|
1437
1346
|
case "LaunchExpr": {
|
|
1438
|
-
// fire-and-forget → void IIFE
|
|
1439
1347
|
const stmts = this.captureBlock(expr.body);
|
|
1440
1348
|
return `(async () => { ${stmts} })()`;
|
|
1441
1349
|
}
|
|
1442
1350
|
case "AsyncExpr": {
|
|
1443
|
-
// returns Promise<T>
|
|
1444
1351
|
const stmts = this.captureBlock(expr.body);
|
|
1445
1352
|
return `(async () => { ${stmts} })()`;
|
|
1446
1353
|
}
|
|
1447
|
-
case "CollectionLiteralExpr":
|
|
1448
|
-
|
|
1449
|
-
case "ObjectExpr":
|
|
1450
|
-
return this.emitObjectExpr(expr);
|
|
1354
|
+
case "CollectionLiteralExpr": return this.emitCollectionLiteral(expr);
|
|
1355
|
+
case "ObjectExpr": return this.emitObjectExpr(expr);
|
|
1451
1356
|
case "ReturnExpr": {
|
|
1452
1357
|
const val = expr.value ? ` ${this.emitExpr(expr.value)}` : "";
|
|
1453
1358
|
return `((() => { return${val}; })())`;
|
|
1454
1359
|
}
|
|
1455
1360
|
case "BreakExpr": return "undefined /* break */";
|
|
1456
1361
|
case "ContinueExpr": return "undefined /* continue */";
|
|
1457
|
-
case "JsxElement":
|
|
1458
|
-
|
|
1459
|
-
default:
|
|
1460
|
-
return "undefined";
|
|
1362
|
+
case "JsxElement": return this.emitJsxElement(expr);
|
|
1363
|
+
default: return "undefined";
|
|
1461
1364
|
}
|
|
1462
1365
|
}
|
|
1463
|
-
// ── JSX ──────────────────────────────────────────────────────────────────
|
|
1464
1366
|
emitJsxElement(expr) {
|
|
1367
|
+
this.runtimeSymbolsNeeded.add("jalvinCreateElement");
|
|
1465
1368
|
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}>`;
|
|
1369
|
+
? "{ " + expr.attrs.map((a) => this.emitJsxAttr(a)).join(", ") + " }"
|
|
1370
|
+
: "{}";
|
|
1371
|
+
const childrenArr = expr.children.length > 0
|
|
1372
|
+
? "[" + expr.children.map((c) => this.emitJsxChild(c)).join(", ") + "]"
|
|
1373
|
+
: "[]";
|
|
1374
|
+
return `jalvinCreateElement(${JSON.stringify(expr.tag)}, ${attrsStr}, ${childrenArr})`;
|
|
1473
1375
|
}
|
|
1474
1376
|
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)}}`;
|
|
1377
|
+
const name = attr.name === "class" ? "className" : attr.name === "for" ? "htmlFor" : attr.name;
|
|
1378
|
+
const val = attr.value === null ? "true" : typeof attr.value === "string" ? JSON.stringify(attr.value) : this.emitExpr(attr.value);
|
|
1379
|
+
return `${name}: ${val}`;
|
|
1484
1380
|
}
|
|
1485
1381
|
emitJsxChild(child) {
|
|
1486
1382
|
switch (child.kind) {
|
|
1487
1383
|
case "JsxElement": return this.emitJsxElement(child);
|
|
1488
|
-
case "JsxExprChild": return
|
|
1489
|
-
case "JsxTextChild": return child.text
|
|
1384
|
+
case "JsxExprChild": return this.emitExpr(child.expr);
|
|
1385
|
+
case "JsxTextChild": return `document.createTextNode(${JSON.stringify(child.text)})`;
|
|
1490
1386
|
}
|
|
1491
1387
|
}
|
|
1492
1388
|
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("");
|
|
1389
|
+
const inner = expr.parts.map((p) => p.kind === "LiteralPart" ? p.value.replace(/`/g, "\\`").replace(/\\/g, "\\\\") : `\${${this.emitExpr(p.expr)}}`).join("");
|
|
1499
1390
|
return `\`${inner}\``;
|
|
1500
1391
|
}
|
|
1501
1392
|
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")) {
|
|
1393
|
+
if (expr.callee.kind === "MemberExpr" && (expr.callee.member === "downTo" || expr.callee.member === "until" || expr.callee.member === "step")) {
|
|
1507
1394
|
const obj = this.emitExpr(expr.callee.target);
|
|
1508
1395
|
const arg = expr.args.length > 0 ? this.emitExpr(expr.args[0].value) : "0";
|
|
1509
1396
|
if (expr.callee.member === "downTo") {
|
|
@@ -1519,51 +1406,39 @@ class CodeGenerator {
|
|
|
1519
1406
|
return `step(${obj}, ${arg})`;
|
|
1520
1407
|
}
|
|
1521
1408
|
}
|
|
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
1409
|
if (expr.callee.kind === "MemberExpr") {
|
|
1525
1410
|
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
|
-
}
|
|
1411
|
+
const receiver = this.emitExpr(expr.callee.target);
|
|
1412
|
+
const lambda = expr.trailingLambda ?? (expr.args.length === 1 && expr.args[0].value.kind === "LambdaExpr" ? expr.args[0].value : null);
|
|
1413
|
+
if (lambda) {
|
|
1414
|
+
if (scopeMember === "let") {
|
|
1415
|
+
this.runtimeSymbolsNeeded.add("let_");
|
|
1416
|
+
return `let_(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1417
|
+
}
|
|
1418
|
+
if (scopeMember === "also") {
|
|
1419
|
+
this.runtimeSymbolsNeeded.add("also");
|
|
1420
|
+
return `also(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1421
|
+
}
|
|
1422
|
+
if (scopeMember === "apply") {
|
|
1423
|
+
this.runtimeSymbolsNeeded.add("apply");
|
|
1424
|
+
const body = this.captureBlockStatements(lambda.body);
|
|
1425
|
+
return `apply(${receiver}, function(this: any) { ${body} })`;
|
|
1426
|
+
}
|
|
1427
|
+
if (scopeMember === "run") {
|
|
1428
|
+
this.runtimeSymbolsNeeded.add("run_");
|
|
1429
|
+
const body = this.captureBlockStatements(lambda.body);
|
|
1430
|
+
return `run_(${receiver}, function(this: any) { ${body} })`;
|
|
1431
|
+
}
|
|
1432
|
+
if (scopeMember === "takeIf") {
|
|
1433
|
+
this.runtimeSymbolsNeeded.add("takeIf");
|
|
1434
|
+
return `takeIf(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1435
|
+
}
|
|
1436
|
+
if (scopeMember === "takeUnless") {
|
|
1437
|
+
this.runtimeSymbolsNeeded.add("takeUnless");
|
|
1438
|
+
return `takeUnless(${receiver}, ${this.emitLambdaExpr(lambda)})`;
|
|
1561
1439
|
}
|
|
1562
1440
|
}
|
|
1563
|
-
// `with(x) { ... }` is a free function handled via seedBuiltins; but rewrite if emitted as member
|
|
1564
1441
|
}
|
|
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
1442
|
if (expr.callee.kind === "MemberExpr") {
|
|
1568
1443
|
const receiverType = this.typeMap.get(expr.callee.target);
|
|
1569
1444
|
const memberName = expr.callee.member;
|
|
@@ -1583,105 +1458,69 @@ class CodeGenerator {
|
|
|
1583
1458
|
}
|
|
1584
1459
|
}
|
|
1585
1460
|
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"))) {
|
|
1461
|
+
const typeArgs = expr.typeArgs.length > 0 ? `<${expr.typeArgs.map((t) => t.star ? "*" : this.emitTypeRef(t.type)).join(", ")}>` : "";
|
|
1462
|
+
if (expr.callee.kind === "NameExpr" && expr.callee.name === "with" && (expr.trailingLambda || (expr.args.length === 2 && expr.args[1].value.kind === "LambdaExpr"))) {
|
|
1592
1463
|
const obj = expr.args.length > 0 ? this.emitExpr(expr.args[0].value) : "undefined";
|
|
1593
|
-
const lambda = expr.trailingLambda ??
|
|
1594
|
-
expr.args[1].value;
|
|
1464
|
+
const lambda = expr.trailingLambda ?? expr.args[1].value;
|
|
1595
1465
|
this.runtimeSymbolsNeeded.add("with_");
|
|
1596
1466
|
const body = this.captureBlockStatements(lambda.body);
|
|
1597
1467
|
return `with_(${obj}, function(this: any) { ${body} })`;
|
|
1598
1468
|
}
|
|
1599
|
-
|
|
1600
|
-
if (expr.callee.kind === "NameExpr" && expr.callee.name === "run" &&
|
|
1601
|
-
expr.args.length === 0 && expr.trailingLambda) {
|
|
1469
|
+
if (expr.callee.kind === "NameExpr" && expr.callee.name === "run" && expr.args.length === 0 && expr.trailingLambda) {
|
|
1602
1470
|
return `(${this.emitLambdaExpr(expr.trailingLambda)})()`;
|
|
1603
1471
|
}
|
|
1604
|
-
// Handle named arguments by reordering them to match positional parameters
|
|
1605
1472
|
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
1473
|
const isKnownPositionalFunc = calleeType?.tag === "func" && calleeType.paramNames !== undefined;
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1474
|
+
let isComponentLike = false;
|
|
1475
|
+
if (expr.callee.kind === "NameExpr") {
|
|
1476
|
+
isComponentLike = this.allComponentLikeNames.has(expr.callee.name) || (this.hasUiStarImport && (!calleeType || calleeType.tag === "unknown") && /^[A-Z]/.test(expr.callee.name));
|
|
1477
|
+
}
|
|
1478
|
+
else if (expr.callee.kind === "MemberExpr" || expr.callee.kind === "SafeMemberExpr") {
|
|
1479
|
+
isComponentLike = this.allComponentLikeNames.has(expr.callee.member);
|
|
1616
1480
|
}
|
|
1481
|
+
if (!isKnownPositionalFunc && isComponentLike)
|
|
1482
|
+
return this.emitComponentCall(expr);
|
|
1617
1483
|
let finalArgs = [];
|
|
1618
1484
|
if (calleeType && calleeType.tag === "func" && calleeType.paramNames) {
|
|
1619
1485
|
const paramNames = calleeType.paramNames;
|
|
1620
1486
|
const argsByName = new Map();
|
|
1621
1487
|
const positionalArgs = [];
|
|
1622
1488
|
for (const arg of expr.args) {
|
|
1623
|
-
if (arg.name)
|
|
1489
|
+
if (arg.name)
|
|
1624
1490
|
argsByName.set(arg.name, arg.spread ? `...${this.emitExpr(arg.value)}` : this.emitExpr(arg.value));
|
|
1625
|
-
|
|
1626
|
-
else {
|
|
1491
|
+
else
|
|
1627
1492
|
positionalArgs.push(arg.spread ? `...${this.emitExpr(arg.value)}` : this.emitExpr(arg.value));
|
|
1628
|
-
}
|
|
1629
1493
|
}
|
|
1630
|
-
// Reconstruct args based on paramNames
|
|
1631
1494
|
for (let i = 0; i < paramNames.length; i++) {
|
|
1632
1495
|
const name = paramNames[i];
|
|
1633
|
-
if (argsByName.has(name))
|
|
1496
|
+
if (argsByName.has(name))
|
|
1634
1497
|
finalArgs.push(argsByName.get(name));
|
|
1635
|
-
|
|
1636
|
-
else if (i < positionalArgs.length) {
|
|
1498
|
+
else if (i < positionalArgs.length)
|
|
1637
1499
|
finalArgs.push(positionalArgs[i]);
|
|
1638
|
-
|
|
1639
|
-
else {
|
|
1640
|
-
// Missing argument - JS default value logic will handle it if we pass undefined
|
|
1500
|
+
else
|
|
1641
1501
|
finalArgs.push("undefined");
|
|
1642
|
-
}
|
|
1643
1502
|
}
|
|
1644
|
-
|
|
1645
|
-
if (positionalArgs.length > paramNames.length) {
|
|
1503
|
+
if (positionalArgs.length > paramNames.length)
|
|
1646
1504
|
finalArgs.push(...positionalArgs.slice(paramNames.length));
|
|
1647
|
-
}
|
|
1648
1505
|
}
|
|
1649
1506
|
else {
|
|
1650
|
-
|
|
1651
|
-
finalArgs = expr.args.map((a) => {
|
|
1652
|
-
return a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value);
|
|
1653
|
-
});
|
|
1507
|
+
finalArgs = expr.args.map((a) => a.spread ? `...${this.emitExpr(a.value)}` : this.emitExpr(a.value));
|
|
1654
1508
|
}
|
|
1655
|
-
if (expr.trailingLambda)
|
|
1509
|
+
if (expr.trailingLambda)
|
|
1656
1510
|
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)) {
|
|
1511
|
+
if (calleeType && calleeType.tag === "class" && calleeType.decl && this.classHasInvokeOperator(calleeType.decl)) {
|
|
1663
1512
|
return `${callee}.invoke(${finalArgs.join(", ")})`;
|
|
1664
1513
|
}
|
|
1665
|
-
|
|
1666
|
-
if (this.isConstructorCall(expr.callee)) {
|
|
1514
|
+
if (this.isConstructorCall(expr.callee))
|
|
1667
1515
|
return `new ${callee}${typeArgs}(${finalArgs.join(", ")})`;
|
|
1668
|
-
}
|
|
1669
1516
|
return `${callee}${typeArgs}(${finalArgs.join(", ")})`;
|
|
1670
1517
|
}
|
|
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
1518
|
resolveLocalComponentImports(program, sourceRoot) {
|
|
1678
1519
|
this.localStarExports.clear();
|
|
1679
1520
|
for (const imp of program.imports) {
|
|
1680
|
-
// Skip scoped packages (@org/pkg)
|
|
1681
1521
|
if (imp.path[0]?.startsWith("@"))
|
|
1682
1522
|
continue;
|
|
1683
1523
|
if (imp.star) {
|
|
1684
|
-
// Local wildcard import: import src.module.* — load all exports from src/module.jalvin
|
|
1685
1524
|
if (imp.path.length < 1)
|
|
1686
1525
|
continue;
|
|
1687
1526
|
const candidatePath = nodePath.join(sourceRoot, imp.path.join("/") + ".jalvin");
|
|
@@ -1704,21 +1543,27 @@ class CodeGenerator {
|
|
|
1704
1543
|
continue;
|
|
1705
1544
|
exportedNames.push(name);
|
|
1706
1545
|
if (d.kind === "ComponentDecl") {
|
|
1707
|
-
this.
|
|
1546
|
+
this.userComponentNames.add(name);
|
|
1547
|
+
this.allComponentLikeNames.add(name);
|
|
1708
1548
|
this.componentParamNames.set(name, d.params.map((p) => p.name));
|
|
1709
1549
|
this.hasComponents = true;
|
|
1710
1550
|
}
|
|
1551
|
+
if (d.kind === "ClassDecl" && d.body) {
|
|
1552
|
+
for (const m of d.body.members) {
|
|
1553
|
+
if (m.kind === "ComponentDecl") {
|
|
1554
|
+
this.allComponentLikeNames.add(m.name);
|
|
1555
|
+
this.componentParamNames.set(m.name, m.params.map((p) => p.name));
|
|
1556
|
+
this.hasComponents = true;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1711
1560
|
}
|
|
1712
|
-
if (exportedNames.length > 0)
|
|
1561
|
+
if (exportedNames.length > 0)
|
|
1713
1562
|
this.localStarExports.set(moduleSpecifier, exportedNames);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
catch {
|
|
1717
|
-
// Silently skip if the file cannot be read or parsed
|
|
1718
1563
|
}
|
|
1564
|
+
catch { }
|
|
1719
1565
|
continue;
|
|
1720
1566
|
}
|
|
1721
|
-
// Named import: last path segment is the symbol, preceding segments form the file path
|
|
1722
1567
|
if (imp.path.length < 2)
|
|
1723
1568
|
continue;
|
|
1724
1569
|
const rawSymbolName = imp.path[imp.path.length - 1];
|
|
@@ -1736,574 +1581,237 @@ class CodeGenerator {
|
|
|
1736
1581
|
const ast = (0, parser_js_1.parse)(tokens, candidatePath, diag, source);
|
|
1737
1582
|
if (diag.hasErrors)
|
|
1738
1583
|
continue;
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
this.
|
|
1743
|
-
this.componentParamNames.set(symbolName,
|
|
1584
|
+
const decl = ast.declarations.find((d) => ((d.kind === "ComponentDecl" || d.kind === "ClassDecl") && d.name === rawSymbolName));
|
|
1585
|
+
if (decl?.kind === "ComponentDecl") {
|
|
1586
|
+
this.userComponentNames.add(symbolName);
|
|
1587
|
+
this.allComponentLikeNames.add(symbolName);
|
|
1588
|
+
this.componentParamNames.set(symbolName, decl.params.map((p) => p.name));
|
|
1744
1589
|
this.hasComponents = true;
|
|
1745
1590
|
}
|
|
1591
|
+
else if (decl?.kind === "ClassDecl" && decl.body) {
|
|
1592
|
+
for (const m of decl.body.members) {
|
|
1593
|
+
if (m.kind === "ComponentDecl") {
|
|
1594
|
+
this.userComponentNames.add(m.name);
|
|
1595
|
+
this.allComponentLikeNames.add(m.name);
|
|
1596
|
+
this.componentParamNames.set(m.name, m.params.map((p) => p.name));
|
|
1597
|
+
this.hasComponents = true;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1746
1601
|
}
|
|
1747
|
-
catch {
|
|
1748
|
-
// Silently skip if the file cannot be read or parsed
|
|
1749
|
-
}
|
|
1602
|
+
catch { }
|
|
1750
1603
|
}
|
|
1751
1604
|
}
|
|
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
1605
|
isConstructorCall(calleeExpr) {
|
|
1755
1606
|
if (calleeExpr.kind === "NameExpr") {
|
|
1756
|
-
|
|
1757
|
-
if (this.componentNames.has(calleeExpr.name))
|
|
1607
|
+
if (this.allComponentLikeNames.has(calleeExpr.name))
|
|
1758
1608
|
return false;
|
|
1759
|
-
// Class names start with uppercase; function names start with lowercase
|
|
1760
1609
|
return /^[A-Z]/.test(calleeExpr.name);
|
|
1761
1610
|
}
|
|
1762
1611
|
if (calleeExpr.kind === "MemberExpr") {
|
|
1763
|
-
|
|
1612
|
+
if (this.allComponentLikeNames.has(calleeExpr.member))
|
|
1613
|
+
return false;
|
|
1764
1614
|
return /^[A-Z]/.test(calleeExpr.member);
|
|
1765
1615
|
}
|
|
1766
1616
|
return false;
|
|
1767
1617
|
}
|
|
1768
|
-
// ── Component call → props object ──────────────────────────────────────
|
|
1769
|
-
/** Emit `Column(modifier = ...) { ... }` as `React.createElement(Column, { modifier: ... }, [children])` */
|
|
1770
1618
|
emitComponentCall(expr) {
|
|
1771
|
-
const
|
|
1619
|
+
const callee = this.emitExpr(expr.callee);
|
|
1620
|
+
const tagName = expr.callee.kind === "NameExpr" ? expr.callee.name : expr.callee.member;
|
|
1772
1621
|
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
|
|
1622
|
+
const paramNames = (calleeType?.tag === "func" ? calleeType.paramNames : undefined) ?? (tagName ? this.componentParamNames.get(tagName) : undefined);
|
|
1777
1623
|
const props = [];
|
|
1778
1624
|
for (let i = 0; i < expr.args.length; i++) {
|
|
1779
1625
|
const arg = expr.args[i];
|
|
1780
|
-
|
|
1781
|
-
const propName = arg.name ??
|
|
1782
|
-
paramNames?.[i] ??
|
|
1783
|
-
(arg.value.kind === "NameExpr" ? arg.value.name : undefined);
|
|
1626
|
+
const propName = arg.name ?? paramNames?.[i] ?? (arg.value.kind === "NameExpr" ? arg.value.name : undefined);
|
|
1784
1627
|
if (!propName)
|
|
1785
1628
|
continue;
|
|
1786
1629
|
const val = this.emitExpr(arg.value);
|
|
1787
|
-
// Use JS shorthand `{ key }` when key and value are the same identifier
|
|
1788
1630
|
props.push(propName === val ? propName : `${propName}: ${val}`);
|
|
1789
1631
|
}
|
|
1790
1632
|
const propsStr = props.length > 0 ? `{ ${props.join(", ")} }` : "{}";
|
|
1791
1633
|
if (expr.trailingLambda) {
|
|
1792
1634
|
const children = this.emitLambdaBodyAsDomChildren(expr.trailingLambda);
|
|
1793
|
-
return
|
|
1635
|
+
return `${callee}(${propsStr}, [${children}])`;
|
|
1794
1636
|
}
|
|
1795
|
-
return
|
|
1637
|
+
return `${callee}(${propsStr})`;
|
|
1796
1638
|
}
|
|
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
1639
|
emitLambdaBodyAsDomChildren(lambda) {
|
|
1800
1640
|
const parts = [];
|
|
1801
1641
|
for (const stmt of lambda.body) {
|
|
1802
1642
|
if (stmt.kind === "ExprStmt") {
|
|
1803
1643
|
parts.push(this.emitExpr(stmt.expr));
|
|
1804
1644
|
}
|
|
1645
|
+
else if (stmt.kind === "IfStmt") {
|
|
1646
|
+
const s = stmt;
|
|
1647
|
+
const cond = this.emitExpr(s.condition);
|
|
1648
|
+
const thenExprs = s.then.statements.filter((st) => st.kind === "ExprStmt").map((st) => this.emitExpr(st.expr));
|
|
1649
|
+
const elseExprs = s.else && s.else.kind === "Block" ? s.else.statements.filter((st) => st.kind === "ExprStmt").map((st) => this.emitExpr(st.expr)) : [];
|
|
1650
|
+
const thenStr = thenExprs.length > 0 ? `...(${cond} ? [${thenExprs.join(", ")}] : [])` : "";
|
|
1651
|
+
const elseStr = elseExprs.length > 0 ? `...(!(${cond}) ? [${elseExprs.join(", ")}] : [])` : "";
|
|
1652
|
+
if (thenStr)
|
|
1653
|
+
parts.push(thenStr);
|
|
1654
|
+
if (elseStr)
|
|
1655
|
+
parts.push(elseStr);
|
|
1656
|
+
}
|
|
1805
1657
|
else if (stmt.kind === "ForStmt") {
|
|
1806
1658
|
const s = stmt;
|
|
1807
1659
|
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));
|
|
1660
|
+
let bindingStr = typeof s.binding === "string" ? `const ${s.binding}` : s.binding.kind === "TupleDestructure" ? `const [${s.binding.names.map((n) => n ?? "_").join(", ")}]` : `const [${s.binding.key}, ${s.binding.value}]`;
|
|
1661
|
+
const innerExprs = s.body.statements.filter((inner) => inner.kind === "ExprStmt").map((inner) => this.emitExpr(inner.expr));
|
|
1822
1662
|
if (innerExprs.length > 0) {
|
|
1823
1663
|
const pushes = innerExprs.map((e) => `__c.push(${e});`).join(" ");
|
|
1824
1664
|
parts.push(`...(() => { const __c: any[] = []; for (${bindingStr} of ${iter}) { ${pushes} } return __c; })()`);
|
|
1825
1665
|
}
|
|
1826
1666
|
}
|
|
1827
|
-
// Other statement kinds (if, while, etc.) are not renderable as UI children inline
|
|
1828
1667
|
}
|
|
1829
1668
|
return parts.join(", ");
|
|
1830
1669
|
}
|
|
1831
1670
|
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(" ")} }`;
|
|
1671
|
+
const params = expr.params.length === 0 ? "it" : expr.params.map((p) => p.name ?? "_").join(", ");
|
|
1672
|
+
if (expr.body.length === 1 && expr.body[0].kind === "ExprStmt")
|
|
1673
|
+
return `(${params}) => ${this.emitExpr(expr.body[0].expr)}`;
|
|
1674
|
+
return `(${params}) => { ${expr.body.map((s) => this.captureStmt(s)).join(" ")} }`;
|
|
1842
1675
|
}
|
|
1843
1676
|
emitIfExpr(expr) {
|
|
1844
1677
|
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);
|
|
1678
|
+
const thenStr = expr.then.kind === "Block" ? `(() => { ${this.captureBlockStatements(expr.then.statements)} })()` : this.emitExpr(expr.then);
|
|
1679
|
+
const elseStr = expr.else.kind === "Block" ? `(() => { ${this.captureBlockStatements(expr.else.statements)} })()` : expr.else.kind === "IfExpr" ? this.emitIfExpr(expr.else) : this.emitExpr(expr.else);
|
|
1854
1680
|
return `(${cond} ? ${thenStr} : ${elseStr})`;
|
|
1855
1681
|
}
|
|
1856
1682
|
emitWhenExpr(expr) {
|
|
1857
|
-
// Compile as an IIFE with if/else chain
|
|
1858
1683
|
const parts = [];
|
|
1859
1684
|
const subject = expr.subject ? `const __s = ${this.emitExpr(expr.subject.expr)};` : "";
|
|
1860
1685
|
const subjectRef = expr.subject ? (expr.subject.binding ?? "__s") : "";
|
|
1861
1686
|
for (const branch of expr.branches) {
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
? this.captureBlock(branch.body)
|
|
1865
|
-
: `return ${this.emitExpr(branch.body)};`;
|
|
1687
|
+
const body = branch.body.kind === "Block" ? this.captureBlock(branch.body) : `return ${this.emitExpr(branch.body)};`;
|
|
1688
|
+
if (branch.isElse)
|
|
1866
1689
|
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
|
-
}
|
|
1690
|
+
else
|
|
1691
|
+
parts.push(`if (${branch.conditions.map((c) => this.emitWhenCondition(c, subjectRef)).join(" || ")}) { ${body} }`);
|
|
1875
1692
|
}
|
|
1876
1693
|
return `(() => { ${subject} ${parts.join(" else ")} })()`;
|
|
1877
1694
|
}
|
|
1878
1695
|
emitTryCatchExpr(expr) {
|
|
1879
1696
|
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(" ");
|
|
1697
|
+
const catches = expr.catches.map((c) => `catch (${c.name}) { ${this.captureBlock(c.body)} }`).join(" ");
|
|
1884
1698
|
const fin = expr.finally ? `finally { ${this.captureBlock(expr.finally)} }` : "";
|
|
1885
1699
|
return `(() => { try { ${body} } ${catches} ${fin} })()`;
|
|
1886
1700
|
}
|
|
1887
1701
|
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(", ")}]`;
|
|
1702
|
+
if (expr.collectionKind === "map")
|
|
1703
|
+
return `new Map([${expr.elements.map((e) => ("kind" in e && e.kind === "MapEntry") ? `[${this.emitExpr(e.key)}, ${this.emitExpr(e.value)}]` : "null").join(", ")}])`;
|
|
1704
|
+
if (expr.collectionKind === "set")
|
|
1705
|
+
return `new Set([${expr.elements.map((e) => this.emitExpr(e)).join(", ")}])`;
|
|
1706
|
+
return `[${expr.elements.map((e) => this.emitExpr(e)).join(", ")}]`;
|
|
1903
1707
|
}
|
|
1904
1708
|
emitObjectExpr(expr) {
|
|
1905
1709
|
const superType = expr.superTypes[0];
|
|
1906
1710
|
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 ────────────────────────────────────────────────
|
|
1711
|
+
return `(new (class${ext} { ${expr.body.members.map((m) => this.captureClassMember(m)).join(" ")} })())`;
|
|
1712
|
+
}
|
|
1713
|
+
captureBlock(block) { return block.statements.map((s) => this.captureStmt(s)).join(" "); }
|
|
1714
|
+
captureBlockStatements(stmts) { return stmts.map((s) => this.captureStmt(s)).join(" "); }
|
|
1715
|
+
captureStmt(stmt) { const saved = this.w; const tmp = new Writer(); this.w = tmp; this.emitStmt(stmt); this.w = saved; return tmp.output.trim(); }
|
|
1716
|
+
captureClassMember(member) { const saved = this.w; const tmp = new Writer(); this.w = tmp; this.emitClassMember(member); this.w = saved; return tmp.output.trim(); }
|
|
1934
1717
|
emitTypeRef(ref) {
|
|
1935
1718
|
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
|
-
*/
|
|
1719
|
+
case "SimpleTypeRef": return PRIMITIVE_TYPE_MAP[ref.name.join(".")] ?? ref.name.join(".");
|
|
1720
|
+
case "NullableTypeRef": return `${this.emitTypeRef(ref.base)} | null | undefined`;
|
|
1721
|
+
case "GenericTypeRef": return `${GENERIC_TYPE_MAP[ref.base.name.join(".")] ?? ref.base.name.join(".")}<${ref.args.map((a) => a.star ? "any" : a.type ? this.emitTypeRef(a.type) : "unknown").join(", ")}>`;
|
|
1722
|
+
case "FunctionTypeRef": return `(${ref.params.map((p, i) => `p${i}: ${this.emitTypeRef(p)}`).join(", ")}) => ${this.emitTypeRef(ref.returnType)}`;
|
|
1723
|
+
case "StarProjection": return "any";
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
emitTypeParamsStr(params) { return params.length === 0 ? "" : `<${params.map((p) => `${p.name}${p.upperBound ? ` extends ${this.emitTypeRef(p.upperBound)}` : ""}`).join(", ")}>`; }
|
|
1727
|
+
emitParamsStr(params) { return params.map((p) => `${p.vararg ? "..." : ""}${p.name}${this.opts.emitTypes ? `: ${this.emitTypeRef(p.type)}${p.vararg ? "[]" : ""}` : ""}${p.defaultValue ? ` = ${this.emitExpr(p.defaultValue)}` : ""}`).join(", "); }
|
|
1728
|
+
emitSuperTypesStr(superTypes) { return superTypes.length === 0 ? "" : superTypes.map((s, i) => i === 0 ? ` extends ${this.emitTypeRef(s.type)}` : ` implements ${this.emitTypeRef(s.type)}`).join(""); }
|
|
1729
|
+
exportPrefix(mods) { return (mods.visibility === "private" || mods.visibility === "internal") ? "" : "export "; }
|
|
1730
|
+
visibilityPrefix(mods) { switch (mods.visibility) {
|
|
1731
|
+
case "private": return "private ";
|
|
1732
|
+
case "protected": return "protected ";
|
|
1733
|
+
case "internal": return "/* internal */ ";
|
|
1734
|
+
default: return "";
|
|
1735
|
+
} }
|
|
1736
|
+
unaryOpStr(op) { return op === "not" ? "!" : op; }
|
|
1737
|
+
binaryOpStr(op) { switch (op) {
|
|
1738
|
+
case "and": return "&&";
|
|
1739
|
+
case "or": return "||";
|
|
1740
|
+
case "xor": return "^";
|
|
1741
|
+
case "shl": return "<<";
|
|
1742
|
+
case "shr": return ">>";
|
|
1743
|
+
case "ushr": return ">>>";
|
|
1744
|
+
case "===": return "===";
|
|
1745
|
+
case "!==": return "!==";
|
|
1746
|
+
default: return op;
|
|
1747
|
+
} }
|
|
2047
1748
|
gatherReferencedNames(program) {
|
|
2048
1749
|
const names = new Set();
|
|
2049
1750
|
const visited = new WeakSet();
|
|
2050
1751
|
const walk = (val) => {
|
|
2051
|
-
if (!val || typeof val !== "object")
|
|
1752
|
+
if (!val || typeof val !== "object" || visited.has(val))
|
|
2052
1753
|
return;
|
|
1754
|
+
visited.add(val);
|
|
2053
1755
|
if (Array.isArray(val)) {
|
|
2054
|
-
|
|
2055
|
-
walk(item);
|
|
1756
|
+
val.forEach(walk);
|
|
2056
1757
|
return;
|
|
2057
1758
|
}
|
|
2058
1759
|
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
|
-
}
|
|
1760
|
+
if (obj.kind === "NameExpr") {
|
|
1761
|
+
if (typeof obj.name === "string")
|
|
1762
|
+
names.add(obj.name);
|
|
2077
1763
|
return;
|
|
2078
1764
|
}
|
|
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
|
|
1765
|
+
if (obj.kind === "SimpleTypeRef") {
|
|
1766
|
+
if (obj.name?.[0] && !(obj.name[0] in PRIMITIVE_TYPE_MAP))
|
|
1767
|
+
names.add(obj.name[0]);
|
|
1768
|
+
return;
|
|
2088
1769
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
1770
|
+
if (obj.kind === "GenericTypeRef") {
|
|
1771
|
+
if (obj.base?.name?.[0] && !(obj.base.name[0] in PRIMITIVE_TYPE_MAP))
|
|
1772
|
+
names.add(obj.base.name[0]);
|
|
2091
1773
|
}
|
|
1774
|
+
Object.values(obj).forEach(walk);
|
|
2092
1775
|
};
|
|
2093
|
-
|
|
2094
|
-
walk(decl);
|
|
1776
|
+
program.declarations.forEach(walk);
|
|
2095
1777
|
return names;
|
|
2096
1778
|
}
|
|
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
1779
|
gatherAllLocalBindings(program) {
|
|
2104
1780
|
const names = new Set();
|
|
2105
1781
|
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
|
-
}
|
|
1782
|
+
program.declarations.forEach((d) => { if (d.name)
|
|
1783
|
+
names.add(d.name); });
|
|
1784
|
+
program.imports.forEach((i) => { if (!i.star)
|
|
1785
|
+
names.add(i.alias ?? i.path[i.path.length - 1]); });
|
|
2120
1786
|
const walk = (val) => {
|
|
2121
|
-
if (!val || typeof val !== "object")
|
|
1787
|
+
if (!val || typeof val !== "object" || visited.has(val))
|
|
2122
1788
|
return;
|
|
1789
|
+
visited.add(val);
|
|
2123
1790
|
if (Array.isArray(val)) {
|
|
2124
|
-
|
|
2125
|
-
walk(item);
|
|
1791
|
+
val.forEach(walk);
|
|
2126
1792
|
return;
|
|
2127
1793
|
}
|
|
2128
1794
|
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
|
-
}
|
|
1795
|
+
if (["PropertyDecl", "DestructuringDecl", "FunDecl", "ExtensionFunDecl", "ComponentDecl", "ClassDecl", "DataClassDecl", "SealedClassDecl", "EnumClassDecl", "InterfaceDecl", "ObjectDecl", "TypeAliasDecl", "LambdaExpr"].includes(obj.kind) && obj.name)
|
|
1796
|
+
names.add(obj.name);
|
|
1797
|
+
if (obj.typeParams)
|
|
1798
|
+
obj.typeParams.forEach((tp) => names.add(tp.name));
|
|
1799
|
+
if (obj.params)
|
|
1800
|
+
obj.params.forEach((p) => names.add(p.name));
|
|
1801
|
+
if (obj.kind === "ForStmt" && typeof obj.binding === "string")
|
|
1802
|
+
names.add(obj.binding);
|
|
1803
|
+
if (obj.kind === "TryCatchStmt" && obj.catches)
|
|
1804
|
+
obj.catches.forEach((c) => names.add(c.name));
|
|
1805
|
+
Object.values(obj).forEach(walk);
|
|
2224
1806
|
};
|
|
2225
|
-
|
|
2226
|
-
walk(decl);
|
|
1807
|
+
program.declarations.forEach(walk);
|
|
2227
1808
|
return names;
|
|
2228
1809
|
}
|
|
2229
1810
|
}
|
|
2230
1811
|
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
|
-
};
|
|
1812
|
+
const PRIMITIVE_TYPE_MAP = { Int: "number", Long: "bigint", Float: "number", Double: "number", Boolean: "boolean", String: "string", Char: "string", Byte: "number", Short: "number", Unit: "void", Any: "unknown", Nothing: "never" };
|
|
1813
|
+
const GENERIC_TYPE_MAP = { List: "ReadonlyArray", MutableList: "Array", Set: "ReadonlySet", MutableSet: "Set", Map: "ReadonlyMap", MutableMap: "Map", Array: "Array", Deferred: "Promise", StateFlow: "StateFlow", MutableStateFlow: "MutableStateFlow", Flow: "AsyncIterable" };
|
|
2263
1814
|
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
|
-
}
|
|
1815
|
+
const JS_GLOBAL_NAMES = new Set(["Array", "Map", "Set", "Object", "String", "Number", "Boolean", "Promise", "Error", "console", "Math", "Date", "JSON", "setTimeout", "setInterval", "fetch", "document", "window", "undefined", "null", "NaN", "Infinity", "it", "this", "super", "any", "unknown", "never", "void"]);
|
|
1816
|
+
function generate(program, opts, operatorOverloads, typeMap) { return new CodeGenerator(opts).generate(program, operatorOverloads, typeMap); }
|
|
2309
1817
|
//# sourceMappingURL=codegen.js.map
|