gofront 1.8.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 (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -0,0 +1,394 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target both
4
+
5
+ // Copyright 2009 The Go Authors. All rights reserved.
6
+ // Use of this source code is governed by a BSD-style
7
+ // license that can be found in the LICENSE file.
8
+
9
+ // Package path implements utility routines for manipulating slash-separated
10
+ // paths.
11
+ //
12
+ // The path package should only be used for paths separated by forward
13
+ // slashes, such as the paths in URLs. This package does not deal with
14
+ // Windows paths with drive letters or backslashes; to manipulate
15
+ // operating system paths, use the [path/filepath] package.
16
+ package path
17
+
18
+ import (
19
+ "errors"
20
+ "unicode/utf8"
21
+ )
22
+
23
+ // Clean returns the shortest path name equivalent to path
24
+ // by purely lexical processing. It applies the following rules
25
+ // iteratively until no further processing can be done:
26
+ //
27
+ // 1. Replace multiple slashes with a single slash.
28
+ // 2. Eliminate each . path name element (the current directory).
29
+ // 3. Eliminate each inner .. path name element (the parent directory)
30
+ // along with the non-.. element that precedes it.
31
+ // 4. Eliminate .. elements that begin a rooted path:
32
+ // that is, replace "/.." by "/" at the beginning of a path.
33
+ //
34
+ // The returned path ends in a slash only if it is the root "/".
35
+ //
36
+ // If the result of this process is an empty string, Clean
37
+ // returns the string ".".
38
+ //
39
+ // See also Rob Pike, “Lexical File Names in Plan 9 or
40
+ // Getting Dot-Dot Right,”
41
+ // https://9p.io/sys/doc/lexnames.html
42
+ func Clean(path string) string {
43
+ if path == "" {
44
+ return "."
45
+ }
46
+ rooted := path[0] == '/'
47
+ // Elements are collected rather than edited in place: GoFront strings are
48
+ // indexed by UTF-16 code unit, so a byte buffer cannot hold their text.
49
+ var out []string
50
+ for i := 0; i < len(path); {
51
+ j := i
52
+ for j < len(path) && path[j] != '/' {
53
+ j++
54
+ }
55
+ elem := path[i:j]
56
+ i = j + 1
57
+ switch elem {
58
+ case "", ".":
59
+ case "..":
60
+ if len(out) > 0 && out[len(out)-1] != ".." {
61
+ out = out[:len(out)-1]
62
+ } else if !rooted {
63
+ out = append(out, "..")
64
+ }
65
+ default:
66
+ out = append(out, elem)
67
+ }
68
+ }
69
+ res := ""
70
+ for i, elem := range out {
71
+ if i > 0 {
72
+ res += "/"
73
+ }
74
+ res += elem
75
+ }
76
+ if rooted {
77
+ res = "/" + res
78
+ }
79
+ if res == "" {
80
+ return "."
81
+ }
82
+ return res
83
+ }
84
+
85
+ // Split splits path immediately following the final slash,
86
+ // separating it into a directory and file name component.
87
+ // If there is no slash in path, Split returns an empty dir and
88
+ // file set to path.
89
+ // The returned values have the property that path = dir+file.
90
+ func Split(path string) (dir, file string) {
91
+ i := lastSlash(path)
92
+ return path[:i+1], path[i+1:]
93
+ }
94
+
95
+ // Join joins any number of path elements into a single path,
96
+ // separating them with slashes. Empty elements are ignored.
97
+ // The result is Cleaned. However, if the argument list is
98
+ // empty or all its elements are empty, Join returns
99
+ // an empty string.
100
+ func Join(elem ...string) string {
101
+ joined := ""
102
+ for _, e := range elem {
103
+ if joined != "" || e != "" {
104
+ if joined != "" {
105
+ joined += "/"
106
+ }
107
+ joined += e
108
+ }
109
+ }
110
+ if joined == "" {
111
+ return ""
112
+ }
113
+ return Clean(joined)
114
+ }
115
+
116
+ // Ext returns the file name extension used by path.
117
+ // The extension is the suffix beginning at the final dot
118
+ // in the final slash-separated element of path;
119
+ // it is empty if there is no dot.
120
+ func Ext(path string) string {
121
+ for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- {
122
+ if path[i] == '.' {
123
+ return path[i:]
124
+ }
125
+ }
126
+ return ""
127
+ }
128
+
129
+ // Base returns the last element of path.
130
+ // Trailing slashes are removed before extracting the last element.
131
+ // If the path is empty, Base returns ".".
132
+ // If the path consists entirely of slashes, Base returns "/".
133
+ func Base(path string) string {
134
+ if path == "" {
135
+ return "."
136
+ }
137
+ // Strip trailing slashes.
138
+ for len(path) > 0 && path[len(path)-1] == '/' {
139
+ path = path[0 : len(path)-1]
140
+ }
141
+ // Find the last element
142
+ if i := lastSlash(path); i >= 0 {
143
+ path = path[i+1:]
144
+ }
145
+ // If empty now, it had only slashes.
146
+ if path == "" {
147
+ return "/"
148
+ }
149
+ return path
150
+ }
151
+
152
+ // IsAbs reports whether the path is absolute.
153
+ func IsAbs(path string) bool {
154
+ return len(path) > 0 && path[0] == '/'
155
+ }
156
+
157
+ // Dir returns all but the last element of path, typically the path's directory.
158
+ // After dropping the final element using [Split], the path is Cleaned and trailing
159
+ // slashes are removed.
160
+ // If the path is empty, Dir returns ".".
161
+ // If the path consists entirely of slashes followed by non-slash bytes, Dir
162
+ // returns a single slash. In any other case, the returned path does not end in a
163
+ // slash.
164
+ func Dir(path string) string {
165
+ dir, _ := Split(path)
166
+ return Clean(dir)
167
+ }
168
+
169
+ // lastSlash returns the index of the last '/' in s, or -1.
170
+ func lastSlash(s string) int {
171
+ for i := len(s) - 1; i >= 0; i-- {
172
+ if s[i] == '/' {
173
+ return i
174
+ }
175
+ }
176
+ return -1
177
+ }
178
+
179
+ // ErrBadPattern indicates a pattern was malformed.
180
+ var ErrBadPattern = errors.New("syntax error in pattern")
181
+
182
+ // Match reports whether name matches the shell pattern.
183
+ // The pattern syntax is:
184
+ //
185
+ // pattern:
186
+ // { term }
187
+ // term:
188
+ // '*' matches any sequence of non-/ characters
189
+ // '?' matches any single non-/ character
190
+ // '[' [ '^' ] { character-range } ']'
191
+ // character class (must be non-empty)
192
+ // c matches character c (c != '*', '?', '\\', '[')
193
+ // '\\' c matches character c
194
+ //
195
+ // character-range:
196
+ // c matches character c (c != '\\', '-', ']')
197
+ // '\\' c matches character c
198
+ // lo '-' hi matches character c for lo <= c <= hi
199
+ //
200
+ // Match requires pattern to match all of name, not just a substring.
201
+ // The only possible returned error is [ErrBadPattern], when pattern
202
+ // is malformed.
203
+ func Match(pattern, name string) (matched bool, err error) {
204
+ Pattern:
205
+ for len(pattern) > 0 {
206
+ var star bool
207
+ var chunk string
208
+ star, chunk, pattern = scanChunk(pattern)
209
+ if star && chunk == "" {
210
+ // Trailing * matches rest of string unless it has a /.
211
+ return indexSlash(name) < 0, nil
212
+ }
213
+ // Look for match at current position.
214
+ t, ok, cerr := matchChunk(chunk, name)
215
+ // if we're the last chunk, make sure we've exhausted the name
216
+ // otherwise we'll give a false result even if we could still match
217
+ // using the star
218
+ if ok && (len(t) == 0 || len(pattern) > 0) {
219
+ name = t
220
+ continue
221
+ }
222
+ if cerr != nil {
223
+ return false, cerr
224
+ }
225
+ if star {
226
+ // Look for match skipping i+1 bytes.
227
+ // Cannot skip /.
228
+ for i := 0; i < len(name) && name[i] != '/'; i++ {
229
+ t, ok, cerr := matchChunk(chunk, name[i+1:])
230
+ if ok {
231
+ // if we're the last chunk, make sure we exhausted the name
232
+ if len(pattern) == 0 && len(t) > 0 {
233
+ continue
234
+ }
235
+ name = t
236
+ continue Pattern
237
+ }
238
+ if cerr != nil {
239
+ return false, cerr
240
+ }
241
+ }
242
+ }
243
+ // Before returning false with no error,
244
+ // check that the remainder of the pattern is syntactically valid.
245
+ for len(pattern) > 0 {
246
+ _, chunk, pattern = scanChunk(pattern)
247
+ if _, _, cerr := matchChunk(chunk, ""); cerr != nil {
248
+ return false, cerr
249
+ }
250
+ }
251
+ return false, nil
252
+ }
253
+ return len(name) == 0, nil
254
+ }
255
+
256
+ // scanChunk gets the next segment of pattern, which is a non-star string
257
+ // possibly preceded by a star.
258
+ func scanChunk(pattern string) (star bool, chunk, rest string) {
259
+ for len(pattern) > 0 && pattern[0] == '*' {
260
+ pattern = pattern[1:]
261
+ star = true
262
+ }
263
+ inrange := false
264
+ for i := 0; i < len(pattern); i++ {
265
+ switch pattern[i] {
266
+ case '\\':
267
+ // error check handled in matchChunk: bad pattern.
268
+ if i+1 < len(pattern) {
269
+ i++
270
+ }
271
+ case '[':
272
+ inrange = true
273
+ case ']':
274
+ inrange = false
275
+ case '*':
276
+ if !inrange {
277
+ return star, pattern[:i], pattern[i:]
278
+ }
279
+ }
280
+ }
281
+ return star, pattern, ""
282
+ }
283
+
284
+ // matchChunk checks whether chunk matches the beginning of s.
285
+ // If so, it returns the remainder of s (after the match).
286
+ // Chunk is all single-character operators: literals, char classes, and ?.
287
+ func matchChunk(chunk, s string) (rest string, ok bool, err error) {
288
+ // failed records whether the match has failed.
289
+ // After the match fails, the loop continues on processing chunk,
290
+ // checking that the pattern is well-formed but no longer reading s.
291
+ failed := false
292
+ for len(chunk) > 0 {
293
+ failed = failed || len(s) == 0
294
+ switch chunk[0] {
295
+ case '[':
296
+ // character class
297
+ var r rune
298
+ if !failed {
299
+ var n int
300
+ r, n = utf8.DecodeRuneInString(s)
301
+ s = s[n:]
302
+ }
303
+ chunk = chunk[1:]
304
+ // possibly negated
305
+ negated := false
306
+ if len(chunk) > 0 && chunk[0] == '^' {
307
+ negated = true
308
+ chunk = chunk[1:]
309
+ }
310
+ // parse all ranges
311
+ match := false
312
+ nrange := 0
313
+ for {
314
+ if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
315
+ chunk = chunk[1:]
316
+ break
317
+ }
318
+ var lo, hi rune
319
+ if lo, chunk, err = getEsc(chunk); err != nil {
320
+ return "", false, err
321
+ }
322
+ hi = lo
323
+ if chunk[0] == '-' {
324
+ if hi, chunk, err = getEsc(chunk[1:]); err != nil {
325
+ return "", false, err
326
+ }
327
+ }
328
+ match = match || lo <= r && r <= hi
329
+ nrange++
330
+ }
331
+ failed = failed || match == negated
332
+
333
+ case '?':
334
+ if !failed {
335
+ failed = s[0] == '/'
336
+ _, n := utf8.DecodeRuneInString(s)
337
+ s = s[n:]
338
+ }
339
+ chunk = chunk[1:]
340
+
341
+ case '\\':
342
+ chunk = chunk[1:]
343
+ if len(chunk) == 0 {
344
+ return "", false, ErrBadPattern
345
+ }
346
+ fallthrough
347
+
348
+ default:
349
+ if !failed {
350
+ failed = chunk[0] != s[0]
351
+ s = s[1:]
352
+ }
353
+ chunk = chunk[1:]
354
+ }
355
+ }
356
+ if failed {
357
+ return "", false, nil
358
+ }
359
+ return s, true, nil
360
+ }
361
+
362
+ // getEsc gets a possibly-escaped character from chunk, for a character class.
363
+ func getEsc(chunk string) (r rune, nchunk string, err error) {
364
+ if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
365
+ err = ErrBadPattern
366
+ return
367
+ }
368
+ if chunk[0] == '\\' {
369
+ chunk = chunk[1:]
370
+ if len(chunk) == 0 {
371
+ err = ErrBadPattern
372
+ return
373
+ }
374
+ }
375
+ r, n := utf8.DecodeRuneInString(chunk)
376
+ if r == utf8.RuneError && n == 1 {
377
+ err = ErrBadPattern
378
+ }
379
+ nchunk = chunk[n:]
380
+ if len(nchunk) == 0 {
381
+ err = ErrBadPattern
382
+ }
383
+ return
384
+ }
385
+
386
+ // indexSlash returns the index of the first '/' in s, or -1.
387
+ func indexSlash(s string) int {
388
+ for i := 0; i < len(s); i++ {
389
+ if s[i] == '/' {
390
+ return i
391
+ }
392
+ }
393
+ return -1
394
+ }
@@ -0,0 +1,231 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target js
4
+
5
+ // Package regexp is backed by the host's RegExp engine, so patterns use
6
+ // JavaScript syntax and indexes count UTF-16 units. A pattern may start with
7
+ // inline flags such as (?i). It is only available in js packages.
8
+ package regexp
9
+
10
+ import "errors"
11
+
12
+ // Regexp is the representation of a compiled regular expression.
13
+ type Regexp struct {
14
+ src string
15
+ re any
16
+ }
17
+
18
+ // inlineFlags splits a leading "(?flags)" group (flags from "gimsuy") off the
19
+ // pattern.
20
+ func inlineFlags(expr string) (pattern, flags string) {
21
+ if len(expr) < 4 || expr[0] != '(' || expr[1] != '?' {
22
+ return expr, ""
23
+ }
24
+ for i := 2; i < len(expr); i++ {
25
+ switch expr[i] {
26
+ case 'g', 'i', 'm', 's', 'u', 'y':
27
+ case ')':
28
+ if i == 2 {
29
+ return expr, ""
30
+ }
31
+ return expr[i+1:], expr[2:i]
32
+ default:
33
+ return expr, ""
34
+ }
35
+ }
36
+ return expr, ""
37
+ }
38
+
39
+ // Compile parses a regular expression and returns, if successful, a Regexp
40
+ // object that can be used to match against text.
41
+ func Compile(expr string) (re *Regexp, err error) {
42
+ defer func() {
43
+ if r := recover(); r != nil {
44
+ re = nil
45
+ err = errors.New("error parsing regexp: " + errorText(r))
46
+ }
47
+ }()
48
+ pattern, flags := inlineFlags(expr)
49
+ return &Regexp{src: expr, re: RegExp(pattern, flags)}, nil
50
+ }
51
+
52
+ func errorText(r any) string {
53
+ if s, ok := r.(string); ok {
54
+ return s
55
+ }
56
+ if e, ok := r.(error); ok {
57
+ return e.Error()
58
+ }
59
+ return String(r.message).(string)
60
+ }
61
+
62
+ // MustCompile is like Compile but panics if the expression cannot be parsed.
63
+ func MustCompile(expr string) *Regexp {
64
+ re, err := Compile(expr)
65
+ if err != nil {
66
+ panic("regexp: Compile(`" + expr + "`): " + err.Error())
67
+ }
68
+ return re
69
+ }
70
+
71
+ // MatchString reports whether the string s contains any match of the regular
72
+ // expression pattern.
73
+ func MatchString(pattern string, s string) (matched bool, err error) {
74
+ re, err := Compile(pattern)
75
+ if err != nil {
76
+ return false, err
77
+ }
78
+ return re.MatchString(s), nil
79
+ }
80
+
81
+ // QuoteMeta returns a string that escapes all regular expression
82
+ // metacharacters inside the argument text.
83
+ func QuoteMeta(s string) string {
84
+ var b []byte
85
+ for i := 0; i < len(s); i++ {
86
+ switch s[i] {
87
+ case '\\', '.', '+', '*', '?', '(', ')', '|', '[', ']', '{', '}', '^', '$':
88
+ b = append(b, '\\')
89
+ }
90
+ b = append(b, s[i])
91
+ }
92
+ return string(b)
93
+ }
94
+
95
+ // String returns the source text used to compile the regular expression.
96
+ func (re *Regexp) String() string {
97
+ return re.src
98
+ }
99
+
100
+ // global returns a copy of the expression that matches every occurrence.
101
+ func (re *Regexp) global() any {
102
+ flags := re.re.flags.(string)
103
+ if !re.re.flags.includes("g").(bool) {
104
+ flags += "g"
105
+ }
106
+ return RegExp(re.re.source, flags)
107
+ }
108
+
109
+ func text(v any) string {
110
+ if v == nil {
111
+ return ""
112
+ }
113
+ return v.(string)
114
+ }
115
+
116
+ func groups(m any) []string {
117
+ items := m.([]any)
118
+ out := make([]string, len(items))
119
+ for i, v := range items {
120
+ out[i] = text(v)
121
+ }
122
+ return out
123
+ }
124
+
125
+ // matches returns the match arrays of up to n occurrences (all when n < 0).
126
+ func (re *Regexp) matches(s string, n int) []any {
127
+ var out []any
128
+ if n == 0 {
129
+ return out
130
+ }
131
+ all := Array.from(any(s).matchAll(re.global())).([]any)
132
+ for _, m := range all {
133
+ out = append(out, m)
134
+ if n > 0 && len(out) >= n {
135
+ break
136
+ }
137
+ }
138
+ return out
139
+ }
140
+
141
+ // MatchString reports whether the string s contains any match of the regular
142
+ // expression re.
143
+ func (re *Regexp) MatchString(s string) bool {
144
+ return re.re.test(s).(bool)
145
+ }
146
+
147
+ // FindString returns a string holding the text of the leftmost match in s of
148
+ // the regular expression, or "" if there is no match.
149
+ func (re *Regexp) FindString(s string) string {
150
+ m := re.re.exec(s)
151
+ if m == nil {
152
+ return ""
153
+ }
154
+ return text(m.([]any)[0])
155
+ }
156
+
157
+ // FindStringIndex returns a two-element slice of integers defining the
158
+ // location of the leftmost match in s of the regular expression, or nil if
159
+ // there is no match.
160
+ func (re *Regexp) FindStringIndex(s string) []int {
161
+ m := re.re.exec(s)
162
+ if m == nil {
163
+ return nil
164
+ }
165
+ start := m.index.(int)
166
+ return []int{start, start + m.([]any)[0].length.(int)}
167
+ }
168
+
169
+ // FindAllString returns a slice of all successive matches of the expression;
170
+ // a negative n means all matches.
171
+ func (re *Regexp) FindAllString(s string, n int) []string {
172
+ var out []string
173
+ for _, m := range re.matches(s, n) {
174
+ out = append(out, text(m.([]any)[0]))
175
+ }
176
+ return out
177
+ }
178
+
179
+ // FindStringSubmatch returns a slice of strings holding the text of the
180
+ // leftmost match and of its subexpressions.
181
+ func (re *Regexp) FindStringSubmatch(s string) []string {
182
+ m := re.re.exec(s)
183
+ if m == nil {
184
+ return nil
185
+ }
186
+ return groups(m)
187
+ }
188
+
189
+ // FindAllStringSubmatch is the 'All' version of FindStringSubmatch.
190
+ func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string {
191
+ var out [][]string
192
+ for _, m := range re.matches(s, n) {
193
+ out = append(out, groups(m))
194
+ }
195
+ return out
196
+ }
197
+
198
+ // ReplaceAllString returns a copy of src, replacing matches of the Regexp
199
+ // with the replacement string repl. Inside repl, $1 and $& refer to the
200
+ // match as in JavaScript.
201
+ func (re *Regexp) ReplaceAllString(src, repl string) string {
202
+ return any(src).replaceAll(re.global(), repl).(string)
203
+ }
204
+
205
+ // ReplaceAllLiteralString returns a copy of src, replacing matches of the
206
+ // Regexp with the replacement string repl, without expanding $.
207
+ func (re *Regexp) ReplaceAllLiteralString(src, repl string) string {
208
+ return re.ReplaceAllStringFunc(src, func(string) string { return repl })
209
+ }
210
+
211
+ // ReplaceAllStringFunc returns a copy of src in which all matches of the
212
+ // Regexp have been replaced by the return value of function repl applied to
213
+ // the matched substring.
214
+ func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string {
215
+ return any(src).replaceAll(re.global(), func(m string) string { return repl(m) }).(string)
216
+ }
217
+
218
+ // Split slices s into substrings separated by the expression. Text captured
219
+ // by groups is included, as in JavaScript. A non-negative n keeps at most n
220
+ // substrings.
221
+ func (re *Regexp) Split(s string, n int) []string {
222
+ parts := Array.from(any(s).split(re.re)).([]any)
223
+ out := make([]string, 0, len(parts))
224
+ for _, p := range parts {
225
+ if n >= 0 && len(out) >= n {
226
+ break
227
+ }
228
+ out = append(out, text(p))
229
+ }
230
+ return out
231
+ }