goscript 0.0.28 → 0.0.30

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 (74) hide show
  1. package/compiler/analysis.go +41 -2
  2. package/compiler/compiler.go +8 -1
  3. package/compiler/expr-call.go +87 -15
  4. package/compiler/expr-selector.go +100 -0
  5. package/compiler/spec-value.go +79 -8
  6. package/compiler/spec.go +236 -0
  7. package/dist/gs/builtin/builtin.d.ts +1 -0
  8. package/dist/gs/builtin/builtin.js +11 -0
  9. package/dist/gs/builtin/builtin.js.map +1 -1
  10. package/dist/gs/internal/oserror/errors.d.ts +6 -0
  11. package/dist/gs/internal/oserror/errors.js +7 -0
  12. package/dist/gs/internal/oserror/errors.js.map +1 -0
  13. package/dist/gs/internal/oserror/index.d.ts +1 -0
  14. package/dist/gs/internal/oserror/index.js +2 -0
  15. package/dist/gs/internal/oserror/index.js.map +1 -0
  16. package/dist/gs/io/fs/format.d.ts +3 -0
  17. package/dist/gs/io/fs/format.js +56 -0
  18. package/dist/gs/io/fs/format.js.map +1 -0
  19. package/dist/gs/io/fs/fs.d.ts +79 -0
  20. package/dist/gs/io/fs/fs.js +200 -0
  21. package/dist/gs/io/fs/fs.js.map +1 -0
  22. package/dist/gs/io/fs/glob.d.ts +10 -0
  23. package/dist/gs/io/fs/glob.js +141 -0
  24. package/dist/gs/io/fs/glob.js.map +1 -0
  25. package/dist/gs/io/fs/index.d.ts +8 -0
  26. package/dist/gs/io/fs/index.js +9 -0
  27. package/dist/gs/io/fs/index.js.map +1 -0
  28. package/dist/gs/io/fs/readdir.d.ts +7 -0
  29. package/dist/gs/io/fs/readdir.js +152 -0
  30. package/dist/gs/io/fs/readdir.js.map +1 -0
  31. package/dist/gs/io/fs/readfile.d.ts +6 -0
  32. package/dist/gs/io/fs/readfile.js +118 -0
  33. package/dist/gs/io/fs/readfile.js.map +1 -0
  34. package/dist/gs/io/fs/stat.d.ts +6 -0
  35. package/dist/gs/io/fs/stat.js +87 -0
  36. package/dist/gs/io/fs/stat.js.map +1 -0
  37. package/dist/gs/io/fs/sub.d.ts +6 -0
  38. package/dist/gs/io/fs/sub.js +172 -0
  39. package/dist/gs/io/fs/sub.js.map +1 -0
  40. package/dist/gs/io/fs/walk.d.ts +7 -0
  41. package/dist/gs/io/fs/walk.js +76 -0
  42. package/dist/gs/io/fs/walk.js.map +1 -0
  43. package/dist/gs/path/index.d.ts +2 -0
  44. package/dist/gs/path/index.js +3 -0
  45. package/dist/gs/path/index.js.map +1 -0
  46. package/dist/gs/path/match.d.ts +6 -0
  47. package/dist/gs/path/match.js +281 -0
  48. package/dist/gs/path/match.js.map +1 -0
  49. package/dist/gs/path/path.d.ts +7 -0
  50. package/dist/gs/path/path.js +256 -0
  51. package/dist/gs/path/path.js.map +1 -0
  52. package/dist/gs/time/time.d.ts +11 -2
  53. package/dist/gs/time/time.js +337 -12
  54. package/dist/gs/time/time.js.map +1 -1
  55. package/gs/builtin/builtin.ts +13 -0
  56. package/gs/internal/oserror/errors.ts +14 -0
  57. package/gs/internal/oserror/index.ts +1 -0
  58. package/gs/io/fs/format.ts +65 -0
  59. package/gs/io/fs/fs.ts +359 -0
  60. package/gs/io/fs/glob.ts +167 -0
  61. package/gs/io/fs/godoc.txt +35 -0
  62. package/gs/io/fs/index.ts +8 -0
  63. package/gs/io/fs/readdir.ts +126 -0
  64. package/gs/io/fs/readfile.ts +77 -0
  65. package/gs/io/fs/stat.ts +38 -0
  66. package/gs/io/fs/sub.ts +208 -0
  67. package/gs/io/fs/walk.ts +89 -0
  68. package/gs/path/index.ts +2 -0
  69. package/gs/path/match.ts +307 -0
  70. package/gs/path/path.ts +301 -0
  71. package/gs/strings/reader.test.ts +0 -1
  72. package/gs/time/time.ts +325 -12
  73. package/package.json +1 -1
  74. package/gs/time/godoc.md +0 -116
