gofront 1.2.3 → 1.3.1
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 +6 -4
- package/src/cli-core.js +520 -15
- package/src/codegen/expressions.js +229 -30
- package/src/codegen/index.js +38 -20
- package/src/codegen/runtime.js +14 -2
- package/src/codegen/statements.js +95 -15
- package/src/codegen/stdlib/builder.js +2 -2
- package/src/codegen/stdlib/bytes.js +1 -1
- package/src/codegen/stdlib/fmt.js +3 -4
- package/src/codegen/stdlib/io.js +1 -2
- package/src/codegen/templ.js +1 -1
- package/src/dev-server.js +279 -83
- package/src/index.js +88 -16
- package/src/parser/expressions.js +11 -0
- package/src/pwa.js +138 -0
- package/src/typechecker/assignability.js +61 -8
- package/src/typechecker/resolve.js +37 -3
- package/src/typechecker/stdlib/core.js +16 -13
- package/src/typechecker/stdlib/extended.js +11 -2
- package/src/typechecker/stdlib/web.js +418 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +78 -17
- package/src/vendor.js +158 -43
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
ERROR,
|
|
3
|
+
isComplex,
|
|
4
|
+
isTypedArraySlice,
|
|
5
|
+
typedArrayConstructorForElem,
|
|
6
|
+
} from "../typechecker/types.js";
|
|
4
7
|
|
|
5
8
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
6
9
|
|
|
@@ -109,9 +112,21 @@ const BUILTIN_GEN = {
|
|
|
109
112
|
},
|
|
110
113
|
copy: (s, e) => {
|
|
111
114
|
const [dst, src] = e.args.map((a) => s.genExpr(a));
|
|
112
|
-
return `((__cd,__cs)=>{const n=Math.min(__cd.length,__cs.length);__cd.splice(0,n
|
|
115
|
+
return `((__cd, __cs) => { if (!__cd || !__cs) return 0; const n = Math.min(__cd.length, __cs.length); if (__cd.set) { __cd.set(__cs.length === n ? __cs : (__cs.subarray ? __cs.subarray(0, n) : __cs.slice(0, n))); } else { __cd.splice(0, n, ...(__cs.slice ? __cs.slice(0, n) : __cs.subarray(0, n))); } return n; })(${dst}, ${src})`;
|
|
116
|
+
},
|
|
117
|
+
new: (s, e) => {
|
|
118
|
+
const arg = e.args[0];
|
|
119
|
+
const typeName = arg.name ?? s.getTypeName(arg);
|
|
120
|
+
if (typeName && s.structNames.has(typeName)) {
|
|
121
|
+
return `new ${typeName}()`;
|
|
122
|
+
}
|
|
123
|
+
const t = arg._type;
|
|
124
|
+
if (s._isStructType(t)) {
|
|
125
|
+
const name = t.name ?? (t.kind === "named" ? t.name : null);
|
|
126
|
+
if (name) return `new ${name}()`;
|
|
127
|
+
}
|
|
128
|
+
return `{ value: ${s.zeroValueForExpr(arg)} }`;
|
|
113
129
|
},
|
|
114
|
-
new: (s, e) => `{ value: ${s.zeroValueForExpr(e.args[0])} }`,
|
|
115
130
|
print: (s, e) => `console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
116
131
|
println: (s, e) =>
|
|
117
132
|
`console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
@@ -171,6 +186,36 @@ export const expressionGenMethods = {
|
|
|
171
186
|
}
|
|
172
187
|
},
|
|
173
188
|
|
|
189
|
+
_isStructType(t) {
|
|
190
|
+
if (!t) return false;
|
|
191
|
+
if (t.name && this.structNames.has(t.name)) return true;
|
|
192
|
+
const base =
|
|
193
|
+
t.kind === "named"
|
|
194
|
+
? (t.underlying ?? this.checker?.types.get(t.name)?.underlying)
|
|
195
|
+
: t;
|
|
196
|
+
if (base?.name && this.structNames.has(base.name)) return true;
|
|
197
|
+
return base?.kind === "struct";
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
_isStructPointerType(t) {
|
|
201
|
+
if (!t) return false;
|
|
202
|
+
const pt =
|
|
203
|
+
t.kind === "named"
|
|
204
|
+
? (t.underlying ?? this.checker?.types.get(t.name)?.underlying)
|
|
205
|
+
: t;
|
|
206
|
+
if (pt?.kind !== "pointer") return false;
|
|
207
|
+
return this._isStructType(pt.base);
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
_isStructExpr(expr) {
|
|
211
|
+
if (!expr) return false;
|
|
212
|
+
if (expr.kind === "CompositeLit") {
|
|
213
|
+
const typeName = expr.typeExpr ? this.getTypeName(expr.typeExpr) : null;
|
|
214
|
+
if (typeName && this.structNames.has(typeName)) return true;
|
|
215
|
+
}
|
|
216
|
+
return this._isStructType(expr._type);
|
|
217
|
+
},
|
|
218
|
+
|
|
174
219
|
_genUnaryExpr(expr) {
|
|
175
220
|
const op = expr.op === "^" ? "~" : expr.op; // bitwise NOT
|
|
176
221
|
// Address-of: &x
|
|
@@ -183,11 +228,22 @@ export const expressionGenMethods = {
|
|
|
183
228
|
return this.genExpr(expr.operand);
|
|
184
229
|
}
|
|
185
230
|
}
|
|
186
|
-
|
|
231
|
+
if (
|
|
232
|
+
this._isStructType(expr.operand._type) ||
|
|
233
|
+
this._isStructExpr(expr.operand)
|
|
234
|
+
) {
|
|
235
|
+
return this.genExpr(expr.operand);
|
|
236
|
+
}
|
|
237
|
+
// For non-boxed vars (primitives, etc.), wrap in { value: x }
|
|
187
238
|
return `{ value: ${this.genExpr(expr.operand)} }`;
|
|
188
239
|
}
|
|
189
|
-
// Dereference: *p
|
|
190
|
-
if (op === "*")
|
|
240
|
+
// Dereference: *p
|
|
241
|
+
if (op === "*") {
|
|
242
|
+
if (this._isStructPointerType(expr.operand._type)) {
|
|
243
|
+
return this.genExpr(expr.operand);
|
|
244
|
+
}
|
|
245
|
+
return `${this.genExpr(expr.operand)}.value`;
|
|
246
|
+
}
|
|
191
247
|
// Unary minus/plus on complex
|
|
192
248
|
if ((op === "-" || op === "+") && isComplex(expr.operand._type)) {
|
|
193
249
|
const inner = this.genExpr(expr.operand);
|
|
@@ -215,6 +271,11 @@ export const expressionGenMethods = {
|
|
|
215
271
|
},
|
|
216
272
|
|
|
217
273
|
_genPointerSelectorField(expr, base) {
|
|
274
|
+
if (this._isStructPointerType(expr.expr._type)) {
|
|
275
|
+
const sel = `${base}.${expr.field}`;
|
|
276
|
+
if (expr._isMethodValue && !expr._callee) return `${sel}.bind(${base})`;
|
|
277
|
+
return sel;
|
|
278
|
+
}
|
|
218
279
|
const sel = `${base}.value.${expr.field}`;
|
|
219
280
|
if (expr._isMethodValue && !expr._callee)
|
|
220
281
|
return `${sel}.bind(${base}.value)`;
|
|
@@ -253,10 +314,11 @@ export const expressionGenMethods = {
|
|
|
253
314
|
const base = this.genExpr(expr.expr);
|
|
254
315
|
const lo = expr.low ? this.genExpr(expr.low) : "";
|
|
255
316
|
const hi = expr.high ? this.genExpr(expr.high) : "";
|
|
256
|
-
|
|
257
|
-
if (!hi) return `${base}
|
|
258
|
-
if (!
|
|
259
|
-
return `${base}
|
|
317
|
+
const method = isTypedArraySlice(expr.expr._type) ? "subarray" : "slice";
|
|
318
|
+
if (!lo && !hi) return `${base}.${method}()`;
|
|
319
|
+
if (!hi) return `${base}.${method}(${lo})`;
|
|
320
|
+
if (!lo) return `${base}.${method}(0, ${hi})`;
|
|
321
|
+
return `${base}.${method}(${lo}, ${hi})`;
|
|
260
322
|
},
|
|
261
323
|
|
|
262
324
|
_genFuncLit(expr) {
|
|
@@ -423,10 +485,21 @@ export const expressionGenMethods = {
|
|
|
423
485
|
const resolvedKind = typeArg._type?.kind;
|
|
424
486
|
if (typeNode.kind === "SliceType" || resolvedKind === "slice") {
|
|
425
487
|
const n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
|
|
426
|
-
// Use the proper zero value for the element type (e.g. new Point() not 0)
|
|
427
488
|
const elemNode = typeNode.kind === "SliceType" ? typeNode.elem : null;
|
|
428
489
|
const elemResolved =
|
|
429
490
|
typeArg._type?.kind === "slice" ? typeArg._type.elem : null;
|
|
491
|
+
const typedCtor =
|
|
492
|
+
typedArrayConstructorForElem(elemResolved) ??
|
|
493
|
+
(elemNode?.name
|
|
494
|
+
? typedArrayConstructorForElem({
|
|
495
|
+
kind: "basic",
|
|
496
|
+
name: elemNode.name,
|
|
497
|
+
})
|
|
498
|
+
: null);
|
|
499
|
+
if (typedCtor) {
|
|
500
|
+
return `new ${typedCtor}(${n})`;
|
|
501
|
+
}
|
|
502
|
+
// Use the proper zero value for the element type (e.g. new Point() not 0)
|
|
430
503
|
let zero = "null";
|
|
431
504
|
if (elemResolved) {
|
|
432
505
|
zero = this.zeroValueForType(elemResolved);
|
|
@@ -495,17 +568,93 @@ export const expressionGenMethods = {
|
|
|
495
568
|
return `[${expr.elems.map((e) => this.genExpr(e)).join(", ")}]`;
|
|
496
569
|
},
|
|
497
570
|
|
|
571
|
+
_fillPositionalStructElems(fields, expr, values) {
|
|
572
|
+
let maxIdx = -1;
|
|
573
|
+
for (let i = 0; i < expr.elems.length; i++) {
|
|
574
|
+
const e = expr.elems[i];
|
|
575
|
+
const name =
|
|
576
|
+
e._positionalField ??
|
|
577
|
+
(e.kind === "KeyValueExpr"
|
|
578
|
+
? (e.key.name ?? this.genExpr(e.key))
|
|
579
|
+
: null);
|
|
580
|
+
const idx = name ? fields.findIndex((f) => f.name === name) : i;
|
|
581
|
+
if (idx >= 0 && idx < fields.length) {
|
|
582
|
+
values[idx] = this.genExpr(e.kind === "KeyValueExpr" ? e.value : e);
|
|
583
|
+
if (idx > maxIdx) maxIdx = idx;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return maxIdx;
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
_fillEmbedField(fields, ef, e, values) {
|
|
590
|
+
const idx = fields.findIndex((f) => f.name === ef);
|
|
591
|
+
if (idx < 0) return -1;
|
|
592
|
+
if (e.value.kind === "CompositeLit") {
|
|
593
|
+
const match = e.value.elems.find(
|
|
594
|
+
(ce) => (ce._positionalField ?? ce.key?.name) === ef,
|
|
595
|
+
);
|
|
596
|
+
values[idx] = match
|
|
597
|
+
? this.genExpr(match.value ?? match)
|
|
598
|
+
: fields[idx].zero;
|
|
599
|
+
} else {
|
|
600
|
+
values[idx] = `${this.genExpr(e.value)}.${ef}`;
|
|
601
|
+
}
|
|
602
|
+
return idx;
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
_fillKeyedStructElems(fields, expr, values) {
|
|
606
|
+
let maxIdx = -1;
|
|
607
|
+
for (const e of expr.elems) {
|
|
608
|
+
if (e.kind !== "KeyValueExpr") continue;
|
|
609
|
+
const k = e.key.name ?? this.genExpr(e.key);
|
|
610
|
+
if (e._isEmbedInit) {
|
|
611
|
+
const embedType = this.checker?.types.get(k)?.underlying;
|
|
612
|
+
const embedFields =
|
|
613
|
+
embedType?.kind === "struct" ? [...embedType.fields.keys()] : [];
|
|
614
|
+
for (const ef of embedFields) {
|
|
615
|
+
const idx = this._fillEmbedField(fields, ef, e, values);
|
|
616
|
+
if (idx > maxIdx) maxIdx = idx;
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
const idx = fields.findIndex((f) => f.name === k);
|
|
620
|
+
if (idx >= 0) {
|
|
621
|
+
values[idx] = this.genExpr(e.value);
|
|
622
|
+
if (idx > maxIdx) maxIdx = idx;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return maxIdx;
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
_genPositionalStructLit(typeName, expr) {
|
|
630
|
+
const fields = this.getStructFields(typeName);
|
|
631
|
+
if (!fields || fields.length === 0 || expr.elems.length === 0) {
|
|
632
|
+
return `new ${typeName}()`;
|
|
633
|
+
}
|
|
634
|
+
const values = fields.map((f) => f.zero);
|
|
635
|
+
const hasPositional = expr.elems.some(
|
|
636
|
+
(e) => e._positionalField || e.kind !== "KeyValueExpr",
|
|
637
|
+
);
|
|
638
|
+
const maxExplicitIdx = hasPositional
|
|
639
|
+
? this._fillPositionalStructElems(fields, expr, values)
|
|
640
|
+
: this._fillKeyedStructElems(fields, expr, values);
|
|
641
|
+
if (maxExplicitIdx < 0) {
|
|
642
|
+
return `new ${typeName}()`;
|
|
643
|
+
}
|
|
644
|
+
const args = values.slice(0, maxExplicitIdx + 1);
|
|
645
|
+
return `new ${typeName}(${args.join(", ")})`;
|
|
646
|
+
},
|
|
647
|
+
|
|
498
648
|
genCompositeLit(expr) {
|
|
499
649
|
const t = expr.typeExpr;
|
|
500
650
|
if (t === null) return this._genImplicitCompositeLit(expr);
|
|
501
651
|
const typeName = this.getTypeName(t);
|
|
502
652
|
if (typeName && this.structNames.has(typeName))
|
|
503
|
-
return
|
|
653
|
+
return this._genPositionalStructLit(typeName, expr);
|
|
504
654
|
if (typeName && this.namedWrapperNames.has(typeName))
|
|
505
655
|
return this._genNamedWrapperLit(typeName, expr);
|
|
506
656
|
if (expr.elems.some((e) => e._positionalField)) {
|
|
507
|
-
if (typeName)
|
|
508
|
-
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
657
|
+
if (typeName) return this._genPositionalStructLit(typeName, expr);
|
|
509
658
|
}
|
|
510
659
|
if (this._isSliceOrArrayType(t)) {
|
|
511
660
|
const elems = expr.elems
|
|
@@ -513,6 +662,23 @@ export const expressionGenMethods = {
|
|
|
513
662
|
e.kind === "KeyValueExpr" ? this.genExpr(e.value) : this.genExpr(e),
|
|
514
663
|
)
|
|
515
664
|
.join(", ");
|
|
665
|
+
const elemNode =
|
|
666
|
+
t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
|
|
667
|
+
const elemResolved =
|
|
668
|
+
expr._type?.kind === "slice" || expr._type?.kind === "array"
|
|
669
|
+
? expr._type.elem
|
|
670
|
+
: null;
|
|
671
|
+
const typedCtor =
|
|
672
|
+
typedArrayConstructorForElem(elemResolved) ??
|
|
673
|
+
(elemNode?.name
|
|
674
|
+
? typedArrayConstructorForElem({
|
|
675
|
+
kind: "basic",
|
|
676
|
+
name: elemNode.name,
|
|
677
|
+
})
|
|
678
|
+
: null);
|
|
679
|
+
if (typedCtor) {
|
|
680
|
+
return `new ${typedCtor}([${elems}])`;
|
|
681
|
+
}
|
|
516
682
|
return `[${elems}]`;
|
|
517
683
|
}
|
|
518
684
|
if (this._isMapTypeNode(t))
|
|
@@ -522,12 +688,12 @@ export const expressionGenMethods = {
|
|
|
522
688
|
|
|
523
689
|
_genImplicitCompositeLit(expr) {
|
|
524
690
|
const typeName = expr._type?.name ?? expr._type?.underlying?.name;
|
|
691
|
+
if (typeName && this.structNames.has(typeName)) {
|
|
692
|
+
return this._genPositionalStructLit(typeName, expr);
|
|
693
|
+
}
|
|
525
694
|
const hasPositional = expr.elems.some((e) => e._positionalField);
|
|
526
695
|
const hasKeyed = expr.elems.some((e) => e.kind === "KeyValueExpr");
|
|
527
696
|
if (hasPositional || hasKeyed) {
|
|
528
|
-
if (typeName && this.structNames.has(typeName)) {
|
|
529
|
-
return `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
|
|
530
|
-
}
|
|
531
697
|
const fields = expr.elems
|
|
532
698
|
.map((e) => {
|
|
533
699
|
if (e._positionalField)
|
|
@@ -537,7 +703,12 @@ export const expressionGenMethods = {
|
|
|
537
703
|
.join(", ");
|
|
538
704
|
return `{ ${fields} }`;
|
|
539
705
|
}
|
|
540
|
-
|
|
706
|
+
const elems = expr.elems.map((e) => this.genExpr(e)).join(", ");
|
|
707
|
+
if (isTypedArraySlice(expr._type)) {
|
|
708
|
+
const typedCtor = typedArrayConstructorForElem(expr._type.elem);
|
|
709
|
+
if (typedCtor) return `new ${typedCtor}([${elems}])`;
|
|
710
|
+
}
|
|
711
|
+
return `[${elems}]`;
|
|
541
712
|
},
|
|
542
713
|
|
|
543
714
|
_genNamedWrapperLit(typeName, expr) {
|
|
@@ -804,8 +975,17 @@ export const expressionGenMethods = {
|
|
|
804
975
|
|
|
805
976
|
_genSliceTypeConversion(expr, inner, t) {
|
|
806
977
|
const elem = t.elem?.name;
|
|
807
|
-
if (elem === "byte" || elem === "uint8")
|
|
808
|
-
|
|
978
|
+
if (elem === "byte" || elem === "uint8") {
|
|
979
|
+
if (this._isStringSource(expr.expr._type))
|
|
980
|
+
return `new TextEncoder().encode(${inner})`;
|
|
981
|
+
return `new Uint8Array(${inner})`;
|
|
982
|
+
}
|
|
983
|
+
const typedCtor = typedArrayConstructorForElem(
|
|
984
|
+
t.elem ? { kind: "basic", name: t.elem.name } : null,
|
|
985
|
+
);
|
|
986
|
+
if (typedCtor) {
|
|
987
|
+
return `new ${typedCtor}(${inner})`;
|
|
988
|
+
}
|
|
809
989
|
if (elem === "rune" || elem === "int32" || elem === "int") {
|
|
810
990
|
if (this._isStringSource(expr.expr._type))
|
|
811
991
|
return `Array.from(${inner}, __c => __c.codePointAt(0))`;
|
|
@@ -813,20 +993,39 @@ export const expressionGenMethods = {
|
|
|
813
993
|
return `Array.from(${inner})`;
|
|
814
994
|
},
|
|
815
995
|
|
|
996
|
+
_genStringConversion(srcType, inner) {
|
|
997
|
+
if (srcType && this.isIntType(srcType))
|
|
998
|
+
return `String.fromCodePoint(${inner})`;
|
|
999
|
+
if (srcType?.kind === "slice") {
|
|
1000
|
+
const elem = srcType.elem?.name;
|
|
1001
|
+
if (elem === "byte" || elem === "uint8")
|
|
1002
|
+
return `new TextDecoder().decode(new Uint8Array(${inner}))`;
|
|
1003
|
+
if (elem === "rune" || elem === "int" || elem === "int32")
|
|
1004
|
+
return `Array.from(${inner}, c => String.fromCodePoint(c)).join("")`;
|
|
1005
|
+
}
|
|
1006
|
+
return `String(${inner})`;
|
|
1007
|
+
},
|
|
1008
|
+
|
|
816
1009
|
_genPrimitiveConversion(expr, inner, target) {
|
|
817
1010
|
switch (target) {
|
|
818
|
-
case "string":
|
|
819
|
-
|
|
820
|
-
if (srcType && this.isIntType(srcType))
|
|
821
|
-
return `String.fromCodePoint(${inner})`;
|
|
822
|
-
if (srcType?.kind === "slice" && srcType.elem?.name === "int")
|
|
823
|
-
return `${inner}.map(c => String.fromCharCode(c)).join("")`;
|
|
824
|
-
return `String(${inner})`;
|
|
825
|
-
}
|
|
1011
|
+
case "string":
|
|
1012
|
+
return this._genStringConversion(expr.expr._type, inner);
|
|
826
1013
|
case "int":
|
|
1014
|
+
case "int8":
|
|
1015
|
+
case "int16":
|
|
1016
|
+
case "int32":
|
|
1017
|
+
case "int64":
|
|
1018
|
+
case "uint":
|
|
1019
|
+
case "uint8":
|
|
1020
|
+
case "uint16":
|
|
1021
|
+
case "uint32":
|
|
1022
|
+
case "uint64":
|
|
1023
|
+
case "uintptr":
|
|
827
1024
|
case "byte":
|
|
828
1025
|
case "rune":
|
|
829
1026
|
return `Math.trunc(Number(${inner}))`;
|
|
1027
|
+
case "float32":
|
|
1028
|
+
return `Math.fround(Number(${inner}))`;
|
|
830
1029
|
case "float64":
|
|
831
1030
|
return `Number(${inner})`;
|
|
832
1031
|
case "bool":
|
package/src/codegen/index.js
CHANGED
|
@@ -52,6 +52,7 @@ export class CodeGen {
|
|
|
52
52
|
this.out = [];
|
|
53
53
|
this.indent = 0;
|
|
54
54
|
this.structNames = new Set();
|
|
55
|
+
this.structFields = new Map();
|
|
55
56
|
this.namedWrapperNames = new Set();
|
|
56
57
|
this.jsImports = jsImports;
|
|
57
58
|
this.bundledPackages = bundledPackages;
|
|
@@ -329,34 +330,48 @@ export class CodeGen {
|
|
|
329
330
|
return fields;
|
|
330
331
|
}
|
|
331
332
|
|
|
333
|
+
getStructFields(typeName) {
|
|
334
|
+
if (this.structFields.has(typeName)) return this.structFields.get(typeName);
|
|
335
|
+
if (!this.checker) return [];
|
|
336
|
+
const resolved = this.checker.types.get(typeName)?.underlying;
|
|
337
|
+
if (resolved?.kind === "struct") {
|
|
338
|
+
const fields = [];
|
|
339
|
+
for (const [name, type] of resolved.fields.entries()) {
|
|
340
|
+
fields.push({ name, zero: this.zeroValueForType(type) });
|
|
341
|
+
}
|
|
342
|
+
this.structFields.set(typeName, fields);
|
|
343
|
+
return fields;
|
|
344
|
+
}
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
|
|
332
348
|
_genStructConstructor(fields) {
|
|
333
349
|
if (fields.length === 0) {
|
|
334
350
|
this.line("constructor() {}");
|
|
335
|
-
|
|
336
|
-
// A field named like a type used in a default (e.g. `Project Project`)
|
|
337
|
-
// would shadow that class inside the parameter scope, so alias it.
|
|
338
|
-
const binding = (f) => {
|
|
339
|
-
const re = new RegExp(`\\b${f.name}\\b`);
|
|
340
|
-
return fields.some((o) => re.test(o.zero)) ? `${f.name}$` : f.name;
|
|
341
|
-
};
|
|
342
|
-
const params = fields
|
|
343
|
-
.map((f) => {
|
|
344
|
-
const b = binding(f);
|
|
345
|
-
return b === f.name
|
|
346
|
-
? `${f.name} = ${f.zero}`
|
|
347
|
-
: `${f.name}: ${b} = ${f.zero}`;
|
|
348
|
-
})
|
|
349
|
-
.join(", ");
|
|
350
|
-
this.line(`constructor({ ${params} } = {}) {`);
|
|
351
|
-
this.indented(() => {
|
|
352
|
-
for (const f of fields) this.line(`this.${f.name} = ${binding(f)};`);
|
|
353
|
-
});
|
|
354
|
-
this.line("}");
|
|
351
|
+
return;
|
|
355
352
|
}
|
|
353
|
+
const binding = (f) => `${f.name}$`;
|
|
354
|
+
const params = fields.map((f) => `${binding(f)} = ${f.zero}`).join(", ");
|
|
355
|
+
this.line(`constructor(${params}) {`);
|
|
356
|
+
this.indented(() => {
|
|
357
|
+
const first = binding(fields[0]);
|
|
358
|
+
const isFirstObj = fields[0].zero === "{}" || fields[0].zero === "null";
|
|
359
|
+
const optCheck = isFirstObj
|
|
360
|
+
? `("${fields[0].name}" in ${first})`
|
|
361
|
+
: "true";
|
|
362
|
+
this.line(
|
|
363
|
+
`if (typeof ${first} === "object" && ${first} !== null && ${first}.constructor === Object && ${optCheck}) { Object.assign(this, ${first}); return; }`,
|
|
364
|
+
);
|
|
365
|
+
for (const f of fields) {
|
|
366
|
+
this.line(`this.${f.name} = ${binding(f)};`);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
this.line("}");
|
|
356
370
|
}
|
|
357
371
|
|
|
358
372
|
genStruct(name, structTypeAst, methodDecls) {
|
|
359
373
|
const fields = this._collectStructFields(name, structTypeAst);
|
|
374
|
+
this.structFields.set(name, fields);
|
|
360
375
|
this.line(`class ${name} {`);
|
|
361
376
|
this.indented(() => {
|
|
362
377
|
this._genStructConstructor(fields);
|
|
@@ -367,6 +382,9 @@ export class CodeGen {
|
|
|
367
382
|
this._genEmbeddedMethodStubs(name, methodDecls);
|
|
368
383
|
});
|
|
369
384
|
this.line("}");
|
|
385
|
+
this.line(
|
|
386
|
+
`Object.defineProperty(${name}.prototype, "value", { get() { return this; }, set(v) { Object.assign(this, v); }, configurable: true });`,
|
|
387
|
+
);
|
|
370
388
|
}
|
|
371
389
|
|
|
372
390
|
_genSingleEmbedStubs(embed, declared) {
|
package/src/codegen/runtime.js
CHANGED
|
@@ -7,14 +7,26 @@ export const HELPER_LEN = `var __len = __len || function(a) {
|
|
|
7
7
|
return a?.length ?? 0;
|
|
8
8
|
};`;
|
|
9
9
|
|
|
10
|
-
export const HELPER_APPEND =
|
|
11
|
-
|
|
10
|
+
export const HELPER_APPEND = `var __append = __append || function(a, ...b) {
|
|
11
|
+
if (a && ArrayBuffer.isView(a)) {
|
|
12
|
+
const res = new a.constructor(a.length + b.length);
|
|
13
|
+
res.set(a);
|
|
14
|
+
res.set(b, a.length);
|
|
15
|
+
return res;
|
|
16
|
+
}
|
|
17
|
+
return a ? [...a, ...b] : b;
|
|
18
|
+
};`;
|
|
12
19
|
|
|
13
20
|
export const HELPER_S = "var __s = __s || function(a) { return a || []; };";
|
|
14
21
|
|
|
15
22
|
export const HELPER_EQUAL = `var __equal = __equal || function __equal(a, b) {
|
|
16
23
|
if (a === b) return true;
|
|
17
24
|
if (a === null || b === null) return false;
|
|
25
|
+
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
|
|
26
|
+
if (a.byteLength !== b.byteLength || a.length !== b.length) return false;
|
|
27
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
18
30
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
19
31
|
if (a.length !== b.length) return false;
|
|
20
32
|
for (let i = 0; i < a.length; i++) if (!__equal(a[i], b[i])) return false;
|
|
@@ -171,6 +171,17 @@ export const statementGenMethods = {
|
|
|
171
171
|
);
|
|
172
172
|
},
|
|
173
173
|
|
|
174
|
+
_isStructDerefAssign(stmt) {
|
|
175
|
+
if (stmt.lhs.length !== 1 || stmt.rhs.length !== 1 || stmt.op !== "=")
|
|
176
|
+
return false;
|
|
177
|
+
const l = stmt.lhs[0];
|
|
178
|
+
return (
|
|
179
|
+
l.kind === "UnaryExpr" &&
|
|
180
|
+
l.op === "*" &&
|
|
181
|
+
this._isStructPointerType(l.operand?._type)
|
|
182
|
+
);
|
|
183
|
+
},
|
|
184
|
+
|
|
174
185
|
_genAssignLhsExpr(e) {
|
|
175
186
|
if (e.kind === "Ident" && e.name !== "_" && this._boxedVars.has(e.name))
|
|
176
187
|
return `${e.name}.value`;
|
|
@@ -203,6 +214,10 @@ export const statementGenMethods = {
|
|
|
203
214
|
this._genCommaOkTypeAssert(stmt);
|
|
204
215
|
return;
|
|
205
216
|
}
|
|
217
|
+
if (this._isStructDerefAssign(stmt)) {
|
|
218
|
+
this._genStructDerefAssign(stmt);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
206
221
|
const rhs = stmt.rhs.map((e) => this.genExpr(e));
|
|
207
222
|
this._markIndexLvalues(stmt.lhs);
|
|
208
223
|
const lhs = stmt.lhs.map((e) => this._genAssignLhsExpr(e));
|
|
@@ -226,6 +241,12 @@ export const statementGenMethods = {
|
|
|
226
241
|
if (okName !== "_") this.line(`${okName} = ${tmp}[1];`);
|
|
227
242
|
},
|
|
228
243
|
|
|
244
|
+
_genStructDerefAssign(stmt) {
|
|
245
|
+
const target = this.genExpr(stmt.lhs[0].operand);
|
|
246
|
+
const source = this.genExpr(stmt.rhs[0]);
|
|
247
|
+
this.line(`Object.assign(${target}, ${source});`);
|
|
248
|
+
},
|
|
249
|
+
|
|
229
250
|
_genMultiAssignWithBlanks(stmt, lhs, rhs, rhsStr) {
|
|
230
251
|
const tmp = "__t";
|
|
231
252
|
this.line(`let ${tmp} = ${rhsStr};`);
|
|
@@ -345,13 +366,21 @@ export const statementGenMethods = {
|
|
|
345
366
|
}
|
|
346
367
|
},
|
|
347
368
|
|
|
369
|
+
_rangeExprLimit(iterType, iteree) {
|
|
370
|
+
if (this._isIntRangeType(iterType)) return iteree;
|
|
371
|
+
if (this._isMapRangeType(iterType)) return `Object.keys(${iteree}).length`;
|
|
372
|
+
return `(${iteree} ? ${iteree}.length : 0)`;
|
|
373
|
+
},
|
|
374
|
+
|
|
348
375
|
_genRangeExprFor(stmt) {
|
|
349
376
|
if (stmt.cond._isIterator) {
|
|
350
377
|
this.genIteratorForCond(stmt);
|
|
351
378
|
return;
|
|
352
379
|
}
|
|
380
|
+
const iterType = stmt.cond.expr._type;
|
|
353
381
|
const iteree = this.genExpr(stmt.cond.expr);
|
|
354
|
-
|
|
382
|
+
const limit = this._rangeExprLimit(iterType, iteree);
|
|
383
|
+
this.line(`for (let _$ = 0; _$ < ${limit}; _$++) {`);
|
|
355
384
|
this.indented(() => this.genBlock(stmt.body));
|
|
356
385
|
this.line("}");
|
|
357
386
|
},
|
|
@@ -418,6 +447,63 @@ export const statementGenMethods = {
|
|
|
418
447
|
);
|
|
419
448
|
},
|
|
420
449
|
|
|
450
|
+
_emitSliceLoopHeader(rawIdx, iteree, isAssign, d) {
|
|
451
|
+
const arr = `__arr${d}`;
|
|
452
|
+
const len = `__len${d}`;
|
|
453
|
+
const hasIdx = rawIdx && rawIdx !== "_";
|
|
454
|
+
if (isAssign && hasIdx) {
|
|
455
|
+
this.line(`const ${arr} = ${iteree};`);
|
|
456
|
+
this.line(`const ${len} = ${arr} ? ${arr}.length : 0;`);
|
|
457
|
+
this.line(`for (${rawIdx} = 0; ${rawIdx} < ${len}; ${rawIdx}++) {`);
|
|
458
|
+
} else {
|
|
459
|
+
const idx = hasIdx ? rawIdx : `__i${d}`;
|
|
460
|
+
this.line(
|
|
461
|
+
`for (let ${idx} = 0, ${arr} = ${iteree}, ${len} = ${arr} ? ${arr}.length : 0; ${idx} < ${len}; ${idx}++) {`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
_emitSliceValAssign(rawIdx, rawVal, isAssign, d) {
|
|
467
|
+
if (!rawVal || rawVal === "_") return;
|
|
468
|
+
const idx = rawIdx && rawIdx !== "_" ? rawIdx : `__i${d}`;
|
|
469
|
+
const kw = isAssign ? "" : "let ";
|
|
470
|
+
this.line(`${kw}${rawVal} = __arr${d}[${idx}];`);
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
_genSliceRangeFor(stmt, iteree, lhs, isAssign) {
|
|
474
|
+
this._loopDepth = this._loopDepth ?? 0;
|
|
475
|
+
const d = this._loopDepth;
|
|
476
|
+
this._loopDepth++;
|
|
477
|
+
this._emitSliceLoopHeader(lhs[0], iteree, isAssign, d);
|
|
478
|
+
this.indented(() => {
|
|
479
|
+
this._emitSliceValAssign(lhs[0], lhs[1], isAssign, d);
|
|
480
|
+
this.genBlock(stmt.body);
|
|
481
|
+
});
|
|
482
|
+
this.line("}");
|
|
483
|
+
this._loopDepth--;
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
_genIntRangeFor(rawVar, iteree, body) {
|
|
487
|
+
const v = rawVar === "_" ? "_$" : rawVar;
|
|
488
|
+
this.line(`for (let ${v} = 0; ${v} < ${iteree}; ${v}++) {`);
|
|
489
|
+
this.indented(() => this.genBlock(body));
|
|
490
|
+
this.line("}");
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
_genIterFor(init, iterType, iteree, lhs, body) {
|
|
494
|
+
const iterExpr = this._genRangeIterExpr(iterType, iteree, lhs);
|
|
495
|
+
const binding =
|
|
496
|
+
lhs.length === 1
|
|
497
|
+
? lhs[0] === "_"
|
|
498
|
+
? "_$"
|
|
499
|
+
: lhs[0]
|
|
500
|
+
: `[${lhs.map((n) => (n === "_" ? "_$" : n)).join(", ")}]`;
|
|
501
|
+
const kw = init.kind === "AssignStmt" ? "" : "let ";
|
|
502
|
+
this.line(`for (${kw}${binding} of ${iterExpr}) {`);
|
|
503
|
+
this.indented(() => this.genBlock(body));
|
|
504
|
+
this.line("}");
|
|
505
|
+
},
|
|
506
|
+
|
|
421
507
|
genRangeFor(stmt) {
|
|
422
508
|
const init = stmt.init;
|
|
423
509
|
const range = init.rhs[0];
|
|
@@ -429,22 +515,16 @@ export const statementGenMethods = {
|
|
|
429
515
|
: this.genExpr(range.expr);
|
|
430
516
|
|
|
431
517
|
if (lhs.length <= 1 && this._isIntRangeType(iterType)) {
|
|
432
|
-
|
|
433
|
-
this.line(`for (let ${v} = 0; ${v} < ${iteree}; ${v}++) {`);
|
|
434
|
-
this.indented(() => this.genBlock(stmt.body));
|
|
435
|
-
this.line("}");
|
|
518
|
+
this._genIntRangeFor(lhs[0], iteree, stmt.body);
|
|
436
519
|
return;
|
|
437
520
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
lhs.
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
this.line(`for (const ${binding} of ${iterExpr}) {`);
|
|
446
|
-
this.indented(() => this.genBlock(stmt.body));
|
|
447
|
-
this.line("}");
|
|
521
|
+
|
|
522
|
+
if (!this._isMapRangeType(iterType) && !this._isStringRangeType(iterType)) {
|
|
523
|
+
this._genSliceRangeFor(stmt, iteree, lhs, init.kind === "AssignStmt");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this._genIterFor(init, iterType, iteree, lhs, stmt.body);
|
|
448
528
|
},
|
|
449
529
|
|
|
450
530
|
_genRangeIterExpr(iterType, iteree, lhs) {
|
|
@@ -28,8 +28,8 @@ const BUILDER_COMMON = {
|
|
|
28
28
|
export const builderMethods = {
|
|
29
29
|
_genBuilderCall(typeName, method, expr) {
|
|
30
30
|
const recv = expr.func.expr;
|
|
31
|
-
const
|
|
32
|
-
const base =
|
|
31
|
+
const recvJs = this.genExpr(recv);
|
|
32
|
+
const base = `(${recvJs}?.value ?? ${recvJs})`;
|
|
33
33
|
const args = expr.args.map((a) => this.genExpr(a));
|
|
34
34
|
const isStr = typeName === "strings.Builder";
|
|
35
35
|
const table = isStr ? BUILDER_STR : BUILDER_BYTES;
|
|
@@ -27,7 +27,7 @@ const BYTES_DISPATCH = {
|
|
|
27
27
|
Split: (a) => `${__bs}(${a[0]}).split(${__bs}(${a[1]})).map(p => ${__sb}(p))`,
|
|
28
28
|
Join: (a) => `${__sb}(${a[0]}.map(p => ${__bs}(p)).join(${__bs}(${a[1]})))`,
|
|
29
29
|
ReplaceAll: (a) =>
|
|
30
|
-
`((b, o, n) => { const r = []; let i = 0; while (i <= b.length - o.length) { if (o.every((v, j) => b[i + j] === v)) { r.push(...n); i += o.length; } else r.push(b[i++]); }
|
|
30
|
+
`((b, o, n) => { const r = []; let i = 0; while (i <= b.length - o.length) { if (o.length > 0 && o.every((v, j) => b[i + j] === v)) { r.push(...n); i += o.length; } else { r.push(b[i++]); } } for (; i < b.length; i++) r.push(b[i]); return ArrayBuffer.isView(b) ? new Uint8Array(r) : r; })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
31
31
|
TrimPrefix: (a) =>
|
|
32
32
|
`((b, p) => p.every((v, i) => b[i] === v) ? b.slice(p.length) : b.slice())(${a[0]}, ${a[1]})`,
|
|
33
33
|
TrimSuffix: (a) =>
|
|
@@ -80,13 +80,12 @@ export const fmtMethods = {
|
|
|
80
80
|
const sprintfCall = this._buildFprintSprintfCall(fn, rest.length, restJs);
|
|
81
81
|
if (targetTypeName === "strings.Builder") {
|
|
82
82
|
const w = this.genExpr(writerArg);
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
return `(${buf} += ${sprintfCall})`;
|
|
83
|
+
const base = `(${w}?.value ?? ${w})`;
|
|
84
|
+
return `(${base}._buf += ${sprintfCall})`;
|
|
86
85
|
}
|
|
87
86
|
if (targetTypeName === "bytes.Buffer") {
|
|
88
87
|
const w = this.genExpr(writerArg);
|
|
89
|
-
const base =
|
|
88
|
+
const base = `(${w}?.value ?? ${w})`;
|
|
90
89
|
return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, ${sprintfCall})`;
|
|
91
90
|
}
|
|
92
91
|
// Generic io.Writer fallback — call .WriteString if available
|