gofront 1.7.0 → 1.9.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 (194) hide show
  1. package/README.md +14 -14
  2. package/package.json +10 -38
  3. package/src/backend/js/expressions.js +2060 -389
  4. package/src/backend/js/index.js +586 -174
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +936 -1644
  7. package/src/backend/js/statements.js +1349 -132
  8. package/src/backend/js/stdlib/index.js +51 -94
  9. package/src/backend/js/templ.js +67 -118
  10. package/src/backend/js/type-info.js +436 -0
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +80 -0
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +753 -872
  15. package/src/backend/wasm/emit-builtins.js +914 -237
  16. package/src/backend/wasm/emit-exprs.js +1456 -195
  17. package/src/backend/wasm/emit-maps.js +78 -380
  18. package/src/backend/wasm/emit-stmts.js +1659 -214
  19. package/src/backend/wasm/emit.js +614 -88
  20. package/src/backend/wasm/encode.js +43 -13
  21. package/src/backend/wasm/globals.js +491 -0
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +406 -3195
  24. package/src/backend/wasm/method-expr.js +392 -0
  25. package/src/backend/wasm/module-emitter.js +1615 -0
  26. package/src/backend/wasm/monomorph.js +164 -9
  27. package/src/backend/wasm/natives.js +596 -0
  28. package/src/backend/wasm/prescan.js +249 -0
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -37
  31. package/src/backend/wasm/stdlib/simd.js +293 -28
  32. package/src/backend/wasm/types.js +260 -27
  33. package/src/backend/wasm/variadic.js +93 -0
  34. package/src/backend/wasm/wat.js +47 -20
  35. package/src/{cli-core.js → cli/cli-core.js} +241 -11
  36. package/src/{test-runner.js → cli/test-runner.js} +188 -122
  37. package/src/{vendor.js → cli/vendor.js} +3 -4
  38. package/src/compiler.js +202 -386
  39. package/src/index.js +15 -169
  40. package/src/{lexer.js → lexer/lexer.js} +203 -62
  41. package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
  42. package/src/{tokens.js → lexer/tokens.js} +2 -0
  43. package/src/link/sourcemap.js +97 -0
  44. package/src/link/stdlib.js +737 -0
  45. package/src/link/type-names.js +148 -0
  46. package/src/link/wasm.js +318 -0
  47. package/src/lower/bounds.js +547 -0
  48. package/src/lower/boxing.js +1 -1
  49. package/src/lower/captures.js +267 -64
  50. package/src/lower/embedding.js +32 -3
  51. package/src/lower/errors-as.js +196 -0
  52. package/src/lower/fmt-formatters.js +905 -0
  53. package/src/lower/functions.js +2 -0
  54. package/src/lower/gen-types.js +274 -0
  55. package/src/lower/index.js +33 -19
  56. package/src/lower/json-codecs.js +1379 -0
  57. package/src/lower/ownership.js +47 -8
  58. package/src/lower/range.js +86 -1
  59. package/src/lower/reachability.js +150 -0
  60. package/src/lower/walk.js +12 -0
  61. package/src/parser/declarations.js +83 -15
  62. package/src/parser/expressions.js +110 -18
  63. package/src/parser/index.js +2 -1
  64. package/src/parser/statements.js +38 -22
  65. package/src/{templ-parser.js → parser/templ.js} +179 -17
  66. package/src/parser/types.js +70 -9
  67. package/src/resolver.js +21 -1
  68. package/src/typechecker/assignability.js +291 -19
  69. package/src/typechecker/const-fold.js +399 -0
  70. package/src/typechecker/constants.js +282 -0
  71. package/src/typechecker/expressions.js +1220 -183
  72. package/src/typechecker/index.js +659 -92
  73. package/src/typechecker/init-order.js +426 -0
  74. package/src/typechecker/operand-text.js +40 -0
  75. package/src/typechecker/resolve.js +613 -62
  76. package/src/typechecker/statements.js +212 -20
  77. package/src/typechecker/stdlib/excluded.js +902 -0
  78. package/src/typechecker/stdlib/index.js +38 -0
  79. package/src/typechecker/stdlib/simd.js +65 -15
  80. package/src/typechecker/termination.js +11 -2
  81. package/src/typechecker/types.js +268 -514
  82. package/stdlib/bytes/buffer.go +479 -0
  83. package/stdlib/bytes/bytes.go +1010 -0
  84. package/stdlib/bytes/reader.go +161 -0
  85. package/stdlib/cmp/cmp.go +65 -0
  86. package/stdlib/context/context.go +115 -0
  87. package/stdlib/crypto/rand/rand.go +34 -0
  88. package/stdlib/crypto/sha256/sha256.go +249 -0
  89. package/stdlib/encoding/base64/base64.go +331 -0
  90. package/stdlib/encoding/binary/binary.go +332 -0
  91. package/stdlib/encoding/hex/hex.go +140 -0
  92. package/stdlib/encoding/json/errors.go +31 -0
  93. package/stdlib/encoding/json/indent.go +71 -0
  94. package/stdlib/encoding/json/json.go +62 -0
  95. package/stdlib/encoding/json/number.go +12 -0
  96. package/stdlib/encoding/json/stream.go +173 -0
  97. package/stdlib/errors/errors.go +174 -0
  98. package/stdlib/fmt/errors.go +110 -0
  99. package/stdlib/fmt/format.go +732 -0
  100. package/stdlib/fmt/print.go +1232 -0
  101. package/stdlib/gom/gom.go +293 -0
  102. package/stdlib/hash/hash.go +53 -0
  103. package/stdlib/html/html.go +251 -0
  104. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  105. package/stdlib/internal/jsonrt/dec.go +871 -0
  106. package/stdlib/internal/jsonrt/enc.go +516 -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 +658 -0
  111. package/stdlib/io/io.go +502 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/maps.go +131 -0
  114. package/stdlib/math/bits/bits.go +507 -0
  115. package/stdlib/math/extra.go +272 -0
  116. package/stdlib/math/math.go +439 -0
  117. package/stdlib/math/rand/rand.go +297 -0
  118. package/stdlib/os/os.go +93 -0
  119. package/stdlib/path/filepath/filepath.go +47 -0
  120. package/stdlib/path/path.go +394 -0
  121. package/stdlib/regexp/regexp.go +231 -0
  122. package/stdlib/slices/slices.go +526 -0
  123. package/stdlib/slices/sort.go +209 -0
  124. package/stdlib/slices/zsort_cmp.go +480 -0
  125. package/stdlib/slices/zsort_ordered.go +482 -0
  126. package/stdlib/sort/sort.go +500 -0
  127. package/stdlib/sort/zsort_func.go +480 -0
  128. package/stdlib/sort/zsort_iface.go +480 -0
  129. package/stdlib/strconv/float.go +1699 -0
  130. package/stdlib/strconv/isprint.go +752 -0
  131. package/stdlib/strconv/quote.go +526 -0
  132. package/stdlib/strconv/strconv.go +616 -0
  133. package/stdlib/strings/builder.go +95 -0
  134. package/stdlib/strings/reader.go +163 -0
  135. package/stdlib/strings/replacer.go +102 -0
  136. package/stdlib/strings/strings.go +807 -0
  137. package/stdlib/sync/sync.go +268 -0
  138. package/stdlib/testing/async_js.go +61 -0
  139. package/stdlib/testing/benchmark.go +297 -0
  140. package/stdlib/testing/testing.go +523 -0
  141. package/stdlib/time/duration.go +233 -0
  142. package/stdlib/time/format.go +745 -0
  143. package/stdlib/time/parse.go +790 -0
  144. package/stdlib/time/sleep_js.go +63 -0
  145. package/stdlib/time/time.go +834 -0
  146. package/stdlib/unicode/unicode.go +250 -0
  147. package/stdlib/unicode/utf8/utf8.go +464 -0
  148. package/src/backend/js/stdlib/base64.js +0 -12
  149. package/src/backend/js/stdlib/binary.js +0 -102
  150. package/src/backend/js/stdlib/bits.js +0 -46
  151. package/src/backend/js/stdlib/builder.js +0 -63
  152. package/src/backend/js/stdlib/bytes.js +0 -122
  153. package/src/backend/js/stdlib/errors.js +0 -44
  154. package/src/backend/js/stdlib/fmt.js +0 -178
  155. package/src/backend/js/stdlib/gom.js +0 -155
  156. package/src/backend/js/stdlib/hex.js +0 -17
  157. package/src/backend/js/stdlib/html.js +0 -20
  158. package/src/backend/js/stdlib/io.js +0 -137
  159. package/src/backend/js/stdlib/json.js +0 -972
  160. package/src/backend/js/stdlib/maps.js +0 -45
  161. package/src/backend/js/stdlib/math.js +0 -69
  162. package/src/backend/js/stdlib/os.js +0 -13
  163. package/src/backend/js/stdlib/path.js +0 -48
  164. package/src/backend/js/stdlib/rand.js +0 -32
  165. package/src/backend/js/stdlib/regexp.js +0 -54
  166. package/src/backend/js/stdlib/sha256.js +0 -12
  167. package/src/backend/js/stdlib/shared.js +0 -17
  168. package/src/backend/js/stdlib/simd.js +0 -24
  169. package/src/backend/js/stdlib/slices.js +0 -77
  170. package/src/backend/js/stdlib/sort.js +0 -30
  171. package/src/backend/js/stdlib/strconv.js +0 -74
  172. package/src/backend/js/stdlib/strings.js +0 -222
  173. package/src/backend/js/stdlib/sync.js +0 -13
  174. package/src/backend/js/stdlib/testing.js +0 -34
  175. package/src/backend/js/stdlib/time.js +0 -128
  176. package/src/backend/js/stdlib/unicode.js +0 -25
  177. package/src/backend/js/stdlib/utf8.js +0 -26
  178. package/src/backend/wasm/const-fold.js +0 -205
  179. package/src/backend/wasm/emit-stdlib.js +0 -13
  180. package/src/backend/wasm/stdlib/bytes.js +0 -5039
  181. package/src/backend/wasm/stdlib/core.js +0 -8903
  182. package/src/backend/wasm/stdlib/io.js +0 -2161
  183. package/src/backend/wasm/stdlib/packages.js +0 -2400
  184. package/src/backend/wasm/stdlib/pure.js +0 -720
  185. package/src/backend/wasm/stdlib/time.js +0 -1200
  186. package/src/typechecker/stdlib/core.js +0 -801
  187. package/src/typechecker/stdlib/extended.js +0 -914
  188. package/src/typechecker/stdlib.js +0 -22
  189. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  190. /package/src/{colors.js → cli/colors.js} +0 -0
  191. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  192. /package/src/{minifier.js → cli/minifier.js} +0 -0
  193. /package/src/{project-config.js → cli/project-config.js} +0 -0
  194. /package/src/{pwa.js → cli/pwa.js} +0 -0
