@shd101wyy/yo 0.1.28 → 0.1.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.
- package/.github/skills/yo-async-effects/SKILL.md +15 -15
- package/.github/skills/yo-async-effects/async-effects-recipes.md +118 -121
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +33 -13
- package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +1 -1
- package/.github/skills/yo-syntax/SKILL.md +2 -2
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +108 -96
- package/README.md +6 -3
- package/out/cjs/index.cjs +812 -706
- package/out/cjs/yo-cli.cjs +1023 -907
- package/out/cjs/yo-lsp.cjs +836 -730
- package/out/esm/index.mjs +757 -651
- package/out/types/src/codegen/exprs/async.d.ts +2 -0
- package/out/types/src/codegen/exprs/await.d.ts +1 -0
- package/out/types/src/codegen/exprs/closures.d.ts +4 -0
- package/out/types/src/codegen/functions/context.d.ts +6 -0
- package/out/types/src/codegen/functions/declarations.d.ts +1 -1
- package/out/types/src/doc/model.d.ts +0 -1
- package/out/types/src/env.d.ts +2 -2
- package/out/types/src/evaluator/builtins/pragma.d.ts +9 -0
- package/out/types/src/evaluator/builtins/unsafe.d.ts +8 -0
- package/out/types/src/evaluator/context.d.ts +3 -1
- package/out/types/src/evaluator/exprs/{escape.d.ts → unwind.d.ts} +1 -1
- package/out/types/src/evaluator/index.d.ts +1 -1
- package/out/types/src/evaluator/memory-safety.d.ts +14 -0
- package/out/types/src/evaluator/types/flowability.d.ts +6 -0
- package/out/types/src/evaluator/types/function.d.ts +1 -2
- package/out/types/src/evaluator/utils.d.ts +0 -1
- package/out/types/src/expr-traversal.d.ts +1 -0
- package/out/types/src/expr.d.ts +9 -7
- package/out/types/src/public-safe-report.d.ts +19 -0
- package/out/types/src/tests/comptime-ref-gate.test.d.ts +1 -0
- package/out/types/src/tests/pragma-validation.test.d.ts +1 -0
- package/out/types/src/tests/public-safe-report.test.d.ts +1 -0
- package/out/types/src/tests/type-representation-pointer.test.d.ts +1 -0
- package/out/types/src/tests/unsafe-gate.test.d.ts +1 -0
- package/out/types/src/tests/unsafe-report-classify.test.d.ts +1 -0
- package/out/types/src/types/creators.d.ts +4 -6
- package/out/types/src/types/definitions.d.ts +9 -16
- package/out/types/src/types/guards.d.ts +1 -2
- package/out/types/src/types/tags.d.ts +0 -1
- package/out/types/src/types/utils.d.ts +5 -0
- package/out/types/src/unsafe-report.d.ts +29 -0
- package/out/types/src/value.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/add-pragma-for-pointer-decls.ts +134 -0
- package/scripts/add-pragma.ts +58 -0
- package/scripts/migrate-amp-method-calls.ts +186 -0
- package/scripts/migrate-clone-calls.ts +93 -0
- package/scripts/migrate-get-unwrap.ts +166 -0
- package/scripts/migrate-index-patterns.ts +210 -0
- package/scripts/migrate-index-trait.ts +142 -0
- package/scripts/migrate-iterator.ts +150 -0
- package/scripts/migrate-self-ptr.ts +220 -0
- package/scripts/migrate-skip-pragmas.ts +109 -0
- package/scripts/migrate-tostring.ts +134 -0
- package/scripts/trim-pragma.ts +130 -0
- package/scripts/wrap-extern-calls.ts +161 -0
- package/std/alg/hash.yo +3 -2
- package/std/allocator.yo +6 -5
- package/std/async.yo +2 -2
- package/std/collections/array_list.yo +59 -40
- package/std/collections/btree_map.yo +19 -18
- package/std/collections/deque.yo +9 -8
- package/std/collections/hash_map.yo +101 -13
- package/std/collections/hash_set.yo +5 -4
- package/std/collections/linked_list.yo +39 -4
- package/std/collections/ordered_map.yo +3 -3
- package/std/collections/priority_queue.yo +14 -13
- package/std/crypto/md5.yo +2 -1
- package/std/crypto/random.yo +21 -20
- package/std/crypto/sha256.yo +2 -1
- package/std/encoding/base64.yo +18 -18
- package/std/encoding/hex.yo +5 -5
- package/std/encoding/json.yo +62 -13
- package/std/encoding/punycode.yo +24 -23
- package/std/encoding/toml.yo +4 -3
- package/std/encoding/utf16.yo +3 -3
- package/std/env.yo +43 -28
- package/std/error.yo +15 -3
- package/std/fmt/display.yo +2 -2
- package/std/fmt/index.yo +6 -5
- package/std/fmt/to_string.yo +39 -38
- package/std/fmt/writer.yo +9 -8
- package/std/fs/dir.yo +61 -66
- package/std/fs/file.yo +121 -126
- package/std/fs/metadata.yo +13 -18
- package/std/fs/temp.yo +35 -30
- package/std/fs/walker.yo +14 -19
- package/std/gc.yo +1 -0
- package/std/glob.yo +7 -7
- package/std/http/client.yo +33 -36
- package/std/http/http.yo +6 -6
- package/std/http/index.yo +4 -4
- package/std/imm/list.yo +33 -0
- package/std/imm/map.yo +2 -1
- package/std/imm/set.yo +1 -0
- package/std/imm/sorted_map.yo +1 -0
- package/std/imm/sorted_set.yo +1 -0
- package/std/imm/string.yo +27 -23
- package/std/imm/vec.yo +18 -2
- package/std/io/reader.yo +2 -1
- package/std/io/writer.yo +3 -2
- package/std/libc/assert.yo +1 -0
- package/std/libc/ctype.yo +1 -0
- package/std/libc/dirent.yo +1 -0
- package/std/libc/errno.yo +1 -0
- package/std/libc/fcntl.yo +1 -0
- package/std/libc/float.yo +1 -0
- package/std/libc/limits.yo +1 -0
- package/std/libc/math.yo +1 -0
- package/std/libc/signal.yo +1 -0
- package/std/libc/stdatomic.yo +1 -0
- package/std/libc/stdint.yo +1 -0
- package/std/libc/stdio.yo +1 -0
- package/std/libc/stdlib.yo +1 -0
- package/std/libc/string.yo +1 -0
- package/std/libc/sys/stat.yo +1 -0
- package/std/libc/time.yo +1 -0
- package/std/libc/unistd.yo +1 -0
- package/std/libc/wctype.yo +1 -0
- package/std/libc/windows.yo +2 -0
- package/std/log.yo +7 -6
- package/std/net/addr.yo +6 -5
- package/std/net/dns.yo +13 -16
- package/std/net/errors.yo +9 -9
- package/std/net/tcp.yo +71 -74
- package/std/net/udp.yo +40 -43
- package/std/os/signal.yo +5 -5
- package/std/path.yo +1 -0
- package/std/prelude.yo +377 -200
- package/std/process/command.yo +57 -46
- package/std/process/index.yo +2 -1
- package/std/regex/compiler.yo +10 -9
- package/std/regex/index.yo +41 -41
- package/std/regex/match.yo +2 -2
- package/std/regex/parser.yo +31 -31
- package/std/regex/vm.yo +42 -41
- package/std/string/string.yo +95 -40
- package/std/string/string_builder.yo +9 -9
- package/std/string/unicode.yo +50 -49
- package/std/sync/channel.yo +2 -1
- package/std/sync/cond.yo +5 -4
- package/std/sync/mutex.yo +4 -3
- package/std/sys/advise.yo +1 -0
- package/std/sys/bufio/buf_reader.yo +27 -26
- package/std/sys/bufio/buf_writer.yo +22 -21
- package/std/sys/clock.yo +1 -0
- package/std/sys/copy.yo +1 -0
- package/std/sys/dir.yo +10 -9
- package/std/sys/dns.yo +6 -5
- package/std/sys/errors.yo +12 -12
- package/std/sys/events.yo +1 -0
- package/std/sys/externs.yo +38 -37
- package/std/sys/file.yo +17 -16
- package/std/sys/future.yo +4 -3
- package/std/sys/iov.yo +1 -0
- package/std/sys/mmap.yo +1 -0
- package/std/sys/path.yo +1 -0
- package/std/sys/perm.yo +2 -1
- package/std/sys/pipe.yo +1 -0
- package/std/sys/process.yo +5 -4
- package/std/sys/signal.yo +1 -0
- package/std/sys/socketpair.yo +1 -0
- package/std/sys/sockinfo.yo +1 -0
- package/std/sys/statfs.yo +2 -1
- package/std/sys/statx.yo +1 -0
- package/std/sys/sysinfo.yo +1 -0
- package/std/sys/tcp.yo +15 -14
- package/std/sys/temp.yo +1 -0
- package/std/sys/time.yo +2 -1
- package/std/sys/timer.yo +6 -6
- package/std/sys/tty.yo +2 -1
- package/std/sys/udp.yo +13 -12
- package/std/sys/unix.yo +12 -11
- package/std/testing/bench.yo +4 -3
- package/std/thread.yo +7 -6
- package/std/time/datetime.yo +18 -15
- package/std/time/duration.yo +11 -10
- package/std/time/instant.yo +4 -4
- package/std/time/sleep.yo +1 -0
- package/std/url/index.yo +5 -5
- package/std/worker.yo +4 -3
package/std/fs/temp.yo
CHANGED
|
@@ -5,25 +5,20 @@
|
|
|
5
5
|
//! ```rust
|
|
6
6
|
//! { TempDir } :: import "std/fs/temp";
|
|
7
7
|
//!
|
|
8
|
-
//! main :: (fn(
|
|
9
|
-
//!
|
|
10
|
-
//! throw : (fn(forall(T : Type), error: AnyError) -> T)(
|
|
11
|
-
//! { println(error.to_string()); exit(i32(1)); }
|
|
12
|
-
//! )
|
|
13
|
-
//! };
|
|
14
|
-
//!
|
|
15
|
-
//! dir := io.await(TempDir.new());
|
|
8
|
+
//! main :: (fn(io : Io, exn : Exception) -> unit)({
|
|
9
|
+
//! dir := io.await(TempDir.new(io), { io, exn });
|
|
16
10
|
//! println(dir.path());
|
|
17
|
-
//! io.await(dir.remove());
|
|
11
|
+
//! io.await(dir.remove(io), { io, exn });
|
|
18
12
|
//! });
|
|
19
13
|
//! ```
|
|
14
|
+
pragma(Pragma.AllowUnsafe);
|
|
20
15
|
{ GlobalAllocator } :: import("../allocator");
|
|
21
16
|
{ malloc, free } :: GlobalAllocator;
|
|
22
17
|
open(import("../string"));
|
|
23
18
|
open(import("../fmt"));
|
|
24
19
|
{ Path } :: import("../path");
|
|
25
|
-
{
|
|
26
|
-
{ Error, AnyError, Exception } :: import("../error");
|
|
20
|
+
{ IoError } :: import("../sys/errors");
|
|
21
|
+
{ Error, AnyError, Exception, IoExn } :: import("../error");
|
|
27
22
|
_file_mod :: import("./file");
|
|
28
23
|
_dir_mod :: import("./dir");
|
|
29
24
|
IO_temp :: import("../sys/temp");
|
|
@@ -59,8 +54,8 @@ TempDir :: object(
|
|
|
59
54
|
impl(
|
|
60
55
|
TempDir,
|
|
61
56
|
// Create a temporary directory in the given parent directory.
|
|
62
|
-
new_in : (fn(parent : Path,
|
|
63
|
-
io.async((
|
|
57
|
+
new_in : (fn(parent : Path, io : Io) -> Impl(Future(TempDir, IoExn)))(
|
|
58
|
+
io.async((e) => {
|
|
64
59
|
// Build template: parent/yo_tmp_XXXXXX
|
|
65
60
|
template_path := parent.join(Path.new(`yo_tmp_XXXXXX`));
|
|
66
61
|
template_str := template_path.to_string();
|
|
@@ -68,13 +63,19 @@ impl(
|
|
|
68
63
|
template_bytes := template_str.as_bytes();
|
|
69
64
|
buf_len := (template_bytes.len() + usize(1));
|
|
70
65
|
buf := *(u8)(malloc(buf_len).unwrap());
|
|
71
|
-
|
|
66
|
+
// SAFETY: `buf` is a fresh malloc'd `buf_len = template_bytes.len() + 1`
|
|
67
|
+
// byte buffer; we copy exactly `template_bytes.len()` bytes from
|
|
68
|
+
// `template_bytes`'s underlying storage (alive for the duration of this
|
|
69
|
+
// statement — it's the bytes of `template_str` which we own) and write
|
|
70
|
+
// the trailing NUL at `buf[template_bytes.len()]`. `buf` is freed in
|
|
71
|
+
// both the error branch and after `IO_temp.mkdtemp` returns.
|
|
72
|
+
unsafe(memcpy(*(void)(buf), *(void)(template_bytes.ptr().unwrap()), template_bytes.len()));
|
|
72
73
|
(buf &+ template_bytes.len()).* = u8(0);
|
|
73
74
|
result := IO_temp.mkdtemp(buf);
|
|
74
75
|
cond(
|
|
75
76
|
(result < i32(0)) => {
|
|
76
77
|
free(.Some(*(void)(buf)));
|
|
77
|
-
exn.throw(dyn(
|
|
78
|
+
e.exn.throw(dyn(IoError.from_errno(i32(0) - result)));
|
|
78
79
|
},
|
|
79
80
|
true => ()
|
|
80
81
|
);
|
|
@@ -84,22 +85,22 @@ impl(
|
|
|
84
85
|
})
|
|
85
86
|
),
|
|
86
87
|
// Create a temporary directory in the system temp dir.
|
|
87
|
-
new : (fn(
|
|
88
|
-
TempDir.new_in(Path.new(_default_tmp_dir()))
|
|
88
|
+
new : (fn(io : Io) -> Impl(Future(TempDir, IoExn)))(
|
|
89
|
+
TempDir.new_in(Path.new(_default_tmp_dir()), io)
|
|
89
90
|
),
|
|
90
91
|
// Get the path of the temporary directory.
|
|
91
92
|
path : (fn(self : Self) -> Path)(
|
|
92
93
|
self._path
|
|
93
94
|
),
|
|
94
95
|
// Remove the temporary directory.
|
|
95
|
-
remove : (fn(self : Self,
|
|
96
|
+
remove : (fn(self : Self, io : Io) -> Impl(Future(unit, IoExn)))({
|
|
96
97
|
self_path := self._path;
|
|
97
98
|
io.async(
|
|
98
|
-
(
|
|
99
|
+
(e) =>
|
|
99
100
|
cond(
|
|
100
101
|
self._removed => (),
|
|
101
102
|
true => {
|
|
102
|
-
io.await(_dir_mod.remove_dir(self_path));
|
|
103
|
+
e.io.await(_dir_mod.remove_dir(self_path, io), e);
|
|
103
104
|
self._removed = true;
|
|
104
105
|
}
|
|
105
106
|
)
|
|
@@ -116,21 +117,25 @@ TempFile :: object(
|
|
|
116
117
|
impl(
|
|
117
118
|
TempFile,
|
|
118
119
|
// Create a temporary file in the given parent directory.
|
|
119
|
-
new_in : (fn(parent : Path,
|
|
120
|
-
io.async((
|
|
120
|
+
new_in : (fn(parent : Path, io : Io) -> Impl(Future(TempFile, IoExn)))(
|
|
121
|
+
io.async((e) => {
|
|
121
122
|
// Build template: parent/yo_tmp_XXXXXX
|
|
122
123
|
template_path := parent.join(Path.new(`yo_tmp_XXXXXX`));
|
|
123
124
|
template_str := template_path.to_string();
|
|
124
125
|
template_bytes := template_str.as_bytes();
|
|
125
126
|
buf_len := (template_bytes.len() + usize(1));
|
|
126
127
|
buf := *(u8)(malloc(buf_len).unwrap());
|
|
127
|
-
|
|
128
|
+
// SAFETY: as in `TempDir.new_in` above — `buf` is a fresh malloc'd
|
|
129
|
+
// `template_bytes.len() + 1` byte buffer, we copy exactly the template
|
|
130
|
+
// bytes plus a trailing NUL. `buf` is freed in the error branch and
|
|
131
|
+
// released to mkstemp's owned-fd lifecycle on success.
|
|
132
|
+
unsafe(memcpy(*(void)(buf), *(void)(template_bytes.ptr().unwrap()), template_bytes.len()));
|
|
128
133
|
(buf &+ template_bytes.len()).* = u8(0);
|
|
129
134
|
fd := IO_temp.mkstemp(buf);
|
|
130
135
|
cond(
|
|
131
136
|
(fd < i32(0)) => {
|
|
132
137
|
free(.Some(*(void)(buf)));
|
|
133
|
-
exn.throw(dyn(
|
|
138
|
+
e.exn.throw(dyn(IoError.from_errno(i32(0) - fd)));
|
|
134
139
|
},
|
|
135
140
|
true => ()
|
|
136
141
|
);
|
|
@@ -142,8 +147,8 @@ impl(
|
|
|
142
147
|
})
|
|
143
148
|
),
|
|
144
149
|
// Create a temporary file in the system temp dir.
|
|
145
|
-
new : (fn(
|
|
146
|
-
TempFile.new_in(Path.new(_default_tmp_dir()))
|
|
150
|
+
new : (fn(io : Io) -> Impl(Future(TempFile, IoExn)))(
|
|
151
|
+
TempFile.new_in(Path.new(_default_tmp_dir()), io)
|
|
147
152
|
),
|
|
148
153
|
// Get the underlying File object.
|
|
149
154
|
file : (fn(self : Self) -> _file_mod.File)(
|
|
@@ -154,16 +159,16 @@ impl(
|
|
|
154
159
|
self._path
|
|
155
160
|
),
|
|
156
161
|
// Remove the temporary file.
|
|
157
|
-
remove : (fn(self : Self,
|
|
162
|
+
remove : (fn(self : Self, io : Io) -> Impl(Future(unit, IoExn)))({
|
|
158
163
|
self_file := self._file;
|
|
159
164
|
self_path := self._path;
|
|
160
165
|
io.async(
|
|
161
|
-
(
|
|
166
|
+
(e) =>
|
|
162
167
|
cond(
|
|
163
168
|
self._removed => (),
|
|
164
169
|
true => {
|
|
165
|
-
io.await(self_file.close());
|
|
166
|
-
io.await(_dir_mod.remove_file(self_path));
|
|
170
|
+
e.io.await(self_file.close(io), e);
|
|
171
|
+
e.io.await(_dir_mod.remove_file(self_path, io), e);
|
|
167
172
|
self._removed = true;
|
|
168
173
|
}
|
|
169
174
|
)
|
package/std/fs/walker.yo
CHANGED
|
@@ -5,27 +5,22 @@
|
|
|
5
5
|
//! ```rust
|
|
6
6
|
//! { walk, WalkEntry } :: import "std/fs/walker";
|
|
7
7
|
//!
|
|
8
|
-
//! main :: (fn(
|
|
9
|
-
//!
|
|
10
|
-
//! throw : (fn(forall(T : Type), error: AnyError) -> T)(
|
|
11
|
-
//! { println(error.to_string()); exit(i32(1)); }
|
|
12
|
-
//! )
|
|
13
|
-
//! };
|
|
14
|
-
//!
|
|
15
|
-
//! entries := io.await(walk(`/tmp`));
|
|
8
|
+
//! main :: (fn(io : Io, exn : Exception) -> unit)({
|
|
9
|
+
//! entries := io.await(walk(Path.new(`/tmp`), io), { io, exn });
|
|
16
10
|
//! i := usize(0);
|
|
17
11
|
//! while runtime((i < entries.len())), {
|
|
18
|
-
//! e := entries
|
|
12
|
+
//! e := entries(i);
|
|
19
13
|
//! println(e.path);
|
|
20
14
|
//! i = (i + usize(1));
|
|
21
15
|
//! };
|
|
22
16
|
//! });
|
|
23
17
|
//! ```
|
|
18
|
+
pragma(Pragma.AllowUnsafe);
|
|
24
19
|
{ ArrayList } :: import("../collections/array_list");
|
|
25
20
|
open(import("../string"));
|
|
26
21
|
{ Path } :: import("../path");
|
|
27
|
-
{
|
|
28
|
-
{ Error, AnyError, Exception } :: import("../error");
|
|
22
|
+
{ IoError } :: import("../sys/errors");
|
|
23
|
+
{ Error, AnyError, Exception, IoExn } :: import("../error");
|
|
29
24
|
{ FileType } :: import("./dir");
|
|
30
25
|
{ DirEntry } :: import("./dir");
|
|
31
26
|
_dir :: import("./dir");
|
|
@@ -67,9 +62,9 @@ _join_path :: (fn(base : String, name : String) -> String)(
|
|
|
67
62
|
// Walk functions
|
|
68
63
|
// ============================================================================
|
|
69
64
|
// Walk a directory tree with custom options.
|
|
70
|
-
walk_with :: (fn(root : Path, options : WalkOptions,
|
|
65
|
+
walk_with :: (fn(root : Path, options : WalkOptions, io : Io) -> Impl(Future(ArrayList(WalkEntry), IoExn)))({
|
|
71
66
|
root_s := root.to_string();
|
|
72
|
-
io.async((
|
|
67
|
+
io.async((e) => {
|
|
73
68
|
results := ArrayList(WalkEntry).new();
|
|
74
69
|
// Stack of (path, depth) pairs to process
|
|
75
70
|
stack := ArrayList(struct(path : String, depth : u32)).new();
|
|
@@ -86,10 +81,10 @@ walk_with :: (fn(root : Path, options : WalkOptions, using(io : IO)) -> Impl(Fut
|
|
|
86
81
|
);
|
|
87
82
|
cond(
|
|
88
83
|
should_continue => {
|
|
89
|
-
entries := io.await(_dir.read_dir(Path.new(cur_path)));
|
|
84
|
+
entries := e.io.await(_dir.read_dir(Path.new(cur_path), e.io), e);
|
|
90
85
|
i := usize(0);
|
|
91
86
|
while(runtime(i < entries.len()), {
|
|
92
|
-
entry := entries
|
|
87
|
+
entry := entries(i);
|
|
93
88
|
entry_path := _join_path(cur_path, entry.name);
|
|
94
89
|
match(
|
|
95
90
|
entry.file_type,
|
|
@@ -130,10 +125,10 @@ walk_with :: (fn(root : Path, options : WalkOptions, using(io : IO)) -> Impl(Fut
|
|
|
130
125
|
results
|
|
131
126
|
})
|
|
132
127
|
});
|
|
133
|
-
walk_with_cstr :: (fn(root : *(u8), options : WalkOptions,
|
|
134
|
-
walk_with(Path.from_cstr(root), options)
|
|
128
|
+
walk_with_cstr :: (fn(root : *(u8), options : WalkOptions, io : Io) -> Impl(Future(ArrayList(WalkEntry), IoExn)))(
|
|
129
|
+
walk_with(Path.from_cstr(root), options, io)
|
|
135
130
|
);
|
|
136
131
|
/// Walk a directory tree with default options.
|
|
137
|
-
walk :: (fn(root : Path,
|
|
138
|
-
walk_cstr :: (fn(root : *(u8),
|
|
132
|
+
walk :: (fn(root : Path, io : Io) -> Impl(Future(ArrayList(WalkEntry), IoExn)))(walk_with(root, WalkOptions.defaults(), io));
|
|
133
|
+
walk_cstr :: (fn(root : *(u8), io : Io) -> Impl(Future(ArrayList(WalkEntry), IoExn)))(walk(Path.from_cstr(root), io));
|
|
139
134
|
export(walk, walk_cstr, walk_with, walk_with_cstr);
|
package/std/gc.yo
CHANGED
package/std/glob.yo
CHANGED
|
@@ -17,12 +17,12 @@ _glob_match_impl :: (fn(pb : ArrayList(u8), pi : usize, tb : ArrayList(u8), ti :
|
|
|
17
17
|
},
|
|
18
18
|
true => ()
|
|
19
19
|
);
|
|
20
|
-
p := pb
|
|
20
|
+
p := pb(pi);
|
|
21
21
|
// Handle * and **
|
|
22
22
|
cond(
|
|
23
23
|
(p == u8(42)) => {
|
|
24
24
|
is_dbl := cond(
|
|
25
|
-
((pi + usize(1)) < pb.len()) => (pb
|
|
25
|
+
((pi + usize(1)) < pb.len()) => (pb(pi + usize(1)) == u8(42)),
|
|
26
26
|
true => false
|
|
27
27
|
);
|
|
28
28
|
cond(
|
|
@@ -33,7 +33,7 @@ _glob_match_impl :: (fn(pb : ArrayList(u8), pi : usize, tb : ArrayList(u8), ti :
|
|
|
33
33
|
cond(
|
|
34
34
|
(npi < pb.len()) => {
|
|
35
35
|
cond(
|
|
36
|
-
(pb
|
|
36
|
+
(pb(npi) == u8(47)) => {
|
|
37
37
|
npi = (npi + usize(1));
|
|
38
38
|
},
|
|
39
39
|
true => ()
|
|
@@ -70,7 +70,7 @@ _glob_match_impl :: (fn(pb : ArrayList(u8), pi : usize, tb : ArrayList(u8), ti :
|
|
|
70
70
|
// Try * consuming one non-/ char
|
|
71
71
|
cond(
|
|
72
72
|
(ti < tb.len()) => {
|
|
73
|
-
tc := tb
|
|
73
|
+
tc := tb(ti);
|
|
74
74
|
cond(
|
|
75
75
|
(tc != u8(47)) => {
|
|
76
76
|
return(recur(pb, pi, tb, ti + usize(1)));
|
|
@@ -96,7 +96,7 @@ _glob_match_impl :: (fn(pb : ArrayList(u8), pi : usize, tb : ArrayList(u8), ti :
|
|
|
96
96
|
},
|
|
97
97
|
true => ()
|
|
98
98
|
);
|
|
99
|
-
t := tb
|
|
99
|
+
t := tb(ti);
|
|
100
100
|
// Handle ?
|
|
101
101
|
cond(
|
|
102
102
|
(p == u8(63)) => {
|
|
@@ -119,7 +119,7 @@ _glob_match_impl :: (fn(pb : ArrayList(u8), pi : usize, tb : ArrayList(u8), ti :
|
|
|
119
119
|
// Check for negation: ! or ^
|
|
120
120
|
cond(
|
|
121
121
|
(ci < pb.len()) => {
|
|
122
|
-
nc := pb
|
|
122
|
+
nc := pb(ci);
|
|
123
123
|
cond(
|
|
124
124
|
((nc == u8(33)) || (nc == u8(94))) => {
|
|
125
125
|
neg = true;
|
|
@@ -134,7 +134,7 @@ _glob_match_impl :: (fn(pb : ArrayList(u8), pi : usize, tb : ArrayList(u8), ti :
|
|
|
134
134
|
found := false;
|
|
135
135
|
cdone := false;
|
|
136
136
|
while((ci < pb.len()) && (!(cdone)), ci = (ci + usize(1)), {
|
|
137
|
-
ch := pb
|
|
137
|
+
ch := pb(ci);
|
|
138
138
|
cond(
|
|
139
139
|
(ch == u8(93)) => {
|
|
140
140
|
cdone = true;
|
package/std/http/client.yo
CHANGED
|
@@ -8,20 +8,17 @@
|
|
|
8
8
|
// { fetch, HttpResponse } :: import "std/http";
|
|
9
9
|
// { Exception } :: import "std/error";
|
|
10
10
|
//
|
|
11
|
-
// main :: (fn(
|
|
12
|
-
//
|
|
13
|
-
// println(err.message());
|
|
14
|
-
// escape ();
|
|
15
|
-
// }));
|
|
16
|
-
// resp := io.await(fetch(`http://example.com/api/data`, using(io)));
|
|
11
|
+
// main :: (fn(io : Io, exn : Exception) -> unit)({
|
|
12
|
+
// resp := io.await(fetch(`http://example.com/api/data`, io), { io, exn });
|
|
17
13
|
// println(resp.body);
|
|
18
14
|
// });
|
|
15
|
+
pragma(Pragma.AllowUnsafe);
|
|
19
16
|
open(import("../string"));
|
|
20
17
|
{ ArrayList } :: import("../collections/array_list");
|
|
21
18
|
{ GlobalAllocator } :: import("../allocator");
|
|
22
19
|
{ malloc, free } :: GlobalAllocator;
|
|
23
20
|
open(import("../fmt"));
|
|
24
|
-
{ Error, AnyError, Exception } :: import("../error");
|
|
21
|
+
{ Error, AnyError, Exception, IoExn } :: import("../error");
|
|
25
22
|
{ TcpStream } :: import("../net/tcp");
|
|
26
23
|
{ IpAddr, SocketAddr } :: import("../net/addr");
|
|
27
24
|
{ lookup_host } :: import("../net/dns");
|
|
@@ -120,12 +117,12 @@ _find_header_end :: (fn(data : ArrayList(u8)) -> usize)({
|
|
|
120
117
|
while(runtime(i < limit), {
|
|
121
118
|
cond(
|
|
122
119
|
(
|
|
123
|
-
(data
|
|
120
|
+
(data(i) == u8(13)) &&
|
|
124
121
|
(
|
|
125
|
-
(data
|
|
122
|
+
(data(i + usize(1)) == u8(10)) &&
|
|
126
123
|
(
|
|
127
|
-
(data
|
|
128
|
-
(data
|
|
124
|
+
(data(i + usize(2)) == u8(13)) &&
|
|
125
|
+
(data(i + usize(3)) == u8(10))
|
|
129
126
|
)
|
|
130
127
|
)
|
|
131
128
|
) => {
|
|
@@ -143,7 +140,7 @@ _find_content_length :: (fn(data : ArrayList(u8), header_end : usize) -> i32)({
|
|
|
143
140
|
header_bytes := ArrayList(u8).new();
|
|
144
141
|
i := usize(0);
|
|
145
142
|
while(runtime(i < header_end), {
|
|
146
|
-
header_bytes.push(data
|
|
143
|
+
header_bytes.push(data(i));
|
|
147
144
|
i = (i + usize(1));
|
|
148
145
|
});
|
|
149
146
|
header_str := String.from_bytes(header_bytes).to_lowercase();
|
|
@@ -179,14 +176,14 @@ _find_content_length :: (fn(data : ArrayList(u8), header_end : usize) -> i32)({
|
|
|
179
176
|
// ============================================================================
|
|
180
177
|
// Internal: read full HTTP response from a TCP stream
|
|
181
178
|
// ============================================================================
|
|
182
|
-
_read_http_response :: (fn(stream : TcpStream,
|
|
179
|
+
_read_http_response :: (fn(stream : TcpStream, e : IoExn) -> String)({
|
|
183
180
|
buf_size := usize(8192);
|
|
184
181
|
buf := *(u8)(malloc(buf_size).unwrap());
|
|
185
182
|
result := ArrayList(u8).new();
|
|
186
183
|
// Read until connection closes or we've read the full response
|
|
187
184
|
done := false;
|
|
188
185
|
while(runtime(!(done)), {
|
|
189
|
-
n := io.await(stream.read(buf, buf_size,
|
|
186
|
+
n := e.io.await(stream.read(buf, buf_size, e.io), e);
|
|
190
187
|
cond(
|
|
191
188
|
(n <= i32(0)) => {
|
|
192
189
|
done = true;
|
|
@@ -243,15 +240,15 @@ _read_http_response :: (fn(stream : TcpStream, using(io : IO, exn : Exception))
|
|
|
243
240
|
///
|
|
244
241
|
/// ```rust
|
|
245
242
|
/// opts := FetchOptions.new().with_method(.POST).with_body(`{"key": "value"}`);
|
|
246
|
-
/// resp := io.await(fetch_with(`http://example.com/api`, opts,
|
|
243
|
+
/// resp := io.await(fetch_with(`http://example.com/api`, opts, io), { io, exn });
|
|
247
244
|
/// ```
|
|
248
245
|
fetch_with :: (
|
|
249
|
-
fn(url_str : String, opts : FetchOptions,
|
|
250
|
-
Impl(Future(HttpResponse,
|
|
246
|
+
fn(url_str : String, opts : FetchOptions, io : Io) ->
|
|
247
|
+
Impl(Future(HttpResponse, IoExn))
|
|
251
248
|
)(
|
|
252
|
-
io.async((
|
|
249
|
+
io.async((e) => {
|
|
253
250
|
// Parse URL
|
|
254
|
-
url := Url.parse(url_str.as_str(),
|
|
251
|
+
url := Url.parse(url_str.as_str(), e.exn);
|
|
255
252
|
// Validate scheme
|
|
256
253
|
scheme := url.scheme();
|
|
257
254
|
is_http := (scheme == `http`);
|
|
@@ -259,7 +256,7 @@ fetch_with :: (
|
|
|
259
256
|
cond(
|
|
260
257
|
(is_http || is_https) => (),
|
|
261
258
|
true => {
|
|
262
|
-
exn.throw(dyn(HttpError.UnsupportedScheme(scheme)));
|
|
259
|
+
e.exn.throw(dyn(HttpError.UnsupportedScheme(scheme)));
|
|
263
260
|
}
|
|
264
261
|
);
|
|
265
262
|
// Extract host
|
|
@@ -267,7 +264,7 @@ fetch_with :: (
|
|
|
267
264
|
url.host(),
|
|
268
265
|
.Some(h) => h,
|
|
269
266
|
.None => {
|
|
270
|
-
exn.throw(dyn(HttpError.InvalidUrl(`missing host`)));
|
|
267
|
+
e.exn.throw(dyn(HttpError.InvalidUrl(`missing host`)));
|
|
271
268
|
`_` // unreachable
|
|
272
269
|
}
|
|
273
270
|
);
|
|
@@ -281,17 +278,17 @@ fetch_with :: (
|
|
|
281
278
|
)
|
|
282
279
|
);
|
|
283
280
|
// Resolve hostname to IP
|
|
284
|
-
addrs := io.await(lookup_host(host,
|
|
281
|
+
addrs := e.io.await(lookup_host(host, e.io), e);
|
|
285
282
|
cond(
|
|
286
283
|
(addrs.len() == usize(0)) => {
|
|
287
|
-
exn.throw(dyn(HttpError.ConnectionFailed(`DNS resolution failed for ${host}`)));
|
|
284
|
+
e.exn.throw(dyn(HttpError.ConnectionFailed(`DNS resolution failed for ${host}`)));
|
|
288
285
|
},
|
|
289
286
|
true => ()
|
|
290
287
|
);
|
|
291
|
-
ip := addrs
|
|
288
|
+
ip := addrs(usize(0));
|
|
292
289
|
addr := SocketAddr.new(ip, port);
|
|
293
290
|
// Connect
|
|
294
|
-
stream := io.await(TcpStream.connect(addr,
|
|
291
|
+
stream := e.io.await(TcpStream.connect(addr, e.io), e);
|
|
295
292
|
// Build the request path (include query string if present)
|
|
296
293
|
req_path := url.path();
|
|
297
294
|
cond(
|
|
@@ -313,7 +310,7 @@ fetch_with :: (
|
|
|
313
310
|
// Add user-provided headers
|
|
314
311
|
hi := usize(0);
|
|
315
312
|
while(runtime(hi < opts.headers.len()), {
|
|
316
|
-
h := opts.headers
|
|
313
|
+
h := opts.headers(hi);
|
|
317
314
|
req.set_header(h.name, h.value);
|
|
318
315
|
hi = (hi + usize(1));
|
|
319
316
|
});
|
|
@@ -329,17 +326,17 @@ fetch_with :: (
|
|
|
329
326
|
req.set_header(`Connection`, `close`);
|
|
330
327
|
// Send request
|
|
331
328
|
req_str := req.to_string();
|
|
332
|
-
io.await(stream.write_string(req_str,
|
|
329
|
+
e.io.await(stream.write_string(req_str, e.io), e);
|
|
333
330
|
// Read response
|
|
334
|
-
raw_response := _read_http_response(stream,
|
|
331
|
+
raw_response := _read_http_response(stream, e);
|
|
335
332
|
// Close connection
|
|
336
|
-
io.await(stream.close(
|
|
333
|
+
e.io.await(stream.close(e.io), e);
|
|
337
334
|
// Parse response
|
|
338
335
|
match(
|
|
339
336
|
parse_response(raw_response),
|
|
340
337
|
.Ok(resp) => resp,
|
|
341
|
-
.Err(
|
|
342
|
-
exn.throw(dyn(HttpError.Other(
|
|
338
|
+
.Err(err) => {
|
|
339
|
+
e.exn.throw(dyn(HttpError.Other(err)));
|
|
343
340
|
HttpResponse.new(i32(0), ``) // unreachable
|
|
344
341
|
}
|
|
345
342
|
)
|
|
@@ -352,15 +349,15 @@ export(fetch_with);
|
|
|
352
349
|
/// # Example
|
|
353
350
|
///
|
|
354
351
|
/// ```rust
|
|
355
|
-
/// resp := io.await(fetch(`http://example.com`,
|
|
352
|
+
/// resp := io.await(fetch(`http://example.com`, io), { io, exn });
|
|
356
353
|
/// cond(resp.is_ok() => println(resp.body), true => println(`Error`));
|
|
357
354
|
/// ```
|
|
358
355
|
fetch :: (
|
|
359
|
-
fn(url_str : String,
|
|
360
|
-
Impl(Future(HttpResponse,
|
|
356
|
+
fn(url_str : String, io : Io) ->
|
|
357
|
+
Impl(Future(HttpResponse, IoExn))
|
|
361
358
|
)(
|
|
362
|
-
io.async((
|
|
363
|
-
return(io.await(fetch_with(url_str, FetchOptions.new(),
|
|
359
|
+
io.async((e) => {
|
|
360
|
+
return(e.io.await(fetch_with(url_str, FetchOptions.new(), e.io), e));
|
|
364
361
|
})
|
|
365
362
|
);
|
|
366
363
|
export(fetch);
|
package/std/http/http.yo
CHANGED
|
@@ -71,7 +71,7 @@ impl(
|
|
|
71
71
|
result := `${self.method.to_string()} ${self.path} HTTP/1.1\r\n`;
|
|
72
72
|
i := usize(0);
|
|
73
73
|
while(i < self.headers.len(), i = (i + usize(1)), {
|
|
74
|
-
h := self.headers
|
|
74
|
+
h := self.headers(i);
|
|
75
75
|
result = `${result}${h.name}: ${h.value}\r\n`;
|
|
76
76
|
});
|
|
77
77
|
result = `${result}\r\n`;
|
|
@@ -102,7 +102,7 @@ impl(
|
|
|
102
102
|
lower_name := name.to_lowercase();
|
|
103
103
|
i := usize(0);
|
|
104
104
|
while(i < self.headers.len(), i = (i + usize(1)), {
|
|
105
|
-
h := self.headers
|
|
105
|
+
h := self.headers(i);
|
|
106
106
|
cond(
|
|
107
107
|
(h.name.to_lowercase() == lower_name) => {
|
|
108
108
|
return(.Some(h.value));
|
|
@@ -134,7 +134,7 @@ parse_response :: (fn(raw : String) -> Result(HttpResponse, String))({
|
|
|
134
134
|
},
|
|
135
135
|
true => ()
|
|
136
136
|
);
|
|
137
|
-
status_line := lines
|
|
137
|
+
status_line := lines(usize(0));
|
|
138
138
|
cond(
|
|
139
139
|
(!(status_line.starts_with(`HTTP/`))) => {
|
|
140
140
|
return(.Err(`Invalid status line`));
|
|
@@ -172,7 +172,7 @@ parse_response :: (fn(raw : String) -> Result(HttpResponse, String))({
|
|
|
172
172
|
j := usize(1);
|
|
173
173
|
body_start := lines.len();
|
|
174
174
|
while(j < lines.len(), j = (j + usize(1)), {
|
|
175
|
-
hline := lines
|
|
175
|
+
hline := lines(j);
|
|
176
176
|
cond(
|
|
177
177
|
hline.is_empty() => {
|
|
178
178
|
body_start = (j + usize(1));
|
|
@@ -194,10 +194,10 @@ parse_response :: (fn(raw : String) -> Result(HttpResponse, String))({
|
|
|
194
194
|
});
|
|
195
195
|
cond(
|
|
196
196
|
(body_start < lines.len()) => {
|
|
197
|
-
resp.body = lines
|
|
197
|
+
resp.body = lines(body_start);
|
|
198
198
|
m := (body_start + usize(1));
|
|
199
199
|
while(m < lines.len(), m = (m + usize(1)), {
|
|
200
|
-
resp.body = `${resp.body}\r\n${lines
|
|
200
|
+
resp.body = `${resp.body}\r\n${lines(m)}`;
|
|
201
201
|
});
|
|
202
202
|
},
|
|
203
203
|
true => ()
|
package/std/http/index.yo
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
// { HttpRequest, HttpResponse, fetch } :: import "std/http";
|
|
6
6
|
// { Exception } :: import "std/error";
|
|
7
7
|
//
|
|
8
|
-
// main :: (fn(
|
|
9
|
-
//
|
|
8
|
+
// main :: (fn(io : Io) -> unit)({
|
|
9
|
+
// exn := Exception(throw : ((err) -> {
|
|
10
10
|
// println(err.message());
|
|
11
|
-
//
|
|
11
|
+
// unwind ();
|
|
12
12
|
// }));
|
|
13
|
-
// resp := io.await(fetch(`http://example.com`,
|
|
13
|
+
// resp := io.await(fetch(`http://example.com`, io));
|
|
14
14
|
// println(resp.body);
|
|
15
15
|
// });
|
|
16
16
|
_http :: import("./http.yo");
|
package/std/imm/list.yo
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
//! assert((xs.head().unwrap() == i32(1)), "head is 1");
|
|
16
16
|
//! assert((xs.len() == usize(3)), "length is 3");
|
|
17
17
|
//! ```
|
|
18
|
+
pragma(Pragma.AllowUnsafe);
|
|
18
19
|
{ GlobalAllocator } :: import("../allocator.yo");
|
|
19
20
|
{ malloc, free } :: GlobalAllocator;
|
|
20
21
|
{ memcpy } :: import("../libc/string.yo");
|
|
@@ -243,6 +244,38 @@ impl(
|
|
|
243
244
|
})
|
|
244
245
|
)
|
|
245
246
|
);
|
|
247
|
+
/// Index trait — `list(idx)` walks the chain and returns the element at
|
|
248
|
+
/// `idx`, panicking on out-of-bounds. O(n). The Rc-managed `ListNode`
|
|
249
|
+
/// keeps the value field on the heap, so `&(node._value)` is a valid
|
|
250
|
+
/// pointer for the immediate read; the dispatcher inlines and
|
|
251
|
+
/// dereferences before any drop runs.
|
|
252
|
+
impl(
|
|
253
|
+
forall(T : Type),
|
|
254
|
+
where(T <: Send),
|
|
255
|
+
List(T),
|
|
256
|
+
Index(usize)(
|
|
257
|
+
Output : T,
|
|
258
|
+
index : (fn(ref(self) : Self, idx : usize) -> *(Self.Output))({
|
|
259
|
+
assert(idx < self._len, "imm.List: index out of bounds");
|
|
260
|
+
(current : Option(ListNode(T))) = self._head;
|
|
261
|
+
i := usize(0);
|
|
262
|
+
while(runtime(true), {
|
|
263
|
+
match(
|
|
264
|
+
current,
|
|
265
|
+
.Some(node) => {
|
|
266
|
+
if(i == idx, {
|
|
267
|
+
return(unsafe(&(node._value)));
|
|
268
|
+
});
|
|
269
|
+
current = node._next;
|
|
270
|
+
i = (i + usize(1));
|
|
271
|
+
},
|
|
272
|
+
.None => panic("imm.List: unreachable past length check")
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
panic("imm.List: index loop exited without return")
|
|
276
|
+
})
|
|
277
|
+
)
|
|
278
|
+
);
|
|
246
279
|
export(
|
|
247
280
|
List,
|
|
248
281
|
ListNode
|
package/std/imm/map.yo
CHANGED
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
//! m := Map(i32, i32).new();
|
|
15
15
|
//! m = m.insert(i32(1), i32(100));
|
|
16
16
|
//! m = m.insert(i32(2), i32(200));
|
|
17
|
-
//! assert((m
|
|
17
|
+
//! assert((m(i32(1)) == i32(100)), "key 1 maps to 100");
|
|
18
18
|
//! assert((m.len() == usize(2)), "two entries");
|
|
19
19
|
//! ```
|
|
20
|
+
pragma(Pragma.AllowUnsafe);
|
|
20
21
|
{ GlobalAllocator } :: import("../allocator.yo");
|
|
21
22
|
{ malloc, free } :: GlobalAllocator;
|
|
22
23
|
/// Number of bits per HAMT level.
|
package/std/imm/set.yo
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
//! assert(s.contains(i32(1)), "set contains 1");
|
|
18
18
|
//! assert((s.len() == usize(2)), "two elements");
|
|
19
19
|
//! ```
|
|
20
|
+
pragma(Pragma.AllowUnsafe);
|
|
20
21
|
{ Map } :: import("./map.yo");
|
|
21
22
|
{ List } :: import("./list.yo");
|
|
22
23
|
/// Persistent immutable hash set backed by a HAMT.
|
package/std/imm/sorted_map.yo
CHANGED
package/std/imm/sorted_set.yo
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
//! s = s.insert(i32(2));
|
|
18
18
|
//! // elements are always sorted: 1, 2, 3
|
|
19
19
|
//! ```
|
|
20
|
+
pragma(Pragma.AllowUnsafe);
|
|
20
21
|
{ SortedMap } :: import("./sorted_map.yo");
|
|
21
22
|
{ List } :: import("./list.yo");
|
|
22
23
|
/// Persistent immutable sorted set backed by a left-leaning red-black tree.
|