gofront 1.7.0 → 1.8.0

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.
Files changed (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +10 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +512 -77
  34. package/src/backend/wasm/encode.js +43 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -102,7 +102,7 @@ export const BROWSER_GLOBALS = new Set([
102
102
  "TextDecoder",
103
103
  ]);
104
104
 
105
- // Stdlib packages the WASM backend can emit. Extend as src/backend/wasm/emit-stdlib.js
105
+ // Stdlib packages the WASM backend can emit. Extend as src/backend/wasm/stdlib/
106
106
  // gains support; anything not listed is rejected at import with "not yet available in
107
107
  // wasm packages".
108
108
  export const WASM_SUPPORTED_STDLIB = new Set([
@@ -133,21 +133,21 @@ export const WASM_SUPPORTED_STDLIB = new Set([
133
133
  "encoding/binary",
134
134
  "encoding/hex",
135
135
  "encoding/base64",
136
+ "os",
137
+ "encoding/json",
136
138
  ]);
137
139
 
138
140
  export const STDLIB_PACKAGES = new Set([
139
141
  ...WASM_SUPPORTED_STDLIB,
140
- "os",
141
142
  "gom",
142
143
  "regexp",
143
- "encoding/json",
144
144
  ]);
145
145
 
146
146
  // Per-namespace member subset the WASM backend implements. `null` means every
147
147
  // member the checker knows about. Members outside the set are rejected in
148
148
  // wasm/both packages with "'strings.Split' is not yet available in wasm packages".
149
149
  export const WASM_STDLIB_MEMBERS = new Map([
150
- ["json", new Set()],
150
+ ["os", new Set(["Stdout", "Stderr", "Stdin", "File"])],
151
151
  [
152
152
  "math",
153
153
  new Set([
@@ -179,8 +179,10 @@ export const WASM_STDLIB_MEMBERS = new Map([
179
179
  "Exp",
180
180
  "Exp2",
181
181
  "Log",
182
+ "Log1p",
182
183
  "Log2",
183
184
  "Log10",
185
+ "Expm1",
184
186
  "Round",
185
187
  "Remainder",
186
188
  "Float64bits",
@@ -226,21 +228,56 @@ export const WASM_STDLIB_MEMBERS = new Map([
226
228
  [
227
229
  "bits",
228
230
  new Set([
231
+ "Add",
232
+ "Add32",
233
+ "Add64",
234
+ "Sub",
235
+ "Sub32",
236
+ "Sub64",
237
+ "Mul",
238
+ "Mul32",
239
+ "Mul64",
240
+ "Div",
241
+ "Div32",
242
+ "Div64",
243
+ "Rem",
244
+ "Rem32",
245
+ "Rem64",
246
+ "LeadingZeros",
247
+ "LeadingZeros8",
248
+ "LeadingZeros16",
229
249
  "LeadingZeros32",
230
250
  "LeadingZeros64",
251
+ "TrailingZeros",
252
+ "TrailingZeros8",
253
+ "TrailingZeros16",
231
254
  "TrailingZeros32",
232
255
  "TrailingZeros64",
256
+ "OnesCount",
257
+ "OnesCount8",
258
+ "OnesCount16",
233
259
  "OnesCount32",
234
260
  "OnesCount64",
261
+ "RotateLeft",
262
+ "RotateLeft8",
263
+ "RotateLeft16",
235
264
  "RotateLeft32",
236
265
  "RotateLeft64",
266
+ "Reverse",
267
+ "Reverse8",
268
+ "Reverse16",
269
+ "Reverse32",
270
+ "Reverse64",
271
+ "ReverseBytes",
237
272
  "ReverseBytes16",
238
273
  "ReverseBytes32",
239
274
  "ReverseBytes64",
275
+ "Len",
276
+ "Len8",
277
+ "Len16",
240
278
  "Len32",
241
279
  "Len64",
242
- "Reverse32",
243
- "Reverse64",
280
+ "UintSize",
244
281
  ]),
245
282
  ],
246
283
  ["errors", new Set(["New", "Is", "Unwrap", "As", "Join"])],
@@ -257,6 +294,15 @@ export const WASM_STDLIB_MEMBERS = new Map([
257
294
  "RuneError",
258
295
  "MaxRune",
259
296
  "UTFMax",
297
+ "RuneSelf",
298
+ "EncodeRune",
299
+ "AppendRune",
300
+ "DecodeRune",
301
+ "DecodeLastRune",
302
+ "RuneCount",
303
+ "FullRune",
304
+ "Valid",
305
+ "RuneStart",
260
306
  ]),
261
307
  ],
262
308
  [
@@ -278,6 +324,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
278
324
  "fmt",
279
325
  new Set([
280
326
  "Sprintf",
327
+ "Sprint",
328
+ "Sprintln",
329
+ "Append",
330
+ "Appendf",
331
+ "Appendln",
281
332
  "Errorf",
282
333
  "Printf",
283
334
  "Println",
@@ -288,6 +339,7 @@ export const WASM_STDLIB_MEMBERS = new Map([
288
339
  "Sscan",
289
340
  "Sscanln",
290
341
  "Sscanf",
342
+ "Stringer",
291
343
  ]),
292
344
  ],
293
345
  [
@@ -337,9 +389,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
337
389
  "TrimRightFunc",
338
390
  "FieldsFunc",
339
391
  "NewReplacer",
392
+ "Replacer",
340
393
  "Builder",
341
394
  "NewReader",
342
395
  "Reader",
396
+ "ToValidUTF8",
343
397
  ]),
344
398
  ],
345
399
  [
@@ -363,6 +417,20 @@ export const WASM_STDLIB_MEMBERS = new Map([
363
417
  "AppendBool",
364
418
  "ErrSyntax",
365
419
  "ErrRange",
420
+ "IntSize",
421
+ "NumError",
422
+ "QuoteRune",
423
+ "QuoteToASCII",
424
+ "QuoteRuneToASCII",
425
+ "AppendQuote",
426
+ "AppendQuoteToASCII",
427
+ "AppendQuoteRune",
428
+ "AppendQuoteRuneToASCII",
429
+ "CanBackquote",
430
+ "UnquoteChar",
431
+ "QuotedPrefix",
432
+ "IsPrint",
433
+ "IsGraphic",
366
434
  ]),
367
435
  ],
368
436
  [
@@ -400,16 +468,7 @@ export const WASM_STDLIB_MEMBERS = new Map([
400
468
  ],
401
469
  [
402
470
  "maps",
403
- new Set([
404
- "Keys",
405
- "Values",
406
- "Clone",
407
- "Copy",
408
- "Equal",
409
- "EqualFunc",
410
- "Delete",
411
- "DeleteFunc",
412
- ]),
471
+ new Set(["Clone", "Copy", "Equal", "EqualFunc", "Delete", "DeleteFunc"]),
413
472
  ],
414
473
  [
415
474
  "unicode",
@@ -481,8 +540,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
481
540
  "Map",
482
541
  "SplitN",
483
542
  "Buffer",
543
+ "NewBuffer",
544
+ "NewBufferString",
484
545
  "NewReader",
485
546
  "Reader",
547
+ "ToValidUTF8",
486
548
  ]),
487
549
  ],
488
550
  [
@@ -522,9 +584,6 @@ export const WASM_STDLIB_MEMBERS = new Map([
522
584
  "ReadWriter",
523
585
  "Closer",
524
586
  "EOF",
525
- "ErrUnexpectedEOF",
526
- "ErrShortWrite",
527
- "ReadFull",
528
587
  "ReadAll",
529
588
  "Discard",
530
589
  "WriteString",
@@ -539,10 +598,12 @@ export const WASM_STDLIB_MEMBERS = new Map([
539
598
  "Now",
540
599
  "Since",
541
600
  "Parse",
601
+ "ParseDuration",
542
602
  "Unix",
543
603
  "UnixMicro",
544
604
  "UnixMilli",
545
605
  "Date",
606
+ "FixedZone",
546
607
  "Duration",
547
608
  "Nanosecond",
548
609
  "Microsecond",
@@ -577,9 +638,12 @@ export const WASM_STDLIB_MEMBERS = new Map([
577
638
  "Friday",
578
639
  "Saturday",
579
640
  "Time",
641
+ "Month",
642
+ "Weekday",
643
+ "Location",
580
644
  ]),
581
645
  ],
582
- ["testing", new Set(["T"])],
646
+ ["testing", new Set(["T", "B", "M", "TB", "BenchmarkResult"])],
583
647
  ["sync", new Set(["Mutex", "RWMutex", "Once", "WaitGroup", "Locker"])],
584
648
  ["sha256", new Set(["Sum256", "New", "Size", "BlockSize"])],
585
649
  [
@@ -625,6 +689,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
625
689
 
626
690
  export const VALID_TARGETS = new Set(["js", "wasm", "both"]);
627
691
 
692
+ // Backend-provided implementations for body-less Go-source stdlib functions
693
+ // marked `//gofront:intrinsic <name>`: name -> required signature. New
694
+ // intrinsics need a plan change.
695
+ export const INTRINSICS = new Map([]);
696
+
628
697
  // ── Static operator sets (module-level for reuse) ────────────
629
698
  export const CMP_OPS = new Set(["==", "!=", "<", ">", "<=", ">="]);
630
699
  export const LOG_OPS = new Set(["&&", "||"]);
@@ -669,7 +738,7 @@ export const UNTYPED_BOOL = { kind: "untyped", base: "bool" };
669
738
 
670
739
  // ── Complex types ────────────────────────────────────────────
671
740
  export const COMPLEX128 = { kind: "basic", name: "complex128" };
672
- const COMPLEX64 = { kind: "basic", name: "complex64" };
741
+ export const COMPLEX64 = { kind: "basic", name: "complex64" };
673
742
  export const UNTYPED_COMPLEX = { kind: "untyped", base: "complex128" };
674
743
 
675
744
  export const BASIC_TYPES = {
@@ -750,6 +819,32 @@ const TYPED_ARRAY_CONSTRUCTORS = {
750
819
  rune: "Int32Array",
751
820
  };
752
821
 
822
+ // Element indices of an array or slice literal (Go spec): `i: v` puts v at
823
+ // index i, any other element goes right after the previous one, so
824
+ // `[5]int{1, 3: 4, 5}` fills indices 0, 3 and 4. Array literal keys are
825
+ // resolved by the type checker (`_index`, which handles named constants and
826
+ // constant expressions); otherwise only integer-literal keys are known and
827
+ // another key is treated as a positional element.
828
+ export function compositeLitIndices(elems) {
829
+ const out = [];
830
+ let next = 0;
831
+ for (const e of elems) {
832
+ if (e.kind === "KeyValueExpr" && e._index !== undefined) next = e._index;
833
+ else if (e.kind === "KeyValueExpr" && e.key?.value !== undefined)
834
+ next = Number(e.key.value);
835
+ out.push(next++);
836
+ }
837
+ return out;
838
+ }
839
+
840
+ // Highest element index of an array or slice literal (-1 when empty). A
841
+ // loop, not `Math.max(...)`: spreading a huge literal overflows the stack.
842
+ export function compositeLitMaxIndex(elems) {
843
+ let max = -1;
844
+ for (const i of compositeLitIndices(elems)) if (i > max) max = i;
845
+ return max;
846
+ }
847
+
753
848
  export function typedArrayConstructorForElem(elemType) {
754
849
  if (!elemType) return null;
755
850
  const name = elemType.kind === "basic" ? elemType.name : null;
@@ -765,12 +860,28 @@ export function isTypedArraySlice(type) {
765
860
 
766
861
  export function isComplex(t) {
767
862
  if (!t) return false;
768
- if (t.kind === "basic" && (t.name === "complex128" || t.name === "complex64"))
863
+ if (
864
+ (t.kind === "basic" || t.kind === "TypeName" || t.kind === "Ident") &&
865
+ (t.name === "complex128" || t.name === "complex64")
866
+ )
769
867
  return true;
770
868
  if (t.kind === "untyped" && t.base === "complex128") return true;
771
869
  if (t.kind === "named") return isComplex(t.underlying);
772
870
  return false;
773
871
  }
872
+ export function isComplex64(t) {
873
+ if (!t) return false;
874
+ if (
875
+ (t.kind === "basic" || t.kind === "TypeName" || t.kind === "Ident") &&
876
+ t.name === "complex64"
877
+ )
878
+ return true;
879
+ if (t.kind === "untyped" && t.base === "complex64") return true;
880
+ if (t.kind === "named") return isComplex64(t.underlying);
881
+ if (t.name === "complex64") return true;
882
+ if (t.underlying) return isComplex64(t.underlying);
883
+ return false;
884
+ }
774
885
  export function isComplexOrNumeric(t) {
775
886
  return isNumeric(t) || isComplex(t);
776
887
  }
@@ -851,10 +962,12 @@ export function typeStr(t) {
851
962
  }
852
963
  case "tuple":
853
964
  return `(${t.types.map(typeStr).join(", ")})`;
854
- case "named":
965
+ case "named": {
966
+ const name = displayTypeName(t.name);
855
967
  return t.typeArgs && t.typeArgs.length > 0
856
- ? `${t.name}[${t.typeArgs.map(typeStr).join(", ")}]`
857
- : t.name;
968
+ ? `${name}[${t.typeArgs.map(typeStr).join(", ")}]`
969
+ : name;
970
+ }
858
971
  case "pointer":
859
972
  return `*${typeStr(t.base)}`;
860
973
  case "typeParam":
@@ -866,6 +979,17 @@ export function typeStr(t) {
866
979
  }
867
980
  }
868
981
 
982
+ // Go-source stdlib names are mangled to `Name$$path` (`List$$container$list`);
983
+ // diagnostics show them qualified, as Go does (`list.List`).
984
+ export function displayTypeName(name) {
985
+ const i = typeof name === "string" ? name.indexOf("$$") : -1;
986
+ if (i < 0) return name;
987
+ return `${name
988
+ .slice(i + 2)
989
+ .split("$")
990
+ .at(-1)}.${name.slice(0, i)}`;
991
+ }
992
+
869
993
  // ── Iterator function detection (Go 1.23 range-over-func) ───
870
994
 
871
995
  /**
@@ -0,0 +1,192 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target both
4
+
5
+ // Overlay on the emitter-backed bytes package: the sequence members of
6
+ // Go 1.24 are written in Go over iter.Seq and merged into the bytes
7
+ // namespace. Like Go's, the slices they yield alias the input and have their
8
+ // capacity clipped, so appending to one does not write into the original.
9
+ package bytes
10
+
11
+ import (
12
+ "iter"
13
+ "unicode"
14
+ )
15
+
16
+ const (
17
+ runeError = 0xFFFD
18
+ runeSelf = 0x80
19
+ )
20
+
21
+ // decodeRune unpacks the first UTF-8 encoding in p and returns the rune and
22
+ // its width in bytes, as utf8.DecodeRune does: an invalid or incomplete
23
+ // encoding is (RuneError, 1) and p must not be empty.
24
+ func decodeRune(p []byte) (rune, int) {
25
+ b0 := int(p[0])
26
+ if b0 < runeSelf {
27
+ return rune(b0), 1
28
+ }
29
+ n, lo, mask := 0, 0, 0
30
+ if b0 >= 0xC2 && b0 <= 0xDF {
31
+ n, lo, mask = 2, 0x80, 0x1F
32
+ } else if b0 >= 0xE0 && b0 <= 0xEF {
33
+ n, lo, mask = 3, 0x800, 0x0F
34
+ } else if b0 >= 0xF0 && b0 <= 0xF4 {
35
+ n, lo, mask = 4, 0x10000, 0x07
36
+ } else {
37
+ return runeError, 1
38
+ }
39
+ if len(p) < n {
40
+ return runeError, 1
41
+ }
42
+ r := b0 & mask
43
+ for i := 1; i < n; i++ {
44
+ b := int(p[i])
45
+ if b&0xC0 != 0x80 {
46
+ return runeError, 1
47
+ }
48
+ r = r<<6 | b&0x3F
49
+ }
50
+ if r < lo || r > 0x10FFFF || (r >= 0xD800 && r <= 0xDFFF) {
51
+ return runeError, 1
52
+ }
53
+ return rune(r), n
54
+ }
55
+
56
+ // index returns the byte offset of the first instance of sep in s, or -1.
57
+ // It compares bytes: the emitter's bytes.Index goes through a decoded JS
58
+ // string on the JS backend and so counts characters, not bytes, once s holds
59
+ // multi-byte text. sep must not be empty.
60
+ func index(s, sep []byte) int {
61
+ first := sep[0]
62
+ for i := 0; i+len(sep) <= len(s); i++ {
63
+ if s[i] != first {
64
+ continue
65
+ }
66
+ j := 1
67
+ for j < len(sep) && s[i+j] == sep[j] {
68
+ j++
69
+ }
70
+ if j == len(sep) {
71
+ return i
72
+ }
73
+ }
74
+ return -1
75
+ }
76
+
77
+ // isSpace is unicode.IsSpace as a func(rune) bool (the checker types the
78
+ // unicode predicates over int).
79
+ func isSpace(r rune) bool {
80
+ return unicode.IsSpace(int(r))
81
+ }
82
+
83
+ // Lines returns an iterator over the newline-terminated lines in the byte slice s.
84
+ // The lines yielded by the iterator include their terminating newlines.
85
+ // If s is empty, the iterator yields no lines at all.
86
+ // If s does not end in a newline, the final yielded line will not end in a newline.
87
+ // The iterator yields slices of the original slice s.
88
+ func Lines(s []byte) iter.Seq[[]byte] {
89
+ return func(yield func([]byte) bool) {
90
+ for len(s) > 0 {
91
+ var line []byte
92
+ if i := IndexByte(s, '\n'); i >= 0 {
93
+ line, s = s[:i+1:i+1], s[i+1:]
94
+ } else {
95
+ line, s = s[:len(s):len(s)], nil
96
+ }
97
+ if !yield(line) {
98
+ return
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ // explodeSeq returns an iterator over the UTF-8 encodings of the runes in s.
105
+ func explodeSeq(s []byte) iter.Seq[[]byte] {
106
+ return func(yield func([]byte) bool) {
107
+ for len(s) > 0 {
108
+ _, size := decodeRune(s)
109
+ if !yield(s[:size:size]) {
110
+ return
111
+ }
112
+ s = s[size:]
113
+ }
114
+ }
115
+ }
116
+
117
+ // splitSeq is SplitSeq or SplitAfterSeq, configured by how many
118
+ // bytes of sep to include in the results (none or all).
119
+ func splitSeq(s, sep []byte, sepSave int) iter.Seq[[]byte] {
120
+ if len(sep) == 0 {
121
+ return explodeSeq(s)
122
+ }
123
+ return func(yield func([]byte) bool) {
124
+ for {
125
+ i := index(s, sep)
126
+ if i < 0 {
127
+ break
128
+ }
129
+ frag := s[:i+sepSave]
130
+ if !yield(frag[:len(frag):len(frag)]) {
131
+ return
132
+ }
133
+ s = s[i+len(sep):]
134
+ }
135
+ yield(s[:len(s):len(s)])
136
+ }
137
+ }
138
+
139
+ // SplitSeq returns an iterator over subslices of s split around sep.
140
+ // The iterator yields the same subslices that would be returned by Split(s, sep),
141
+ // but without constructing the slice.
142
+ // It returns a single-use iterator.
143
+ func SplitSeq(s, sep []byte) iter.Seq[[]byte] {
144
+ return splitSeq(s, sep, 0)
145
+ }
146
+
147
+ // SplitAfterSeq returns an iterator over subslices of s split after each instance of sep.
148
+ // The iterator yields the same subslices that would be returned by SplitAfter(s, sep),
149
+ // but without constructing the slice.
150
+ // It returns a single-use iterator.
151
+ func SplitAfterSeq(s, sep []byte) iter.Seq[[]byte] {
152
+ return splitSeq(s, sep, len(sep))
153
+ }
154
+
155
+ // FieldsSeq returns an iterator over subslices of s split around runs of
156
+ // whitespace characters, as defined by unicode.IsSpace.
157
+ // The iterator yields the same subslices that would be returned by Fields(s),
158
+ // but without constructing the slice.
159
+ func FieldsSeq(s []byte) iter.Seq[[]byte] {
160
+ return FieldsFuncSeq(s, isSpace)
161
+ }
162
+
163
+ // FieldsFuncSeq returns an iterator over subslices of s split around runs of
164
+ // Unicode code points satisfying f(c).
165
+ // The iterator yields the same subslices that would be returned by FieldsFunc(s),
166
+ // but without constructing the slice.
167
+ func FieldsFuncSeq(s []byte, f func(rune) bool) iter.Seq[[]byte] {
168
+ return func(yield func([]byte) bool) {
169
+ start := -1
170
+ for i := 0; i < len(s); {
171
+ size := 1
172
+ r := rune(s[i])
173
+ if r >= runeSelf {
174
+ r, size = decodeRune(s[i:])
175
+ }
176
+ if f(r) {
177
+ if start >= 0 {
178
+ if !yield(s[start:i:i]) {
179
+ return
180
+ }
181
+ start = -1
182
+ }
183
+ } else if start < 0 {
184
+ start = i
185
+ }
186
+ i += size
187
+ }
188
+ if start >= 0 {
189
+ yield(s[start:len(s):len(s)])
190
+ }
191
+ }
192
+ }