@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.
Files changed (183) hide show
  1. package/.github/skills/yo-async-effects/SKILL.md +15 -15
  2. package/.github/skills/yo-async-effects/async-effects-recipes.md +118 -121
  3. package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +33 -13
  4. package/.github/skills/yo-project-workflow/workflow-cheatsheet.md +1 -1
  5. package/.github/skills/yo-syntax/SKILL.md +2 -2
  6. package/.github/skills/yo-syntax/syntax-cheatsheet.md +108 -96
  7. package/README.md +6 -3
  8. package/out/cjs/index.cjs +812 -706
  9. package/out/cjs/yo-cli.cjs +1023 -907
  10. package/out/cjs/yo-lsp.cjs +836 -730
  11. package/out/esm/index.mjs +757 -651
  12. package/out/types/src/codegen/exprs/async.d.ts +2 -0
  13. package/out/types/src/codegen/exprs/await.d.ts +1 -0
  14. package/out/types/src/codegen/exprs/closures.d.ts +4 -0
  15. package/out/types/src/codegen/functions/context.d.ts +6 -0
  16. package/out/types/src/codegen/functions/declarations.d.ts +1 -1
  17. package/out/types/src/doc/model.d.ts +0 -1
  18. package/out/types/src/env.d.ts +2 -2
  19. package/out/types/src/evaluator/builtins/pragma.d.ts +9 -0
  20. package/out/types/src/evaluator/builtins/unsafe.d.ts +8 -0
  21. package/out/types/src/evaluator/context.d.ts +3 -1
  22. package/out/types/src/evaluator/exprs/{escape.d.ts → unwind.d.ts} +1 -1
  23. package/out/types/src/evaluator/index.d.ts +1 -1
  24. package/out/types/src/evaluator/memory-safety.d.ts +14 -0
  25. package/out/types/src/evaluator/types/flowability.d.ts +6 -0
  26. package/out/types/src/evaluator/types/function.d.ts +1 -2
  27. package/out/types/src/evaluator/utils.d.ts +0 -1
  28. package/out/types/src/expr-traversal.d.ts +1 -0
  29. package/out/types/src/expr.d.ts +9 -7
  30. package/out/types/src/public-safe-report.d.ts +19 -0
  31. package/out/types/src/tests/comptime-ref-gate.test.d.ts +1 -0
  32. package/out/types/src/tests/pragma-validation.test.d.ts +1 -0
  33. package/out/types/src/tests/public-safe-report.test.d.ts +1 -0
  34. package/out/types/src/tests/type-representation-pointer.test.d.ts +1 -0
  35. package/out/types/src/tests/unsafe-gate.test.d.ts +1 -0
  36. package/out/types/src/tests/unsafe-report-classify.test.d.ts +1 -0
  37. package/out/types/src/types/creators.d.ts +4 -6
  38. package/out/types/src/types/definitions.d.ts +9 -16
  39. package/out/types/src/types/guards.d.ts +1 -2
  40. package/out/types/src/types/tags.d.ts +0 -1
  41. package/out/types/src/types/utils.d.ts +5 -0
  42. package/out/types/src/unsafe-report.d.ts +29 -0
  43. package/out/types/src/value.d.ts +1 -0
  44. package/out/types/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +1 -1
  46. package/scripts/add-pragma-for-pointer-decls.ts +134 -0
  47. package/scripts/add-pragma.ts +58 -0
  48. package/scripts/migrate-amp-method-calls.ts +186 -0
  49. package/scripts/migrate-clone-calls.ts +93 -0
  50. package/scripts/migrate-get-unwrap.ts +166 -0
  51. package/scripts/migrate-index-patterns.ts +210 -0
  52. package/scripts/migrate-index-trait.ts +142 -0
  53. package/scripts/migrate-iterator.ts +150 -0
  54. package/scripts/migrate-self-ptr.ts +220 -0
  55. package/scripts/migrate-skip-pragmas.ts +109 -0
  56. package/scripts/migrate-tostring.ts +134 -0
  57. package/scripts/trim-pragma.ts +130 -0
  58. package/scripts/wrap-extern-calls.ts +161 -0
  59. package/std/alg/hash.yo +3 -2
  60. package/std/allocator.yo +6 -5
  61. package/std/async.yo +2 -2
  62. package/std/collections/array_list.yo +59 -40
  63. package/std/collections/btree_map.yo +19 -18
  64. package/std/collections/deque.yo +9 -8
  65. package/std/collections/hash_map.yo +101 -13
  66. package/std/collections/hash_set.yo +5 -4
  67. package/std/collections/linked_list.yo +39 -4
  68. package/std/collections/ordered_map.yo +3 -3
  69. package/std/collections/priority_queue.yo +14 -13
  70. package/std/crypto/md5.yo +2 -1
  71. package/std/crypto/random.yo +21 -20
  72. package/std/crypto/sha256.yo +2 -1
  73. package/std/encoding/base64.yo +18 -18
  74. package/std/encoding/hex.yo +5 -5
  75. package/std/encoding/json.yo +62 -13
  76. package/std/encoding/punycode.yo +24 -23
  77. package/std/encoding/toml.yo +4 -3
  78. package/std/encoding/utf16.yo +3 -3
  79. package/std/env.yo +43 -28
  80. package/std/error.yo +15 -3
  81. package/std/fmt/display.yo +2 -2
  82. package/std/fmt/index.yo +6 -5
  83. package/std/fmt/to_string.yo +39 -38
  84. package/std/fmt/writer.yo +9 -8
  85. package/std/fs/dir.yo +61 -66
  86. package/std/fs/file.yo +121 -126
  87. package/std/fs/metadata.yo +13 -18
  88. package/std/fs/temp.yo +35 -30
  89. package/std/fs/walker.yo +14 -19
  90. package/std/gc.yo +1 -0
  91. package/std/glob.yo +7 -7
  92. package/std/http/client.yo +33 -36
  93. package/std/http/http.yo +6 -6
  94. package/std/http/index.yo +4 -4
  95. package/std/imm/list.yo +33 -0
  96. package/std/imm/map.yo +2 -1
  97. package/std/imm/set.yo +1 -0
  98. package/std/imm/sorted_map.yo +1 -0
  99. package/std/imm/sorted_set.yo +1 -0
  100. package/std/imm/string.yo +27 -23
  101. package/std/imm/vec.yo +18 -2
  102. package/std/io/reader.yo +2 -1
  103. package/std/io/writer.yo +3 -2
  104. package/std/libc/assert.yo +1 -0
  105. package/std/libc/ctype.yo +1 -0
  106. package/std/libc/dirent.yo +1 -0
  107. package/std/libc/errno.yo +1 -0
  108. package/std/libc/fcntl.yo +1 -0
  109. package/std/libc/float.yo +1 -0
  110. package/std/libc/limits.yo +1 -0
  111. package/std/libc/math.yo +1 -0
  112. package/std/libc/signal.yo +1 -0
  113. package/std/libc/stdatomic.yo +1 -0
  114. package/std/libc/stdint.yo +1 -0
  115. package/std/libc/stdio.yo +1 -0
  116. package/std/libc/stdlib.yo +1 -0
  117. package/std/libc/string.yo +1 -0
  118. package/std/libc/sys/stat.yo +1 -0
  119. package/std/libc/time.yo +1 -0
  120. package/std/libc/unistd.yo +1 -0
  121. package/std/libc/wctype.yo +1 -0
  122. package/std/libc/windows.yo +2 -0
  123. package/std/log.yo +7 -6
  124. package/std/net/addr.yo +6 -5
  125. package/std/net/dns.yo +13 -16
  126. package/std/net/errors.yo +9 -9
  127. package/std/net/tcp.yo +71 -74
  128. package/std/net/udp.yo +40 -43
  129. package/std/os/signal.yo +5 -5
  130. package/std/path.yo +1 -0
  131. package/std/prelude.yo +377 -200
  132. package/std/process/command.yo +57 -46
  133. package/std/process/index.yo +2 -1
  134. package/std/regex/compiler.yo +10 -9
  135. package/std/regex/index.yo +41 -41
  136. package/std/regex/match.yo +2 -2
  137. package/std/regex/parser.yo +31 -31
  138. package/std/regex/vm.yo +42 -41
  139. package/std/string/string.yo +95 -40
  140. package/std/string/string_builder.yo +9 -9
  141. package/std/string/unicode.yo +50 -49
  142. package/std/sync/channel.yo +2 -1
  143. package/std/sync/cond.yo +5 -4
  144. package/std/sync/mutex.yo +4 -3
  145. package/std/sys/advise.yo +1 -0
  146. package/std/sys/bufio/buf_reader.yo +27 -26
  147. package/std/sys/bufio/buf_writer.yo +22 -21
  148. package/std/sys/clock.yo +1 -0
  149. package/std/sys/copy.yo +1 -0
  150. package/std/sys/dir.yo +10 -9
  151. package/std/sys/dns.yo +6 -5
  152. package/std/sys/errors.yo +12 -12
  153. package/std/sys/events.yo +1 -0
  154. package/std/sys/externs.yo +38 -37
  155. package/std/sys/file.yo +17 -16
  156. package/std/sys/future.yo +4 -3
  157. package/std/sys/iov.yo +1 -0
  158. package/std/sys/mmap.yo +1 -0
  159. package/std/sys/path.yo +1 -0
  160. package/std/sys/perm.yo +2 -1
  161. package/std/sys/pipe.yo +1 -0
  162. package/std/sys/process.yo +5 -4
  163. package/std/sys/signal.yo +1 -0
  164. package/std/sys/socketpair.yo +1 -0
  165. package/std/sys/sockinfo.yo +1 -0
  166. package/std/sys/statfs.yo +2 -1
  167. package/std/sys/statx.yo +1 -0
  168. package/std/sys/sysinfo.yo +1 -0
  169. package/std/sys/tcp.yo +15 -14
  170. package/std/sys/temp.yo +1 -0
  171. package/std/sys/time.yo +2 -1
  172. package/std/sys/timer.yo +6 -6
  173. package/std/sys/tty.yo +2 -1
  174. package/std/sys/udp.yo +13 -12
  175. package/std/sys/unix.yo +12 -11
  176. package/std/testing/bench.yo +4 -3
  177. package/std/thread.yo +7 -6
  178. package/std/time/datetime.yo +18 -15
  179. package/std/time/duration.yo +11 -10
  180. package/std/time/instant.yo +4 -4
  181. package/std/time/sleep.yo +1 -0
  182. package/std/url/index.yo +5 -5
  183. 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(using(io : IO)) -> unit)({
9
- //! given(exn) : Exception = {
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
- { IOError } :: import("../sys/errors");
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, using(io : IO)) -> Impl(Future(TempDir, IO, Exception)))(
63
- io.async((using(io, exn)) => {
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
- memcpy(*(void)(buf), *(void)(template_bytes.ptr().unwrap()), template_bytes.len());
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(IOError.from_errno(i32(0) - result)));
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(using(io : IO)) -> Impl(Future(TempDir, IO, Exception)))(
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, using(io : IO)) -> Impl(Future(unit, IO, Exception)))({
96
+ remove : (fn(self : Self, io : Io) -> Impl(Future(unit, IoExn)))({
96
97
  self_path := self._path;
97
98
  io.async(
98
- (using(io, exn)) =>
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, using(io : IO)) -> Impl(Future(TempFile, IO, Exception)))(
120
- io.async((using(io, exn)) => {
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
- memcpy(*(void)(buf), *(void)(template_bytes.ptr().unwrap()), template_bytes.len());
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(IOError.from_errno(i32(0) - fd)));
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(using(io : IO)) -> Impl(Future(TempFile, IO, Exception)))(
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, using(io : IO)) -> Impl(Future(unit, IO, Exception)))({
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
- (using(io, exn)) =>
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(using(io : IO)) -> unit)({
9
- //! given(exn) : Exception = {
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.get(i).unwrap();
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
- { IOError } :: import("../sys/errors");
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, using(io : IO)) -> Impl(Future(ArrayList(WalkEntry), IO, Exception)))({
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((using(io, exn)) => {
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.get(i).unwrap();
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, using(io : IO)) -> Impl(Future(ArrayList(WalkEntry), IO, Exception)))(
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, using(io : IO)) -> Impl(Future(ArrayList(WalkEntry), IO, Exception)))(walk_with(root, WalkOptions.defaults()));
138
- walk_cstr :: (fn(root : *(u8), using(io : IO)) -> Impl(Future(ArrayList(WalkEntry), IO, Exception)))(walk(Path.from_cstr(root)));
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
@@ -1,4 +1,5 @@
1
1
  //! Garbage collection interface for cycle collection.
2
+ pragma(Pragma.AllowUnsafe);
2
3
  extern(
3
4
  "Yo",
4
5
  __yo_gc_collect : (fn() -> unit),
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.get(pi).unwrap();
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.get(pi + usize(1)).unwrap() == u8(42)),
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.get(npi).unwrap() == u8(47)) => {
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.get(ti).unwrap();
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.get(ti).unwrap();
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.get(ci).unwrap();
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.get(ci).unwrap();
137
+ ch := pb(ci);
138
138
  cond(
139
139
  (ch == u8(93)) => {
140
140
  cdone = true;
@@ -8,20 +8,17 @@
8
8
  // { fetch, HttpResponse } :: import "std/http";
9
9
  // { Exception } :: import "std/error";
10
10
  //
11
- // main :: (fn(using(io : IO)) -> unit)({
12
- // given(exn) := Exception(throw : ((err) -> {
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.get(i).unwrap() == u8(13)) &&
120
+ (data(i) == u8(13)) &&
124
121
  (
125
- (data.get(i + usize(1)).unwrap() == u8(10)) &&
122
+ (data(i + usize(1)) == u8(10)) &&
126
123
  (
127
- (data.get(i + usize(2)).unwrap() == u8(13)) &&
128
- (data.get(i + usize(3)).unwrap() == u8(10))
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.get(i).unwrap());
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, using(io : IO, exn : Exception)) -> String)({
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, using(io)));
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, using(io)));
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, using(io : IO)) ->
250
- Impl(Future(HttpResponse, IO, Exception))
246
+ fn(url_str : String, opts : FetchOptions, io : Io) ->
247
+ Impl(Future(HttpResponse, IoExn))
251
248
  )(
252
- io.async((using(io : IO, exn : Exception)) => {
249
+ io.async((e) => {
253
250
  // Parse URL
254
- url := Url.parse(url_str.as_str(), using(exn));
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, using(io)));
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.get(usize(0)).unwrap();
288
+ ip := addrs(usize(0));
292
289
  addr := SocketAddr.new(ip, port);
293
290
  // Connect
294
- stream := io.await(TcpStream.connect(addr, using(io)));
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.get(hi).unwrap();
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, using(io)));
329
+ e.io.await(stream.write_string(req_str, e.io), e);
333
330
  // Read response
334
- raw_response := _read_http_response(stream, using(io, exn));
331
+ raw_response := _read_http_response(stream, e);
335
332
  // Close connection
336
- io.await(stream.close(using(io)));
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(e) => {
342
- exn.throw(dyn(HttpError.Other(e)));
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`, using(io)));
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, using(io : IO)) ->
360
- Impl(Future(HttpResponse, IO, Exception))
356
+ fn(url_str : String, io : Io) ->
357
+ Impl(Future(HttpResponse, IoExn))
361
358
  )(
362
- io.async((using(io : IO, exn : Exception)) => {
363
- return(io.await(fetch_with(url_str, FetchOptions.new(), using(io))));
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.get(i).unwrap();
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.get(i).unwrap();
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.get(usize(0)).unwrap();
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.get(j).unwrap();
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.get(body_start).unwrap();
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.get(m).unwrap()}`;
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(using(io : IO)) -> unit)({
9
- // given(exn) := Exception(throw : ((err) -> {
8
+ // main :: (fn(io : Io) -> unit)({
9
+ // exn := Exception(throw : ((err) -> {
10
10
  // println(err.message());
11
- // escape ();
11
+ // unwind ();
12
12
  // }));
13
- // resp := io.await(fetch(`http://example.com`, using(io)));
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.get(i32(1)).unwrap() == i32(100)), "key 1 maps to 100");
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.
@@ -17,6 +17,7 @@
17
17
  //! m = m.insert(i32(2), i32(20));
18
18
  //! // keys are always sorted: 1, 2, 3
19
19
  //! ```
20
+ pragma(Pragma.AllowUnsafe);
20
21
  { List } :: import("./list.yo");
21
22
  { Pair } :: import("./map.yo");
22
23
  /// Color of a red-black tree node.
@@ -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.