package/gs/io/fs/fs.ts ADDED
@@ -0,0 +1,359 @@
1
+ import * as $ from "@goscript/builtin/builtin.js";
2
+
3
+ import * as time from "@goscript/time/index.js"
4
+
5
+ import * as utf8 from "@goscript/unicode/utf8/index.js"
6
+
7
+ import * as oserror from "@goscript/internal/oserror/index.js"
8
+
9
+ export type FS = null | {
10
+ // Open opens the named file.
11
+ // [File.Close] must be called to release any associated resources.
12
+ //
13
+ // When Open returns an error, it should be of type *PathError
14
+ // with the Op field set to "open", the Path field set to name,
15
+ // and the Err field describing the problem.
16
+ //
17
+ // Open should reject attempts to open names that do not satisfy
18
+ // ValidPath(name), returning a *PathError with Err set to
19
+ // ErrInvalid or ErrNotExist.
20
+ //
21
+ // Path names passed to open are UTF-8-encoded,
22
+ // unrooted, slash-separated sequences of path elements, like "x/y/z".
23
+ // Path names must not contain an element that is "." or ".." or the empty string,
24
+ // except for the special case that the name "." may be used for the root directory.
25
+ // Paths must not start or end with a slash: "/x" and "x/" are invalid.
26
+ //
27
+ // Note that paths are slash-separated on all systems, even Windows.
28
+ // Paths containing other characters such as backslash and colon
29
+ // are accepted as valid, but those characters must never be
30
+ // interpreted by an [FS] implementation as path element separators.
31
+ Open(name: string): [File, $.GoError]
32
+ }
33
+
34
+ $.registerInterfaceType(
35
+ 'FS',
36
+ null, // Zero value for interface is null
37
+ [{ name: "Open", args: [{ name: "name", type: { kind: $.TypeKind.Basic, name: "string" } }], returns: [{ type: "File" }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }]
38
+ );
39
+
40
+ // ValidPath reports whether the given path name
41
+ // is valid for use in a call to Open.
42
+ //
43
+ // Path names passed to open are UTF-8-encoded,
44
+ // unrooted, slash-separated sequences of path elements, like "x/y/z".
45
+ // Path names must not contain an element that is "." or ".." or the empty string,
46
+ // except for the special case that the name "." may be used for the root directory.
47
+ // Paths must not start or end with a slash: "/x" and "x/" are invalid.
48
+ //
49
+ // Note that paths are slash-separated on all systems, even Windows.
50
+ // Paths containing other characters such as backslash and colon
51
+ // are accepted as valid, but those characters must never be
52
+ // interpreted by an [FS] implementation as path element separators.
53
+ export function ValidPath(name: string): boolean {
54
+ if (!utf8.ValidString(name)) {
55
+ return false
56
+ }
57
+
58
+ // special case
59
+ if (name == ".") {
60
+ // special case
61
+ return true
62
+ }
63
+
64
+ // Iterate over elements in name, checking each.
65
+
66
+ // reached clean ending
67
+ for (; ; ) {
68
+ let i = 0
69
+ for (; i < $.len(name) && $.indexString(name, i) != 47; ) {
70
+ i++
71
+ }
72
+ let elem = $.sliceString(name, undefined, i)
73
+ if (elem == "" || elem == "." || elem == "..") {
74
+ return false
75
+ }
76
+
77
+ // reached clean ending
78
+ if (i == $.len(name)) {
79
+ return true
80
+ }
81
+ name = $.sliceString(name, i + 1, undefined)
82
+ }
83
+ }
84
+
85
+ export type File = null | {
86
+ Close(): $.GoError
87
+ Read(_p0: Uint8Array): [number, $.GoError]
88
+ Stat(): [FileInfo, $.GoError]
89
+ }
90
+
91
+ $.registerInterfaceType(
92
+ 'File',
93
+ null, // Zero value for interface is null
94
+ [{ name: "Close", args: [], returns: [{ type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }, { name: "Read", args: [{ name: "", type: { kind: $.TypeKind.Slice, elemType: { kind: $.TypeKind.Basic, name: "number" } } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }, { name: "Stat", args: [], returns: [{ type: "FileInfo" }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }]
95
+ );
96
+
97
+ export type DirEntry = null | {
98
+ // Info returns the FileInfo for the file or subdirectory described by the entry.
99
+ // The returned FileInfo may be from the time of the original directory read
100
+ // or from the time of the call to Info. If the file has been removed or renamed
101
+ // since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist).
102
+ // If the entry denotes a symbolic link, Info reports the information about the link itself,
103
+ // not the link's target.
104
+ Info(): [FileInfo, $.GoError]
105
+ // IsDir reports whether the entry describes a directory.
106
+ IsDir(): boolean
107
+ // Name returns the name of the file (or subdirectory) described by the entry.
108
+ // This name is only the final element of the path (the base name), not the entire path.
109
+ // For example, Name would return "hello.go" not "home/gopher/hello.go".
110
+ Name(): string
111
+ // Type returns the type bits for the entry.
112
+ // The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method.
113
+ Type(): FileMode
114
+ }
115
+
116
+ $.registerInterfaceType(
117
+ 'DirEntry',
118
+ null, // Zero value for interface is null
119
+ [{ name: "Info", args: [], returns: [{ type: "FileInfo" }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }, { name: "IsDir", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Name", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "string" } }] }, { name: "Type", args: [], returns: [{ type: "FileMode" }] }]
120
+ );
121
+
122
+ export type ReadDirFile = null | {
123
+ // ReadDir reads the contents of the directory and returns
124
+ // a slice of up to n DirEntry values in directory order.
125
+ // Subsequent calls on the same file will yield further DirEntry values.
126
+ //
127
+ // If n > 0, ReadDir returns at most n DirEntry structures.
128
+ // In this case, if ReadDir returns an empty slice, it will return
129
+ // a non-nil error explaining why.
130
+ // At the end of a directory, the error is io.EOF.
131
+ // (ReadDir must return io.EOF itself, not an error wrapping io.EOF.)
132
+ //
133
+ // If n <= 0, ReadDir returns all the DirEntry values from the directory
134
+ // in a single slice. In this case, if ReadDir succeeds (reads all the way
135
+ // to the end of the directory), it returns the slice and a nil error.
136
+ // If it encounters an error before the end of the directory,
137
+ // ReadDir returns the DirEntry list read until that point and a non-nil error.
138
+ ReadDir(n: number): [$.Slice<DirEntry>, $.GoError]
139
+ } & File
140
+
141
+ $.registerInterfaceType(
142
+ 'ReadDirFile',
143
+ null, // Zero value for interface is null
144
+ [{ name: "ReadDir", args: [{ name: "n", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Slice, elemType: "DirEntry" } }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }]
145
+ );
146
+
147
+ // "invalid argument"
148
+ export let ErrInvalid: $.GoError = errInvalid()
149
+
150
+ // "permission denied"
151
+ export let ErrPermission: $.GoError = errPermission()
152
+
153
+ // "file already exists"
154
+ export let ErrExist: $.GoError = errExist()
155
+
156
+ // "file does not exist"
157
+ export let ErrNotExist: $.GoError = errNotExist()
158
+
159
+ // "file already closed"
160
+ export let ErrClosed: $.GoError = errClosed()
161
+
162
+ export function errInvalid(): $.GoError {
163
+ return oserror.ErrInvalid
164
+ }
165
+
166
+ export function errPermission(): $.GoError {
167
+ return oserror.ErrPermission
168
+ }
169
+
170
+ export function errExist(): $.GoError {
171
+ return oserror.ErrExist
172
+ }
173
+
174
+ export function errNotExist(): $.GoError {
175
+ return oserror.ErrNotExist
176
+ }
177
+
178
+ export function errClosed(): $.GoError {
179
+ return oserror.ErrClosed
180
+ }
181
+
182
+ export type FileInfo = null | {
183
+ // abbreviation for Mode().IsDir()
184
+ IsDir(): boolean
185
+ // modification time
186
+ ModTime(): time.Time
187
+ // file mode bits
188
+ Mode(): FileMode
189
+ // base name of the file
190
+ Name(): string
191
+ // length in bytes for regular files; system-dependent for others
192
+ Size(): number
193
+ // underlying data source (can return nil)
194
+ Sys(): null | any
195
+ }
196
+
197
+ $.registerInterfaceType(
198
+ 'FileInfo',
199
+ null, // Zero value for interface is null
200
+ [{ name: "IsDir", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "ModTime", args: [], returns: [{ type: "Time" }] }, { name: "Mode", args: [], returns: [{ type: "FileMode" }] }, { name: "Name", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "string" } }] }, { name: "Size", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Sys", args: [], returns: [{ type: { kind: $.TypeKind.Interface, methods: [] } }] }]
201
+ );
202
+
203
+ export type FileMode = number;
204
+
205
+ // The single letters are the abbreviations
206
+ // used by the String method's formatting.
207
+ // d: is a directory
208
+ export let ModeDir: FileMode = 2147483648
209
+
210
+ // a: append-only
211
+ export let ModeAppend: FileMode = 0
212
+
213
+ // l: exclusive use
214
+ export let ModeExclusive: FileMode = 0
215
+
216
+ // T: temporary file; Plan 9 only
217
+ export let ModeTemporary: FileMode = 0
218
+
219
+ // L: symbolic link
220
+ export let ModeSymlink: FileMode = 0
221
+
222
+ // D: device file
223
+ export let ModeDevice: FileMode = 0
224
+
225
+ // p: named pipe (FIFO)
226
+ export let ModeNamedPipe: FileMode = 0
227
+
228
+ // S: Unix domain socket
229
+ export let ModeSocket: FileMode = 0
230
+
231
+ // u: setuid
232
+ export let ModeSetuid: FileMode = 0
233
+
234
+ // g: setgid
235
+ export let ModeSetgid: FileMode = 0
236
+
237
+ // c: Unix character device, when ModeDevice is set
238
+ export let ModeCharDevice: FileMode = 0
239
+
240
+ // t: sticky
241
+ export let ModeSticky: FileMode = 0
242
+
243
+ // ?: non-regular file; nothing else is known about this file
244
+ export let ModeIrregular: FileMode = 0
245
+
246
+ // Mask for the type bits. For regular files, none will be set.
247
+ export let ModeType: FileMode = ((((((2147483648 | 134217728) | 33554432) | 16777216) | 67108864) | 2097152) | 524288)
248
+
249
+ // Unix permission bits
250
+ export let ModePerm: FileMode = 0o777
251
+
252
+ // FileMode methods
253
+ export function fileModeString(mode: FileMode): string {
254
+ const buf: string[] = []
255
+
256
+ // File type
257
+ if (mode & ModeDir) buf.push("d")
258
+ else if (mode & ModeSymlink) buf.push("L")
259
+ else if (mode & ModeDevice) buf.push("D")
260
+ else if (mode & ModeNamedPipe) buf.push("p")
261
+ else if (mode & ModeSocket) buf.push("S")
262
+ else if (mode & ModeCharDevice) buf.push("c")
263
+ else if (mode & ModeIrregular) buf.push("?")
264
+ else buf.push("-")
265
+
266
+ // Permission bits
267
+ const perm = mode & ModePerm
268
+ buf.push((perm & 0o400) ? "r" : "-")
269
+ buf.push((perm & 0o200) ? "w" : "-")
270
+ buf.push((perm & 0o100) ? "x" : "-")
271
+ buf.push((perm & 0o040) ? "r" : "-")
272
+ buf.push((perm & 0o020) ? "w" : "-")
273
+ buf.push((perm & 0o010) ? "x" : "-")
274
+ buf.push((perm & 0o004) ? "r" : "-")
275
+ buf.push((perm & 0o002) ? "w" : "-")
276
+ buf.push((perm & 0o001) ? "x" : "-")
277
+
278
+ return buf.join("")
279
+ }
280
+
281
+ export function fileModeType(mode: FileMode): FileMode {
282
+ return mode & ModeType
283
+ }
284
+
285
+ export class PathError {
286
+ public get Op(): string {
287
+ return this._fields.Op.value
288
+ }
289
+ public set Op(value: string) {
290
+ this._fields.Op.value = value
291
+ }
292
+
293
+ public get Path(): string {
294
+ return this._fields.Path.value
295
+ }
296
+ public set Path(value: string) {
297
+ this._fields.Path.value = value
298
+ }
299
+
300
+ public get Err(): $.GoError {
301
+ return this._fields.Err.value
302
+ }
303
+ public set Err(value: $.GoError) {
304
+ this._fields.Err.value = value
305
+ }
306
+
307
+ public _fields: {
308
+ Op: $.VarRef<string>;
309
+ Path: $.VarRef<string>;
310
+ Err: $.VarRef<$.GoError>;
311
+ }
312
+
313
+ constructor(init?: Partial<{Err?: $.GoError, Op?: string, Path?: string}>) {
314
+ this._fields = {
315
+ Op: $.varRef(init?.Op ?? ""),
316
+ Path: $.varRef(init?.Path ?? ""),
317
+ Err: $.varRef(init?.Err ?? null)
318
+ }
319
+ }
320
+
321
+ public clone(): PathError {
322
+ const cloned = new PathError()
323
+ cloned._fields = {
324
+ Op: $.varRef(this._fields.Op.value),
325
+ Path: $.varRef(this._fields.Path.value),
326
+ Err: $.varRef(this._fields.Err.value)
327
+ }
328
+ return cloned
329
+ }
330
+
331
+ public Error(): string {
332
+ const e = this
333
+ return e!.Op + " " + e!.Path + ": " + e!.Err!.Error()
334
+ }
335
+
336
+ public Unwrap(): $.GoError {
337
+ const e = this
338
+ return e!.Err
339
+ }
340
+
341
+ // Timeout reports whether this error represents a timeout.
342
+ public Timeout(): boolean {
343
+ const e = this
344
+ let { value: t, ok: ok } = $.typeAssert<null | {
345
+ Timeout(): boolean
346
+ }>(e!.Err, {kind: $.TypeKind.Interface, methods: [{ name: 'Timeout', args: [], returns: [{ type: {kind: $.TypeKind.Basic, name: 'boolean'} }] }]})
347
+ return ok && t!.Timeout()
348
+ }
349
+
350
+ // Register this type with the runtime type system
351
+ static __typeInfo = $.registerStructType(
352
+ 'PathError',
353
+ new PathError(),
354
+ [{ name: "Error", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "string" } }] }, { name: "Unwrap", args: [], returns: [{ type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }, { name: "Timeout", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }],
355
+ PathError,
356
+ {"Op": { kind: $.TypeKind.Basic, name: "string" }, "Path": { kind: $.TypeKind.Basic, name: "string" }, "Err": { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] }}
357
+ );
358
+ }
359
+
@@ -0,0 +1,167 @@
1
+ import * as $ from "@goscript/builtin/builtin.js";
2
+ import { Stat } from "./stat.js";
3
+ import { ReadDir } from "./readdir.js";
4
+ import { FS } from "./fs.js";
5
+
6
+ import * as path from "@goscript/path/index.js"
7
+
8
+ export type GlobFS = null | {
9
+ // Glob returns the names of all files matching pattern,
10
+ // providing an implementation of the top-level
11
+ // Glob function.
12
+ Glob(pattern: string): [$.Slice<string>, $.GoError]
13
+ } & FS
14
+
15
+ $.registerInterfaceType(
16
+ 'GlobFS',
17
+ null, // Zero value for interface is null
18
+ [{ name: "Glob", args: [{ name: "pattern", type: { kind: $.TypeKind.Basic, name: "string" } }], returns: [{ type: { kind: $.TypeKind.Slice, elemType: { kind: $.TypeKind.Basic, name: "string" } } }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }]
19
+ );
20
+
21
+ // Glob returns the names of all files matching pattern or nil
22
+ // if there is no matching file. The syntax of patterns is the same
23
+ // as in [path.Match]. The pattern may describe hierarchical names such as
24
+ // usr/*/bin/ed.
25
+ //
26
+ // Glob ignores file system errors such as I/O errors reading directories.
27
+ // The only possible returned error is [path.ErrBadPattern], reporting that
28
+ // the pattern is malformed.
29
+ //
30
+ // If fs implements [GlobFS], Glob calls fs.Glob.
31
+ // Otherwise, Glob uses [ReadDir] to traverse the directory tree
32
+ // and look for matches for the pattern.
33
+ export function Glob(fsys: FS, pattern: string): [$.Slice<string>, $.GoError] {
34
+ let matches: $.Slice<string> = null
35
+ let err: $.GoError = null
36
+ {
37
+ return globWithLimit(fsys, pattern, 0)
38
+ }
39
+ }
40
+
41
+ export function globWithLimit(fsys: FS, pattern: string, depth: number): [$.Slice<string>, $.GoError] {
42
+ let matches: $.Slice<string> = null
43
+ let err: $.GoError = null
44
+ {
45
+ // This limit is added to prevent stack exhaustion issues. See
46
+ // CVE-2022-30630.
47
+ let pathSeparatorsLimit: number = 10000
48
+ if (depth > 10000) {
49
+ return [null, path.ErrBadPattern]
50
+ }
51
+ {
52
+ let { value: fsysTyped, ok: ok } = $.typeAssert<GlobFS>(fsys, 'GlobFS')
53
+ if (ok) {
54
+ return fsysTyped!.Glob(pattern)
55
+ }
56
+ }
57
+
58
+ // Check pattern is well-formed.
59
+ {
60
+ let [, err] = path.Match(pattern, "")
61
+ if (err != null) {
62
+ return [null, err]
63
+ }
64
+ }
65
+ if (!hasMeta(pattern)) {
66
+ {
67
+ [, err] = Stat(fsys, pattern)
68
+ if (err != null) {
69
+ return [null, null]
70
+ }
71
+ }
72
+ return [$.arrayToSlice<string>([pattern]), null]
73
+ }
74
+
75
+ let [dir, file] = path.Split(pattern)
76
+ dir = cleanGlobPath(dir)
77
+
78
+ if (!hasMeta(dir)) {
79
+ return glob(fsys, dir, file, null)
80
+ }
81
+
82
+ // Prevent infinite recursion. See issue 15879.
83
+ if (dir == pattern) {
84
+ return [null, path.ErrBadPattern]
85
+ }
86
+
87
+ let m: $.Slice<string>
88
+ [m, err] = globWithLimit(fsys, dir, depth + 1)
89
+ if (err != null) {
90
+ return [null, err]
91
+ }
92
+ for (let _i = 0; _i < $.len(m); _i++) {
93
+ const d = m![_i]
94
+ {
95
+ [matches, err] = glob(fsys, d, file, matches)
96
+ if (err != null) {
97
+ return [matches, err]
98
+ }
99
+ }
100
+ }
101
+ return [matches, err]
102
+ }
103
+ }
104
+
105
+ // cleanGlobPath prepares path for glob matching.
106
+ export function cleanGlobPath(path: string): string {
107
+
108
+ // chop off trailing separator
109
+ switch (path) {
110
+ case "":
111
+ return "."
112
+ break
113
+ default:
114
+ return $.sliceString(path, 0, $.len(path) - 1)
115
+ break
116
+ }
117
+ }
118
+
119
+ // glob searches for files matching pattern in the directory dir
120
+ // and appends them to matches, returning the updated slice.
121
+ // If the directory cannot be opened, glob returns the existing matches.
122
+ // New matches are added in lexicographical order.
123
+ export function glob(fs: FS, dir: string, pattern: string, matches: $.Slice<string>): [$.Slice<string>, $.GoError] {
124
+ let m: $.Slice<string> = null
125
+ let e: $.GoError = null
126
+ {
127
+ m = matches
128
+ let [infos, err] = ReadDir(fs, dir)
129
+
130
+ // ignore I/O error
131
+ if (err != null) {
132
+ return [m, e]
133
+ }
134
+
135
+ for (let _i = 0; _i < $.len(infos); _i++) {
136
+ const info = infos![_i]
137
+ {
138
+ let n = info!.Name()
139
+ let [matched, err] = path.Match(pattern, n)
140
+ if (err != null) {
141
+ return [m, err]
142
+ }
143
+ if (matched) {
144
+ m = $.append(m, path.Join(dir, n))
145
+ }
146
+ }
147
+ }
148
+ return [m, e]
149
+ }
150
+ }
151
+
152
+ // hasMeta reports whether path contains any of the magic characters
153
+ // recognized by path.Match.
154
+ export function hasMeta(path: string): boolean {
155
+ for (let i = 0; i < $.len(path); i++) {
156
+ switch ($.indexString(path, i)) {
157
+ case 42:
158
+ case 63:
159
+ case 91:
160
+ case 92:
161
+ return true
162
+ break
163
+ }
164
+ }
165
+ return false
166
+ }
167
+
@@ -0,0 +1,35 @@
1
+ package fs // import "io/fs"
2
+
3
+ Package fs defines basic interfaces to a file system. A file system can be
4
+ provided by the host operating system but also by other packages.
5
+
6
+ See the testing/fstest package for support with testing implementations of file
7
+ systems.
8
+
9
+ var ErrInvalid = errInvalid() ...
10
+ var SkipAll = errors.New("skip everything and stop the walk")
11
+ var SkipDir = errors.New("skip this directory")
12
+ func FormatDirEntry(dir DirEntry) string
13
+ func FormatFileInfo(info FileInfo) string
14
+ func Glob(fsys FS, pattern string) (matches []string, err error)
15
+ func ReadFile(fsys FS, name string) ([]byte, error)
16
+ func ValidPath(name string) bool
17
+ func WalkDir(fsys FS, root string, fn WalkDirFunc) error
18
+ type DirEntry interface{ ... }
19
+ func FileInfoToDirEntry(info FileInfo) DirEntry
20
+ func ReadDir(fsys FS, name string) ([]DirEntry, error)
21
+ type FS interface{ ... }
22
+ func Sub(fsys FS, dir string) (FS, error)
23
+ type File interface{ ... }
24
+ type FileInfo interface{ ... }
25
+ func Stat(fsys FS, name string) (FileInfo, error)
26
+ type FileMode uint32
27
+ const ModeDir FileMode = 1 << (32 - 1 - iota) ...
28
+ type GlobFS interface{ ... }
29
+ type PathError struct{ ... }
30
+ type ReadDirFS interface{ ... }
31
+ type ReadDirFile interface{ ... }
32
+ type ReadFileFS interface{ ... }
33
+ type StatFS interface{ ... }
34
+ type SubFS interface{ ... }
35
+ type WalkDirFunc func(path string, d DirEntry, err error) error
@@ -0,0 +1,8 @@
1
+ export * from "./format.js"
2
+ export * from "./fs.js"
3
+ export * from "./glob.js"
4
+ export * from "./readdir.js"
5
+ export * from "./readfile.js"
6
+ export * from "./stat.js"
7
+ export * from "./sub.js"
8
+ export * from "./walk.js"
@@ -0,0 +1,126 @@
1
+ import * as $ from "@goscript/builtin/builtin.js";
2
+ import { FormatDirEntry } from "./format.js";
3
+ import { FS, DirEntry, ReadDirFile, PathError, FileInfo, FileMode, fileModeType } from "./fs.js";
4
+
5
+ import * as errors from "@goscript/errors/index.js"
6
+
7
+ import * as slices from "@goscript/slices/index.js"
8
+
9
+ export type ReadDirFS = null | {
10
+ // ReadDir reads the named directory
11
+ // and returns a list of directory entries sorted by filename.
12
+ ReadDir(name: string): [$.Slice<DirEntry>, $.GoError]
13
+ } & FS
14
+
15
+ $.registerInterfaceType(
16
+ 'ReadDirFS',
17
+ null, // Zero value for interface is null
18
+ [{ name: "ReadDir", args: [{ name: "name", type: { kind: $.TypeKind.Basic, name: "string" } }], returns: [{ type: { kind: $.TypeKind.Slice, elemType: "DirEntry" } }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }]
19
+ );
20
+
21
+ // ReadDir reads the named directory
22
+ // and returns a list of directory entries sorted by filename.
23
+ //
24
+ // If fs implements [ReadDirFS], ReadDir calls fs.ReadDir.
25
+ // Otherwise ReadDir calls fs.Open and uses ReadDir and Close
26
+ // on the returned file.
27
+ export function ReadDir(fsys: FS, name: string): [$.Slice<DirEntry>, $.GoError] {
28
+ using __defer = new $.DisposableStack();
29
+ {
30
+ let { value: fsysTyped, ok: ok } = $.typeAssert<ReadDirFS>(fsys, 'ReadDirFS')
31
+ if (ok) {
32
+ return fsysTyped!.ReadDir(name)
33
+ }
34
+ }
35
+
36
+ let [file, err] = fsys!.Open(name)
37
+ if (err != null) {
38
+ return [null, err]
39
+ }
40
+ __defer.defer(() => {
41
+ file!.Close()
42
+ });
43
+
44
+ let { value: dir, ok: ok } = $.typeAssert<ReadDirFile>(file, 'ReadDirFile')
45
+ if (!ok) {
46
+ return [null, new PathError({Err: errors.New("not implemented"), Op: "readdir", Path: name})]
47
+ }
48
+
49
+ let list: $.Slice<DirEntry>
50
+ [list, err] = dir!.ReadDir(-1)
51
+ list!.sort((a: DirEntry, b: DirEntry): number => {
52
+ return a!.Name().localeCompare(b!.Name())
53
+ })
54
+ return [list, err]
55
+ }
56
+
57
+ class dirInfo {
58
+ public get fileInfo(): FileInfo {
59
+ return this._fields.fileInfo.value
60
+ }
61
+ public set fileInfo(value: FileInfo) {
62
+ this._fields.fileInfo.value = value
63
+ }
64
+
65
+ public _fields: {
66
+ fileInfo: $.VarRef<FileInfo>;
67
+ }
68
+
69
+ constructor(init?: Partial<{fileInfo?: FileInfo}>) {
70
+ this._fields = {
71
+ fileInfo: $.varRef(init?.fileInfo ?? null)
72
+ }
73
+ }
74
+
75
+ public clone(): dirInfo {
76
+ const cloned = new dirInfo()
77
+ cloned._fields = {
78
+ fileInfo: $.varRef(this._fields.fileInfo.value)
79
+ }
80
+ return cloned
81
+ }
82
+
83
+ public IsDir(): boolean {
84
+ const di = this
85
+ return di.fileInfo!.IsDir()
86
+ }
87
+
88
+ public Type(): FileMode {
89
+ const di = this
90
+ return fileModeType(di.fileInfo!.Mode())
91
+ }
92
+
93
+ public Info(): [FileInfo, $.GoError] {
94
+ const di = this
95
+ return [di.fileInfo, null]
96
+ }
97
+
98
+ public Name(): string {
99
+ const di = this
100
+ return di.fileInfo!.Name()
101
+ }
102
+
103
+ public String(): string {
104
+ const di = this
105
+ return FormatDirEntry(di)
106
+ }
107
+
108
+ // Register this type with the runtime type system
109
+ static __typeInfo = $.registerStructType(
110
+ 'dirInfo',
111
+ new dirInfo(),
112
+ [{ name: "IsDir", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Type", args: [], returns: [{ type: "FileMode" }] }, { name: "Info", args: [], returns: [{ type: "FileInfo" }, { type: { kind: $.TypeKind.Interface, name: 'GoError', methods: [{ name: 'Error', args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }] }] } }] }, { name: "Name", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "string" } }] }, { name: "String", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "string" } }] }],
113
+ dirInfo,
114
+ {"fileInfo": "FileInfo"}
115
+ );
116
+ }
117
+
118
+ // FileInfoToDirEntry returns a [DirEntry] that returns information from info.
119
+ // If info is nil, FileInfoToDirEntry returns nil.
120
+ export function FileInfoToDirEntry(info: FileInfo): DirEntry {
121
+ if (info == null) {
122
+ return null
123
+ }
124
+ return new dirInfo({fileInfo: info})
125
+ }
126
+