gofront 0.0.4 → 0.0.5

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,264 +408,39 @@ 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
- }
543
- }
544
-
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))`;
569
- }
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;
570
444
  }
571
445
 
572
446
  // Mark the callee so SelectorExpr knows it's being called, not used as a value
@@ -718,10 +592,18 @@ export const expressionGenMethods = {
718
592
 
719
593
  // ── Helpers ───────────────────────────────────────────────────
720
594
 
595
+ _genComplexOperand(expr) {
596
+ if (isComplex(expr._type)) return this.genExpr(expr);
597
+ return `{ re: ${this.genExpr(expr)}, im: 0 }`;
598
+ },
599
+
721
600
  getTypeName(typeNode) {
722
601
  if (!typeNode) return null;
723
602
  if (typeNode.kind === "TypeName") return typeNode.name;
603
+ if (typeNode.kind === "GenericTypeName") return typeNode.name;
724
604
  if (typeNode.kind === "Ident") return typeNode.name;
605
+ if (typeNode.kind === "InstantiationExpr")
606
+ return this.getTypeName(typeNode.expr);
725
607
  if (typeNode.kind === "SelectorExpr") return typeNode.field;
726
608
  return null;
727
609
  },
@@ -762,6 +644,9 @@ export const expressionGenMethods = {
762
644
  case "byte":
763
645
  case "rune":
764
646
  return "0";
647
+ case "complex64":
648
+ case "complex128":
649
+ return "{ re: 0, im: 0 }";
765
650
  case "string":
766
651
  return '""';
767
652
  case "bool":
@@ -856,7 +741,7 @@ export const expressionGenMethods = {
856
741
  case "nil":
857
742
  return `${val} === null`;
858
743
  case "error":
859
- return `typeof ${val} === "string"`;
744
+ return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
860
745
  default:
861
746
  if (this.structNames.has(typeNode.name))
862
747
  return `${val} instanceof ${typeNode.name}`;
@@ -904,4 +789,311 @@ export const expressionGenMethods = {
904
789
  return typeNode.kind;
905
790
  }
906
791
  },
792
+
793
+ // Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
794
+ // Returns the generated JS string, or undefined if not handled.
795
+ _genStdlibCall(ns, fn, expr) {
796
+ const a = () => expr.args.map((e) => this.genExpr(e));
797
+ switch (ns) {
798
+ case "fmt":
799
+ return this._genFmt(fn, a, expr);
800
+ case "strings":
801
+ return this._genStrings(fn, a);
802
+ case "bytes":
803
+ return this._genBytes(fn, a);
804
+ case "strconv":
805
+ return this._genStrconv(fn, a);
806
+ case "sort":
807
+ return this._genSort(fn, a);
808
+ case "math":
809
+ return this._genMath(fn, a);
810
+ case "unicode":
811
+ return this._genUnicode(fn, a);
812
+ case "os":
813
+ return this._genOs(fn, a);
814
+ case "errors":
815
+ return this._genErrors(fn, a);
816
+ case "time":
817
+ return this._genTime(fn, a);
818
+ default:
819
+ return undefined;
820
+ }
821
+ },
822
+
823
+ _genFmt(fn, _a, expr) {
824
+ const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
825
+ switch (fn) {
826
+ case "Sprintf":
827
+ this._usesSprintf = true;
828
+ return `__sprintf(${fmtArgs})`;
829
+ case "Errorf": {
830
+ this._usesSprintf = true;
831
+ this._usesError = true;
832
+ const fmtStr = expr.args[0];
833
+ if (fmtStr?.kind === "BasicLit" && fmtStr.value?.includes("%w")) {
834
+ const lastArg = this.genExpr(expr.args[expr.args.length - 1]);
835
+ return `__error(__sprintf(${fmtArgs}), ${lastArg})`;
836
+ }
837
+ return `__error(__sprintf(${fmtArgs}))`;
838
+ }
839
+ case "Printf":
840
+ case "Print":
841
+ this._usesSprintf = true;
842
+ return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
843
+ case "Println":
844
+ this._usesSprintf = true;
845
+ return `console.log(__sprintf(${fmtArgs}))`;
846
+ default:
847
+ return undefined;
848
+ }
849
+ },
850
+
851
+ _genStrings(fn, a) {
852
+ const args = a();
853
+ switch (fn) {
854
+ case "Contains":
855
+ return `${args[0]}.includes(${args[1]})`;
856
+ case "HasPrefix":
857
+ return `${args[0]}.startsWith(${args[1]})`;
858
+ case "HasSuffix":
859
+ return `${args[0]}.endsWith(${args[1]})`;
860
+ case "Index":
861
+ return `${args[0]}.indexOf(${args[1]})`;
862
+ case "LastIndex":
863
+ return `${args[0]}.lastIndexOf(${args[1]})`;
864
+ case "Count":
865
+ return `${args[0]}.split(${args[1]}).length - 1`;
866
+ case "Repeat":
867
+ return `${args[0]}.repeat(${args[1]})`;
868
+ case "Replace":
869
+ return `${args[0]}.replace(${args[1]}, ${args[2]})`;
870
+ case "ReplaceAll":
871
+ return `${args[0]}.replaceAll(${args[1]}, ${args[2]})`;
872
+ case "ToUpper":
873
+ return `${args[0]}.toUpperCase()`;
874
+ case "ToLower":
875
+ return `${args[0]}.toLowerCase()`;
876
+ case "TrimSpace":
877
+ return `${args[0]}.trim()`;
878
+ case "Trim":
879
+ return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), "")`;
880
+ case "TrimPrefix":
881
+ return `(${args[0]}.startsWith(${args[1]}) ? ${args[0]}.slice(${args[1]}.length) : ${args[0]})`;
882
+ case "TrimSuffix":
883
+ return `(${args[0]}.endsWith(${args[1]}) ? ${args[0]}.slice(0, -${args[1]}.length) : ${args[0]})`;
884
+ case "TrimLeft":
885
+ return `${args[0]}.replace(new RegExp(\`^[\${${args[1]}}]+\`), "")`;
886
+ case "TrimRight":
887
+ return `${args[0]}.replace(new RegExp(\`[\${${args[1]}}]+$\`), "")`;
888
+ case "Split":
889
+ return `${args[0]}.split(${args[1]})`;
890
+ case "Join":
891
+ return `${args[0]}.join(${args[1]})`;
892
+ case "EqualFold":
893
+ return `${args[0]}.toLowerCase() === ${args[1]}.toLowerCase()`;
894
+ default:
895
+ return undefined;
896
+ }
897
+ },
898
+
899
+ _genBytes(fn, a) {
900
+ const __bs = `(b => String.fromCharCode(...b))`;
901
+ const __sb = `(s => [...s].map(c => c.charCodeAt(0)))`;
902
+ const args = a();
903
+ switch (fn) {
904
+ case "Contains":
905
+ return `${__bs}(${args[0]}).includes(${__bs}(${args[1]}))`;
906
+ case "HasPrefix":
907
+ 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]})`;
908
+ case "HasSuffix":
909
+ 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]})`;
910
+ case "Index":
911
+ return `${__bs}(${args[0]}).indexOf(${__bs}(${args[1]}))`;
912
+ case "Count":
913
+ return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).length - 1`;
914
+ case "Repeat":
915
+ return `${__sb}(${__bs}(${args[0]}).repeat(${args[1]}))`;
916
+ case "Replace":
917
+ 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]})`;
918
+ case "ToUpper":
919
+ return `${__sb}(${__bs}(${args[0]}).toUpperCase())`;
920
+ case "ToLower":
921
+ return `${__sb}(${__bs}(${args[0]}).toLowerCase())`;
922
+ case "TrimSpace":
923
+ return `${__sb}(${__bs}(${args[0]}).trim())`;
924
+ case "Trim":
925
+ return `${__sb}(${__bs}(${args[0]}).replace(new RegExp(\`^[\${${args[1]}}]+|[\${${args[1]}}]+$\`, "g"), ""))`;
926
+ case "Equal":
927
+ 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]})`;
928
+ case "Split":
929
+ return `${__bs}(${args[0]}).split(${__bs}(${args[1]})).map(p => ${__sb}(p))`;
930
+ case "Join":
931
+ return `${__sb}(${args[0]}.map(p => ${__bs}(p)).join(${__bs}(${args[1]})))`;
932
+ default:
933
+ return undefined;
934
+ }
935
+ },
936
+
937
+ _genStrconv(fn, a) {
938
+ const args = a();
939
+ switch (fn) {
940
+ case "Itoa":
941
+ return `String(${args[0]})`;
942
+ case "Atoi":
943
+ return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}) | 0, null])`;
944
+ case "FormatBool":
945
+ return `String(${args[0]})`;
946
+ case "FormatInt":
947
+ return `(${args[0]}).toString(${args[1]})`;
948
+ case "FormatFloat":
949
+ return `String(${args[0]})`;
950
+ case "ParseFloat":
951
+ return `(Number.isNaN(Number(${args[0]})) ? [0, "invalid syntax"] : [Number(${args[0]}), null])`;
952
+ case "ParseInt":
953
+ return `(Number.isNaN(parseInt(${args[0]}, ${args[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${args[0]}, ${args[1]} || 10), null])`;
954
+ case "ParseBool":
955
+ return `(${args[0]} === "true" || ${args[0]} === "1" ? [true, null] : ${args[0]} === "false" || ${args[0]} === "0" ? [false, null] : [false, "invalid syntax"])`;
956
+ default:
957
+ return undefined;
958
+ }
959
+ },
960
+
961
+ _genSort(fn, a) {
962
+ const args = a();
963
+ switch (fn) {
964
+ case "Ints":
965
+ case "Float64s":
966
+ return `${args[0]}.sort((a, b) => a - b)`;
967
+ case "Strings":
968
+ return `${args[0]}.sort()`;
969
+ case "Slice":
970
+ case "SliceStable":
971
+ return `${args[0]}.sort((a, b) => ${args[1]}(a, b) ? -1 : ${args[1]}(b, a) ? 1 : 0)`;
972
+ case "SliceIsSorted":
973
+ return `${args[0]}.every((v, i, a) => i === 0 || ${args[1]}(a[i - 1], v))`;
974
+ default:
975
+ return undefined;
976
+ }
977
+ },
978
+
979
+ _genMath(fn, a) {
980
+ const args = a();
981
+ switch (fn) {
982
+ case "Abs":
983
+ return `Math.abs(${args[0]})`;
984
+ case "Floor":
985
+ return `Math.floor(${args[0]})`;
986
+ case "Ceil":
987
+ return `Math.ceil(${args[0]})`;
988
+ case "Round":
989
+ return `Math.round(${args[0]})`;
990
+ case "Sqrt":
991
+ return `Math.sqrt(${args[0]})`;
992
+ case "Cbrt":
993
+ return `Math.cbrt(${args[0]})`;
994
+ case "Pow":
995
+ return `Math.pow(${args[0]}, ${args[1]})`;
996
+ case "Log":
997
+ return `Math.log(${args[0]})`;
998
+ case "Log2":
999
+ return `Math.log2(${args[0]})`;
1000
+ case "Log10":
1001
+ return `Math.log10(${args[0]})`;
1002
+ case "Sin":
1003
+ return `Math.sin(${args[0]})`;
1004
+ case "Cos":
1005
+ return `Math.cos(${args[0]})`;
1006
+ case "Tan":
1007
+ return `Math.tan(${args[0]})`;
1008
+ case "Min":
1009
+ return `Math.min(${args[0]}, ${args[1]})`;
1010
+ case "Max":
1011
+ return `Math.max(${args[0]}, ${args[1]})`;
1012
+ case "Mod":
1013
+ return `${args[0]} % ${args[1]}`;
1014
+ case "Inf":
1015
+ return `(${args[0]} >= 0 ? Infinity : -Infinity)`;
1016
+ case "IsNaN":
1017
+ return `Number.isNaN(${args[0]})`;
1018
+ case "IsInf":
1019
+ return `(${args[1]} > 0 ? ${args[0]} === Infinity : ${args[1]} < 0 ? ${args[0]} === -Infinity : !Number.isFinite(${args[0]}))`;
1020
+ case "NaN":
1021
+ return "NaN";
1022
+ default:
1023
+ return undefined;
1024
+ }
1025
+ },
1026
+
1027
+ _genUnicode(fn, a) {
1028
+ const args = a();
1029
+ const cp = `String.fromCodePoint(${args[0]})`;
1030
+ switch (fn) {
1031
+ case "IsLetter":
1032
+ return `/\\p{L}/u.test(${cp})`;
1033
+ case "IsDigit":
1034
+ return `/\\p{Nd}/u.test(${cp})`;
1035
+ case "IsSpace":
1036
+ return `/\\s/.test(${cp})`;
1037
+ case "IsUpper":
1038
+ return `((__c) => __c === __c.toUpperCase() && /\\p{L}/u.test(__c))(${cp})`;
1039
+ case "IsLower":
1040
+ return `((__c) => __c === __c.toLowerCase() && /\\p{L}/u.test(__c))(${cp})`;
1041
+ case "IsPunct":
1042
+ return `/\\p{P}/u.test(${cp})`;
1043
+ case "IsControl":
1044
+ return `/\\p{Cc}/u.test(${cp})`;
1045
+ case "IsPrint":
1046
+ return `!/\\p{Cc}/u.test(${cp})`;
1047
+ case "IsGraphic":
1048
+ return `!/\\p{Cc}/u.test(${cp})`;
1049
+ case "ToUpper":
1050
+ return `${cp}.toUpperCase().codePointAt(0)`;
1051
+ case "ToLower":
1052
+ return `${cp}.toLowerCase().codePointAt(0)`;
1053
+ default:
1054
+ return undefined;
1055
+ }
1056
+ },
1057
+
1058
+ _genOs(fn, a) {
1059
+ const args = a();
1060
+ switch (fn) {
1061
+ case "Exit":
1062
+ return `process.exit(${args[0]})`;
1063
+ case "Getenv":
1064
+ return `(process.env[${args[0]}] ?? "")`;
1065
+ default:
1066
+ return undefined;
1067
+ }
1068
+ },
1069
+
1070
+ _genErrors(fn, a) {
1071
+ const args = a();
1072
+ switch (fn) {
1073
+ case "New":
1074
+ this._usesError = true;
1075
+ return `__error(${args[0]})`;
1076
+ case "Is":
1077
+ this._usesErrorIs = true;
1078
+ return `__errorIs(${args[0]}, ${args[1]})`;
1079
+ case "Unwrap":
1080
+ return `(${args[0]}?._cause ?? null)`;
1081
+ default:
1082
+ return undefined;
1083
+ }
1084
+ },
1085
+
1086
+ _genTime(fn, a) {
1087
+ const args = a();
1088
+ switch (fn) {
1089
+ case "Now":
1090
+ return "Date.now()";
1091
+ case "Since":
1092
+ return `(Date.now() - ${args[0]})`;
1093
+ case "Sleep":
1094
+ return `await new Promise(r => setTimeout(r, ${args[0]} / 1000000))`;
1095
+ default:
1096
+ return undefined;
1097
+ }
1098
+ },
907
1099
  };