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.
@@ -1,6 +1,9 @@
1
- // CodeGen expression methods and helpers — installed as a mixin on CodeGen.prototype.
2
-
3
- import { ERROR, isComplex } from "../typechecker/types.js";
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,...__cs.slice(0,n));return n;})(${dst},${src})`;
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
- // For non-boxed vars (structs, etc.), wrap in { value: x }
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 → p.value
190
- if (op === "*") return `${this.genExpr(expr.operand)}.value`;
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
- if (!lo && !hi) return `${base}.slice()`;
257
- if (!hi) return `${base}.slice(${lo})`;
258
- if (!lo) return `${base}.slice(0, ${hi})`;
259
- return `${base}.slice(${lo}, ${hi})`;
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 `new ${typeName}({ ${this._genStructFields(expr.elems)} })`;
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
- return `[${expr.elems.map((e) => this.genExpr(e)).join(", ")}]`;
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
- return `Array.from(new TextEncoder().encode(${inner}))`;
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
- const srcType = expr.expr._type;
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":
@@ -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
- } else {
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) {
@@ -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
- "var __append = __append || function(a, ...b) { return a ? [...a, ...b] : b; };";
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
- this.line(`for (let _$ = 0; _$ < ${iteree}; _$++) {`);
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
- const v = lhs[0] === "_" ? "_$" : lhs[0];
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
- const iterExpr = this._genRangeIterExpr(iterType, iteree, lhs);
439
- const binding =
440
- lhs.length === 1
441
- ? lhs[0] === "_"
442
- ? "_$"
443
- : lhs[0]
444
- : `[${lhs.map((n) => (n === "_" ? "_$" : n)).join(", ")}]`;
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 isPtr = recv._type?.kind === "pointer";
32
- const base = isPtr ? `${this.genExpr(recv)}.value` : this.genExpr(recv);
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++]); } return r.concat(b.slice(i)); })(${a[0]}, ${a[1]}, ${a[2]})`,
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 buf =
84
- writerType?.kind === "pointer" ? `${w}.value._buf` : `${w}._buf`;
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 = writerType?.kind === "pointer" ? `${w}.value` : w;
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