gofront 1.3.4 → 1.3.6
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/package.json +1 -1
- package/src/cli-core.js +118 -13
- package/src/codegen/expressions.js +408 -89
- package/src/codegen/index.js +213 -29
- package/src/codegen/runtime.js +22 -1
- package/src/codegen/statements.js +170 -45
- package/src/codegen/stdlib/builder.js +7 -2
- package/src/codegen/stdlib/fmt.js +2 -2
- package/src/codegen/stdlib/io.js +1 -1
- package/src/dev-server.js +22 -10
- package/src/index.js +8 -2
- package/src/parser/declarations.js +2 -1
- package/src/pwa.js +3 -1
- package/src/typechecker/assignability.js +50 -4
- package/src/typechecker/expressions.js +13 -1
- package/src/typechecker/index.js +71 -31
- package/src/typechecker/resolve.js +2 -0
- package/src/typechecker/statements.js +10 -1
- package/src/typechecker/stdlib/web.js +54 -26
- package/src/typechecker/types.js +3 -2
- package/src/vendor.js +12 -7
|
@@ -58,42 +58,45 @@ const NS_CONSTANTS = {
|
|
|
58
58
|
utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
// JavaScript precedence of the operators emitted for Go binary expressions.
|
|
62
|
+
const JS_OP_PREC = {
|
|
62
63
|
"||": 1,
|
|
63
64
|
"&&": 2,
|
|
64
|
-
"
|
|
65
|
-
"!=": 3,
|
|
66
|
-
"<": 3,
|
|
67
|
-
">": 3,
|
|
68
|
-
"<=": 3,
|
|
69
|
-
">=": 3,
|
|
70
|
-
"+": 4,
|
|
71
|
-
"-": 4,
|
|
72
|
-
"|": 4,
|
|
65
|
+
"|": 3,
|
|
73
66
|
"^": 4,
|
|
74
|
-
"*": 5,
|
|
75
|
-
"/": 5,
|
|
76
|
-
"%": 5,
|
|
77
|
-
"<<": 5,
|
|
78
|
-
">>": 5,
|
|
79
67
|
"&": 5,
|
|
80
|
-
"
|
|
68
|
+
"==": 6,
|
|
69
|
+
"!=": 6,
|
|
70
|
+
"===": 6,
|
|
71
|
+
"!==": 6,
|
|
72
|
+
"<": 7,
|
|
73
|
+
">": 7,
|
|
74
|
+
"<=": 7,
|
|
75
|
+
">=": 7,
|
|
76
|
+
"<<": 8,
|
|
77
|
+
">>": 8,
|
|
78
|
+
">>>": 8,
|
|
79
|
+
"+": 9,
|
|
80
|
+
"-": 9,
|
|
81
|
+
"*": 10,
|
|
82
|
+
"/": 10,
|
|
83
|
+
"%": 10,
|
|
81
84
|
};
|
|
82
85
|
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
return (isRight ? childPrec <= parentPrec : childPrec < parentPrec)
|
|
89
|
-
? `(${code})`
|
|
90
|
-
: code;
|
|
86
|
+
function wrapForJsOp(code, childOp, parentOp, isRight) {
|
|
87
|
+
if (!childOp) return code;
|
|
88
|
+
const p = JS_OP_PREC[parentOp];
|
|
89
|
+
const c = JS_OP_PREC[childOp];
|
|
90
|
+
return (isRight ? c <= p : c < p) ? `(${code})` : code;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
const WEB_PASCAL_ALIAS_TYPES = new Set([
|
|
94
|
+
"WebGLRenderingContext",
|
|
95
|
+
"WebGL2RenderingContext",
|
|
96
|
+
"GPUDevice",
|
|
97
|
+
"GPUQueue",
|
|
98
|
+
"GPUAdapter",
|
|
99
|
+
]);
|
|
97
100
|
|
|
98
101
|
const INT_TYPE_NAMES = new Set([
|
|
99
102
|
"int",
|
|
@@ -200,6 +203,16 @@ const EXPR_GEN_DELEGATE = {
|
|
|
200
203
|
/** @type {ThisType<CodeGen>} */
|
|
201
204
|
export const expressionGenMethods = {
|
|
202
205
|
genExpr(expr) {
|
|
206
|
+
if (expr._ifaceBox && !expr._ifaceBoxing) {
|
|
207
|
+
expr._ifaceBoxing = true;
|
|
208
|
+
try {
|
|
209
|
+
this._usesIfaceBox = true;
|
|
210
|
+
const fn = expr._ifaceBox === "ptr" ? "__ifp" : "__ifv";
|
|
211
|
+
return `${fn}(${this.genExpr(expr)})`;
|
|
212
|
+
} finally {
|
|
213
|
+
expr._ifaceBoxing = false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
203
216
|
switch (expr.kind) {
|
|
204
217
|
case "BasicLit":
|
|
205
218
|
if (expr.litKind === "STRING") return JSON.stringify(expr.value);
|
|
@@ -253,6 +266,204 @@ export const expressionGenMethods = {
|
|
|
253
266
|
return this._isStructType(expr._type);
|
|
254
267
|
},
|
|
255
268
|
|
|
269
|
+
// ── Go value semantics for structs and arrays ─────────────────
|
|
270
|
+
|
|
271
|
+
_aggregateBase(t) {
|
|
272
|
+
if (!t) return null;
|
|
273
|
+
const b =
|
|
274
|
+
t.kind === "named"
|
|
275
|
+
? (t.underlying ?? this.checker?.types.get(t.name)?.underlying)
|
|
276
|
+
: t;
|
|
277
|
+
return b?.kind === "struct" || b?.kind === "array" ? b : null;
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
_structClassName(t) {
|
|
281
|
+
const name =
|
|
282
|
+
typeof t?.name === "string" ? t.name.replace(/\[.*$/, "") : null;
|
|
283
|
+
return name && this.structNames.has(name) ? name : null;
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
// Pseudo type object for an AST type node (used where no checker type is available).
|
|
287
|
+
_typeFromNode(node) {
|
|
288
|
+
if (!node) return null;
|
|
289
|
+
if (node.kind === "TypeName" && this.structNames.has(node.name))
|
|
290
|
+
return { kind: "named", name: node.name, underlying: { kind: "struct" } };
|
|
291
|
+
if (node.kind === "ArrayType")
|
|
292
|
+
return { kind: "array", elem: this._typeFromNode(node.elem) };
|
|
293
|
+
return null;
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
_cloneJs(t, js) {
|
|
297
|
+
const base = this._aggregateBase(t);
|
|
298
|
+
if (!base) return js;
|
|
299
|
+
if (base.kind === "struct") {
|
|
300
|
+
if (this._structClassName(t) || this._structClassName(base))
|
|
301
|
+
return `${js}.__clone()`;
|
|
302
|
+
this._usesSClone = true;
|
|
303
|
+
return `__sclone(${js})`;
|
|
304
|
+
}
|
|
305
|
+
const elemClone = this._cloneJs(base.elem, "__e");
|
|
306
|
+
return elemClone === "__e"
|
|
307
|
+
? `${js}?.slice()`
|
|
308
|
+
: `${js}?.map((__e) => ${elemClone})`;
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
_isAddressableExpr(e) {
|
|
312
|
+
switch (e?.kind) {
|
|
313
|
+
case "Ident":
|
|
314
|
+
return e.name !== "_" && !e._isTypeRef;
|
|
315
|
+
case "SelectorExpr":
|
|
316
|
+
return !e._isMethodValue && !e._isMethodExpr;
|
|
317
|
+
case "IndexExpr":
|
|
318
|
+
return true;
|
|
319
|
+
case "UnaryExpr":
|
|
320
|
+
return e.op === "*";
|
|
321
|
+
case "TypeAssertExpr":
|
|
322
|
+
return !e._commaOk;
|
|
323
|
+
default:
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
// Emits `expr` as a Go value: struct/array operands that alias existing storage are copied.
|
|
329
|
+
genValueExpr(expr) {
|
|
330
|
+
const js = this.genExpr(expr);
|
|
331
|
+
if (expr._ifaceBox) return js;
|
|
332
|
+
if (!this._aggregateBase(expr._type) || !this._isAddressableExpr(expr))
|
|
333
|
+
return js;
|
|
334
|
+
return this._cloneJs(expr._type, js);
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
_rootIdentName(e) {
|
|
338
|
+
while (e) {
|
|
339
|
+
if (e.kind === "Ident") return e.name;
|
|
340
|
+
if (e.kind === "SelectorExpr" || e.kind === "IndexExpr") e = e.expr;
|
|
341
|
+
else if (e.kind === "UnaryExpr" && e.op === "*") e = e.operand;
|
|
342
|
+
else return null;
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
// True when `node` itself (not its children) writes to variable `name`.
|
|
348
|
+
_nodeWritesVar(node, name) {
|
|
349
|
+
const root = (e) => this._rootIdentName(e) === name;
|
|
350
|
+
switch (node.kind) {
|
|
351
|
+
case "AssignStmt":
|
|
352
|
+
return node.lhs.some(root);
|
|
353
|
+
case "DefineStmt":
|
|
354
|
+
return node.lhs.some((e) => e._redecl && e.name === name);
|
|
355
|
+
case "IncDecStmt":
|
|
356
|
+
return root(node.expr);
|
|
357
|
+
case "SelectorExpr":
|
|
358
|
+
return Boolean(
|
|
359
|
+
node._isMethodValue && node._type?._ptrRecv && root(node.expr),
|
|
360
|
+
);
|
|
361
|
+
default:
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
// True when `node` may modify (or take the address of) the value held by variable `name`.
|
|
367
|
+
_nodeMutatesVar(node, name, addrOnly = false) {
|
|
368
|
+
if (!node || typeof node !== "object") return false;
|
|
369
|
+
if (Array.isArray(node))
|
|
370
|
+
return node.some((n) => this._nodeMutatesVar(n, name, addrOnly));
|
|
371
|
+
if (
|
|
372
|
+
node.kind === "UnaryExpr" &&
|
|
373
|
+
node.op === "&" &&
|
|
374
|
+
this._rootIdentName(node.operand) === name
|
|
375
|
+
)
|
|
376
|
+
return true;
|
|
377
|
+
if (!addrOnly && this._nodeWritesVar(node, name)) return true;
|
|
378
|
+
for (const key of Object.keys(node)) {
|
|
379
|
+
if (key.startsWith("_")) continue;
|
|
380
|
+
if (this._nodeMutatesVar(node[key], name, addrOnly)) return true;
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
_fnMutates(name, addrOnly = false) {
|
|
386
|
+
const ctx = this._fnCtx;
|
|
387
|
+
if (!ctx) return true;
|
|
388
|
+
const key = `${addrOnly ? "&" : ""}${name}`;
|
|
389
|
+
if (!ctx.mut.has(key))
|
|
390
|
+
ctx.mut.set(key, this._nodeMutatesVar(ctx.body, name, addrOnly));
|
|
391
|
+
return ctx.mut.get(key);
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
_withFnCtx(body, fn) {
|
|
395
|
+
const prev = this._fnCtx;
|
|
396
|
+
this._fnCtx = {
|
|
397
|
+
body,
|
|
398
|
+
mut: new Map(),
|
|
399
|
+
owned: new Set(),
|
|
400
|
+
borrowed: new Set(),
|
|
401
|
+
};
|
|
402
|
+
try {
|
|
403
|
+
fn();
|
|
404
|
+
} finally {
|
|
405
|
+
this._fnCtx = prev;
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
_markOwnership(name, owned) {
|
|
410
|
+
if (!this._fnCtx || !name || name === "_") return;
|
|
411
|
+
(owned ? this._fnCtx.owned : this._fnCtx.borrowed).add(name);
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
// Value for a newly declared local: addressable aggregates are copied (Go value semantics).
|
|
415
|
+
_genDeclValue(name, rhsNode) {
|
|
416
|
+
this._markOwnership(name, true);
|
|
417
|
+
return this.genValueExpr(rhsNode);
|
|
418
|
+
},
|
|
419
|
+
|
|
420
|
+
// Copies aggregate params/receivers that the function body mutates.
|
|
421
|
+
_emitParamCopies(params) {
|
|
422
|
+
for (const p of params) {
|
|
423
|
+
if (p.variadic || !p.name || p.name === "_") continue;
|
|
424
|
+
const t = this._typeFromNode(p.type);
|
|
425
|
+
if (!t || this._boxedVars.has(p.name)) continue;
|
|
426
|
+
if (this._fnMutates(p.name)) {
|
|
427
|
+
this.line(`${p.name} = ${this._cloneJs(t, p.name)};`);
|
|
428
|
+
this._markOwnership(p.name, true);
|
|
429
|
+
} else {
|
|
430
|
+
this._markOwnership(p.name, false);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
// An owned local can be returned without a copy unless something else may still write it.
|
|
436
|
+
_genReturnValue(v) {
|
|
437
|
+
const ctx = this._fnCtx;
|
|
438
|
+
if (
|
|
439
|
+
v.kind === "Ident" &&
|
|
440
|
+
ctx?.owned.has(v.name) &&
|
|
441
|
+
!ctx.borrowed.has(v.name) &&
|
|
442
|
+
!this._fnMutates(v.name, true) &&
|
|
443
|
+
!this._closureMutates(v.name)
|
|
444
|
+
)
|
|
445
|
+
return this.genExpr(v);
|
|
446
|
+
return this.genValueExpr(v);
|
|
447
|
+
},
|
|
448
|
+
|
|
449
|
+
_closureMutates(name) {
|
|
450
|
+
const ctx = this._fnCtx;
|
|
451
|
+
const key = `λ${name}`;
|
|
452
|
+
if (!ctx.mut.has(key)) {
|
|
453
|
+
const walk = (node) => {
|
|
454
|
+
if (!node || typeof node !== "object") return false;
|
|
455
|
+
if (Array.isArray(node)) return node.some(walk);
|
|
456
|
+
if (node.kind === "FuncLit")
|
|
457
|
+
return this._nodeMutatesVar(node.body, name);
|
|
458
|
+
return Object.keys(node).some(
|
|
459
|
+
(k) => !k.startsWith("_") && walk(node[k]),
|
|
460
|
+
);
|
|
461
|
+
};
|
|
462
|
+
ctx.mut.set(key, walk(ctx.body));
|
|
463
|
+
}
|
|
464
|
+
return ctx.mut.get(key);
|
|
465
|
+
},
|
|
466
|
+
|
|
256
467
|
_genUnaryExpr(expr) {
|
|
257
468
|
const op = expr.op === "^" ? "~" : expr.op; // bitwise NOT
|
|
258
469
|
// Address-of: &x
|
|
@@ -287,7 +498,11 @@ export const expressionGenMethods = {
|
|
|
287
498
|
if (op === "-") return `{ re: -${inner}.re, im: -${inner}.im }`;
|
|
288
499
|
return inner;
|
|
289
500
|
}
|
|
290
|
-
|
|
501
|
+
const code = this.genExpr(expr.operand);
|
|
502
|
+
const needsParens =
|
|
503
|
+
this._emittedBinaryOp(expr.operand) !== null ||
|
|
504
|
+
((op === "-" || op === "+") && /^[-+]/.test(code));
|
|
505
|
+
return needsParens ? `${op}(${code})` : `${op}${code}`;
|
|
291
506
|
},
|
|
292
507
|
|
|
293
508
|
_genSelectorExpr(expr) {
|
|
@@ -302,11 +517,27 @@ export const expressionGenMethods = {
|
|
|
302
517
|
if (this.bundledPackages.has(base)) return expr.field;
|
|
303
518
|
if (expr.expr._type?.kind === "pointer" && expr.field !== "value")
|
|
304
519
|
return this._genPointerSelectorField(expr, base);
|
|
305
|
-
const sel = `${base}.${expr
|
|
520
|
+
const sel = `${base}.${this._jsMemberName(expr)}`;
|
|
306
521
|
if (expr._isMethodValue && !expr._callee) return `${sel}.bind(${base})`;
|
|
307
522
|
return sel;
|
|
308
523
|
},
|
|
309
524
|
|
|
525
|
+
// PascalCase aliases of Web API methods (gl.ClearColor) map to the real camelCase member.
|
|
526
|
+
_jsMemberName(expr) {
|
|
527
|
+
const f = expr.field;
|
|
528
|
+
const t = expr.expr._type;
|
|
529
|
+
if (
|
|
530
|
+
t?.kind === "named" &&
|
|
531
|
+
WEB_PASCAL_ALIAS_TYPES.has(t.name) &&
|
|
532
|
+
/^[A-Z]/.test(f) &&
|
|
533
|
+
/[a-z]/.test(f)
|
|
534
|
+
) {
|
|
535
|
+
const camel = f[0].toLowerCase() + f.slice(1);
|
|
536
|
+
if (t.underlying?.methods?.has(camel)) return camel;
|
|
537
|
+
}
|
|
538
|
+
return f;
|
|
539
|
+
},
|
|
540
|
+
|
|
310
541
|
_genPointerSelectorField(expr, base) {
|
|
311
542
|
if (this._isStructPointerType(expr.expr._type)) {
|
|
312
543
|
const sel = `${base}.${expr.field}`;
|
|
@@ -366,7 +597,12 @@ export const expressionGenMethods = {
|
|
|
366
597
|
this._boxedVars = new Set(prevBoxed); // inherit parent's boxed vars (closures)
|
|
367
598
|
this._scanAddressTaken(expr.body);
|
|
368
599
|
this.out = [];
|
|
369
|
-
this.
|
|
600
|
+
this._withFnCtx(expr.body, () =>
|
|
601
|
+
this.indented(() => {
|
|
602
|
+
this._emitParamCopies(expr.params);
|
|
603
|
+
this._genBody(expr.body);
|
|
604
|
+
}),
|
|
605
|
+
);
|
|
370
606
|
const body = this.out.join("\n");
|
|
371
607
|
this.out = saved;
|
|
372
608
|
this._boxedVars = prevBoxed;
|
|
@@ -500,7 +736,9 @@ export const expressionGenMethods = {
|
|
|
500
736
|
const sliceJS = `${this.genExpr(expr.args[0])}.${wrapField}`;
|
|
501
737
|
const elems = expr.args
|
|
502
738
|
.slice(1)
|
|
503
|
-
.map((a) =>
|
|
739
|
+
.map((a) =>
|
|
740
|
+
a._spread ? `...${this.genExpr(a)}` : this.genValueExpr(a),
|
|
741
|
+
);
|
|
504
742
|
if (elems.length === 0) return this.genExpr(expr.args[0]);
|
|
505
743
|
this._usesAppend = true;
|
|
506
744
|
return `new ${retType.name}(__append(${sliceJS}, ${elems.join(", ")}))`;
|
|
@@ -508,9 +746,17 @@ export const expressionGenMethods = {
|
|
|
508
746
|
const slice = this.genExpr(expr.args[0]);
|
|
509
747
|
const elems = expr.args
|
|
510
748
|
.slice(1)
|
|
511
|
-
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.
|
|
749
|
+
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genValueExpr(a)));
|
|
512
750
|
if (elems.length === 0) return slice;
|
|
513
751
|
this._usesAppend = true;
|
|
752
|
+
const sliceType = expr._type ?? expr.args[0]._type;
|
|
753
|
+
const typedCtor = isTypedArraySlice(sliceType)
|
|
754
|
+
? typedArrayConstructorForElem(
|
|
755
|
+
(sliceType.kind === "named" ? sliceType.underlying : sliceType).elem,
|
|
756
|
+
)
|
|
757
|
+
: null;
|
|
758
|
+
if (typedCtor)
|
|
759
|
+
return `__append((${slice}) ?? new ${typedCtor}(0), ${elems.join(", ")})`;
|
|
514
760
|
return `__append(${slice}, ${elems.join(", ")})`;
|
|
515
761
|
},
|
|
516
762
|
|
|
@@ -585,9 +831,9 @@ export const expressionGenMethods = {
|
|
|
585
831
|
e.key.litKind === "STRING"
|
|
586
832
|
? JSON.stringify(e.key.value)
|
|
587
833
|
: `[${this.genExpr(e.key)}]`;
|
|
588
|
-
return `${k}: ${this.
|
|
834
|
+
return `${k}: ${this.genValueExpr(e.value)}`;
|
|
589
835
|
}
|
|
590
|
-
return this.
|
|
836
|
+
return this.genValueExpr(e);
|
|
591
837
|
})
|
|
592
838
|
.join(", ");
|
|
593
839
|
},
|
|
@@ -616,7 +862,9 @@ export const expressionGenMethods = {
|
|
|
616
862
|
: null);
|
|
617
863
|
const idx = name ? fields.findIndex((f) => f.name === name) : i;
|
|
618
864
|
if (idx >= 0 && idx < fields.length) {
|
|
619
|
-
values[idx] = this.
|
|
865
|
+
values[idx] = this.genValueExpr(
|
|
866
|
+
e.kind === "KeyValueExpr" ? e.value : e,
|
|
867
|
+
);
|
|
620
868
|
if (idx > maxIdx) maxIdx = idx;
|
|
621
869
|
}
|
|
622
870
|
}
|
|
@@ -631,7 +879,7 @@ export const expressionGenMethods = {
|
|
|
631
879
|
(ce) => (ce._positionalField ?? ce.key?.name) === ef,
|
|
632
880
|
);
|
|
633
881
|
values[idx] = match
|
|
634
|
-
? this.
|
|
882
|
+
? this.genValueExpr(match.value ?? match)
|
|
635
883
|
: fields[idx].zero;
|
|
636
884
|
} else {
|
|
637
885
|
values[idx] = `${this.genExpr(e.value)}.${ef}`;
|
|
@@ -655,7 +903,7 @@ export const expressionGenMethods = {
|
|
|
655
903
|
} else {
|
|
656
904
|
const idx = fields.findIndex((f) => f.name === k);
|
|
657
905
|
if (idx >= 0) {
|
|
658
|
-
values[idx] = this.
|
|
906
|
+
values[idx] = this.genValueExpr(e.value);
|
|
659
907
|
if (idx > maxIdx) maxIdx = idx;
|
|
660
908
|
}
|
|
661
909
|
}
|
|
@@ -695,9 +943,7 @@ export const expressionGenMethods = {
|
|
|
695
943
|
}
|
|
696
944
|
if (this._isSliceOrArrayType(t)) {
|
|
697
945
|
const elems = expr.elems
|
|
698
|
-
.map((e) =>
|
|
699
|
-
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
700
|
-
)
|
|
946
|
+
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
701
947
|
.join(", ");
|
|
702
948
|
const elemNode =
|
|
703
949
|
t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
|
|
@@ -734,13 +980,13 @@ export const expressionGenMethods = {
|
|
|
734
980
|
const fields = expr.elems
|
|
735
981
|
.map((e) => {
|
|
736
982
|
if (e._positionalField)
|
|
737
|
-
return `${e._positionalField}: ${this.
|
|
738
|
-
return `${e.key.name ?? this.genExpr(e.key)}: ${this.
|
|
983
|
+
return `${e._positionalField}: ${this.genValueExpr(e)}`;
|
|
984
|
+
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genValueExpr(e.value)}`;
|
|
739
985
|
})
|
|
740
986
|
.join(", ");
|
|
741
987
|
return `{ ${fields} }`;
|
|
742
988
|
}
|
|
743
|
-
const elems = expr.elems.map((e) => this.
|
|
989
|
+
const elems = expr.elems.map((e) => this.genValueExpr(e)).join(", ");
|
|
744
990
|
if (isTypedArraySlice(expr._type)) {
|
|
745
991
|
const typedCtor = typedArrayConstructorForElem(expr._type.elem);
|
|
746
992
|
if (typedCtor) return `new ${typedCtor}([${elems}])`;
|
|
@@ -759,18 +1005,16 @@ export const expressionGenMethods = {
|
|
|
759
1005
|
e.key.litKind === "STRING"
|
|
760
1006
|
? JSON.stringify(e.key.value)
|
|
761
1007
|
: `[${this.genExpr(e.key)}]`;
|
|
762
|
-
return `${k}: ${this.
|
|
1008
|
+
return `${k}: ${this.genValueExpr(e.value)}`;
|
|
763
1009
|
}
|
|
764
|
-
return this.
|
|
1010
|
+
return this.genValueExpr(e);
|
|
765
1011
|
})
|
|
766
1012
|
.join(", ");
|
|
767
1013
|
return `new ${typeName}({ ${entries} })`;
|
|
768
1014
|
}
|
|
769
1015
|
// Default: slice
|
|
770
1016
|
const elems = expr.elems
|
|
771
|
-
.map((e) =>
|
|
772
|
-
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
773
|
-
)
|
|
1017
|
+
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
774
1018
|
.join(", ");
|
|
775
1019
|
return `new ${typeName}([${elems}])`;
|
|
776
1020
|
},
|
|
@@ -833,8 +1077,20 @@ export const expressionGenMethods = {
|
|
|
833
1077
|
}
|
|
834
1078
|
case "SliceType":
|
|
835
1079
|
return "null";
|
|
836
|
-
case "ArrayType":
|
|
837
|
-
|
|
1080
|
+
case "ArrayType": {
|
|
1081
|
+
const n = Number(typeNode.size?.value ?? 0) || 0;
|
|
1082
|
+
const ctor = typeNode.elem?.name
|
|
1083
|
+
? typedArrayConstructorForElem({
|
|
1084
|
+
kind: "basic",
|
|
1085
|
+
name: typeNode.elem.name,
|
|
1086
|
+
})
|
|
1087
|
+
: null;
|
|
1088
|
+
return this._arrayZero(
|
|
1089
|
+
n,
|
|
1090
|
+
ctor,
|
|
1091
|
+
this.zeroValueForTypeNode(typeNode.elem),
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
838
1094
|
case "MapType":
|
|
839
1095
|
return "{}";
|
|
840
1096
|
case "PointerType":
|
|
@@ -854,6 +1110,14 @@ export const expressionGenMethods = {
|
|
|
854
1110
|
}
|
|
855
1111
|
},
|
|
856
1112
|
|
|
1113
|
+
_arrayZero(n, ctor, elemZero) {
|
|
1114
|
+
if (ctor) return `new ${ctor}(${n})`;
|
|
1115
|
+
if (n === 0) return "[]";
|
|
1116
|
+
if (/^(new |\{|\[|Array)/.test(elemZero))
|
|
1117
|
+
return `Array.from({ length: ${n} }, () => ${elemZero})`;
|
|
1118
|
+
return `new Array(${n}).fill(${elemZero})`;
|
|
1119
|
+
},
|
|
1120
|
+
|
|
857
1121
|
// zeroValueForType operates on typechecker type objects (not AST type nodes).
|
|
858
1122
|
zeroValueForType(t) {
|
|
859
1123
|
if (!t) return "null";
|
|
@@ -862,6 +1126,12 @@ export const expressionGenMethods = {
|
|
|
862
1126
|
return this._zeroForBasicName(t.name) ?? "null";
|
|
863
1127
|
case "slice":
|
|
864
1128
|
return "null";
|
|
1129
|
+
case "array":
|
|
1130
|
+
return this._arrayZero(
|
|
1131
|
+
Number(t.size ?? 0) || 0,
|
|
1132
|
+
typedArrayConstructorForElem(t.elem),
|
|
1133
|
+
this.zeroValueForType(t.elem),
|
|
1134
|
+
);
|
|
865
1135
|
case "map":
|
|
866
1136
|
return "{}";
|
|
867
1137
|
case "struct": {
|
|
@@ -888,9 +1158,18 @@ export const expressionGenMethods = {
|
|
|
888
1158
|
if (name === "nil") return `${val} === null`;
|
|
889
1159
|
if (name === "error")
|
|
890
1160
|
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
891
|
-
if (this.structNames.has(name))
|
|
1161
|
+
if (this.structNames.has(name))
|
|
1162
|
+
return `(${val} instanceof ${name} && ${val}.__p !== true)`;
|
|
892
1163
|
return "true"; // unknown type — can't check at runtime
|
|
893
1164
|
}
|
|
1165
|
+
if (
|
|
1166
|
+
typeNode.kind === "PointerType" &&
|
|
1167
|
+
typeNode.base?.kind === "TypeName" &&
|
|
1168
|
+
this.structNames.has(typeNode.base.name)
|
|
1169
|
+
) {
|
|
1170
|
+
const name = typeNode.base.name;
|
|
1171
|
+
return `(${val} instanceof ${name} && ${val}.__v !== true)`;
|
|
1172
|
+
}
|
|
894
1173
|
return "true";
|
|
895
1174
|
},
|
|
896
1175
|
|
|
@@ -948,39 +1227,72 @@ export const expressionGenMethods = {
|
|
|
948
1227
|
)
|
|
949
1228
|
return this._genComplexBinary(expr);
|
|
950
1229
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
expr.left,
|
|
954
|
-
expr.op
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1230
|
+
if (this._isStructEquality(expr)) {
|
|
1231
|
+
this._usesEqual = true;
|
|
1232
|
+
const cmp = `__equal(${this.genExpr(expr.left)}, ${this.genExpr(expr.right)})`;
|
|
1233
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
const jsOp = this._emittedBinaryOp(expr) ?? "/";
|
|
1237
|
+
const operand = (child, isRight) =>
|
|
1238
|
+
wrapForJsOp(
|
|
1239
|
+
this.genExpr(child),
|
|
1240
|
+
this._emittedBinaryOp(child),
|
|
1241
|
+
jsOp,
|
|
1242
|
+
isRight,
|
|
1243
|
+
);
|
|
1244
|
+
const l = operand(expr.left, false);
|
|
1245
|
+
|
|
1246
|
+
if (expr.op === "&^") {
|
|
1247
|
+
const rCode = this.genExpr(expr.right);
|
|
1248
|
+
const r =
|
|
1249
|
+
this._emittedBinaryOp(expr.right) !== null ? `(${rCode})` : rCode;
|
|
1250
|
+
return `${l} & ~${r}`;
|
|
1251
|
+
}
|
|
1252
|
+
const r = operand(expr.right, true);
|
|
1253
|
+
if (this._isIntDivision(expr)) return `Math.trunc(${l} / ${r})`;
|
|
1254
|
+
return `${l} ${jsOp} ${r}`;
|
|
1255
|
+
},
|
|
1256
|
+
|
|
1257
|
+
_isStructEquality(expr) {
|
|
1258
|
+
return (
|
|
1259
|
+
(expr.op === "==" || expr.op === "!=") &&
|
|
1260
|
+
this._isStructOrArrayType(expr.left._type)
|
|
962
1261
|
);
|
|
1262
|
+
},
|
|
963
1263
|
|
|
964
|
-
|
|
1264
|
+
_isIntDivision(expr) {
|
|
1265
|
+
return (
|
|
965
1266
|
expr.op === "/" &&
|
|
966
1267
|
this.isIntType(expr.left._type) &&
|
|
967
1268
|
this.isIntType(expr.right._type)
|
|
1269
|
+
);
|
|
1270
|
+
},
|
|
1271
|
+
|
|
1272
|
+
_isNilLiteral(expr) {
|
|
1273
|
+
return (
|
|
1274
|
+
(expr.kind === "Ident" && expr.name === "nil") ||
|
|
1275
|
+
(expr.kind === "BasicLit" && expr.value === "null")
|
|
1276
|
+
);
|
|
1277
|
+
},
|
|
1278
|
+
|
|
1279
|
+
// Top-level JS operator a BinaryExpr compiles to, or null when the output is atomic.
|
|
1280
|
+
_emittedBinaryOp(expr) {
|
|
1281
|
+
if (expr?.kind !== "BinaryExpr") return null;
|
|
1282
|
+
if (
|
|
1283
|
+
isComplex(expr._type) ||
|
|
1284
|
+
isComplex(expr.left._type) ||
|
|
1285
|
+
isComplex(expr.right._type)
|
|
968
1286
|
)
|
|
969
|
-
return
|
|
970
|
-
if (expr
|
|
1287
|
+
return null;
|
|
1288
|
+
if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
|
|
1289
|
+
if (expr.op === "&^") return "&";
|
|
971
1290
|
if (expr.op === "==" || expr.op === "!=") {
|
|
972
|
-
if (this.
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
976
|
-
}
|
|
977
|
-
if (l === "null" || r === "null") {
|
|
978
|
-
const op = expr.op === "==" ? "==" : "!=";
|
|
979
|
-
return `${l} ${op} ${r}`;
|
|
980
|
-
}
|
|
1291
|
+
if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
|
|
1292
|
+
return expr.op;
|
|
1293
|
+
return expr.op === "==" ? "===" : "!==";
|
|
981
1294
|
}
|
|
982
|
-
|
|
983
|
-
return `${l} ${op} ${r}`;
|
|
1295
|
+
return expr.op;
|
|
984
1296
|
},
|
|
985
1297
|
|
|
986
1298
|
_genTypeConversion(expr) {
|
|
@@ -1032,13 +1344,14 @@ export const expressionGenMethods = {
|
|
|
1032
1344
|
const typedCtor = typedArrayConstructorForElem(
|
|
1033
1345
|
t.elem ? { kind: "basic", name: t.elem.name } : null,
|
|
1034
1346
|
);
|
|
1347
|
+
const fromString = this._isStringSource(expr.expr._type);
|
|
1035
1348
|
if (typedCtor) {
|
|
1036
|
-
return
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (this._isStringSource(expr.expr._type))
|
|
1040
|
-
return `Array.from(${inner}, __c => __c.codePointAt(0))`;
|
|
1349
|
+
return fromString
|
|
1350
|
+
? `${typedCtor}.from(${inner}, __c => __c.codePointAt(0))`
|
|
1351
|
+
: `new ${typedCtor}(${inner})`;
|
|
1041
1352
|
}
|
|
1353
|
+
if ((elem === "rune" || elem === "int32" || elem === "int") && fromString)
|
|
1354
|
+
return `Array.from(${inner}, __c => __c.codePointAt(0))`;
|
|
1042
1355
|
return `Array.from(${inner})`;
|
|
1043
1356
|
},
|
|
1044
1357
|
|
|
@@ -1060,19 +1373,25 @@ export const expressionGenMethods = {
|
|
|
1060
1373
|
case "string":
|
|
1061
1374
|
return this._genStringConversion(expr.expr._type, inner);
|
|
1062
1375
|
case "int":
|
|
1376
|
+
case "int64":
|
|
1377
|
+
case "uint":
|
|
1378
|
+
case "uint64":
|
|
1379
|
+
case "uintptr":
|
|
1380
|
+
return `Math.trunc(Number(${inner}))`;
|
|
1063
1381
|
case "int8":
|
|
1382
|
+
return `(Number(${inner}) << 24 >> 24)`;
|
|
1064
1383
|
case "int16":
|
|
1384
|
+
return `(Number(${inner}) << 16 >> 16)`;
|
|
1065
1385
|
case "int32":
|
|
1066
|
-
case "
|
|
1067
|
-
|
|
1386
|
+
case "rune":
|
|
1387
|
+
return `(Number(${inner}) | 0)`;
|
|
1068
1388
|
case "uint8":
|
|
1389
|
+
case "byte":
|
|
1390
|
+
return `(Number(${inner}) & 0xFF)`;
|
|
1069
1391
|
case "uint16":
|
|
1392
|
+
return `(Number(${inner}) & 0xFFFF)`;
|
|
1070
1393
|
case "uint32":
|
|
1071
|
-
|
|
1072
|
-
case "uintptr":
|
|
1073
|
-
case "byte":
|
|
1074
|
-
case "rune":
|
|
1075
|
-
return `Math.trunc(Number(${inner}))`;
|
|
1394
|
+
return `(Number(${inner}) >>> 0)`;
|
|
1076
1395
|
case "float32":
|
|
1077
1396
|
return `Math.fround(Number(${inner}))`;
|
|
1078
1397
|
case "float64":
|