gofront 1.6.1 → 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 (132) hide show
  1. package/README.md +45 -49
  2. package/package.json +12 -8
  3. package/src/backend/js/expressions.js +1627 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +508 -102
  6. package/src/backend/js/runtime.js +1997 -106
  7. package/src/backend/js/statements.js +931 -87
  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 +7 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +19 -2
  14. package/src/backend/js/stdlib/json.js +1167 -0
  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 +34 -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 +446 -72
  29. package/src/backend/wasm/emit-builtins.js +320 -13
  30. package/src/backend/wasm/emit-exprs.js +1404 -199
  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 +518 -74
  34. package/src/backend/wasm/encode.js +128 -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/optimize.js +4 -2
  41. package/src/backend/wasm/prescan.js +397 -0
  42. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  43. package/src/backend/wasm/stdlib/core.js +1574 -199
  44. package/src/backend/wasm/stdlib/index.js +3 -5
  45. package/src/backend/wasm/stdlib/io.js +1567 -150
  46. package/src/backend/wasm/stdlib/packages.js +2 -0
  47. package/src/backend/wasm/stdlib/simd.js +499 -0
  48. package/src/backend/wasm/stdlib/time.js +1041 -12
  49. package/src/backend/wasm/types.js +221 -15
  50. package/src/backend/wasm/variadic.js +93 -0
  51. package/src/backend/wasm/wat.js +84 -20
  52. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  53. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  54. package/src/{vendor.js → cli/vendor.js} +89 -15
  55. package/src/compiler.js +107 -269
  56. package/src/dts-parser.js +71 -14
  57. package/src/index.js +15 -169
  58. package/src/{lexer.js → lexer/lexer.js} +102 -55
  59. package/src/{tokens.js → lexer/tokens.js} +1 -0
  60. package/src/link/sourcemap.js +97 -0
  61. package/src/link/stdlib.js +515 -0
  62. package/src/link/wasm.js +294 -0
  63. package/src/lower/bounds.js +394 -0
  64. package/src/lower/boxing.js +1 -6
  65. package/src/lower/captures.js +176 -32
  66. package/src/lower/embedding.js +2 -1
  67. package/src/lower/index.js +26 -18
  68. package/src/lower/json-codecs.js +1398 -0
  69. package/src/lower/ownership.js +22 -1
  70. package/src/lower/range.js +86 -1
  71. package/src/lower/reachability.js +132 -0
  72. package/src/parser/declarations.js +58 -13
  73. package/src/parser/expressions.js +29 -7
  74. package/src/parser/index.js +2 -1
  75. package/src/parser/statements.js +37 -22
  76. package/src/parser/struct-tag.js +144 -0
  77. package/src/{templ-parser.js → parser/templ.js} +138 -15
  78. package/src/parser/types.js +130 -13
  79. package/src/resolver.js +22 -1
  80. package/src/typechecker/assignability.js +215 -11
  81. package/src/typechecker/const-fold.js +356 -0
  82. package/src/typechecker/constants.js +256 -0
  83. package/src/typechecker/expressions.js +960 -99
  84. package/src/typechecker/index.js +491 -72
  85. package/src/typechecker/init-order.js +330 -0
  86. package/src/typechecker/resolve.js +596 -52
  87. package/src/typechecker/statements.js +157 -17
  88. package/src/typechecker/stdlib/core.js +333 -55
  89. package/src/typechecker/stdlib/excluded.js +898 -0
  90. package/src/typechecker/stdlib/extended.js +359 -26
  91. package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
  92. package/src/typechecker/stdlib/simd.js +123 -0
  93. package/src/typechecker/termination.js +1 -1
  94. package/src/typechecker/types.js +165 -26
  95. package/stdlib/bytes/iter.go +192 -0
  96. package/stdlib/bytes/search.go +240 -0
  97. package/stdlib/bytes/split.go +218 -0
  98. package/stdlib/cmp/cmp.go +65 -0
  99. package/stdlib/encoding/json/errors.go +31 -0
  100. package/stdlib/encoding/json/indent.go +71 -0
  101. package/stdlib/encoding/json/number.go +12 -0
  102. package/stdlib/encoding/json/stream.go +173 -0
  103. package/stdlib/errors/overlay.go +12 -0
  104. package/stdlib/hash/hash.go +53 -0
  105. package/stdlib/internal/jsonrt/dec.go +738 -0
  106. package/stdlib/internal/jsonrt/enc.go +366 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +649 -0
  111. package/stdlib/io/io.go +346 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/iter.go +65 -0
  114. package/stdlib/math/extra.go +270 -0
  115. package/stdlib/slices/extra.go +73 -0
  116. package/stdlib/slices/iter.go +113 -0
  117. package/stdlib/sort/sort.go +561 -0
  118. package/stdlib/strconv/quote.go +341 -0
  119. package/stdlib/strings/extra.go +35 -0
  120. package/stdlib/strings/iter.go +122 -0
  121. package/stdlib/sync/extra.go +105 -0
  122. package/stdlib/unicode/unicode.go +112 -0
  123. package/stdlib/unicode/utf8/bytes.go +239 -0
  124. package/src/backend/wasm/const-fold.js +0 -205
  125. package/src/backend/wasm/emit-stdlib.js +0 -13
  126. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  127. /package/src/{colors.js → cli/colors.js} +0 -0
  128. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  129. /package/src/{minifier.js → cli/minifier.js} +0 -0
  130. /package/src/{project-config.js → cli/project-config.js} +0 -0
  131. /package/src/{pwa.js → cli/pwa.js} +0 -0
  132. /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([
@@ -126,17 +126,19 @@ export const WASM_SUPPORTED_STDLIB = new Set([
126
126
  "time",
127
127
  "testing",
128
128
  "gofront/shared",
129
+ "gofront/simd",
129
130
  "sync",
130
131
  "crypto/rand",
131
132
  "crypto/sha256",
132
133
  "encoding/binary",
133
134
  "encoding/hex",
134
135
  "encoding/base64",
136
+ "os",
137
+ "encoding/json",
135
138
  ]);
136
139
 
137
140
  export const STDLIB_PACKAGES = new Set([
138
141
  ...WASM_SUPPORTED_STDLIB,
139
- "os",
140
142
  "gom",
141
143
  "regexp",
142
144
  ]);
@@ -145,6 +147,7 @@ export const STDLIB_PACKAGES = new Set([
145
147
  // member the checker knows about. Members outside the set are rejected in
146
148
  // wasm/both packages with "'strings.Split' is not yet available in wasm packages".
147
149
  export const WASM_STDLIB_MEMBERS = new Map([
150
+ ["os", new Set(["Stdout", "Stderr", "Stdin", "File"])],
148
151
  [
149
152
  "math",
150
153
  new Set([
@@ -176,8 +179,10 @@ export const WASM_STDLIB_MEMBERS = new Map([
176
179
  "Exp",
177
180
  "Exp2",
178
181
  "Log",
182
+ "Log1p",
179
183
  "Log2",
180
184
  "Log10",
185
+ "Expm1",
181
186
  "Round",
182
187
  "Remainder",
183
188
  "Float64bits",
@@ -223,21 +228,56 @@ export const WASM_STDLIB_MEMBERS = new Map([
223
228
  [
224
229
  "bits",
225
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",
226
249
  "LeadingZeros32",
227
250
  "LeadingZeros64",
251
+ "TrailingZeros",
252
+ "TrailingZeros8",
253
+ "TrailingZeros16",
228
254
  "TrailingZeros32",
229
255
  "TrailingZeros64",
256
+ "OnesCount",
257
+ "OnesCount8",
258
+ "OnesCount16",
230
259
  "OnesCount32",
231
260
  "OnesCount64",
261
+ "RotateLeft",
262
+ "RotateLeft8",
263
+ "RotateLeft16",
232
264
  "RotateLeft32",
233
265
  "RotateLeft64",
266
+ "Reverse",
267
+ "Reverse8",
268
+ "Reverse16",
269
+ "Reverse32",
270
+ "Reverse64",
271
+ "ReverseBytes",
234
272
  "ReverseBytes16",
235
273
  "ReverseBytes32",
236
274
  "ReverseBytes64",
275
+ "Len",
276
+ "Len8",
277
+ "Len16",
237
278
  "Len32",
238
279
  "Len64",
239
- "Reverse32",
240
- "Reverse64",
280
+ "UintSize",
241
281
  ]),
242
282
  ],
243
283
  ["errors", new Set(["New", "Is", "Unwrap", "As", "Join"])],
@@ -254,6 +294,15 @@ export const WASM_STDLIB_MEMBERS = new Map([
254
294
  "RuneError",
255
295
  "MaxRune",
256
296
  "UTFMax",
297
+ "RuneSelf",
298
+ "EncodeRune",
299
+ "AppendRune",
300
+ "DecodeRune",
301
+ "DecodeLastRune",
302
+ "RuneCount",
303
+ "FullRune",
304
+ "Valid",
305
+ "RuneStart",
257
306
  ]),
258
307
  ],
259
308
  [
@@ -275,6 +324,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
275
324
  "fmt",
276
325
  new Set([
277
326
  "Sprintf",
327
+ "Sprint",
328
+ "Sprintln",
329
+ "Append",
330
+ "Appendf",
331
+ "Appendln",
278
332
  "Errorf",
279
333
  "Printf",
280
334
  "Println",
@@ -285,6 +339,7 @@ export const WASM_STDLIB_MEMBERS = new Map([
285
339
  "Sscan",
286
340
  "Sscanln",
287
341
  "Sscanf",
342
+ "Stringer",
288
343
  ]),
289
344
  ],
290
345
  [
@@ -334,9 +389,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
334
389
  "TrimRightFunc",
335
390
  "FieldsFunc",
336
391
  "NewReplacer",
392
+ "Replacer",
337
393
  "Builder",
338
394
  "NewReader",
339
395
  "Reader",
396
+ "ToValidUTF8",
340
397
  ]),
341
398
  ],
342
399
  [
@@ -360,6 +417,20 @@ export const WASM_STDLIB_MEMBERS = new Map([
360
417
  "AppendBool",
361
418
  "ErrSyntax",
362
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",
363
434
  ]),
364
435
  ],
365
436
  [
@@ -397,16 +468,7 @@ export const WASM_STDLIB_MEMBERS = new Map([
397
468
  ],
398
469
  [
399
470
  "maps",
400
- new Set([
401
- "Keys",
402
- "Values",
403
- "Clone",
404
- "Copy",
405
- "Equal",
406
- "EqualFunc",
407
- "Delete",
408
- "DeleteFunc",
409
- ]),
471
+ new Set(["Clone", "Copy", "Equal", "EqualFunc", "Delete", "DeleteFunc"]),
410
472
  ],
411
473
  [
412
474
  "unicode",
@@ -478,8 +540,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
478
540
  "Map",
479
541
  "SplitN",
480
542
  "Buffer",
543
+ "NewBuffer",
544
+ "NewBufferString",
481
545
  "NewReader",
482
546
  "Reader",
547
+ "ToValidUTF8",
483
548
  ]),
484
549
  ],
485
550
  [
@@ -519,9 +584,6 @@ export const WASM_STDLIB_MEMBERS = new Map([
519
584
  "ReadWriter",
520
585
  "Closer",
521
586
  "EOF",
522
- "ErrUnexpectedEOF",
523
- "ErrShortWrite",
524
- "ReadFull",
525
587
  "ReadAll",
526
588
  "Discard",
527
589
  "WriteString",
@@ -536,10 +598,12 @@ export const WASM_STDLIB_MEMBERS = new Map([
536
598
  "Now",
537
599
  "Since",
538
600
  "Parse",
601
+ "ParseDuration",
539
602
  "Unix",
540
603
  "UnixMicro",
541
604
  "UnixMilli",
542
605
  "Date",
606
+ "FixedZone",
543
607
  "Duration",
544
608
  "Nanosecond",
545
609
  "Microsecond",
@@ -574,9 +638,12 @@ export const WASM_STDLIB_MEMBERS = new Map([
574
638
  "Friday",
575
639
  "Saturday",
576
640
  "Time",
641
+ "Month",
642
+ "Weekday",
643
+ "Location",
577
644
  ]),
578
645
  ],
579
- ["testing", new Set(["T"])],
646
+ ["testing", new Set(["T", "B", "M", "TB", "BenchmarkResult"])],
580
647
  ["sync", new Set(["Mutex", "RWMutex", "Once", "WaitGroup", "Locker"])],
581
648
  ["sha256", new Set(["Sum256", "New", "Size", "BlockSize"])],
582
649
  [
@@ -622,6 +689,11 @@ export const WASM_STDLIB_MEMBERS = new Map([
622
689
 
623
690
  export const VALID_TARGETS = new Set(["js", "wasm", "both"]);
624
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
+
625
697
  // ── Static operator sets (module-level for reuse) ────────────
626
698
  export const CMP_OPS = new Set(["==", "!=", "<", ">", "<=", ">="]);
627
699
  export const LOG_OPS = new Set(["&&", "||"]);
@@ -666,7 +738,7 @@ export const UNTYPED_BOOL = { kind: "untyped", base: "bool" };
666
738
 
667
739
  // ── Complex types ────────────────────────────────────────────
668
740
  export const COMPLEX128 = { kind: "basic", name: "complex128" };
669
- const COMPLEX64 = { kind: "basic", name: "complex64" };
741
+ export const COMPLEX64 = { kind: "basic", name: "complex64" };
670
742
  export const UNTYPED_COMPLEX = { kind: "untyped", base: "complex128" };
671
743
 
672
744
  export const BASIC_TYPES = {
@@ -747,6 +819,32 @@ const TYPED_ARRAY_CONSTRUCTORS = {
747
819
  rune: "Int32Array",
748
820
  };
749
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
+
750
848
  export function typedArrayConstructorForElem(elemType) {
751
849
  if (!elemType) return null;
752
850
  const name = elemType.kind === "basic" ? elemType.name : null;
@@ -762,12 +860,28 @@ export function isTypedArraySlice(type) {
762
860
 
763
861
  export function isComplex(t) {
764
862
  if (!t) return false;
765
- 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
+ )
766
867
  return true;
767
868
  if (t.kind === "untyped" && t.base === "complex128") return true;
768
869
  if (t.kind === "named") return isComplex(t.underlying);
769
870
  return false;
770
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
+ }
771
885
  export function isComplexOrNumeric(t) {
772
886
  return isNumeric(t) || isComplex(t);
773
887
  }
@@ -839,14 +953,21 @@ export function typeStr(t) {
839
953
  return t.name || "interface{...}";
840
954
  case "namespace":
841
955
  return t.name || "namespace{...}";
842
- case "func":
843
- return `func(${t.params.map(typeStr).join(", ")}) ${typeStr(t.returns[0] ?? VOID)}`;
956
+ case "func": {
957
+ const retStr =
958
+ t.returns.length > 1
959
+ ? `(${t.returns.map(typeStr).join(", ")})`
960
+ : typeStr(t.returns[0] ?? VOID);
961
+ return `func(${t.params.map(typeStr).join(", ")}) ${retStr}`;
962
+ }
844
963
  case "tuple":
845
964
  return `(${t.types.map(typeStr).join(", ")})`;
846
- case "named":
965
+ case "named": {
966
+ const name = displayTypeName(t.name);
847
967
  return t.typeArgs && t.typeArgs.length > 0
848
- ? `${t.name}[${t.typeArgs.map(typeStr).join(", ")}]`
849
- : t.name;
968
+ ? `${name}[${t.typeArgs.map(typeStr).join(", ")}]`
969
+ : name;
970
+ }
850
971
  case "pointer":
851
972
  return `*${typeStr(t.base)}`;
852
973
  case "typeParam":
@@ -858,6 +979,17 @@ export function typeStr(t) {
858
979
  }
859
980
  }
860
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
+
861
993
  // ── Iterator function detection (Go 1.23 range-over-func) ───
862
994
 
863
995
  /**
@@ -887,6 +1019,7 @@ export class Scope {
887
1019
  this.parent = parent;
888
1020
  this.symbols = new Map();
889
1021
  this._consts = new Set(); // names declared as const in this scope
1022
+ this._constExprs = new Map(); // name -> AST initializer expr
890
1023
  this._locals = new Set(); // names declared as local variables (var / :=)
891
1024
  this._used = new Set(); // names referenced in this scope
892
1025
  }
@@ -897,9 +1030,15 @@ export class Scope {
897
1030
  this.symbols.set(name, type);
898
1031
  if (name !== "_") this._locals.add(name);
899
1032
  }
900
- defineConst(name, type) {
1033
+ defineConst(name, type, expr = null) {
901
1034
  this.symbols.set(name, type);
902
1035
  this._consts.add(name);
1036
+ if (expr) this._constExprs.set(name, expr);
1037
+ }
1038
+ lookupConstExpr(name) {
1039
+ if (this._constExprs.has(name)) return this._constExprs.get(name);
1040
+ if (this.parent) return this.parent.lookupConstExpr(name);
1041
+ return null;
903
1042
  }
904
1043
  isConst(name) {
905
1044
  if (this._consts.has(name)) return true;
@@ -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
+ }