gofront 0.0.1 → 0.0.3
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/README.md +357 -33
- package/package.json +7 -3
- package/src/codegen/expressions.js +804 -0
- package/src/codegen/source-map.js +45 -0
- package/src/codegen/statements.js +406 -0
- package/src/codegen.js +50 -851
- package/src/compiler.js +44 -23
- package/src/dts-parser.js +0 -4
- package/src/index.js +11 -20
- package/src/lexer.js +170 -12
- package/src/parser/expressions.js +389 -0
- package/src/parser/statements.js +355 -0
- package/src/parser/types.js +140 -0
- package/src/parser.js +57 -745
- package/src/resolver.js +1 -1
- package/src/typechecker/expressions.js +359 -0
- package/src/typechecker/statements.js +250 -0
- package/src/typechecker/types.js +226 -0
- package/src/typechecker.js +390 -647
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Minimal VLQ / source-map helpers for GoFront code generation.
|
|
2
|
+
|
|
3
|
+
// ── Minimal VLQ / source-map helpers ─────────────────────────
|
|
4
|
+
const B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
5
|
+
|
|
6
|
+
function vlqEncode(value) {
|
|
7
|
+
// Signed VLQ: sign bit in LSB of first group, then 5-bit continuation chunks
|
|
8
|
+
let vlq = value < 0 ? (-value << 1) | 1 : value << 1;
|
|
9
|
+
let result = "";
|
|
10
|
+
do {
|
|
11
|
+
let digit = vlq & 0x1f;
|
|
12
|
+
vlq >>>= 5;
|
|
13
|
+
if (vlq > 0) digit |= 0x20; // continuation bit
|
|
14
|
+
result += B64[digit];
|
|
15
|
+
} while (vlq > 0);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function buildSourceMap(sourceName, mappings) {
|
|
20
|
+
// mappings: Array<{ genLine: number, srcLine: number }> (0-based)
|
|
21
|
+
// Emits one segment per generated line, column 0 → source line (delta-encoded).
|
|
22
|
+
const lines = [];
|
|
23
|
+
let prevSrcLine = 0;
|
|
24
|
+
const maxGen = mappings.reduce((m, e) => Math.max(m, e.genLine), -1);
|
|
25
|
+
const byLine = new Map(mappings.map((e) => [e.genLine, e]));
|
|
26
|
+
for (let g = 0; g <= maxGen; g++) {
|
|
27
|
+
const entry = byLine.get(g);
|
|
28
|
+
if (entry) {
|
|
29
|
+
const srcDelta = entry.srcLine - prevSrcLine;
|
|
30
|
+
// Segment: [genCol=0, srcFileIdx=0, srcLineDelta, srcCol=0]
|
|
31
|
+
lines.push(
|
|
32
|
+
vlqEncode(0) + vlqEncode(0) + vlqEncode(srcDelta) + vlqEncode(0),
|
|
33
|
+
);
|
|
34
|
+
prevSrcLine = entry.srcLine;
|
|
35
|
+
} else {
|
|
36
|
+
lines.push(""); // no mapping for this generated line
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return JSON.stringify({
|
|
40
|
+
version: 3,
|
|
41
|
+
sources: [sourceName],
|
|
42
|
+
names: [],
|
|
43
|
+
mappings: lines.join(";"),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
// CodeGen statement methods — installed as a mixin on CodeGen.prototype.
|
|
2
|
+
|
|
3
|
+
export const statementGenMethods = {
|
|
4
|
+
genBlock(block) {
|
|
5
|
+
for (const stmt of block.stmts) this.genStmt(stmt);
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
genStmt(stmt) {
|
|
9
|
+
// Record source mapping for the first line this statement produces
|
|
10
|
+
const srcLine = stmt._line ?? null;
|
|
11
|
+
const _line0 = this.out.length;
|
|
12
|
+
switch (stmt.kind) {
|
|
13
|
+
case "VarDecl":
|
|
14
|
+
this.genVarDecl(stmt);
|
|
15
|
+
break;
|
|
16
|
+
case "ConstDecl":
|
|
17
|
+
this.genConstDecl(stmt);
|
|
18
|
+
break;
|
|
19
|
+
case "TypeDecl":
|
|
20
|
+
this.genTypeDeclWithMethods(stmt, []);
|
|
21
|
+
break;
|
|
22
|
+
|
|
23
|
+
case "DefineStmt": {
|
|
24
|
+
// Map comma-ok: v, ok := m[key] → let v = m[key]; let ok = key in m;
|
|
25
|
+
const rhsNode = stmt.rhs[0];
|
|
26
|
+
if (
|
|
27
|
+
stmt.rhs.length === 1 &&
|
|
28
|
+
stmt.lhs.length === 2 &&
|
|
29
|
+
rhsNode?.kind === "IndexExpr" &&
|
|
30
|
+
rhsNode.expr?._type?.kind === "map"
|
|
31
|
+
) {
|
|
32
|
+
const mapExpr = this.genExpr(rhsNode.expr);
|
|
33
|
+
const keyExpr = this.genExpr(rhsNode.index);
|
|
34
|
+
const [vName, okName] = stmt.lhs.map(
|
|
35
|
+
(e) => e.name ?? this.genExpr(e),
|
|
36
|
+
);
|
|
37
|
+
if (vName !== "_")
|
|
38
|
+
this.line(`let ${vName} = ${mapExpr}[${keyExpr}];`);
|
|
39
|
+
if (okName !== "_")
|
|
40
|
+
this.line(`let ${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
const rhs = stmt.rhs.map((e) => this.genExpr(e));
|
|
44
|
+
const lhs = stmt.lhs.map((e) => e.name ?? this.genExpr(e));
|
|
45
|
+
const redecls = stmt.lhs.map((e) => !!e._redecl);
|
|
46
|
+
const anyRedecl = redecls.some((r) => r);
|
|
47
|
+
if (lhs.length === 1) {
|
|
48
|
+
if (redecls[0]) {
|
|
49
|
+
this.line(`${lhs[0]} = ${rhs[0]};`);
|
|
50
|
+
} else {
|
|
51
|
+
this.line(`let ${lhs[0]} = ${rhs[0]};`);
|
|
52
|
+
}
|
|
53
|
+
} else if (!anyRedecl) {
|
|
54
|
+
// All new: let [a, b] = ...
|
|
55
|
+
const rhsStr = rhs.length === 1 ? rhs[0] : `[${rhs.join(", ")}]`;
|
|
56
|
+
this.line(`let [${lhs.join(", ")}] = ${rhsStr};`);
|
|
57
|
+
} else {
|
|
58
|
+
// Mixed new/existing: emit separate statements
|
|
59
|
+
// First compute the tuple into a temp if needed
|
|
60
|
+
if (rhs.length === 1 && lhs.length > 1) {
|
|
61
|
+
const tmp = "__t";
|
|
62
|
+
this.line(`let ${tmp} = ${rhs[0]};`);
|
|
63
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
64
|
+
const prefix = redecls[i] ? "" : "let ";
|
|
65
|
+
this.line(`${prefix}${lhs[i]} = ${tmp}[${i}];`);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
69
|
+
const prefix = redecls[i] ? "" : "let ";
|
|
70
|
+
this.line(`${prefix}${lhs[i]} = ${rhs[i]};`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
case "AssignStmt": {
|
|
78
|
+
const rhs = stmt.rhs.map((e) => this.genExpr(e));
|
|
79
|
+
for (const e of stmt.lhs) if (e.kind === "IndexExpr") e._lvalue = true;
|
|
80
|
+
const lhs = stmt.lhs.map((e) => this.genExpr(e));
|
|
81
|
+
if (lhs.length === 1) {
|
|
82
|
+
this.line(`${lhs[0]} ${stmt.op} ${rhs[0]};`);
|
|
83
|
+
} else {
|
|
84
|
+
const rhsStr = rhs.length === 1 ? rhs[0] : `[${rhs.join(", ")}]`;
|
|
85
|
+
this.line(`[${lhs.join(", ")}] = ${rhsStr};`);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case "IncDecStmt":
|
|
91
|
+
this.line(`${this.genExpr(stmt.expr)}${stmt.op};`);
|
|
92
|
+
break;
|
|
93
|
+
|
|
94
|
+
case "ExprStmt":
|
|
95
|
+
this.line(`${this.genExpr(stmt.expr)};`);
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case "ReturnStmt": {
|
|
99
|
+
if (stmt.values.length === 0) {
|
|
100
|
+
// bare return — emit named return vars if present
|
|
101
|
+
if (this.namedReturnVars?.length > 0) {
|
|
102
|
+
const vars = this.namedReturnVars;
|
|
103
|
+
this.line(
|
|
104
|
+
vars.length === 1
|
|
105
|
+
? `return ${vars[0]};`
|
|
106
|
+
: `return [${vars.join(", ")}];`,
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
this.line("return;");
|
|
110
|
+
}
|
|
111
|
+
} else if (stmt.values.length === 1) {
|
|
112
|
+
this.line(`return ${this.genExpr(stmt.values[0])};`);
|
|
113
|
+
} else {
|
|
114
|
+
this.line(
|
|
115
|
+
`return [${stmt.values.map((v) => this.genExpr(v)).join(", ")}];`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case "IfStmt": {
|
|
122
|
+
if (stmt.init) {
|
|
123
|
+
// Wrap in a block so init variable is scoped
|
|
124
|
+
this.line("{");
|
|
125
|
+
this.indented(() => {
|
|
126
|
+
this.genStmt(stmt.init);
|
|
127
|
+
this._genIf(stmt);
|
|
128
|
+
});
|
|
129
|
+
this.line("}");
|
|
130
|
+
} else {
|
|
131
|
+
this._genIf(stmt);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case "ForStmt":
|
|
137
|
+
this.genFor(stmt);
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case "SwitchStmt":
|
|
141
|
+
this.genSwitch(stmt);
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case "TypeSwitchStmt":
|
|
145
|
+
this.genTypeSwitch(stmt);
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case "DeferStmt":
|
|
149
|
+
this.line(`__defers.push(() => { ${this.genExpr(stmt.call)}; });`);
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
case "LabeledStmt":
|
|
153
|
+
this.line(`${stmt.label}:`);
|
|
154
|
+
this.genStmt(stmt.body);
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case "BranchStmt":
|
|
158
|
+
if (stmt.keyword !== "fallthrough")
|
|
159
|
+
this.line(
|
|
160
|
+
stmt.label ? `${stmt.keyword} ${stmt.label};` : `${stmt.keyword};`,
|
|
161
|
+
);
|
|
162
|
+
// fallthrough: omit — JS switch falls through naturally without a break
|
|
163
|
+
break;
|
|
164
|
+
|
|
165
|
+
case "Block":
|
|
166
|
+
this.line("{");
|
|
167
|
+
this.indented(() => this.genBlock(stmt));
|
|
168
|
+
this.line("}");
|
|
169
|
+
break;
|
|
170
|
+
|
|
171
|
+
default:
|
|
172
|
+
throw new Error(`CodeGen: unhandled statement kind '${stmt.kind}'`);
|
|
173
|
+
}
|
|
174
|
+
// Record source mapping: first output line this statement produced
|
|
175
|
+
if (srcLine != null && this.out.length > _line0) {
|
|
176
|
+
this._srcMappings.push({ genLine: _line0, srcLine: srcLine - 1 });
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
_genIf(stmt) {
|
|
181
|
+
this.line(`if (${this.genExpr(stmt.cond)}) {`);
|
|
182
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
183
|
+
this._genElse(stmt.elseBody);
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
_genElse(elseBody) {
|
|
187
|
+
if (!elseBody) {
|
|
188
|
+
this.line("}");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (elseBody.kind === "IfStmt") {
|
|
192
|
+
this.line(`} else if (${this.genExpr(elseBody.cond)}) {`);
|
|
193
|
+
this.indented(() => this.genBlock(elseBody.body));
|
|
194
|
+
this._genElse(elseBody.elseBody);
|
|
195
|
+
} else {
|
|
196
|
+
this.line("} else {");
|
|
197
|
+
this.indented(() => this.genBlock(elseBody));
|
|
198
|
+
this.line("}");
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
genFor(stmt) {
|
|
203
|
+
// Detect range: init is DefineStmt with rhs[0] being a RangeExpr
|
|
204
|
+
if (this.isRangeFor(stmt)) {
|
|
205
|
+
this.genRangeFor(stmt);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// for range N { } — no variable, just repeat N times
|
|
210
|
+
if (stmt.cond?.kind === "RangeExpr") {
|
|
211
|
+
const iteree = this.genExpr(stmt.cond.expr);
|
|
212
|
+
this.line(`for (let _$ = 0; _$ < ${iteree}; _$++) {`);
|
|
213
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
214
|
+
this.line("}");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!stmt.init && !stmt.post) {
|
|
219
|
+
if (!stmt.cond) {
|
|
220
|
+
// infinite loop
|
|
221
|
+
this.line("while (true) {");
|
|
222
|
+
} else {
|
|
223
|
+
this.line(`while (${this.genExpr(stmt.cond)}) {`);
|
|
224
|
+
}
|
|
225
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
226
|
+
this.line("}");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Standard C-style for
|
|
231
|
+
const init = stmt.init ? this.stmtInline(stmt.init) : "";
|
|
232
|
+
const cond = stmt.cond ? this.genExpr(stmt.cond) : "";
|
|
233
|
+
const post = stmt.post ? this.stmtInline(stmt.post) : "";
|
|
234
|
+
this.line(`for (${init}; ${cond}; ${post}) {`);
|
|
235
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
236
|
+
this.line("}");
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
isRangeFor(stmt) {
|
|
240
|
+
if (!stmt.init) return false;
|
|
241
|
+
const init = stmt.init;
|
|
242
|
+
if (init.kind !== "DefineStmt" && init.kind !== "AssignStmt") return false;
|
|
243
|
+
return init.rhs?.[0]?.kind === "RangeExpr";
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
genRangeFor(stmt) {
|
|
247
|
+
const init = stmt.init;
|
|
248
|
+
const range = init.rhs[0];
|
|
249
|
+
const lhs = init.lhs.map((e) => e.name ?? this.genExpr(e));
|
|
250
|
+
const iteree = this.genExpr(range.expr);
|
|
251
|
+
const iterType = range.expr._type;
|
|
252
|
+
|
|
253
|
+
let iterExpr;
|
|
254
|
+
if (
|
|
255
|
+
lhs.length <= 1 &&
|
|
256
|
+
((iterType?.kind === "basic" &&
|
|
257
|
+
(iterType.name === "int" || iterType.name === "float64")) ||
|
|
258
|
+
(iterType?.kind === "untyped" &&
|
|
259
|
+
(iterType.base === "int" || iterType.base === "float64")))
|
|
260
|
+
) {
|
|
261
|
+
// integer range (Go 1.22): for i := range n → C-style for loop
|
|
262
|
+
const v = lhs[0] === "_" ? "_$" : lhs[0];
|
|
263
|
+
this.line(`for (let ${v} = 0; ${v} < ${iteree}; ${v}++) {`);
|
|
264
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
265
|
+
this.line("}");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (
|
|
269
|
+
iterType?.kind === "map" ||
|
|
270
|
+
(iterType?.kind === "named" && iterType.underlying?.kind === "map")
|
|
271
|
+
) {
|
|
272
|
+
// map: for k, v := range m → for (const [k, v] of Object.entries(m))
|
|
273
|
+
iterExpr = `Object.entries(${iteree})`;
|
|
274
|
+
} else if (
|
|
275
|
+
(iterType?.kind === "basic" && iterType.name === "string") ||
|
|
276
|
+
(iterType?.kind === "untyped" && iterType.base === "string")
|
|
277
|
+
) {
|
|
278
|
+
// string: for i, ch := range s → for (const [i, ch] of s.split('').entries())
|
|
279
|
+
iterExpr =
|
|
280
|
+
lhs.length === 1
|
|
281
|
+
? `Array.from(${iteree}).keys()`
|
|
282
|
+
: `Array.from(${iteree}).entries()`;
|
|
283
|
+
} else {
|
|
284
|
+
// slice/array: for i, v := range arr → for (const [i, v] of __s(arr).entries())
|
|
285
|
+
// null-guard handles nil slices (zero value) gracefully.
|
|
286
|
+
this._usesSliceGuard = true;
|
|
287
|
+
if (lhs.length === 1) {
|
|
288
|
+
iterExpr = `__s(${iteree}).keys()`;
|
|
289
|
+
} else {
|
|
290
|
+
iterExpr = `__s(${iteree}).entries()`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const binding =
|
|
295
|
+
lhs.length === 1
|
|
296
|
+
? lhs[0] === "_"
|
|
297
|
+
? "_$"
|
|
298
|
+
: lhs[0]
|
|
299
|
+
: `[${lhs.map((n) => (n === "_" ? "_$" : n)).join(", ")}]`;
|
|
300
|
+
|
|
301
|
+
this.line(`for (const ${binding} of ${iterExpr}) {`);
|
|
302
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
303
|
+
this.line("}");
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// Inline a statement as a for-init/post string (no semicolon)
|
|
307
|
+
stmtInline(stmt) {
|
|
308
|
+
switch (stmt.kind) {
|
|
309
|
+
case "DefineStmt": {
|
|
310
|
+
const rhs = stmt.rhs.map((e) => this.genExpr(e)).join(", ");
|
|
311
|
+
const lhs = stmt.lhs.map((e) => e.name ?? this.genExpr(e)).join(", ");
|
|
312
|
+
return `let ${lhs} = ${rhs}`;
|
|
313
|
+
}
|
|
314
|
+
case "AssignStmt": {
|
|
315
|
+
const rhs = stmt.rhs.map((e) => this.genExpr(e)).join(", ");
|
|
316
|
+
const lhs = stmt.lhs.map((e) => this.genExpr(e)).join(", ");
|
|
317
|
+
return `${lhs} ${stmt.op} ${rhs}`;
|
|
318
|
+
}
|
|
319
|
+
case "IncDecStmt":
|
|
320
|
+
return `${this.genExpr(stmt.expr)}${stmt.op}`;
|
|
321
|
+
case "ExprStmt":
|
|
322
|
+
return this.genExpr(stmt.expr);
|
|
323
|
+
default:
|
|
324
|
+
return "";
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
genSwitch(stmt) {
|
|
329
|
+
const genBody = () => {
|
|
330
|
+
const tag = stmt.tag ? this.genExpr(stmt.tag) : "true";
|
|
331
|
+
this.line(`switch (${tag}) {`);
|
|
332
|
+
this.indented(() => {
|
|
333
|
+
for (const c of stmt.cases) {
|
|
334
|
+
if (c.list) {
|
|
335
|
+
for (const e of c.list) this.line(`case ${this.genExpr(e)}:`);
|
|
336
|
+
} else {
|
|
337
|
+
this.line("default:");
|
|
338
|
+
}
|
|
339
|
+
// Wrap each case body in {} so `let` declarations don't bleed
|
|
340
|
+
// across sibling cases (JS switch shares one block scope).
|
|
341
|
+
this.line("{");
|
|
342
|
+
this.indented(() => {
|
|
343
|
+
for (const s of c.stmts) this.genStmt(s);
|
|
344
|
+
// Go doesn't fall through by default — add break unless last stmt is return/break
|
|
345
|
+
const last = c.stmts[c.stmts.length - 1];
|
|
346
|
+
if (
|
|
347
|
+
!last ||
|
|
348
|
+
(last.kind !== "ReturnStmt" && last.kind !== "BranchStmt")
|
|
349
|
+
) {
|
|
350
|
+
this.line("break;");
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
this.line("}");
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
this.line("}");
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
if (stmt.init) {
|
|
360
|
+
this.line("{");
|
|
361
|
+
this.indented(() => {
|
|
362
|
+
this.genStmt(stmt.init);
|
|
363
|
+
genBody();
|
|
364
|
+
});
|
|
365
|
+
this.line("}");
|
|
366
|
+
} else {
|
|
367
|
+
genBody();
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
genTypeSwitch(stmt) {
|
|
372
|
+
this.line("{");
|
|
373
|
+
this.indented(() => {
|
|
374
|
+
const val = this.genExpr(stmt.expr);
|
|
375
|
+
this.line(`const __tsw = ${val};`);
|
|
376
|
+
if (stmt.assign) this.line(`let ${stmt.assign} = __tsw;`);
|
|
377
|
+
|
|
378
|
+
let first = true;
|
|
379
|
+
let hasDefault = false;
|
|
380
|
+
for (const c of stmt.cases) {
|
|
381
|
+
if (!c.types) {
|
|
382
|
+
// default case — emit last
|
|
383
|
+
hasDefault = c;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const cond = c.types
|
|
387
|
+
.map((t) => this._typeCheckExpr(t, "__tsw"))
|
|
388
|
+
.join(" || ");
|
|
389
|
+
this.line(`${first ? "if" : "else if"} (${cond}) {`);
|
|
390
|
+
this.indented(() => {
|
|
391
|
+
for (const s of c.stmts) this.genStmt(s);
|
|
392
|
+
});
|
|
393
|
+
this.line("}");
|
|
394
|
+
first = false;
|
|
395
|
+
}
|
|
396
|
+
if (hasDefault) {
|
|
397
|
+
this.line(first ? "{" : "else {");
|
|
398
|
+
this.indented(() => {
|
|
399
|
+
for (const s of hasDefault.stmts) this.genStmt(s);
|
|
400
|
+
});
|
|
401
|
+
this.line("}");
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
this.line("}");
|
|
405
|
+
},
|
|
406
|
+
};
|