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.
- package/README.md +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- 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
|
+
}
|