@@ -0,0 +1,807 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target both
4
+
5
+ // Package strings implements simple functions to manipulate UTF-8 encoded
6
+ // strings, written in Go over the real package's algorithms. Only Builder
7
+ // is left to the backends.
8
+ //
9
+ // GoFront strings are host strings, so offsets, lengths and slice bounds
10
+ // count UTF-16 code units, not UTF-8 bytes: Index returns a value that
11
+ // s[i:] accepts, and strings with only ASCII characters behave exactly as
12
+ // in Go. Runes are code points, and a lone surrogate reads as RuneError.
13
+ // The search, split, join, replace, case and trim functions keep host
14
+ // fast paths as natives that give the results the Go bodies give.
15
+ package strings
16
+
17
+ import (
18
+ "iter"
19
+ "unicode"
20
+ "unicode/utf8"
21
+ )
22
+
23
+ // isSpace is unicode.IsSpace as a func(rune) bool.
24
+ func isSpace(r rune) bool {
25
+ return unicode.IsSpace(r)
26
+ }
27
+
28
+ func upperRune(r rune) rune { return unicode.ToUpper(r) }
29
+ func lowerRune(r rune) rune { return unicode.ToLower(r) }
30
+
31
+ // validRune reports whether r is a code point a string can hold.
32
+ func validRune(r rune) bool {
33
+ return r >= 0 && r <= 0x10FFFF && (r < 0xD800 || r > 0xDFFF)
34
+ }
35
+
36
+ // Clone returns a fresh copy of s. Strings are immutable values here, so
37
+ // the copy is s itself.
38
+ func Clone(s string) string {
39
+ return s
40
+ }
41
+
42
+ // Compare returns an integer comparing two strings lexicographically.
43
+ // The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
44
+ func Compare(a, b string) int {
45
+ if a == b {
46
+ return 0
47
+ }
48
+ if a < b {
49
+ return -1
50
+ }
51
+ return +1
52
+ }
53
+
54
+ // ── Search ────────────────────────────────────────────────────
55
+
56
+ // Index returns the index of the first instance of substr in s, or -1 if
57
+ // substr is not present in s.
58
+ //
59
+ //gofront:native strings.Index
60
+ func Index(s, substr string) int {
61
+ n := len(substr)
62
+ for i := 0; i+n <= len(s); i++ {
63
+ if s[i:i+n] == substr {
64
+ return i
65
+ }
66
+ }
67
+ return -1
68
+ }
69
+
70
+ // LastIndex returns the index of the last instance of substr in s, or -1 if
71
+ // substr is not present in s.
72
+ //
73
+ //gofront:native strings.LastIndex
74
+ func LastIndex(s, substr string) int {
75
+ n := len(substr)
76
+ for i := len(s) - n; i >= 0; i-- {
77
+ if s[i:i+n] == substr {
78
+ return i
79
+ }
80
+ }
81
+ return -1
82
+ }
83
+
84
+ // IndexByte returns the index of the first instance of c in s, or -1 if c
85
+ // is not present in s.
86
+ //
87
+ //gofront:native strings.IndexByte
88
+ func IndexByte(s string, c byte) int {
89
+ return Index(s, string(rune(c)))
90
+ }
91
+
92
+ // LastIndexByte returns the index of the last instance of c in s, or -1 if
93
+ // c is not present in s.
94
+ //
95
+ //gofront:native strings.LastIndexByte
96
+ func LastIndexByte(s string, c byte) int {
97
+ return LastIndex(s, string(rune(c)))
98
+ }
99
+
100
+ // IndexRune returns the index of the first instance of the Unicode code
101
+ // point r, or -1 if rune is not present in s.
102
+ // If r is utf8.RuneError, it returns the first instance of U+FFFD.
103
+ // If r is not a valid code point, IndexRune returns -1.
104
+ //
105
+ //gofront:native strings.IndexRune
106
+ func IndexRune(s string, r rune) int {
107
+ if !validRune(r) {
108
+ return -1
109
+ }
110
+ return Index(s, string(r))
111
+ }
112
+
113
+ // IndexAny returns the index of the first instance of any Unicode code point
114
+ // from chars in s, or -1 if no Unicode code point from chars is present in s.
115
+ //
116
+ //gofront:native strings.IndexAny
117
+ func IndexAny(s, chars string) int {
118
+ m := -1
119
+ for _, c := range chars {
120
+ if i := IndexRune(s, c); i >= 0 && (m < 0 || i < m) {
121
+ m = i
122
+ }
123
+ }
124
+ return m
125
+ }
126
+
127
+ // LastIndexAny returns the index of the last instance of any Unicode code
128
+ // point from chars in s, or -1 if no Unicode code point from chars is
129
+ // present in s.
130
+ //
131
+ //gofront:native strings.LastIndexAny
132
+ func LastIndexAny(s, chars string) int {
133
+ m := -1
134
+ for _, c := range chars {
135
+ if !validRune(c) {
136
+ continue
137
+ }
138
+ if i := LastIndex(s, string(c)); i > m {
139
+ m = i
140
+ }
141
+ }
142
+ return m
143
+ }
144
+
145
+ // Contains reports whether substr is within s.
146
+ //
147
+ //gofront:native strings.Contains
148
+ func Contains(s, substr string) bool {
149
+ return Index(s, substr) >= 0
150
+ }
151
+
152
+ // ContainsAny reports whether any Unicode code points in chars are within s.
153
+ //
154
+ //gofront:native strings.ContainsAny
155
+ func ContainsAny(s, chars string) bool {
156
+ return IndexAny(s, chars) >= 0
157
+ }
158
+
159
+ // ContainsRune reports whether the Unicode code point r is within s.
160
+ //
161
+ //gofront:native strings.ContainsRune
162
+ func ContainsRune(s string, r rune) bool {
163
+ return IndexRune(s, r) >= 0
164
+ }
165
+
166
+ // ContainsFunc reports whether any Unicode code points in s satisfy f(c).
167
+ func ContainsFunc(s string, f func(rune) bool) bool {
168
+ return IndexFunc(s, f) >= 0
169
+ }
170
+
171
+ // HasPrefix reports whether the string s begins with prefix.
172
+ //
173
+ //gofront:native strings.HasPrefix
174
+ func HasPrefix(s, prefix string) bool {
175
+ return len(s) >= len(prefix) && s[:len(prefix)] == prefix
176
+ }
177
+
178
+ // HasSuffix reports whether the string s ends with suffix.
179
+ //
180
+ //gofront:native strings.HasSuffix
181
+ func HasSuffix(s, suffix string) bool {
182
+ return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
183
+ }
184
+
185
+ // Count counts the number of non-overlapping instances of substr in s.
186
+ // If substr is an empty string, Count returns 1 + the number of Unicode
187
+ // code points in s.
188
+ //
189
+ //gofront:native strings.Count
190
+ func Count(s, substr string) int {
191
+ if len(substr) == 0 {
192
+ return utf8.RuneCountInString(s) + 1
193
+ }
194
+ n := 0
195
+ for {
196
+ i := Index(s, substr)
197
+ if i == -1 {
198
+ return n
199
+ }
200
+ n++
201
+ s = s[i+len(substr):]
202
+ }
203
+ }
204
+
205
+ // ── Split, join, repeat and replace ───────────────────────────
206
+
207
+ // explode splits s into a slice of single-code-point strings, up to n of
208
+ // them (the last holds the rest of s) when n > 0.
209
+ func explode(s string, n int) []string {
210
+ runes := []rune(s)
211
+ l := len(runes)
212
+ if n < 0 || n > l {
213
+ n = l
214
+ }
215
+ a := make([]string, n)
216
+ for i := 0; i < n-1; i++ {
217
+ a[i] = string(runes[i])
218
+ }
219
+ if n > 0 {
220
+ a[n-1] = string(runes[n-1:])
221
+ }
222
+ return a
223
+ }
224
+
225
+ // Generic split: splits after each instance of sep, including sepSave
226
+ // units of sep in the subarrays.
227
+ func genSplit(s, sep string, sepSave, n int) []string {
228
+ if n == 0 {
229
+ return nil
230
+ }
231
+ if sep == "" {
232
+ return explode(s, n)
233
+ }
234
+ if n < 0 {
235
+ n = Count(s, sep) + 1
236
+ }
237
+ if n > len(s)+1 {
238
+ n = len(s) + 1
239
+ }
240
+ a := make([]string, n)
241
+ n--
242
+ i := 0
243
+ for i < n {
244
+ m := Index(s, sep)
245
+ if m < 0 {
246
+ break
247
+ }
248
+ a[i] = s[:m+sepSave]
249
+ s = s[m+len(sep):]
250
+ i++
251
+ }
252
+ a[i] = s
253
+ return a[:i+1]
254
+ }
255
+
256
+ // SplitN slices s into substrings separated by sep and returns a slice of
257
+ // the substrings between those separators.
258
+ //
259
+ // The count determines the number of substrings to return:
260
+ // - n > 0: at most n substrings; the last substring will be the unsplit remainder;
261
+ // - n == 0: the result is nil (zero substrings);
262
+ // - n < 0: all substrings.
263
+ func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }
264
+
265
+ // SplitAfterN slices s into substrings after each instance of sep and
266
+ // returns a slice of those substrings.
267
+ func SplitAfterN(s, sep string, n int) []string {
268
+ return genSplit(s, sep, len(sep), n)
269
+ }
270
+
271
+ // Split slices s into all substrings separated by sep and returns a slice of
272
+ // the substrings between those separators.
273
+ //
274
+ // If s does not contain sep and sep is not empty, Split returns a
275
+ // slice of length 1 whose only element is s.
276
+ //
277
+ // If sep is empty, Split splits after each UTF-8 sequence.
278
+ //
279
+ //gofront:native strings.Split
280
+ func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) }
281
+
282
+ // SplitAfter slices s into all substrings after each instance of sep and
283
+ // returns a slice of those substrings.
284
+ func SplitAfter(s, sep string) []string {
285
+ return genSplit(s, sep, len(sep), -1)
286
+ }
287
+
288
+ // Join concatenates the elements of its first argument to create a single
289
+ // string. The separator string sep is placed between elements in the
290
+ // resulting string.
291
+ //
292
+ //gofront:native strings.Join
293
+ func Join(elems []string, sep string) string {
294
+ switch len(elems) {
295
+ case 0:
296
+ return ""
297
+ case 1:
298
+ return elems[0]
299
+ }
300
+ var b Builder
301
+ b.WriteString(elems[0])
302
+ for _, s := range elems[1:] {
303
+ b.WriteString(sep)
304
+ b.WriteString(s)
305
+ }
306
+ return b.String()
307
+ }
308
+
309
+ // Repeat returns a new string consisting of count copies of the string s.
310
+ //
311
+ // It panics if count is negative.
312
+ //
313
+ //gofront:native strings.Repeat
314
+ func Repeat(s string, count int) string {
315
+ if count < 0 {
316
+ panic("strings: negative Repeat count")
317
+ }
318
+ var b Builder
319
+ for i := 0; i < count; i++ {
320
+ b.WriteString(s)
321
+ }
322
+ return b.String()
323
+ }
324
+
325
+ // Replace returns a copy of the string s with the first n
326
+ // non-overlapping instances of old replaced by new.
327
+ // If old is empty, it matches at the beginning of the string
328
+ // and after each UTF-8 sequence, yielding up to k+1 replacements
329
+ // for a k-rune string.
330
+ // If n < 0, there is no limit on the number of replacements.
331
+ //
332
+ //gofront:native strings.Replace
333
+ func Replace(s, old, new string, n int) string {
334
+ if old == new || n == 0 {
335
+ return s
336
+ }
337
+ if m := Count(s, old); m == 0 {
338
+ return s
339
+ } else if n < 0 || m < n {
340
+ n = m
341
+ }
342
+ var b Builder
343
+ start := 0
344
+ for i := 0; i < n; i++ {
345
+ j := start
346
+ if len(old) == 0 {
347
+ if i > 0 {
348
+ _, wid := utf8.DecodeRuneInString(s[start:])
349
+ j += wid
350
+ }
351
+ } else {
352
+ j += Index(s[start:], old)
353
+ }
354
+ b.WriteString(s[start:j])
355
+ b.WriteString(new)
356
+ start = j + len(old)
357
+ }
358
+ b.WriteString(s[start:])
359
+ return b.String()
360
+ }
361
+
362
+ // ReplaceAll returns a copy of the string s with all
363
+ // non-overlapping instances of old replaced by new.
364
+ // If old is empty, it matches at the beginning of the string
365
+ // and after each UTF-8 sequence, yielding up to k+1 replacements
366
+ // for a k-rune string.
367
+ //
368
+ //gofront:native strings.ReplaceAll
369
+ func ReplaceAll(s, old, new string) string {
370
+ return Replace(s, old, new, -1)
371
+ }
372
+
373
+ // Cut slices s around the first instance of sep,
374
+ // returning the text before and after sep.
375
+ // The found result reports whether sep appears in s.
376
+ // If sep does not appear in s, cut returns s, "", false.
377
+ func Cut(s, sep string) (before, after string, found bool) {
378
+ if i := Index(s, sep); i >= 0 {
379
+ return s[:i], s[i+len(sep):], true
380
+ }
381
+ return s, "", false
382
+ }
383
+
384
+ // CutPrefix returns s without the provided leading prefix string
385
+ // and reports whether it found the prefix.
386
+ // If s doesn't start with prefix, CutPrefix returns s, false.
387
+ // If prefix is the empty string, CutPrefix returns s, true.
388
+ func CutPrefix(s, prefix string) (after string, found bool) {
389
+ if !HasPrefix(s, prefix) {
390
+ return s, false
391
+ }
392
+ return s[len(prefix):], true
393
+ }
394
+
395
+ // CutSuffix returns s without the provided ending suffix string
396
+ // and reports whether it found the suffix.
397
+ // If s doesn't end with suffix, CutSuffix returns s, false.
398
+ // If suffix is the empty string, CutSuffix returns s, true.
399
+ func CutSuffix(s, suffix string) (before string, found bool) {
400
+ if !HasSuffix(s, suffix) {
401
+ return s, false
402
+ }
403
+ return s[:len(s)-len(suffix)], true
404
+ }
405
+
406
+ // Fields splits the string s around each instance of one or more
407
+ // consecutive white space characters, as defined by unicode.IsSpace,
408
+ // returning a slice of substrings of s or an empty slice if s contains only
409
+ // white space.
410
+ //
411
+ //gofront:native strings.Fields
412
+ func Fields(s string) []string {
413
+ return FieldsFunc(s, isSpace)
414
+ }
415
+
416
+ // FieldsFunc splits the string s at each run of Unicode code points c
417
+ // satisfying f(c) and returns an array of slices of s. If all code points
418
+ // in s satisfy f(c) or the string is empty, an empty slice is returned.
419
+ func FieldsFunc(s string, f func(rune) bool) []string {
420
+ fields := make([]string, 0, 8)
421
+ start := -1
422
+ for i := 0; i < len(s); {
423
+ r, size := utf8.DecodeRuneInString(s[i:])
424
+ if f(r) {
425
+ if start >= 0 {
426
+ fields = append(fields, s[start:i])
427
+ start = -1
428
+ }
429
+ } else if start < 0 {
430
+ start = i
431
+ }
432
+ i += size
433
+ }
434
+ if start >= 0 {
435
+ fields = append(fields, s[start:])
436
+ }
437
+ return fields
438
+ }
439
+
440
+ // ── Case mapping, folding and mapping ─────────────────────────
441
+
442
+ // Map returns a copy of the string s with all its characters modified
443
+ // according to the mapping function. If mapping returns a negative value, the
444
+ // character is dropped from the string with no replacement.
445
+ func Map(mapping func(rune) rune, s string) string {
446
+ var b Builder
447
+ for _, c := range s {
448
+ r := mapping(c)
449
+ if r >= 0 {
450
+ b.WriteRune(r)
451
+ }
452
+ }
453
+ return b.String()
454
+ }
455
+
456
+ // ToUpper returns s with all Unicode letters mapped to their upper case.
457
+ //
458
+ //gofront:native strings.ToUpper
459
+ func ToUpper(s string) string { return Map(upperRune, s) }
460
+
461
+ // ToLower returns s with all Unicode letters mapped to their lower case.
462
+ //
463
+ //gofront:native strings.ToLower
464
+ func ToLower(s string) string { return Map(lowerRune, s) }
465
+
466
+ // ToTitle returns a copy of the string s with all Unicode letters mapped to
467
+ // their Unicode title case.
468
+ func ToTitle(s string) string { return Map(unicode.ToTitle, s) }
469
+
470
+ // isSeparator reports whether the rune could mark a word boundary.
471
+ func isSeparator(r rune) bool {
472
+ if r <= 0x7F {
473
+ switch {
474
+ case '0' <= r && r <= '9':
475
+ return false
476
+ case 'a' <= r && r <= 'z':
477
+ return false
478
+ case 'A' <= r && r <= 'Z':
479
+ return false
480
+ case r == '_':
481
+ return false
482
+ }
483
+ return true
484
+ }
485
+ if unicode.IsLetter(r) || unicode.IsDigit(r) {
486
+ return false
487
+ }
488
+ return unicode.IsSpace(r)
489
+ }
490
+
491
+ // Title returns a copy of the string s with all Unicode letters that begin
492
+ // words mapped to their Unicode title case.
493
+ //
494
+ // Deprecated: The rule Title uses for word boundaries does not handle Unicode
495
+ // punctuation properly.
496
+ func Title(s string) string {
497
+ prev := rune(' ')
498
+ return Map(
499
+ func(r rune) rune {
500
+ if isSeparator(prev) {
501
+ prev = r
502
+ return unicode.ToTitle(r)
503
+ }
504
+ prev = r
505
+ return r
506
+ },
507
+ s)
508
+ }
509
+
510
+ // EqualFold reports whether s and t, interpreted as UTF-8 strings,
511
+ // are equal under simple Unicode case-folding, which is a more general
512
+ // form of case-insensitivity.
513
+ func EqualFold(s, t string) bool {
514
+ for len(s) != 0 && len(t) != 0 {
515
+ sr, ssize := utf8.DecodeRuneInString(s)
516
+ tr, tsize := utf8.DecodeRuneInString(t)
517
+ s, t = s[ssize:], t[tsize:]
518
+ if tr == sr {
519
+ continue
520
+ }
521
+ if tr < sr {
522
+ tr, sr = sr, tr
523
+ }
524
+ if tr < 0x80 {
525
+ if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' {
526
+ continue
527
+ }
528
+ return false
529
+ }
530
+ r := unicode.SimpleFold(sr)
531
+ for r != sr && r < tr {
532
+ r = unicode.SimpleFold(r)
533
+ }
534
+ if r == tr {
535
+ continue
536
+ }
537
+ return false
538
+ }
539
+ return len(s) == len(t)
540
+ }
541
+
542
+ // ToValidUTF8 returns a copy of the string s with each run of invalid UTF-8
543
+ // byte sequences replaced by the replacement string, which may be empty.
544
+ // A lone surrogate is the invalid sequence a GoFront string can hold.
545
+ func ToValidUTF8(s, replacement string) string {
546
+ var b Builder
547
+ invalid := false
548
+ for i := 0; i < len(s); {
549
+ _, size := utf8.DecodeRuneInString(s[i:])
550
+ if size == 1 && int(s[i]) >= 0xD800 && int(s[i]) <= 0xDFFF {
551
+ if !invalid {
552
+ invalid = true
553
+ b.WriteString(replacement)
554
+ }
555
+ i += size
556
+ continue
557
+ }
558
+ invalid = false
559
+ b.WriteString(s[i : i+size])
560
+ i += size
561
+ }
562
+ return b.String()
563
+ }
564
+
565
+ // ── Trim ──────────────────────────────────────────────────────
566
+
567
+ // IndexFunc returns the index into s of the first Unicode
568
+ // code point satisfying f(c), or -1 if none do.
569
+ func IndexFunc(s string, f func(rune) bool) int {
570
+ return indexFunc(s, f, true)
571
+ }
572
+
573
+ // LastIndexFunc returns the index into s of the last
574
+ // Unicode code point satisfying f(c), or -1 if none do.
575
+ func LastIndexFunc(s string, f func(rune) bool) int {
576
+ return lastIndexFunc(s, f, true)
577
+ }
578
+
579
+ func indexFunc(s string, f func(rune) bool, truth bool) int {
580
+ for i := 0; i < len(s); {
581
+ r, size := utf8.DecodeRuneInString(s[i:])
582
+ if f(r) == truth {
583
+ return i
584
+ }
585
+ i += size
586
+ }
587
+ return -1
588
+ }
589
+
590
+ func lastIndexFunc(s string, f func(rune) bool, truth bool) int {
591
+ for i := len(s); i > 0; {
592
+ r, size := utf8.DecodeLastRuneInString(s[:i])
593
+ i -= size
594
+ if f(r) == truth {
595
+ return i
596
+ }
597
+ }
598
+ return -1
599
+ }
600
+
601
+ // inCutset reports whether r is one of the code points of cutset.
602
+ func inCutset(cutset string, r rune) bool {
603
+ for _, c := range cutset {
604
+ if c == r {
605
+ return true
606
+ }
607
+ }
608
+ return false
609
+ }
610
+
611
+ // Trim returns a slice of the string s with all leading and
612
+ // trailing Unicode code points contained in cutset removed.
613
+ //
614
+ //gofront:native strings.Trim
615
+ func Trim(s, cutset string) string {
616
+ if s == "" || cutset == "" {
617
+ return s
618
+ }
619
+ in := func(r rune) bool { return inCutset(cutset, r) }
620
+ return TrimFunc(s, in)
621
+ }
622
+
623
+ // TrimLeft returns a slice of the string s with all leading
624
+ // Unicode code points contained in cutset removed.
625
+ //
626
+ // To remove a prefix, use TrimPrefix instead.
627
+ //
628
+ //gofront:native strings.TrimLeft
629
+ func TrimLeft(s, cutset string) string {
630
+ if s == "" || cutset == "" {
631
+ return s
632
+ }
633
+ return TrimLeftFunc(s, func(r rune) bool { return inCutset(cutset, r) })
634
+ }
635
+
636
+ // TrimRight returns a slice of the string s, with all trailing
637
+ // Unicode code points contained in cutset removed.
638
+ //
639
+ // To remove a suffix, use TrimSuffix instead.
640
+ //
641
+ //gofront:native strings.TrimRight
642
+ func TrimRight(s, cutset string) string {
643
+ if s == "" || cutset == "" {
644
+ return s
645
+ }
646
+ return TrimRightFunc(s, func(r rune) bool { return inCutset(cutset, r) })
647
+ }
648
+
649
+ // TrimFunc returns a slice of the string s with all leading
650
+ // and trailing Unicode code points c satisfying f(c) removed.
651
+ func TrimFunc(s string, f func(rune) bool) string {
652
+ return TrimRightFunc(TrimLeftFunc(s, f), f)
653
+ }
654
+
655
+ // TrimLeftFunc returns a slice of the string s with all leading
656
+ // Unicode code points c satisfying f(c) removed.
657
+ func TrimLeftFunc(s string, f func(rune) bool) string {
658
+ i := indexFunc(s, f, false)
659
+ if i == -1 {
660
+ return ""
661
+ }
662
+ return s[i:]
663
+ }
664
+
665
+ // TrimRightFunc returns a slice of the string s with all trailing
666
+ // Unicode code points c satisfying f(c) removed.
667
+ func TrimRightFunc(s string, f func(rune) bool) string {
668
+ i := lastIndexFunc(s, f, false)
669
+ if i >= 0 {
670
+ _, wid := utf8.DecodeRuneInString(s[i:])
671
+ i += wid
672
+ } else {
673
+ i++
674
+ }
675
+ return s[0:i]
676
+ }
677
+
678
+ // TrimSpace returns a slice of the string s, with all leading
679
+ // and trailing white space removed, as defined by Unicode.
680
+ //
681
+ //gofront:native strings.TrimSpace
682
+ func TrimSpace(s string) string { return TrimFunc(s, isSpace) }
683
+
684
+ // TrimPrefix returns s without the provided leading prefix string.
685
+ // If s doesn't start with prefix, s is returned unchanged.
686
+ //
687
+ //gofront:native strings.TrimPrefix
688
+ func TrimPrefix(s, prefix string) string {
689
+ if HasPrefix(s, prefix) {
690
+ return s[len(prefix):]
691
+ }
692
+ return s
693
+ }
694
+
695
+ // TrimSuffix returns s without the provided trailing suffix string.
696
+ // If s doesn't end with suffix, s is returned unchanged.
697
+ //
698
+ //gofront:native strings.TrimSuffix
699
+ func TrimSuffix(s, suffix string) string {
700
+ if HasSuffix(s, suffix) {
701
+ return s[:len(s)-len(suffix)]
702
+ }
703
+ return s
704
+ }
705
+
706
+ // ── Iterators ─────────────────────────────────────────────────
707
+
708
+ // Lines returns an iterator over the newline-terminated lines in the string s.
709
+ // The lines yielded by the iterator include their terminating newlines.
710
+ // If s is empty, the iterator yields no lines at all.
711
+ // If s does not end in a newline, the final yielded line will not end in a newline.
712
+ func Lines(s string) iter.Seq[string] {
713
+ return func(yield func(string) bool) {
714
+ for len(s) > 0 {
715
+ var line string
716
+ if i := IndexByte(s, '\n'); i >= 0 {
717
+ line, s = s[:i+1], s[i+1:]
718
+ } else {
719
+ line, s = s, ""
720
+ }
721
+ if !yield(line) {
722
+ return
723
+ }
724
+ }
725
+ }
726
+ }
727
+
728
+ // explodeSeq returns an iterator over the runes in s.
729
+ func explodeSeq(s string) iter.Seq[string] {
730
+ return func(yield func(string) bool) {
731
+ for _, r := range []rune(s) {
732
+ if !yield(string(r)) {
733
+ return
734
+ }
735
+ }
736
+ }
737
+ }
738
+
739
+ // splitSeq is SplitSeq or SplitAfterSeq, configured by how many
740
+ // units of sep to include in the results (none or all).
741
+ func splitSeq(s, sep string, sepSave int) iter.Seq[string] {
742
+ if len(sep) == 0 {
743
+ return explodeSeq(s)
744
+ }
745
+ return func(yield func(string) bool) {
746
+ for {
747
+ i := Index(s, sep)
748
+ if i < 0 {
749
+ break
750
+ }
751
+ frag := s[:i+sepSave]
752
+ if !yield(frag) {
753
+ return
754
+ }
755
+ s = s[i+len(sep):]
756
+ }
757
+ yield(s)
758
+ }
759
+ }
760
+
761
+ // SplitSeq returns an iterator over substrings of s split by sep.
762
+ // The iterator yields the same strings that would be returned by Split(s, sep),
763
+ // but without constructing the slice.
764
+ func SplitSeq(s, sep string) iter.Seq[string] {
765
+ return splitSeq(s, sep, 0)
766
+ }
767
+
768
+ // SplitAfterSeq returns an iterator over substrings of s split after each instance of sep.
769
+ // The iterator yields the same strings that would be returned by SplitAfter(s, sep),
770
+ // but without constructing the slice.
771
+ func SplitAfterSeq(s, sep string) iter.Seq[string] {
772
+ return splitSeq(s, sep, len(sep))
773
+ }
774
+
775
+ // FieldsSeq returns an iterator over substrings of s split around runs of
776
+ // whitespace characters, as defined by unicode.IsSpace.
777
+ // The iterator yields the same strings that would be returned by Fields(s),
778
+ // but without constructing the slice.
779
+ func FieldsSeq(s string) iter.Seq[string] {
780
+ return FieldsFuncSeq(s, isSpace)
781
+ }
782
+
783
+ // FieldsFuncSeq returns an iterator over substrings of s split around runs of
784
+ // Unicode code points satisfying f(c).
785
+ // The iterator yields the same strings that would be returned by FieldsFunc(s),
786
+ // but without constructing the slice.
787
+ func FieldsFuncSeq(s string, f func(rune) bool) iter.Seq[string] {
788
+ return func(yield func(string) bool) {
789
+ runes := []rune(s)
790
+ start := -1
791
+ for i, r := range runes {
792
+ if f(r) {
793
+ if start >= 0 {
794
+ if !yield(string(runes[start:i])) {
795
+ return
796
+ }
797
+ start = -1
798
+ }
799
+ } else if start < 0 {
800
+ start = i
801
+ }
802
+ }
803
+ if start >= 0 {
804
+ yield(string(runes[start:]))
805
+ }
806
+ }
807
+ }