@shd101wyy/yo 0.1.21 → 0.1.22
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-core-patterns/core-patterns-cheatsheet.md +2 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +2 -1
- package/out/cjs/index.cjs +552 -548
- package/out/cjs/yo-cli.cjs +671 -667
- package/out/cjs/yo-lsp.cjs +558 -554
- package/out/esm/index.mjs +485 -481
- package/out/types/src/codegen/exprs/drop-dup.d.ts +1 -0
- package/out/types/src/evaluator/trait-checking.d.ts +11 -1
- package/out/types/src/evaluator/values/impl.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/install.ps1 +143 -0
- package/scripts/install.sh +139 -0
- package/std/cli/arg_parser.yo +148 -24
- package/std/collections/array_list.yo +77 -0
- package/std/collections/hash_map.yo +87 -0
- package/std/collections/hash_set.yo +44 -0
- package/std/collections/ordered_map.yo +244 -0
- package/std/{process.yo → env/index.yo} +38 -89
- package/std/fs/temp.yo +2 -1
- package/std/os/env.yo +2 -1
- package/std/prelude.yo +404 -3
- package/std/process/command.yo +303 -0
- package/std/process/index.yo +45 -0
- package/std/string/index.yo +3 -1
- package/std/string/string.yo +245 -1
- package/std/string/string_builder.yo +138 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
//! High-level child-process spawning with builder-style configuration.
|
|
2
|
+
//!
|
|
3
|
+
//! Wraps `std/sys/process` with a fluent API for constructing argv, capturing
|
|
4
|
+
//! stdout/stderr through pipes, and waiting for the child to exit.
|
|
5
|
+
//!
|
|
6
|
+
//! # Example
|
|
7
|
+
//!
|
|
8
|
+
//! ```rust
|
|
9
|
+
//! { Command } :: import "std/process/command";
|
|
10
|
+
//! { Exception, AnyError } :: import "std/error";
|
|
11
|
+
//!
|
|
12
|
+
//! main :: (fn(using(io : IO)) -> unit)({
|
|
13
|
+
//! given(exn) := Exception(
|
|
14
|
+
//! throw : (fn(forall(T : Type), error: AnyError) -> T)(
|
|
15
|
+
//! { println(error); panic("Exception"); }
|
|
16
|
+
//! )
|
|
17
|
+
//! );
|
|
18
|
+
//!
|
|
19
|
+
//! cmd := Command.new(`echo`);
|
|
20
|
+
//! cmd.arg(`hello`);
|
|
21
|
+
//! cmd.arg(`world`);
|
|
22
|
+
//! out := io.await(cmd.output());
|
|
23
|
+
//! assert(out.status.success(), "echo should succeed");
|
|
24
|
+
//! });
|
|
25
|
+
//! ```
|
|
26
|
+
|
|
27
|
+
open import "../collections/array_list";
|
|
28
|
+
open import "../string";
|
|
29
|
+
{ Exception } :: import "../error";
|
|
30
|
+
{ IOError } :: import "../sys/errors";
|
|
31
|
+
IO_process :: import "../sys/process";
|
|
32
|
+
IO_file :: import "../sys/file";
|
|
33
|
+
IO_pipe :: import "../sys/pipe";
|
|
34
|
+
{ GlobalAllocator } :: import "../allocator";
|
|
35
|
+
{ malloc, free } :: GlobalAllocator;
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// ExitStatus
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/// The result of a finished child process.
|
|
42
|
+
///
|
|
43
|
+
/// Holds the raw waitpid status code along with helpers to extract the exit
|
|
44
|
+
/// code and termination signal.
|
|
45
|
+
ExitStatus :: struct(
|
|
46
|
+
/// Encoded waitpid status. Use `code()` / `signal()` helpers instead of
|
|
47
|
+
/// reading this directly when possible.
|
|
48
|
+
raw : i32
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
impl(ExitStatus,
|
|
52
|
+
/// Returns the process exit code (0..255 on Unix). Returns 0 when the
|
|
53
|
+
/// process was terminated by a signal.
|
|
54
|
+
code : (fn(self: Self) -> i32)(
|
|
55
|
+
IO_process.exit_status(self.raw)
|
|
56
|
+
),
|
|
57
|
+
|
|
58
|
+
/// Returns the signal number that terminated the process, or 0 if the
|
|
59
|
+
/// process exited normally.
|
|
60
|
+
signal : (fn(self: Self) -> i32)(
|
|
61
|
+
IO_process.term_signal(self.raw)
|
|
62
|
+
),
|
|
63
|
+
|
|
64
|
+
/// Returns true if the child exited with status code 0.
|
|
65
|
+
success : (fn(self: Self) -> bool)(
|
|
66
|
+
((IO_process.exit_status(self.raw) == i32(0)) && (IO_process.term_signal(self.raw) == i32(0)))
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// Output
|
|
72
|
+
// ============================================================================
|
|
73
|
+
|
|
74
|
+
/// Captured output of a child process.
|
|
75
|
+
///
|
|
76
|
+
/// Returned by `Command.output`. Holds the exit status plus the bytes captured
|
|
77
|
+
/// from the child's stdout and stderr.
|
|
78
|
+
Output :: object(
|
|
79
|
+
status : ExitStatus,
|
|
80
|
+
stdout : ArrayList(u8),
|
|
81
|
+
stderr : ArrayList(u8)
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Command
|
|
86
|
+
// ============================================================================
|
|
87
|
+
|
|
88
|
+
/// Builder for a child-process invocation.
|
|
89
|
+
///
|
|
90
|
+
/// Construct with `Command.new(program)`, then chain mutating builder methods
|
|
91
|
+
/// (`arg`, `args`, `env_clear`) before invoking `status()` or `output()`.
|
|
92
|
+
Command :: object(
|
|
93
|
+
_program : String,
|
|
94
|
+
_args : ArrayList(String),
|
|
95
|
+
_stdin_fd : i32,
|
|
96
|
+
_stdout_fd : i32,
|
|
97
|
+
_stderr_fd : i32
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
impl(Command,
|
|
101
|
+
/// Create a new `Command` invoking `program`. The program name is also used
|
|
102
|
+
/// as `argv[0]` unless a different first arg is pushed manually before
|
|
103
|
+
/// invocation.
|
|
104
|
+
new : (fn(program: String) -> Self)(
|
|
105
|
+
Self(
|
|
106
|
+
_program: program,
|
|
107
|
+
_args: ArrayList(String).new(),
|
|
108
|
+
_stdin_fd: i32(-1),
|
|
109
|
+
_stdout_fd: i32(-1),
|
|
110
|
+
_stderr_fd: i32(-1)
|
|
111
|
+
)
|
|
112
|
+
),
|
|
113
|
+
|
|
114
|
+
/// Append a single argument to the argv list.
|
|
115
|
+
arg : (fn(self: *(Self), a: String) -> unit)({
|
|
116
|
+
self._args.push(a);
|
|
117
|
+
()
|
|
118
|
+
}),
|
|
119
|
+
|
|
120
|
+
/// Append multiple arguments to the argv list.
|
|
121
|
+
args : (fn(self: *(Self), more: ArrayList(String)) -> unit)({
|
|
122
|
+
n := more.len();
|
|
123
|
+
i := usize(0);
|
|
124
|
+
while runtime((i < n)), {
|
|
125
|
+
match(more.get(i),
|
|
126
|
+
.Some(s) => { self._args.push(s); },
|
|
127
|
+
.None => ()
|
|
128
|
+
);
|
|
129
|
+
i = (i + usize(1));
|
|
130
|
+
};
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Internal helpers
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
// Convert each entry of `(program + args)` into an owned NUL-terminated
|
|
139
|
+
// `ArrayList(u8)`. The returned list owns the C-string storage; callers
|
|
140
|
+
// extract the raw `*(u8)` pointers separately.
|
|
141
|
+
_build_cstr_storage :: (fn(self: *(Command)) -> ArrayList(ArrayList(u8)))({
|
|
142
|
+
storage := ArrayList(ArrayList(u8)).new();
|
|
143
|
+
storage.push(self._program.to_cstr());
|
|
144
|
+
n := self._args.len();
|
|
145
|
+
i := usize(0);
|
|
146
|
+
while runtime((i < n)), {
|
|
147
|
+
match(self._args.get(i),
|
|
148
|
+
.Some(s) => { storage.push(s.to_cstr()); },
|
|
149
|
+
.None => ()
|
|
150
|
+
);
|
|
151
|
+
i = (i + usize(1));
|
|
152
|
+
};
|
|
153
|
+
return storage;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Spawn the child with the given inherited/redirected fds and return the pid.
|
|
157
|
+
// Builds argv as a fixed `?*(u8)` array on the heap. Caller is responsible for
|
|
158
|
+
// freeing `argv_buf` AND keeping `storage` alive until after `__yo_async_spawn_start`
|
|
159
|
+
// returns the pid (since the kernel reads argv synchronously inside the syscall).
|
|
160
|
+
_spawn_with_fds :: (fn(
|
|
161
|
+
self: *(Command),
|
|
162
|
+
stdin_fd: i32,
|
|
163
|
+
stdout_fd: i32,
|
|
164
|
+
stderr_fd: i32,
|
|
165
|
+
using(io : IO, exn : Exception)
|
|
166
|
+
) -> Impl(Future(i32, IO, Exception)))({
|
|
167
|
+
io.async((using(io, exn)) => {
|
|
168
|
+
storage := _build_cstr_storage(self);
|
|
169
|
+
argc := storage.len();
|
|
170
|
+
|
|
171
|
+
// Allocate argv buffer: argc entries + 1 null terminator.
|
|
172
|
+
argv_bytes := (sizeof(?*(u8)) * (argc + usize(1)));
|
|
173
|
+
argv_raw := malloc(argv_bytes).unwrap();
|
|
174
|
+
argv_buf := *(?*(u8))(argv_raw);
|
|
175
|
+
|
|
176
|
+
// Fill argv pointers from each owning ArrayList(u8) in storage.
|
|
177
|
+
j := usize(0);
|
|
178
|
+
while runtime((j < argc)), {
|
|
179
|
+
match(storage.get(j),
|
|
180
|
+
.Some(cs) => match(cs.ptr(),
|
|
181
|
+
.Some(p) => { (argv_buf &+ j).* = .Some(p); },
|
|
182
|
+
.None => { (argv_buf &+ j).* = .None; }
|
|
183
|
+
),
|
|
184
|
+
.None => { (argv_buf &+ j).* = .None; }
|
|
185
|
+
);
|
|
186
|
+
j = (j + usize(1));
|
|
187
|
+
};
|
|
188
|
+
(argv_buf &+ argc).* = .None;
|
|
189
|
+
|
|
190
|
+
// Resolve program path C string (always present — _build_cstr_storage pushes program first).
|
|
191
|
+
program_cstr := (argv_buf &+ usize(0)).*.unwrap();
|
|
192
|
+
|
|
193
|
+
pid := io.await(IO_process.spawn(program_cstr, argv_buf, .None, stdin_fd, stdout_fd, stderr_fd));
|
|
194
|
+
|
|
195
|
+
// Free argv buffer (storage is dropped naturally at scope end).
|
|
196
|
+
free(.Some(argv_raw));
|
|
197
|
+
|
|
198
|
+
IOError.check(pid)
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Read all available bytes from `fd` into a freshly returned ArrayList(u8)
|
|
203
|
+
// until EOF (read returns 0).
|
|
204
|
+
_drain_fd :: (fn(fd: i32, using(io : IO, exn : Exception)) -> Impl(Future(ArrayList(u8), IO, Exception)))({
|
|
205
|
+
io.async((using(io, exn)) => {
|
|
206
|
+
buf_size := usize(4096);
|
|
207
|
+
buf := *(u8)(malloc(buf_size).unwrap());
|
|
208
|
+
out := ArrayList(u8).new();
|
|
209
|
+
while runtime(true), {
|
|
210
|
+
n := io.await(IO_file.read(fd, buf, u32(buf_size), u64(0)));
|
|
211
|
+
cond(
|
|
212
|
+
(n < i32(0)) => {
|
|
213
|
+
free(.Some(*(void)(buf)));
|
|
214
|
+
exn.throw(dyn IOError.from_errno((i32(0) - n)));
|
|
215
|
+
},
|
|
216
|
+
(n == i32(0)) => break,
|
|
217
|
+
true => {
|
|
218
|
+
k := usize(0);
|
|
219
|
+
while runtime((k < usize(n))), {
|
|
220
|
+
out.push((buf &+ k).*);
|
|
221
|
+
k = (k + usize(1));
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
free(.Some(*(void)(buf)));
|
|
227
|
+
out
|
|
228
|
+
})
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// ============================================================================
|
|
232
|
+
// Public Command methods (status / output)
|
|
233
|
+
// ============================================================================
|
|
234
|
+
|
|
235
|
+
impl(Command,
|
|
236
|
+
/// Spawn the child with stdio inherited from the parent, wait for it to
|
|
237
|
+
/// exit, and return its `ExitStatus`.
|
|
238
|
+
status : (fn(self: *(Self), using(io : IO)) -> Impl(Future(ExitStatus, IO, Exception)))({
|
|
239
|
+
io.async((using(io, exn)) => {
|
|
240
|
+
pid := io.await(_spawn_with_fds(self, i32(-1), i32(-1), i32(-1)));
|
|
241
|
+
raw := io.await(IO_process.waitpid(pid, i32(0)));
|
|
242
|
+
IOError.check(raw);
|
|
243
|
+
ExitStatus(raw: raw)
|
|
244
|
+
})
|
|
245
|
+
}),
|
|
246
|
+
|
|
247
|
+
/// Spawn the child with stdout and stderr captured through pipes. Waits for
|
|
248
|
+
/// the child to exit and returns the exit status plus the captured bytes.
|
|
249
|
+
output : (fn(self: *(Self), using(io : IO)) -> Impl(Future(Output, IO, Exception)))({
|
|
250
|
+
io.async((using(io, exn)) => {
|
|
251
|
+
// Create stdout and stderr pipes.
|
|
252
|
+
out_fd_buf := MaybeUninit(Array(i32, usize(2))).new();
|
|
253
|
+
out_fd := *(i32)(out_fd_buf.as_ptr());
|
|
254
|
+
pr1 := IO_pipe.pipe(out_fd);
|
|
255
|
+
cond(
|
|
256
|
+
(pr1 < i32(0)) => exn.throw(dyn IOError.from_errno((i32(0) - pr1))),
|
|
257
|
+
true => ()
|
|
258
|
+
);
|
|
259
|
+
out_read := (out_fd &+ usize(0)).*;
|
|
260
|
+
out_write := (out_fd &+ usize(1)).*;
|
|
261
|
+
|
|
262
|
+
err_fd_buf := MaybeUninit(Array(i32, usize(2))).new();
|
|
263
|
+
err_fd := *(i32)(err_fd_buf.as_ptr());
|
|
264
|
+
pr2 := IO_pipe.pipe(err_fd);
|
|
265
|
+
cond(
|
|
266
|
+
(pr2 < i32(0)) => {
|
|
267
|
+
io.await(IO_file.close(out_read));
|
|
268
|
+
io.await(IO_file.close(out_write));
|
|
269
|
+
exn.throw(dyn IOError.from_errno((i32(0) - pr2)));
|
|
270
|
+
},
|
|
271
|
+
true => ()
|
|
272
|
+
);
|
|
273
|
+
err_read := (err_fd &+ usize(0)).*;
|
|
274
|
+
err_write := (err_fd &+ usize(1)).*;
|
|
275
|
+
|
|
276
|
+
// Spawn child with the write ends of both pipes.
|
|
277
|
+
pid := io.await(_spawn_with_fds(self, i32(-1), out_write, err_write));
|
|
278
|
+
|
|
279
|
+
// Close write ends in parent so reads see EOF when child exits.
|
|
280
|
+
io.await(IO_file.close(out_write));
|
|
281
|
+
io.await(IO_file.close(err_write));
|
|
282
|
+
|
|
283
|
+
// Drain both pipes sequentially.
|
|
284
|
+
stdout_buf := io.await(_drain_fd(out_read));
|
|
285
|
+
stderr_buf := io.await(_drain_fd(err_read));
|
|
286
|
+
|
|
287
|
+
io.await(IO_file.close(out_read));
|
|
288
|
+
io.await(IO_file.close(err_read));
|
|
289
|
+
|
|
290
|
+
raw := io.await(IO_process.waitpid(pid, i32(0)));
|
|
291
|
+
IOError.check(raw);
|
|
292
|
+
Output(
|
|
293
|
+
status: ExitStatus(raw: raw),
|
|
294
|
+
stdout: stdout_buf,
|
|
295
|
+
stderr: stderr_buf
|
|
296
|
+
)
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
export
|
|
302
|
+
Command, ExitStatus, Output
|
|
303
|
+
;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Process information: platform/arch detection, child-process spawning, and exit.
|
|
2
|
+
//!
|
|
3
|
+
//! Environment variables, command-line arguments, and current working
|
|
4
|
+
//! directory live in `std/env`.
|
|
5
|
+
|
|
6
|
+
/// Current target platform as a compile-time string.
|
|
7
|
+
/// One of: "linux", "macos", "windows", "freebsd", "emscripten", "wasi".
|
|
8
|
+
platform :: __yo_process_platform();
|
|
9
|
+
export platform;
|
|
10
|
+
|
|
11
|
+
/// Platform constants for compile-time platform comparisons.
|
|
12
|
+
Platform :: {
|
|
13
|
+
Linux : "linux",
|
|
14
|
+
Macos : "macos",
|
|
15
|
+
Windows : "windows",
|
|
16
|
+
FreeBSD : "freebsd",
|
|
17
|
+
Emscripten : "emscripten",
|
|
18
|
+
Wasi : "wasi"
|
|
19
|
+
};
|
|
20
|
+
export Platform;
|
|
21
|
+
|
|
22
|
+
/// Current target architecture as a compile-time string.
|
|
23
|
+
/// One of: "x86_64", "aarch64", "x86", "arm", "wasm32".
|
|
24
|
+
arch :: __yo_process_arch();
|
|
25
|
+
export arch;
|
|
26
|
+
|
|
27
|
+
/// Architecture constants for compile-time architecture comparisons.
|
|
28
|
+
Arch :: {
|
|
29
|
+
X86_64 : "x86_64",
|
|
30
|
+
Aarch64 : "aarch64",
|
|
31
|
+
X86 : "x86",
|
|
32
|
+
Arm : "arm",
|
|
33
|
+
Wasm32 : "wasm32"
|
|
34
|
+
};
|
|
35
|
+
export Arch;
|
|
36
|
+
|
|
37
|
+
/// Exit the process with the given status code.
|
|
38
|
+
exit :: (fn(code : usize) -> unit) {
|
|
39
|
+
{ exit : _exit } :: import "../libc/stdlib";
|
|
40
|
+
_exit(int(code));
|
|
41
|
+
};
|
|
42
|
+
export exit;
|
|
43
|
+
|
|
44
|
+
_command :: import "./command.yo";
|
|
45
|
+
export ...(_command);
|
package/std/string/index.yo
CHANGED
package/std/string/string.yo
CHANGED
|
@@ -1558,6 +1558,226 @@ impl(String,
|
|
|
1558
1558
|
)
|
|
1559
1559
|
);
|
|
1560
1560
|
|
|
1561
|
+
/// === String utility methods ===
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Line iterator for `String` — yields one line at a time (split on `\n`).
|
|
1565
|
+
* The trailing newline is not included in each yielded line.
|
|
1566
|
+
* Used by `lines()`.
|
|
1567
|
+
*/
|
|
1568
|
+
StringLines :: struct(
|
|
1569
|
+
_string : String,
|
|
1570
|
+
_byte_index : usize
|
|
1571
|
+
);
|
|
1572
|
+
|
|
1573
|
+
impl(StringLines, Iterator(
|
|
1574
|
+
Item : String,
|
|
1575
|
+
next : (fn(self : *(Self)) -> Option(String))(
|
|
1576
|
+
match(self._string._bytes,
|
|
1577
|
+
.None => .None,
|
|
1578
|
+
.Some(al) => {
|
|
1579
|
+
total := al.len();
|
|
1580
|
+
cond(
|
|
1581
|
+
(self._byte_index >= total) => .None,
|
|
1582
|
+
true => {
|
|
1583
|
+
start := self._byte_index;
|
|
1584
|
+
i := start;
|
|
1585
|
+
while ((i < total)),
|
|
1586
|
+
(i = (i + usize(1))),
|
|
1587
|
+
{
|
|
1588
|
+
byte_opt := al.get(i);
|
|
1589
|
+
match(byte_opt,
|
|
1590
|
+
.Some(b) => {
|
|
1591
|
+
cond(
|
|
1592
|
+
(b == u8(10)) => { break; },
|
|
1593
|
+
true => ()
|
|
1594
|
+
);
|
|
1595
|
+
},
|
|
1596
|
+
.None => ()
|
|
1597
|
+
);
|
|
1598
|
+
};
|
|
1599
|
+
line_buf := ArrayList(u8).with_capacity((i - start));
|
|
1600
|
+
j := start;
|
|
1601
|
+
while ((j < i)),
|
|
1602
|
+
(j = (j + usize(1))),
|
|
1603
|
+
{
|
|
1604
|
+
byte_opt := al.get(j);
|
|
1605
|
+
match(byte_opt,
|
|
1606
|
+
.Some(b) => { line_buf.push(b); },
|
|
1607
|
+
.None => ()
|
|
1608
|
+
);
|
|
1609
|
+
};
|
|
1610
|
+
self._byte_index = cond(
|
|
1611
|
+
(i < total) => (i + usize(1)),
|
|
1612
|
+
true => total
|
|
1613
|
+
);
|
|
1614
|
+
cond(
|
|
1615
|
+
(line_buf.len() == usize(0)) => .Some(String(_bytes: .None)),
|
|
1616
|
+
true => .Some(String(_bytes: .Some(line_buf)))
|
|
1617
|
+
)
|
|
1618
|
+
}
|
|
1619
|
+
)
|
|
1620
|
+
}
|
|
1621
|
+
)
|
|
1622
|
+
)
|
|
1623
|
+
));
|
|
1624
|
+
|
|
1625
|
+
impl(String,
|
|
1626
|
+
/**
|
|
1627
|
+
* Returns a line iterator over the string.
|
|
1628
|
+
* Each call to `next()` yields the next line (without the trailing `\n`).
|
|
1629
|
+
*
|
|
1630
|
+
* ## Example
|
|
1631
|
+
* ```rust
|
|
1632
|
+
* s := `hello\nworld`;
|
|
1633
|
+
* iter := s.lines();
|
|
1634
|
+
* assert(iter.next() == .Some(`hello`), "first line");
|
|
1635
|
+
* assert(iter.next() == .Some(`world`), "second line");
|
|
1636
|
+
* assert(iter.next() == .None, "done");
|
|
1637
|
+
* ```
|
|
1638
|
+
*/
|
|
1639
|
+
lines : (fn(self : Self) -> StringLines)(
|
|
1640
|
+
StringLines(_string: self, _byte_index: usize(0))
|
|
1641
|
+
),
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Returns a new string containing `n` copies of `self`.
|
|
1645
|
+
* Returns an empty string when `n == 0` or `self` is empty.
|
|
1646
|
+
*
|
|
1647
|
+
* ## Example
|
|
1648
|
+
* ```rust
|
|
1649
|
+
* assert(`ab`.repeat(usize(3)) == `ababab`, "repeat");
|
|
1650
|
+
* assert(`x`.repeat(usize(0)) == ``, "repeat 0");
|
|
1651
|
+
* ```
|
|
1652
|
+
*/
|
|
1653
|
+
repeat : (fn(self: Self, n: usize) -> Self)({
|
|
1654
|
+
cond(
|
|
1655
|
+
(n == usize(0)) => Self(_bytes: .None),
|
|
1656
|
+
self.is_empty() => Self(_bytes: .None),
|
|
1657
|
+
true => {
|
|
1658
|
+
self_byte_len := self.bytes_len();
|
|
1659
|
+
total := (self_byte_len * n);
|
|
1660
|
+
buf := ArrayList(u8).with_capacity(total);
|
|
1661
|
+
i := usize(0);
|
|
1662
|
+
while ((i < n)),
|
|
1663
|
+
(i = (i + usize(1))),
|
|
1664
|
+
{
|
|
1665
|
+
match(self._bytes,
|
|
1666
|
+
.Some(al) => {
|
|
1667
|
+
j := usize(0);
|
|
1668
|
+
while ((j < self_byte_len)),
|
|
1669
|
+
(j = (j + usize(1))),
|
|
1670
|
+
{
|
|
1671
|
+
byte_opt := al.get(j);
|
|
1672
|
+
match(byte_opt,
|
|
1673
|
+
.Some(b) => { buf.push(b); },
|
|
1674
|
+
.None => ()
|
|
1675
|
+
);
|
|
1676
|
+
};
|
|
1677
|
+
},
|
|
1678
|
+
.None => ()
|
|
1679
|
+
);
|
|
1680
|
+
};
|
|
1681
|
+
Self(_bytes: .Some(buf))
|
|
1682
|
+
}
|
|
1683
|
+
)
|
|
1684
|
+
}),
|
|
1685
|
+
|
|
1686
|
+
/**
|
|
1687
|
+
* Join an `ArrayList(String)` with this string as separator.
|
|
1688
|
+
* Returns an empty string when `items` is empty.
|
|
1689
|
+
*
|
|
1690
|
+
* ## Example
|
|
1691
|
+
* ```rust
|
|
1692
|
+
* items := ArrayList(String).new();
|
|
1693
|
+
* items.push(`a`);
|
|
1694
|
+
* items.push(`b`);
|
|
1695
|
+
* items.push(`c`);
|
|
1696
|
+
* result := `, `.join(items);
|
|
1697
|
+
* assert(result == `a, b, c`, "join");
|
|
1698
|
+
* ```
|
|
1699
|
+
*/
|
|
1700
|
+
join : (fn(self: Self, items: ArrayList(Self)) -> Self)({
|
|
1701
|
+
count := items.len();
|
|
1702
|
+
cond(
|
|
1703
|
+
(count == usize(0)) => Self(_bytes: .None),
|
|
1704
|
+
true => {
|
|
1705
|
+
sep_byte_len := self.bytes_len();
|
|
1706
|
+
total_bytes := usize(0);
|
|
1707
|
+
k := usize(0);
|
|
1708
|
+
while ((k < count)),
|
|
1709
|
+
(k = (k + usize(1))),
|
|
1710
|
+
{
|
|
1711
|
+
item_opt := items.get(k);
|
|
1712
|
+
match(item_opt,
|
|
1713
|
+
.Some(item) => {
|
|
1714
|
+
total_bytes = (total_bytes + item.bytes_len());
|
|
1715
|
+
},
|
|
1716
|
+
.None => ()
|
|
1717
|
+
);
|
|
1718
|
+
};
|
|
1719
|
+
cond(
|
|
1720
|
+
(count > usize(1)) => {
|
|
1721
|
+
total_bytes = (total_bytes + (sep_byte_len * (count - usize(1))));
|
|
1722
|
+
},
|
|
1723
|
+
true => ()
|
|
1724
|
+
);
|
|
1725
|
+
buf := ArrayList(u8).with_capacity(total_bytes);
|
|
1726
|
+
m := usize(0);
|
|
1727
|
+
while ((m < count)),
|
|
1728
|
+
(m = (m + usize(1))),
|
|
1729
|
+
{
|
|
1730
|
+
item_opt := items.get(m);
|
|
1731
|
+
match(item_opt,
|
|
1732
|
+
.Some(item) => {
|
|
1733
|
+
match(item._bytes,
|
|
1734
|
+
.Some(al) => {
|
|
1735
|
+
p := usize(0);
|
|
1736
|
+
while ((p < al.len())),
|
|
1737
|
+
(p = (p + usize(1))),
|
|
1738
|
+
{
|
|
1739
|
+
byte_opt := al.get(p);
|
|
1740
|
+
match(byte_opt,
|
|
1741
|
+
.Some(b) => { buf.push(b); },
|
|
1742
|
+
.None => ()
|
|
1743
|
+
);
|
|
1744
|
+
};
|
|
1745
|
+
},
|
|
1746
|
+
.None => ()
|
|
1747
|
+
);
|
|
1748
|
+
cond(
|
|
1749
|
+
(m < (count - usize(1))) => {
|
|
1750
|
+
match(self._bytes,
|
|
1751
|
+
.Some(sep_al) => {
|
|
1752
|
+
q := usize(0);
|
|
1753
|
+
while ((q < sep_byte_len)),
|
|
1754
|
+
(q = (q + usize(1))),
|
|
1755
|
+
{
|
|
1756
|
+
byte_opt := sep_al.get(q);
|
|
1757
|
+
match(byte_opt,
|
|
1758
|
+
.Some(b) => { buf.push(b); },
|
|
1759
|
+
.None => ()
|
|
1760
|
+
);
|
|
1761
|
+
};
|
|
1762
|
+
},
|
|
1763
|
+
.None => ()
|
|
1764
|
+
);
|
|
1765
|
+
},
|
|
1766
|
+
true => ()
|
|
1767
|
+
);
|
|
1768
|
+
},
|
|
1769
|
+
.None => ()
|
|
1770
|
+
);
|
|
1771
|
+
};
|
|
1772
|
+
cond(
|
|
1773
|
+
(buf.len() == usize(0)) => Self(_bytes: .None),
|
|
1774
|
+
true => Self(_bytes: .Some(buf))
|
|
1775
|
+
)
|
|
1776
|
+
}
|
|
1777
|
+
)
|
|
1778
|
+
})
|
|
1779
|
+
);
|
|
1780
|
+
|
|
1561
1781
|
/// === Numeric parsing methods ===
|
|
1562
1782
|
|
|
1563
1783
|
impl(String,
|
|
@@ -1932,9 +2152,33 @@ impl(String, Index(usize)(
|
|
|
1932
2152
|
)
|
|
1933
2153
|
));
|
|
1934
2154
|
|
|
2155
|
+
/// Clone implementation — produces an independent byte-level copy.
|
|
2156
|
+
impl(String, Clone(
|
|
2157
|
+
clone : (fn(self: *(Self)) -> Self)(
|
|
2158
|
+
match(self._bytes,
|
|
2159
|
+
.None => Self(_bytes: .None),
|
|
2160
|
+
.Some(al) => {
|
|
2161
|
+
n := al.len();
|
|
2162
|
+
cond(
|
|
2163
|
+
(n == usize(0)) => Self(_bytes: .None),
|
|
2164
|
+
true => {
|
|
2165
|
+
buf := ArrayList(u8).with_capacity(n);
|
|
2166
|
+
match(al._ptr,
|
|
2167
|
+
.Some(ptr) => buf.extend_from_ptr(ptr, n),
|
|
2168
|
+
.None => ()
|
|
2169
|
+
);
|
|
2170
|
+
Self(_bytes: .Some(buf))
|
|
2171
|
+
}
|
|
2172
|
+
)
|
|
2173
|
+
}
|
|
2174
|
+
)
|
|
2175
|
+
)
|
|
2176
|
+
));
|
|
2177
|
+
|
|
1935
2178
|
export
|
|
1936
2179
|
String,
|
|
1937
2180
|
StringError,
|
|
1938
2181
|
StringChars,
|
|
1939
|
-
StringBytes
|
|
2182
|
+
StringBytes,
|
|
2183
|
+
StringLines
|
|
1940
2184
|
;
|