gofront 0.0.4 → 0.0.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.
@@ -1,5 +1,9 @@
1
1
  // CodeGen expression methods and helpers — installed as a mixin on CodeGen.prototype.
2
2
 
3
+ import { ERROR, isComplex } from "../typechecker/types.js";
4
+
5
+ const globalError = ERROR;
6
+
3
7
  const INT_TYPE_NAMES = new Set([
4
8
  "int",
5
9
  "uint",
@@ -23,17 +27,68 @@ export const expressionGenMethods = {
23
27
  if (expr.litKind === "STRING") return JSON.stringify(expr.value);
24
28
  return expr.value; // int, float, bool, nil(null)
25
29
 
30
+ case "ImagLit":
31
+ return `{ re: 0, im: ${expr.value} }`;
32
+
26
33
  case "Ident":
34
+ if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
35
+ return `${expr.name}.value`;
27
36
  return expr.name;
28
37
 
29
38
  case "UnaryExpr": {
30
39
  const op = expr.op === "^" ? "~" : expr.op; // bitwise NOT
31
- // Dereference/address-of — transparent in JS
32
- if (op === "*" || op === "&") return this.genExpr(expr.operand);
40
+ // Address-of: &x
41
+ if (op === "&") {
42
+ if (expr.operand.kind === "Ident") {
43
+ // Mark so Ident codegen does NOT append .value
44
+ expr.operand._isAddressOf = true;
45
+ // For boxed vars, &x returns the box itself
46
+ if (this._boxedVars.has(expr.operand.name)) {
47
+ return this.genExpr(expr.operand);
48
+ }
49
+ }
50
+ // For non-boxed vars (structs, etc.), wrap in { value: x }
51
+ return `{ value: ${this.genExpr(expr.operand)} }`;
52
+ }
53
+ // Dereference: *p → p.value
54
+ if (op === "*") {
55
+ return `${this.genExpr(expr.operand)}.value`;
56
+ }
57
+ // Unary minus/plus on complex
58
+ if ((op === "-" || op === "+") && isComplex(expr.operand._type)) {
59
+ const inner = this.genExpr(expr.operand);
60
+ if (op === "-") return `{ re: -${inner}.re, im: -${inner}.im }`;
61
+ return inner;
62
+ }
33
63
  return `${op}${this.genExpr(expr.operand)}`;
34
64
  }
35
65
 
36
66
  case "BinaryExpr": {
67
+ // Complex binary operations
68
+ if (
69
+ isComplex(expr._type) ||
70
+ isComplex(expr.left._type) ||
71
+ isComplex(expr.right._type)
72
+ ) {
73
+ const l = this._genComplexOperand(expr.left);
74
+ const r = this._genComplexOperand(expr.right);
75
+ switch (expr.op) {
76
+ case "+":
77
+ return `{ re: ${l}.re + ${r}.re, im: ${l}.im + ${r}.im }`;
78
+ case "-":
79
+ return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
80
+ case "*":
81
+ this._usesCmul = true;
82
+ return `__cmul(${l}, ${r})`;
83
+ case "/":
84
+ this._usesCdiv = true;
85
+ return `__cdiv(${l}, ${r})`;
86
+ case "==":
87
+ return `(${l}.re === ${r}.re && ${l}.im === ${r}.im)`;
88
+ case "!=":
89
+ return `(${l}.re !== ${r}.re || ${l}.im !== ${r}.im)`;
90
+ }
91
+ }
37
92
  const l = this.genExpr(expr.left);
38
93
  const r = this.genExpr(expr.right);
39
94
  // Integer division: only when both sides are int
@@ -66,6 +121,9 @@ export const expressionGenMethods = {
66
121
  case "CallExpr":
67
122
  return this.genCall(expr);
68
123
 
124
+ case "InstantiationExpr":
125
+ return this.genExpr(expr.expr); // type erasure
126
+
69
127
  case "SelectorExpr": {
70
128
  // Method expression: TypeName.MethodName → (recv, ...args) => recv.method(...args)
71
129
  if (expr._isMethodExpr) {
@@ -112,7 +170,7 @@ export const expressionGenMethods = {
112
170
  const base = this.genExpr(expr.expr);
113
171
  // Bundled GoFront packages are inlined — drop the qualifier.
114
172
  if (this.bundledPackages.has(base)) return expr.field;
115
- // Pointer types are wrapped as { value: T }; route through .value
173
+ // Pointer types: route through .value
116
174
  if (expr.expr._type?.kind === "pointer" && expr.field !== "value") {
117
175
  const sel = `${base}.value.${expr.field}`;
118
176
  if (expr._isMethodValue && !expr._callee)
@@ -164,10 +222,14 @@ export const expressionGenMethods = {
164
222
  const params = expr.params.map((p) => p.name).join(", ");
165
223
  const asyncPrefix = expr.async ? "async " : "";
166
224
  const saved = this.out;
225
+ const prevBoxed = this._boxedVars;
226
+ this._boxedVars = new Set(prevBoxed); // inherit parent's boxed vars (closures)
227
+ this._scanAddressTaken(expr.body);
167
228
  this.out = [];
168
229
  this.indented(() => this._genBody(expr.body));
169
230
  const body = this.out.join("\n");
170
231
  this.out = saved;
232
+ this._boxedVars = prevBoxed;
171
233
  return `${asyncPrefix}function(${params}) {\n${body}\n${" ".repeat(this.indent)}}`;
172
234
  }
173
235
 
@@ -175,6 +237,26 @@ export const expressionGenMethods = {
175
237
  const inner = this.genExpr(expr.expr);
176
238
  const t = expr.targetType;
177
239
 
240
+ // Complex type conversions
241
+ if (t?.name === "complex128" || t?.name === "complex64") {
242
+ const srcType = expr.expr._type;
243
+ if (isComplex(srcType)) return inner; // complex→complex identity
244
+ return `{ re: ${inner}, im: 0 }`; // numeric→complex
245
+ }
246
+
247
+ // Array type conversion: [N]T(slice) → slice(0, N)
248
+ if (t?.kind === "ArrayType") {
249
+ const srcType = expr.expr._type;
250
+ const srcResolved =
251
+ srcType?.kind === "named" ? srcType.underlying : srcType;
252
+ if (srcResolved?.kind === "slice") {
253
+ const size =
254
+ t.size?.value !== undefined ? Number(t.size.value) : t.size;
255
+ return `${inner}.slice(0, ${size})`;
256
+ }
257
+ return inner;
258
+ }
259
+
178
260
  // Slice type conversions
179
261
  if (t?.kind === "SliceType") {
180
262
  const elem = t.elem?.name;
@@ -197,6 +279,11 @@ export const expressionGenMethods = {
197
279
  }
198
280
 
199
281
  const target = t?.name;
282
+ // error("msg") → __error("msg")
283
+ if (target === "error") {
284
+ this._usesError = true;
285
+ return `__error(${inner})`;
286
+ }
200
287
  switch (target) {
201
288
  case "string": {
202
289
  // Go string(65) → "A" (Unicode code point), not "65"
@@ -204,6 +291,14 @@ export const expressionGenMethods = {
204
291
  if (srcType && this.isIntType(srcType)) {
205
292
  return `String.fromCodePoint(${inner})`;
206
293
  }
294
+ // string([]byte) → decode byte array to string
295
+ if (
296
+ srcType &&
297
+ srcType.kind === "slice" &&
298
+ srcType.elem?.name === "int"
299
+ ) {
300
+ return `${inner}.map(c => String.fromCharCode(c)).join("")`;
301
+ }
207
302
  return `String(${inner})`;
208
303
  }
209
304
  case "int":
@@ -244,12 +339,16 @@ export const expressionGenMethods = {
244
339
  },
245
340
 
246
341
  genCall(expr) {
342
+ // Unwrap InstantiationExpr for function calls: Foo[int](42) → Foo(42)
343
+ const funcExpr =
344
+ expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
247
345
  // Handle built-ins that need special JS translation
248
- if (expr.func.kind === "Ident") {
249
- switch (expr.func.name) {
346
+ if (funcExpr.kind === "Ident") {
347
+ switch (funcExpr.name) {
250
348
  case "append":
251
349
  return this.genAppend(expr);
252
350
  case "len": {
351
+ if (expr._constLen != null) return String(expr._constLen);
253
352
  const arg = expr.args[0];
254
353
  const t = arg?._type;
255
354
  const js = this.genExpr(arg);
@@ -285,9 +384,9 @@ export const expressionGenMethods = {
285
384
  case "recover":
286
385
  return `(typeof __panic !== "undefined" && __panic !== null ? (() => { const __r = __panic.message ?? String(__panic); __panic = null; return __r; })() : null)`;
287
386
  case "error": {
288
- // errors are plain strings; nil (null) means no error
387
+ this._usesError = true;
289
388
  const arg = this.genExpr(expr.args[0]);
290
- return arg;
389
+ return `__error(${arg})`;
291
390
  }
292
391
  case "min": {
293
392
  const args = expr.args.map((a) => this.genExpr(a)).join(", ");
@@ -309,263 +408,51 @@ export const expressionGenMethods = {
309
408
  }
310
409
  return `(${js}).length = 0`;
311
410
  }
411
+ case "complex": {
412
+ const re = this.genExpr(expr.args[0]);
413
+ const im = this.genExpr(expr.args[1]);
414
+ return `{ re: ${re}, im: ${im} }`;
415
+ }
416
+ case "real":
417
+ return `(${this.genExpr(expr.args[0])}).re`;
418
+ case "imag":
419
+ return `(${this.genExpr(expr.args[0])}).im`;
312
420
  }
313
421
  }
314
422
 
315
- // error.Error() → the error string itself (errors are plain strings at runtime)
423
+ // error.Error() → real method call
316
424
  if (
317
425
  expr.func.kind === "SelectorExpr" &&
318
426
  expr.func.field === "Error" &&
319
- expr.func.expr._type?.name === "error"
427
+ expr.func.expr._type &&
428
+ (expr.func.expr._type === globalError ||
429
+ expr.func.expr._type?.name === "error" ||
430
+ (expr.func.expr._type?.kind === "named" &&
431
+ expr.func.expr._type?.underlying?.kind === "interface"))
320
432
  ) {
321
- return this.genExpr(expr.func.expr);
433
+ return `${this.genExpr(expr.func.expr)}.Error()`;
322
434
  }
323
435
 
324
436
  // fmt.Sprintf / fmt.Printf / fmt.Println / fmt.Print / fmt.Errorf
325
- if (
326
- expr.func.kind === "SelectorExpr" &&
327
- expr.func.expr.kind === "Ident" &&
328
- expr.func.expr.name === "fmt"
329
- ) {
330
- const fmtArgs = expr.args.map((a) => this.genExpr(a)).join(", ");
331
- switch (expr.func.field) {
332
- case "Sprintf":
333
- case "Errorf":
334
- this._usesSprintf = true;
335
- return `__sprintf(${fmtArgs})`;
336
- case "Printf":
337
- case "Print":
338
- this._usesSprintf = true;
339
- return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
340
- case "Println":
341
- this._usesSprintf = true;
342
- return `console.log(__sprintf(${fmtArgs}))`;
343
- }
344
- }
345
-
346
- // strings.* — direct JS string method mappings
347
- if (
348
- expr.func.kind === "SelectorExpr" &&
349
- expr.func.expr.kind === "Ident" &&
350
- expr.func.expr.name === "strings"
351
- ) {
352
- const a = expr.args.map((e) => this.genExpr(e));
353
- switch (expr.func.field) {
354
- case "Contains":
355
- return `${a[0]}.includes(${a[1]})`;
356
- case "HasPrefix":
357
- return `${a[0]}.startsWith(${a[1]})`;
358
- case "HasSuffix":
359
- return `${a[0]}.endsWith(${a[1]})`;
360
- case "Index":
361
- return `${a[0]}.indexOf(${a[1]})`;
362
- case "LastIndex":
363
- return `${a[0]}.lastIndexOf(${a[1]})`;
364
- case "Count":
365
- return `${a[0]}.split(${a[1]}).length - 1`;
366
- case "Repeat":
367
- return `${a[0]}.repeat(${a[1]})`;
368
- case "Replace":
369
- return `${a[0]}.replace(${a[1]}, ${a[2]})`;
370
- case "ReplaceAll":
371
- return `${a[0]}.replaceAll(${a[1]}, ${a[2]})`;
372
- case "ToUpper":
373
- return `${a[0]}.toUpperCase()`;
374
- case "ToLower":
375
- return `${a[0]}.toLowerCase()`;
376
- case "TrimSpace":
377
- return `${a[0]}.trim()`;
378
- case "Trim":
379
- return `${a[0]}.replace(new RegExp(\`^[\${${a[1]}}]+|[\${${a[1]}}]+$\`, "g"), "")`;
380
- case "TrimPrefix":
381
- return `(${a[0]}.startsWith(${a[1]}) ? ${a[0]}.slice(${a[1]}.length) : ${a[0]})`;
382
- case "TrimSuffix":
383
- return `(${a[0]}.endsWith(${a[1]}) ? ${a[0]}.slice(0, -${a[1]}.length) : ${a[0]})`;
384
- case "TrimLeft":
385
- return `${a[0]}.replace(new RegExp(\`^[\${${a[1]}}]+\`), "")`;
386
- case "TrimRight":
387
- return `${a[0]}.replace(new RegExp(\`[\${${a[1]}}]+$\`), "")`;
388
- case "Split":
389
- return `${a[0]}.split(${a[1]})`;
390
- case "Join":
391
- return `${a[0]}.join(${a[1]})`;
392
- case "EqualFold":
393
- return `${a[0]}.toLowerCase() === ${a[1]}.toLowerCase()`;
394
- }
395
- }
396
-
397
- // strconv.* — number/string conversion
398
- if (
399
- expr.func.kind === "SelectorExpr" &&
400
- expr.func.expr.kind === "Ident" &&
401
- expr.func.expr.name === "strconv"
402
- ) {
403
- const a = expr.args.map((e) => this.genExpr(e));
404
- switch (expr.func.field) {
405
- case "Itoa":
406
- return `String(${a[0]})`;
407
- case "Atoi":
408
- return `(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}) | 0, null])`;
409
- case "FormatBool":
410
- return `String(${a[0]})`;
411
- case "FormatInt":
412
- return `(${a[0]}).toString(${a[1]})`;
413
- case "FormatFloat":
414
- return `String(${a[0]})`;
415
- case "ParseFloat":
416
- return `(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}), null])`;
417
- case "ParseInt":
418
- return `(Number.isNaN(parseInt(${a[0]}, ${a[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${a[0]}, ${a[1]} || 10), null])`;
419
- case "ParseBool":
420
- return `(${a[0]} === "true" || ${a[0]} === "1" ? [true, null] : ${a[0]} === "false" || ${a[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
421
- }
422
- }
423
-
424
- // sort.* — in-place sorting
425
- if (
426
- expr.func.kind === "SelectorExpr" &&
427
- expr.func.expr.kind === "Ident" &&
428
- expr.func.expr.name === "sort"
429
- ) {
430
- const a = expr.args.map((e) => this.genExpr(e));
431
- switch (expr.func.field) {
432
- case "Ints":
433
- case "Float64s":
434
- return `${a[0]}.sort((a, b) => a - b)`;
435
- case "Strings":
436
- return `${a[0]}.sort()`;
437
- case "Slice":
438
- return `${a[0]}.sort((a, b) => ${a[1]}(a, b) ? -1 : ${a[1]}(b, a) ? 1 : 0)`;
439
- case "SliceStable":
440
- return `${a[0]}.sort((a, b) => ${a[1]}(a, b) ? -1 : ${a[1]}(b, a) ? 1 : 0)`;
441
- case "SliceIsSorted":
442
- return `${a[0]}.every((v, i, a) => i === 0 || ${a[1]}(a[i - 1], v))`;
443
- }
444
- }
445
-
446
- // math.* — math functions
447
- if (
448
- expr.func.kind === "SelectorExpr" &&
449
- expr.func.expr.kind === "Ident" &&
450
- expr.func.expr.name === "math"
451
- ) {
452
- const a = expr.args.map((e) => this.genExpr(e));
453
- switch (expr.func.field) {
454
- case "Abs":
455
- return `Math.abs(${a[0]})`;
456
- case "Floor":
457
- return `Math.floor(${a[0]})`;
458
- case "Ceil":
459
- return `Math.ceil(${a[0]})`;
460
- case "Round":
461
- return `Math.round(${a[0]})`;
462
- case "Sqrt":
463
- return `Math.sqrt(${a[0]})`;
464
- case "Cbrt":
465
- return `Math.cbrt(${a[0]})`;
466
- case "Pow":
467
- return `Math.pow(${a[0]}, ${a[1]})`;
468
- case "Log":
469
- return `Math.log(${a[0]})`;
470
- case "Log2":
471
- return `Math.log2(${a[0]})`;
472
- case "Log10":
473
- return `Math.log10(${a[0]})`;
474
- case "Sin":
475
- return `Math.sin(${a[0]})`;
476
- case "Cos":
477
- return `Math.cos(${a[0]})`;
478
- case "Tan":
479
- return `Math.tan(${a[0]})`;
480
- case "Min":
481
- return `Math.min(${a[0]}, ${a[1]})`;
482
- case "Max":
483
- return `Math.max(${a[0]}, ${a[1]})`;
484
- case "Mod":
485
- return `${a[0]} % ${a[1]}`;
486
- case "Inf":
487
- return `(${a[0]} >= 0 ? Infinity : -Infinity)`;
488
- case "IsNaN":
489
- return `Number.isNaN(${a[0]})`;
490
- case "IsInf":
491
- return `(${a[1]} > 0 ? ${a[0]} === Infinity : ${a[1]} < 0 ? ${a[0]} === -Infinity : !Number.isFinite(${a[0]}))`;
492
- case "NaN":
493
- return "NaN";
494
- }
495
- }
496
-
497
- // unicode.* — rune/character classification
498
- if (
499
- expr.func.kind === "SelectorExpr" &&
500
- expr.func.expr.kind === "Ident" &&
501
- expr.func.expr.name === "unicode"
502
- ) {
503
- const a = expr.args.map((e) => this.genExpr(e));
504
- switch (expr.func.field) {
505
- case "IsLetter":
506
- return `/\\p{L}/u.test(String.fromCodePoint(${a[0]}))`;
507
- case "IsDigit":
508
- return `/\\p{Nd}/u.test(String.fromCodePoint(${a[0]}))`;
509
- case "IsSpace":
510
- return `/\\s/.test(String.fromCodePoint(${a[0]}))`;
511
- case "IsUpper":
512
- return `((__c) => __c === __c.toUpperCase() && /\\p{L}/u.test(__c))(String.fromCodePoint(${a[0]}))`;
513
- case "IsLower":
514
- return `((__c) => __c === __c.toLowerCase() && /\\p{L}/u.test(__c))(String.fromCodePoint(${a[0]}))`;
515
- case "IsPunct":
516
- return `/\\p{P}/u.test(String.fromCodePoint(${a[0]}))`;
517
- case "IsControl":
518
- return `/\\p{Cc}/u.test(String.fromCodePoint(${a[0]}))`;
519
- case "IsPrint":
520
- return `!/\\p{Cc}/u.test(String.fromCodePoint(${a[0]}))`;
521
- case "IsGraphic":
522
- return `!/\\p{Cc}/u.test(String.fromCodePoint(${a[0]}))`;
523
- case "ToUpper":
524
- return `String.fromCodePoint(${a[0]}).toUpperCase().codePointAt(0)`;
525
- case "ToLower":
526
- return `String.fromCodePoint(${a[0]}).toLowerCase().codePointAt(0)`;
527
- }
528
- }
529
-
530
- // os.* — process/environment access
531
- if (
532
- expr.func.kind === "SelectorExpr" &&
533
- expr.func.expr.kind === "Ident" &&
534
- expr.func.expr.name === "os"
535
- ) {
536
- const a = expr.args.map((e) => this.genExpr(e));
537
- switch (expr.func.field) {
538
- case "Exit":
539
- return `process.exit(${a[0]})`;
540
- case "Getenv":
541
- return `(process.env[${a[0]}] ?? "")`;
542
- }
437
+ // Standard library namespace dispatch — all follow the pattern:
438
+ // pkg.Func(args) → inline JS
439
+ if (expr.func.kind === "SelectorExpr" && expr.func.expr.kind === "Ident") {
440
+ const ns = expr.func.expr.name;
441
+ const fn = expr.func.field;
442
+ const result = this._genStdlibCall(ns, fn, expr);
443
+ if (result !== undefined) return result;
543
444
  }
544
445
 
545
- // errors.New — identity (errors are plain strings)
546
- if (
547
- expr.func.kind === "SelectorExpr" &&
548
- expr.func.expr.kind === "Ident" &&
549
- expr.func.expr.name === "errors"
550
- ) {
551
- const a = expr.args.map((e) => this.genExpr(e));
552
- if (expr.func.field === "New") return a[0];
553
- }
554
-
555
- // time.* — partial JS-friendly time support
556
- if (
557
- expr.func.kind === "SelectorExpr" &&
558
- expr.func.expr.kind === "Ident" &&
559
- expr.func.expr.name === "time"
560
- ) {
561
- const a = expr.args.map((e) => this.genExpr(e));
562
- switch (expr.func.field) {
563
- case "Now":
564
- return "Date.now()";
565
- case "Since":
566
- return `(Date.now() - ${a[0]})`;
567
- case "Sleep":
568
- return `await new Promise(r => setTimeout(r, ${a[0]} / 1000000))`;
446
+ // strings.Builder / bytes.Buffer / regexp.Regexp method dispatch
447
+ if (expr.func.kind === "SelectorExpr") {
448
+ const recvType = expr.func.expr._type;
449
+ const recvName = recvType?.name ?? recvType?.base?.name;
450
+ if (recvName === "strings.Builder" || recvName === "bytes.Buffer") {
451
+ const typeName = recvType?.name ?? recvType?.base?.name;
452
+ return this._genBuilderCall(typeName, expr.func.field, expr);
453
+ }
454
+ if (recvName === "regexp.Regexp") {
455
+ return this._genRegexpMethodCall(expr.func.field, expr);
569
456
  }
570
457
  }
571
458
 
@@ -718,10 +605,18 @@ export const expressionGenMethods = {
718
605
 
719
606
  // ── Helpers ───────────────────────────────────────────────────
720
607
 
608
+ _genComplexOperand(expr) {
609
+ if (isComplex(expr._type)) return this.genExpr(expr);
610
+ return `{ re: ${this.genExpr(expr)}, im: 0 }`;
611
+ },
612
+
721
613
  getTypeName(typeNode) {
722
614
  if (!typeNode) return null;
723
615
  if (typeNode.kind === "TypeName") return typeNode.name;
616
+ if (typeNode.kind === "GenericTypeName") return typeNode.name;
724
617
  if (typeNode.kind === "Ident") return typeNode.name;
618
+ if (typeNode.kind === "InstantiationExpr")
619
+ return this.getTypeName(typeNode.expr);
725
620
  if (typeNode.kind === "SelectorExpr") return typeNode.field;
726
621
  return null;
727
622
  },
@@ -762,6 +657,9 @@ export const expressionGenMethods = {
762
657
  case "byte":
763
658
  case "rune":
764
659
  return "0";
660
+ case "complex64":
661
+ case "complex128":
662
+ return "{ re: 0, im: 0 }";
765
663
  case "string":
766
664
  return '""';
767
665
  case "bool":
@@ -777,6 +675,8 @@ export const expressionGenMethods = {
777
675
  case "TypeName": {
778
676
  const basic = this._zeroForBasicName(typeNode.name);
779
677
  if (basic !== null) return basic;
678
+ if (typeNode.name === "strings.Builder") return '{ _buf: "" }';
679
+ if (typeNode.name === "bytes.Buffer") return "{ _buf: [] }";
780
680
  if (this.structNames.has(typeNode.name))
781
681
  return `new ${typeNode.name}()`;
782
682
  return "null";
@@ -821,6 +721,8 @@ export const expressionGenMethods = {
821
721
  return `{ ${fields} }`;
822
722
  }
823
723
  case "named":
724
+ if (t.name === "strings.Builder") return '{ _buf: "" }';
725
+ if (t.name === "bytes.Buffer") return "{ _buf: [] }";
824
726
  if (this.structNames.has(t.name)) return `new ${t.name}()`;
825
727
  return "null";
826
728
  default:
@@ -856,7 +758,7 @@ export const expressionGenMethods = {
856
758
  case "nil":
857
759
  return `${val} === null`;
858
760
  case "error":
859
- return `typeof ${val} === "string"`;
761
+ return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
860
762
  default:
861
763
  if (this.structNames.has(typeNode.name))
862
764
  return `${val} instanceof ${typeNode.name}`;
@@ -891,6 +793,268 @@ export const expressionGenMethods = {
891
793
  return base?.kind === "struct" || base?.kind === "array";
892
794
  },
893
795
 
796
+ _genBuilderCall(typeName, method, expr) {
797
+ // Resolve the receiver — may be a direct var or a &var (pointer)
798
+ const recv = expr.func.expr;
799
+ const isPtr = recv._type?.kind === "pointer";
800
+ const base = isPtr ? `${this.genExpr(recv)}.value` : this.genExpr(recv);
801
+ const args = expr.args.map((a) => this.genExpr(a));
802
+
803
+ if (typeName === "strings.Builder") {
804
+ switch (method) {
805
+ case "WriteString":
806
+ return `(${base}._buf += ${args[0]}, [${args[0]}.length, null])`;
807
+ case "WriteByte":
808
+ case "WriteRune":
809
+ return `(${base}._buf += String.fromCodePoint(${args[0]}))`;
810
+ case "Write":
811
+ return `(${base}._buf += String.fromCharCode(...${args[0]}))`;
812
+ case "String":
813
+ return `${base}._buf`;
814
+ case "Len":
815
+ return `${base}._buf.length`;
816
+ case "Reset":
817
+ return `(${base}._buf = "")`;
818
+ case "Grow":
819
+ return "undefined";
820
+ }
821
+ }
822
+
823
+ if (typeName === "bytes.Buffer") {
824
+ switch (method) {
825
+ case "WriteString":
826
+ return `(${base}._buf.push(...new TextEncoder().encode(${args[0]})), [${args[0]}.length, null])`;
827
+ case "WriteByte":
828
+ return `${base}._buf.push(${args[0]})`;
829
+ case "Write":
830
+ return `(${base}._buf.push(...${args[0]}), [${args[0]}.length, null])`;
831
+ case "String":
832
+ return `new TextDecoder().decode(new Uint8Array(${base}._buf))`;
833
+ case "Bytes":
834
+ return `${base}._buf.slice()`;
835
+ case "Len":
836
+ return `${base}._buf.length`;
837
+ case "Reset":
838
+ return `(${base}._buf = [])`;
839
+ case "Grow":
840
+ return "undefined";
841
+ }
842
+ }
843
+
844
+ return undefined;
845
+ },
846
+
847
+ _genRegexp(fn, a) {
848
+ // Converts a Go regexp pattern string (which may contain inline flags like (?i))
849
+ // into a JS RegExp, extracting the flags into the second constructor argument.
850
+ const makeRegExp = (pat) =>
851
+ `((p) => { const m = /^\\(\\?([gimsuy]+)\\)/.exec(p); return m ? new RegExp(p.slice(m[0].length), m[1]) : new RegExp(p); })(${pat})`;
852
+ switch (fn) {
853
+ case "MustCompile":
854
+ return makeRegExp(a()[0]);
855
+ case "Compile":
856
+ return `[${makeRegExp(a()[0])}, null]`;
857
+ case "MatchString": {
858
+ const [pat, s] = a();
859
+ return `[new RegExp(${pat}).test(${s}), null]`;
860
+ }
861
+ case "QuoteMeta":
862
+ return `${a()[0]}.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&')`;
863
+ }
864
+ return undefined;
865
+ },
866
+
867
+ _genRegexpMethodCall(method, expr) {
868
+ const recv = expr.func.expr;
869
+ // *Regexp is a plain JS RegExp — no .value boxing needed
870
+ const re = this.genExpr(recv);
871
+ const args = expr.args.map((a) => this.genExpr(a));
872
+
873
+ switch (method) {
874
+ case "MatchString":
875
+ return `${re}.test(${args[0]})`;
876
+ case "FindString":
877
+ return `(${re}.exec(${args[0]})?.[0] ?? "")`;
878
+ case "FindStringIndex":
879
+ return `((m => m ? [m.index, m.index + m[0].length] : null)(${re}.exec(${args[0]})))`;
880
+ case "FindAllString":
881
+ return `[...${args[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${args[1]} < 0 ? undefined : ${args[1]}).map(m => m[0])`;
882
+ case "FindStringSubmatch":
883
+ return `[...(${re}.exec(${args[0]}) ?? [])]`;
884
+ case "FindAllStringSubmatch":
885
+ return `[...${args[0]}.matchAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"))].slice(0, ${args[1]} < 0 ? undefined : ${args[1]}).map(m => [...m])`;
886
+ case "ReplaceAllString":
887
+ return `${args[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${args[1]})`;
888
+ case "ReplaceAllLiteralString":
889
+ return `${args[0]}.replaceAll(new RegExp(${re}.source, ${re}.flags.includes("g") ? ${re}.flags : ${re}.flags + "g"), ${args[1]}.replace(/\\$/g, '$$$$'))`;
890
+ case "Split":
891
+ return `${args[0]}.split(${re}).slice(0, ${args[1]} < 0 ? undefined : ${args[1]})`;
892
+ case "String":
893
+ return `${re}.source`;
894
+ }
895
+ return undefined;
896
+ },
897
+
898
+ _genSlices(fn, a) {
899
+ switch (fn) {
900
+ case "Contains": {
901
+ const [s, v] = a();
902
+ return `${s}.includes(${v})`;
903
+ }
904
+ case "Index": {
905
+ const [s, v] = a();
906
+ return `${s}.indexOf(${v})`;
907
+ }
908
+ case "Equal": {
909
+ this._usesEqual = true;
910
+ const [a1, b1] = a();
911
+ return `__equal(${a1}, ${b1})`;
912
+ }
913
+ case "Compare": {
914
+ const [a1, b1] = a();
915
+ return `((a, b) => { for (let i = 0; i < a.length && i < b.length; i++) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; } return a.length - b.length; })(${a1}, ${b1})`;
916
+ }
917
+ case "Sort": {
918
+ const [s] = a();
919
+ return `${s}.sort((a, b) => a < b ? -1 : a > b ? 1 : 0)`;
920
+ }
921
+ case "SortFunc": {
922
+ const [s, cmp] = a();
923
+ return `${s}.sort(${cmp})`;
924
+ }
925
+ case "SortStableFunc": {
926
+ const [s, cmp] = a();
927
+ return `${s}.sort(${cmp})`;
928
+ }
929
+ case "IsSorted": {
930
+ const [s] = a();
931
+ return `((s) => { for (let i = 1; i < s.length; i++) { if (s[i] < s[i-1]) return false; } return true; })(${s})`;
932
+ }
933
+ case "IsSortedFunc": {
934
+ const [s, cmp] = a();
935
+ return `((s, f) => { for (let i = 1; i < s.length; i++) { if (f(s[i], s[i-1]) < 0) return false; } return true; })(${s}, ${cmp})`;
936
+ }
937
+ case "Reverse": {
938
+ const [s] = a();
939
+ return `${s}.reverse()`;
940
+ }
941
+ case "Max": {
942
+ const [s] = a();
943
+ return `Math.max(...${s})`;
944
+ }
945
+ case "Min": {
946
+ const [s] = a();
947
+ return `Math.min(...${s})`;
948
+ }
949
+ case "MaxFunc": {
950
+ const [s, cmp] = a();
951
+ return `((s, f) => s.reduce((m, x) => f(x, m) > 0 ? x : m))(${s}, ${cmp})`;
952
+ }
953
+ case "MinFunc": {
954
+ const [s, cmp] = a();
955
+ return `((s, f) => s.reduce((m, x) => f(x, m) < 0 ? x : m))(${s}, ${cmp})`;
956
+ }
957
+ case "Clone": {
958
+ const [s] = a();
959
+ return `${s}.slice()`;
960
+ }
961
+ case "Compact": {
962
+ const [s] = a();
963
+ return `((s) => s.filter((v, i) => i === 0 || v !== s[i-1]))(${s})`;
964
+ }
965
+ case "CompactFunc": {
966
+ const [s, eq] = a();
967
+ return `((s, f) => s.filter((v, i) => i === 0 || !f(v, s[i-1])))(${s}, ${eq})`;
968
+ }
969
+ case "Concat": {
970
+ const args = a();
971
+ return `[].concat(${args.join(", ")})`;
972
+ }
973
+ case "Delete": {
974
+ const [s, i, j] = a();
975
+ return `[...${s}.slice(0, ${i}), ...${s}.slice(${j})]`;
976
+ }
977
+ case "DeleteFunc": {
978
+ const [s, fn2] = a();
979
+ return `${s}.filter((v) => !${fn2}(v))`;
980
+ }
981
+ case "Insert": {
982
+ const args = a();
983
+ const s = args[0];
984
+ const i = args[1];
985
+ const vs = args.slice(2);
986
+ return `[...${s}.slice(0, ${i}), ${vs.join(", ")}, ...${s}.slice(${i})]`;
987
+ }
988
+ case "Replace": {
989
+ const args = a();
990
+ const s = args[0];
991
+ const i = args[1];
992
+ const j = args[2];
993
+ const vs = args.slice(3);
994
+ return `[...${s}.slice(0, ${i}), ${vs.join(", ")}, ...${s}.slice(${j})]`;
995
+ }
996
+ case "Grow":
997
+ case "Clip": {
998
+ const [s] = a();
999
+ return `${s}.slice()`;
1000
+ }
1001
+ }
1002
+ return undefined;
1003
+ },
1004
+
1005
+ _genHtml(fn, a) {
1006
+ switch (fn) {
1007
+ case "EscapeString": {
1008
+ const [s] = a();
1009
+ return `${s}.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&#34;").replace(/'/g,"&#39;")`;
1010
+ }
1011
+ case "UnescapeString": {
1012
+ const [s] = a();
1013
+ return `${s}.replace(/&#39;/g,"'").replace(/&#34;/g,'"').replace(/&gt;/g,">").replace(/&lt;/g,"<").replace(/&amp;/g,"&")`;
1014
+ }
1015
+ }
1016
+ return undefined;
1017
+ },
1018
+
1019
+ _genMaps(fn, a) {
1020
+ switch (fn) {
1021
+ case "Keys": {
1022
+ const [m] = a();
1023
+ return `Object.keys(${m})`;
1024
+ }
1025
+ case "Values": {
1026
+ const [m] = a();
1027
+ return `Object.values(${m})`;
1028
+ }
1029
+ case "Clone": {
1030
+ const [m] = a();
1031
+ return `({...${m}})`;
1032
+ }
1033
+ case "Copy": {
1034
+ const [dst, src] = a();
1035
+ return `Object.assign(${dst}, ${src})`;
1036
+ }
1037
+ case "Equal": {
1038
+ this._usesEqual = true;
1039
+ const [a1, b1] = a();
1040
+ return `__equal(${a1}, ${b1})`;
1041
+ }
1042
+ case "EqualFunc": {
1043
+ const [m1, m2, eq] = a();
1044
+ return `((a, b, f) => { const ka = Object.keys(a); if (ka.length !== Object.keys(b).length) return false; return ka.every(k => k in b && f(a[k], b[k])); })(${m1}, ${m2}, ${eq})`;
1045
+ }
1046
+ case "Delete": {
1047
+ const [m, k] = a();
1048
+ return `(delete ${m}[${k}], undefined)`;
1049
+ }
1050
+ case "DeleteFunc": {
1051
+ const [m, fn2] = a();
1052
+ return `Object.keys(${m}).forEach(k => { if (${fn2}(k, ${m}[k])) delete ${m}[k]; })`;
1053
+ }
1054
+ }
1055
+ return undefined;
1056
+ },
1057
+
894
1058
  typeComment(typeNode) {
895
1059
  if (!typeNode) return "unknown";
896
1060
  switch (typeNode.kind) {
@@ -904,4 +1068,365 @@ export const expressionGenMethods = {
904
1068
  return typeNode.kind;
905
1069
  }
906
1070
  },
1071
+
1072
+ // Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
1073
+ // Returns the generated JS string, or undefined if not handled.
1074
+ _genStdlibCall(ns, fn, expr) {
1075
+ const a = () => expr.args.map((e) => this.genExpr(e));
1076
+ switch (ns) {
1077
+ case "fmt":
1078
+ return this._genFmt(fn, a, expr);
1079
+ case "strings":
1080
+ return this._genStrings(fn, a);
1081
+ case "bytes":
1082
+ return this._genBytes(fn, a);
1083
+ case "strconv":
1084
+ return this._genStrconv(fn, a);
1085
+ case "sort":
1086
+ return this._genSort(fn, a);
1087
+ case "math":
1088
+ return this._genMath(fn, a);
1089
+ case "unicode":
1090
+ return this._genUnicode(fn, a);
1091
+ case "os":
1092
+ return this._genOs(fn, a);
1093
+ case "errors":
1094
+ return this._genErrors(fn, a);
1095
+ case "time":
1096
+ return this._genTime(fn, a);
1097
+ case "regexp":
1098
+ return this._genRegexp(fn, a);
1099
+ case "slices":
1100
+ return this._genSlices(fn, a);
1101
+ case "maps":
1102
+ return this._genMaps(fn, a);
1103
+ case "html":
1104
+ return this._genHtml(fn, a);
1105
+ default:
1106
+ return undefined;
1107
+ }
1108
+ },
1109
+
1110
+ _genFmt(fn, _a, expr) {
1111
+ const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
1112
+ switch (fn) {
1113
+ case "Sprintf":
1114
+ this._usesSprintf = true;
1115
+ return `__sprintf(${fmtArgs})`;
1116
+ case "Errorf": {
1117
+ this._usesSprintf = true;
1118
+ this._usesError = true;
1119
+ const fmtStr = expr.args[0];
1120
+ if (fmtStr?.kind === "BasicLit" && fmtStr.value?.includes("%w")) {
1121
+ const lastArg = this.genExpr(expr.args[expr.args.length - 1]);
1122
+ return `__error(__sprintf(${fmtArgs}), ${lastArg})`;
1123
+ }
1124
+ return `__error(__sprintf(${fmtArgs}))`;
1125
+ }
1126
+ case "Printf":
1127
+ case "Print":
1128
+ this._usesSprintf = true;
1129
+ return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
1130
+ case "Println":
1131
+ this._usesSprintf = true;
1132
+ return `console.log(__sprintf(${fmtArgs}))`;
1133
+ case "Fprintf":
1134
+ case "Fprintln":
1135
+ case "Fprint": {
1136
+ this._usesSprintf = true;
1137
+ const writerArg = expr.args[0];
1138
+ const rest = expr.args.slice(1);
1139
+ const restJs = rest.map((e) => this.genExpr(e)).join(", ");
1140
+ // Resolve the writer to its underlying buffer expression
1141
+ const writerType = writerArg._type;
1142
+ const targetTypeName =
1143
+ writerType?.name ??
1144
+ (writerType?.kind === "pointer" ? writerType.base?.name : null);
1145
+ let buf;
1146
+ if (targetTypeName === "strings.Builder") {
1147
+ const w = this.genExpr(writerArg);
1148
+ buf =
1149
+ writerType?.kind === "pointer" ? `${w}.value._buf` : `${w}._buf`;
1150
+ } else if (targetTypeName === "bytes.Buffer") {
1151
+ const w = this.genExpr(writerArg);
1152
+ const base = writerType?.kind === "pointer" ? `${w}.value` : w;
1153
+ buf = null;
1154
+ if (fn === "Fprintf") {
1155
+ return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, __sprintf(${restJs}))`;
1156
+ }
1157
+ const valJs =
1158
+ fn === "Fprintln"
1159
+ ? `__sprintf("%v\\n", ${restJs})`
1160
+ : `__sprintf("%v", ${restJs})`;
1161
+ return `((__b,__s)=>{ __b._buf.push(...new TextEncoder().encode(__s)); })(${base}, ${valJs})`;
1162
+ } else {
1163
+ // Generic io.Writer fallback — call .WriteString if available
1164
+ const w = this.genExpr(writerArg);
1165
+ const formatted =
1166
+ fn === "Fprintf"
1167
+ ? `__sprintf(${restJs})`
1168
+ : fn === "Fprintln"
1169
+ ? `__sprintf("%v\\n", ${restJs})`
1170
+ : `__sprintf("%v", ${restJs})`;
1171
+ return `${w}.WriteString(${formatted})`;
1172
+ }
1173
+ // strings.Builder path
1174
+ if (fn === "Fprintf") return `(${buf} += __sprintf(${restJs}))`;
1175
+ if (fn === "Fprintln")
1176
+ return `(${buf} += __sprintf("%v\\n", ${restJs}))`;
1177
+ return `(${buf} += __sprintf("%v", ${restJs}))`;
1178
+ }
1179
+ default:
1180
+ return undefined;
1181
+ }
1182
+ },
1183
+
1184
+ _genStrings(fn, a) {
1185
+ const args = a();
1186
+ switch (fn) {
1187
+ case "Contains":
1188
+ return `${args[0]}.includes(${args[1]})`;
1189
+ case "HasPrefix":
1190
+ return `${args[0]}.startsWith(${args[1]})`;
1191
+ case "HasSuffix":
1192
+ return `${args[0]}.endsWith(${args[1]})`;
1193
+ case "Index":
1194
+ return `${args[0]}.indexOf(${args[1]})`;
1195
+ case "LastIndex":
1196
+ return `${args[0]}.lastIndexOf(${args[1]})`;
1197
+ case "Count":
1198
+ return `((s, sep) => sep === "" ? s.length + 1 : s.split(sep).length - 1)(${args[0]}, ${args[1]})`;
1199
+ case "Repeat":
1200
+ return `${args[0]}.repeat(${args[1]})`;
1201
+ case "Replace":
1202
+ return `${args[0]}.replace(${args[1]}, ${args[2]})`;
1203
+ case "ReplaceAll":
1204
+ return `${args[0]}.replaceAll(${args[1]}, ${args[2]})`;
1205
+ case "ToUpper":
1206
+ return `${args[0]}.toUpperCase()`;
1207
+ case "ToLower":
1208
+ return `${args[0]}.toLowerCase()`;
1209
+ case "TrimSpace":
1210
+ return `${args[0]}.trim()`;
1211
+ case "Trim":
1212
+ return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), "")`;
1213
+ case "TrimPrefix":
1214
+ return `((s, pre) => s.startsWith(pre) ? s.slice(pre.length) : s)(${args[0]}, ${args[1]})`;
1215
+ case "TrimSuffix":
1216
+ return `((s, suf) => !suf.length || !s.endsWith(suf) ? s : s.slice(0, -suf.length))(${args[0]}, ${args[1]})`;
1217
+ case "TrimLeft":
1218
+ return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+\`), "")`;
1219
+ case "TrimRight":
1220
+ return `${args[0]}.replace(new RegExp(\`[\${${args[1]}}]+$\`), "")`;
1221
+ case "Split":
1222
+ return `${args[0]}.split(${args[1]})`;
1223
+ case "Join":
1224
+ return `${args[0]}.join(${args[1]})`;
1225
+ case "EqualFold":
1226
+ return `${args[0]}.toLowerCase() === ${args[1]}.toLowerCase()`;
1227
+ default:
1228
+ return undefined;
1229
+ }
1230
+ },
1231
+
1232
+ _genBytes(fn, a) {
1233
+ const __bs = `(b => String.fromCharCode(...b))`;
1234
+ const __sb = `(s => [...s].map(c => c.charCodeAt(0)))`;
1235
+ const args = a();
1236
+ switch (fn) {
1237
+ case "Contains":
1238
+ return `${__bs}(${args[0]}).includes(${__bs}(${args[1]}))`;
1239
+ case "HasPrefix":
1240
+ return `((b, p) => { for (let i = 0; i < p.length; i++) if (b[i] !== p[i]) return false; return b.length >= p.length; })(${args[0]}, ${args[1]})`;
1241
+ case "HasSuffix":
1242
+ return `((b, s) => { const off = b.length - s.length; if (off < 0) return false; for (let i = 0; i < s.length; i++) if (b[off + i] !== s[i]) return false; return true; })(${args[0]}, ${args[1]})`;
1243
+ case "Index":
1244
+ return `${__bs}(${args[0]}).indexOf(${__bs}(${args[1]}))`;
1245
+ case "Count":
1246
+ return `((b, sep) => sep.length === 0 ? b.length + 1 : ${__bs}(b).split(${__bs}(sep)).length - 1)(${args[0]}, ${args[1]})`;
1247
+ case "Repeat":
1248
+ return `${__sb}(${__bs}(${args[0]}).repeat(${args[1]}))`;
1249
+ case "Replace":
1250
+ return `((b, o, n, cnt) => { let s = ${__bs}(b), os = ${__bs}(o), ns = ${__bs}(n); if (cnt < 0) return ${__sb}(s.replaceAll(os, ns)); for (let i = 0; i < cnt; i++) s = s.replace(os, ns); return ${__sb}(s); })(${args[0]}, ${args[1]}, ${args[2]}, ${args[3]})`;
1251
+ case "ToUpper":
1252
+ return `${__sb}(${__bs}(${args[0]}).toUpperCase())`;
1253
+ case "ToLower":
1254
+ return `${__sb}(${__bs}(${args[0]}).toLowerCase())`;
1255
+ case "TrimSpace":
1256
+ return `${__sb}(${__bs}(${args[0]}).trim())`;
1257
+ case "Trim":
1258
+ return `${__sb}(${__bs}(${args[0]}).replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), ""))`;
1259
+ case "Equal":
1260
+ return `((a, b) => { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; })(${args[0]}, ${args[1]})`;
1261
+ case "Split":
1262
+ return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).map(p => ${__sb}(p))`;
1263
+ case "Join":
1264
+ return `${__sb}(${args[0]}.map(p => ${__bs}(p)).join(${__bs}(${args[1]})))`;
1265
+ default:
1266
+ return undefined;
1267
+ }
1268
+ },
1269
+
1270
+ _genStrconv(fn, a) {
1271
+ const args = a();
1272
+ switch (fn) {
1273
+ case "Itoa":
1274
+ return `String(${args[0]})`;
1275
+ case "Atoi":
1276
+ return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}) | 0, null])`;
1277
+ case "FormatBool":
1278
+ return `String(${args[0]})`;
1279
+ case "FormatInt":
1280
+ return `(${args[0]}).toString(${args[1]})`;
1281
+ case "FormatFloat":
1282
+ return `String(${args[0]})`;
1283
+ case "ParseFloat":
1284
+ return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}), null])`;
1285
+ case "ParseInt":
1286
+ return `(Number.isNaN(parseInt(${args[0]}, ${args[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${args[0]}, ${args[1]} || 10), null])`;
1287
+ case "ParseBool":
1288
+ return `(${args[0]} === "true" || ${args[0]} === "1" ? [true, null] : ${args[0]} === "false" || ${args[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
1289
+ default:
1290
+ return undefined;
1291
+ }
1292
+ },
1293
+
1294
+ _genSort(fn, a) {
1295
+ const args = a();
1296
+ switch (fn) {
1297
+ case "Ints":
1298
+ case "Float64s":
1299
+ return `${args[0]}.sort((a, b) => a - b)`;
1300
+ case "Strings":
1301
+ return `${args[0]}.sort()`;
1302
+ case "Slice":
1303
+ case "SliceStable":
1304
+ return `${args[0]}.sort((a, b) => ${args[1]}(a, b) ? -1 : ${args[1]}(b, a) ? 1 : 0)`;
1305
+ case "SliceIsSorted":
1306
+ return `${args[0]}.every((v, i, a) => i === 0 || ${args[1]}(a[i - 1], v))`;
1307
+ default:
1308
+ return undefined;
1309
+ }
1310
+ },
1311
+
1312
+ _genMath(fn, a) {
1313
+ const args = a();
1314
+ switch (fn) {
1315
+ case "Abs":
1316
+ return `Math.abs(${args[0]})`;
1317
+ case "Floor":
1318
+ return `Math.floor(${args[0]})`;
1319
+ case "Ceil":
1320
+ return `Math.ceil(${args[0]})`;
1321
+ case "Round":
1322
+ return `Math.round(${args[0]})`;
1323
+ case "Sqrt":
1324
+ return `Math.sqrt(${args[0]})`;
1325
+ case "Cbrt":
1326
+ return `Math.cbrt(${args[0]})`;
1327
+ case "Pow":
1328
+ return `Math.pow(${args[0]}, ${args[1]})`;
1329
+ case "Log":
1330
+ return `Math.log(${args[0]})`;
1331
+ case "Log2":
1332
+ return `Math.log2(${args[0]})`;
1333
+ case "Log10":
1334
+ return `Math.log10(${args[0]})`;
1335
+ case "Sin":
1336
+ return `Math.sin(${args[0]})`;
1337
+ case "Cos":
1338
+ return `Math.cos(${args[0]})`;
1339
+ case "Tan":
1340
+ return `Math.tan(${args[0]})`;
1341
+ case "Min":
1342
+ return `Math.min(${args[0]}, ${args[1]})`;
1343
+ case "Max":
1344
+ return `Math.max(${args[0]}, ${args[1]})`;
1345
+ case "Mod":
1346
+ return `${args[0]} % ${args[1]}`;
1347
+ case "Inf":
1348
+ return `(${args[0]} >= 0 ? Infinity : -Infinity)`;
1349
+ case "IsNaN":
1350
+ return `Number.isNaN(${args[0]})`;
1351
+ case "IsInf":
1352
+ return `(${args[1]} > 0 ? ${args[0]} === Infinity : ${args[1]} < 0 ? ${args[0]} === -Infinity : !Number.isFinite(${args[0]}))`;
1353
+ case "NaN":
1354
+ return "NaN";
1355
+ default:
1356
+ return undefined;
1357
+ }
1358
+ },
1359
+
1360
+ _genUnicode(fn, a) {
1361
+ const args = a();
1362
+ const cp = `String.fromCodePoint(${args[0]})`;
1363
+ switch (fn) {
1364
+ case "IsLetter":
1365
+ return `/\\p{L}/u.test(${cp})`;
1366
+ case "IsDigit":
1367
+ return `/\\p{Nd}/u.test(${cp})`;
1368
+ case "IsSpace":
1369
+ return `/\\s/.test(${cp})`;
1370
+ case "IsUpper":
1371
+ return `((__c) => __c === __c.toUpperCase() && /\\p{L}/u.test(__c))(${cp})`;
1372
+ case "IsLower":
1373
+ return `((__c) => __c === __c.toLowerCase() && /\\p{L}/u.test(__c))(${cp})`;
1374
+ case "IsPunct":
1375
+ return `/\\p{P}/u.test(${cp})`;
1376
+ case "IsControl":
1377
+ return `/\\p{Cc}/u.test(${cp})`;
1378
+ case "IsPrint":
1379
+ return `!/\\p{Cc}/u.test(${cp})`;
1380
+ case "IsGraphic":
1381
+ return `!/\\p{Cc}/u.test(${cp})`;
1382
+ case "ToUpper":
1383
+ return `${cp}.toUpperCase().codePointAt(0)`;
1384
+ case "ToLower":
1385
+ return `${cp}.toLowerCase().codePointAt(0)`;
1386
+ default:
1387
+ return undefined;
1388
+ }
1389
+ },
1390
+
1391
+ _genOs(fn, a) {
1392
+ const args = a();
1393
+ switch (fn) {
1394
+ case "Exit":
1395
+ return `process.exit(${args[0]})`;
1396
+ case "Getenv":
1397
+ return `(process.env[${args[0]}] ?? "")`;
1398
+ default:
1399
+ return undefined;
1400
+ }
1401
+ },
1402
+
1403
+ _genErrors(fn, a) {
1404
+ const args = a();
1405
+ switch (fn) {
1406
+ case "New":
1407
+ this._usesError = true;
1408
+ return `__error(${args[0]})`;
1409
+ case "Is":
1410
+ this._usesErrorIs = true;
1411
+ return `__errorIs(${args[0]}, ${args[1]})`;
1412
+ case "Unwrap":
1413
+ return `(${args[0]}?._cause ?? null)`;
1414
+ default:
1415
+ return undefined;
1416
+ }
1417
+ },
1418
+
1419
+ _genTime(fn, a) {
1420
+ const args = a();
1421
+ switch (fn) {
1422
+ case "Now":
1423
+ return "Date.now()";
1424
+ case "Since":
1425
+ return `(Date.now() - ${args[0]})`;
1426
+ case "Sleep":
1427
+ return `await new Promise(r => setTimeout(r, ${args[0]} / 1000000))`;
1428
+ default:
1429
+ return undefined;
1430
+ }
1431
+ },
907
1432
  };