@shd101wyy/yo 0.0.23 → 0.0.25
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/out/cjs/index.cjs +560 -520
- package/out/cjs/yo-cli.cjs +598 -558
- package/out/esm/index.mjs +551 -511
- package/out/types/src/codegen/exprs/arc.d.ts +5 -0
- package/out/types/src/codegen/types/generation.d.ts +2 -0
- package/out/types/src/codegen/utils/index.d.ts +8 -1
- package/out/types/src/evaluator/builtins/rc-fns.d.ts +5 -0
- package/out/types/src/evaluator/calls/arc.d.ts +15 -0
- package/out/types/src/evaluator/calls/trait-type.d.ts +8 -1
- package/out/types/src/evaluator/context.d.ts +11 -1
- package/out/types/src/evaluator/types/utils.d.ts +8 -3
- package/out/types/src/evaluator/utils/closure.d.ts +7 -1
- package/out/types/src/evaluator/values/impl.d.ts +8 -0
- package/out/types/src/expr.d.ts +6 -0
- package/out/types/src/test-runner.d.ts +2 -0
- package/out/types/src/types/creators.d.ts +2 -1
- package/out/types/src/types/definitions.d.ts +10 -0
- package/out/types/src/types/guards.d.ts +2 -1
- package/out/types/src/types/tags.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/std/collections/array_list.yo +80 -10
- package/std/collections/btree_map.yo +120 -2
- package/std/collections/deque.yo +98 -6
- package/std/collections/hash_map.yo +137 -1
- package/std/collections/hash_set.yo +85 -1
- package/std/collections/linked_list.yo +61 -1
- package/std/collections/priority_queue.yo +70 -1
- package/std/crypto/md5.yo +4 -4
- package/std/crypto/random.yo +3 -3
- package/std/crypto/sha256.yo +8 -7
- package/std/encoding/base64.yo +6 -6
- package/std/encoding/hex.yo +27 -22
- package/std/encoding/json.yo +185 -186
- package/std/encoding/utf16.yo +2 -2
- package/std/fmt/display.yo +1 -1
- package/std/fmt/writer.yo +2 -4
- package/std/fs/dir.yo +5 -5
- package/std/fs/file.yo +21 -13
- package/std/fs/metadata.yo +4 -4
- package/std/fs/temp.yo +3 -3
- package/std/fs/types.yo +1 -1
- package/std/fs/walker.yo +1 -1
- package/std/net/addr.yo +2 -2
- package/std/net/dns.yo +21 -20
- package/std/net/errors.yo +1 -1
- package/std/net/tcp.yo +134 -100
- package/std/net/udp.yo +51 -42
- package/std/os/env.yo +21 -23
- package/std/os/signal.yo +10 -9
- package/std/prelude.yo +158 -22
- package/std/string/string.yo +99 -1
- package/std/sync/once.yo +1 -1
- package/std/{io → sys}/advise.yo +1 -1
- package/std/sys/bufio/buf_reader.yo +300 -0
- package/std/sys/bufio/buf_writer.yo +168 -0
- package/std/{io → sys}/clock.yo +1 -1
- package/std/{io → sys}/constants.yo +1 -1
- package/std/{io → sys}/copy.yo +1 -1
- package/std/{io → sys}/dir.yo +6 -6
- package/std/{io → sys}/dns.yo +3 -3
- package/std/{io → sys}/errors.yo +1 -1
- package/std/{io → sys}/events.yo +1 -1
- package/std/{io → sys}/externs.yo +1 -1
- package/std/{io → sys}/fallocate.yo +1 -1
- package/std/{io → sys}/fcntl.yo +1 -1
- package/std/{io → sys}/file.yo +1 -1
- package/std/{io → sys}/future.yo +1 -1
- package/std/{io → sys}/iov.yo +1 -1
- package/std/{io → sys}/lock.yo +1 -1
- package/std/{io → sys}/mmap.yo +1 -1
- package/std/{io → sys}/path.yo +1 -1
- package/std/{io → sys}/perm.yo +3 -3
- package/std/{io → sys}/pipe.yo +1 -1
- package/std/{io → sys}/process.yo +1 -1
- package/std/{io → sys}/seek.yo +1 -1
- package/std/{io → sys}/signal.yo +1 -1
- package/std/{io → sys}/signals.yo +1 -1
- package/std/{io → sys}/socket.yo +1 -1
- package/std/{io → sys}/socketpair.yo +1 -1
- package/std/{io → sys}/sockinfo.yo +1 -1
- package/std/{io → sys}/statfs.yo +2 -2
- package/std/{io → sys}/statx.yo +1 -1
- package/std/{io → sys}/sysinfo.yo +1 -1
- package/std/{io → sys}/tcp.yo +3 -3
- package/std/{io → sys}/temp.yo +1 -1
- package/std/{io → sys}/time.yo +2 -2
- package/std/{io → sys}/timer.yo +1 -1
- package/std/{io → sys}/tty.yo +1 -1
- package/std/{io → sys}/udp.yo +4 -4
- package/std/{io → sys}/umask.yo +1 -1
- package/std/{io → sys}/unix.yo +1 -1
- package/std/time/datetime.yo +18 -23
- package/std/time/instant.yo +13 -11
- package/std/url/url.yo +533 -0
- package/std/math/functions.yo +0 -74
- package/std/math/random.yo +0 -94
package/std/url/url.yo
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
// std/url/url.yo - URL parsing and formatting
|
|
2
|
+
//
|
|
3
|
+
// Parse, manipulate, and format URLs per RFC 3986 (simplified).
|
|
4
|
+
//
|
|
5
|
+
// Example:
|
|
6
|
+
// { Url, UrlError } :: import "std/url/url";
|
|
7
|
+
//
|
|
8
|
+
// url := Url.parse("https://example.com:8080/path?q=1#frag").unwrap();
|
|
9
|
+
// assert(url.scheme() == `https`);
|
|
10
|
+
// assert(url.host() == .Some(`example.com`));
|
|
11
|
+
// assert(url.port() == .Some(u16(8080)));
|
|
12
|
+
// assert(url.path() == `/path`));
|
|
13
|
+
// assert(url.query() == .Some(`q=1`));
|
|
14
|
+
// assert(url.fragment() == .Some(`frag`));
|
|
15
|
+
// println(url.to_string()); // "https://example.com:8080/path?q=1#frag"
|
|
16
|
+
|
|
17
|
+
open import "../string";
|
|
18
|
+
{ ArrayList } :: import "../collections/array_list";
|
|
19
|
+
{ ToString } :: import "../fmt";
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// UrlError
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
UrlError :: enum(
|
|
26
|
+
EmptyInput,
|
|
27
|
+
MissingScheme,
|
|
28
|
+
InvalidPort,
|
|
29
|
+
InvalidCharacter(pos: usize),
|
|
30
|
+
Other(msg: String)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
impl(UrlError, ToString(
|
|
34
|
+
to_string : (self ->
|
|
35
|
+
match(self,
|
|
36
|
+
.EmptyInput => `URL error: empty input`,
|
|
37
|
+
.MissingScheme => `URL error: missing scheme`,
|
|
38
|
+
.InvalidPort => `URL error: invalid port`,
|
|
39
|
+
.InvalidCharacter(pos) => `URL error: invalid character at position ${pos}`,
|
|
40
|
+
.Other(msg) => `URL error: ${msg}`
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
));
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Url
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
// ASCII constants for URL parsing
|
|
50
|
+
_COLON :: u8(58); // ':'
|
|
51
|
+
_SLASH :: u8(47); // '/'
|
|
52
|
+
_QUESTION :: u8(63); // '?'
|
|
53
|
+
_HASH :: u8(35); // '#'
|
|
54
|
+
_AT :: u8(64); // '@'
|
|
55
|
+
_LBRACKET :: u8(91); // '['
|
|
56
|
+
_RBRACKET :: u8(93); // ']'
|
|
57
|
+
_ZERO :: u8(48); // '0'
|
|
58
|
+
_NINE :: u8(57); // '9'
|
|
59
|
+
_LOWER_A :: u8(97); // 'a'
|
|
60
|
+
_LOWER_Z :: u8(122); // 'z'
|
|
61
|
+
_UPPER_A :: u8(65); // 'A'
|
|
62
|
+
_UPPER_Z :: u8(90); // 'Z'
|
|
63
|
+
_PLUS :: u8(43); // '+'
|
|
64
|
+
_MINUS :: u8(45); // '-'
|
|
65
|
+
_DOT :: u8(46); // '.'
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Helper: parse port number from bytes
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
_parse_port :: (fn(s: str, start: usize, end: usize) -> Result(u16, UrlError))({
|
|
72
|
+
cond(
|
|
73
|
+
(start >= end) => {
|
|
74
|
+
return .Err(.InvalidPort);
|
|
75
|
+
},
|
|
76
|
+
true => ()
|
|
77
|
+
);
|
|
78
|
+
val := u32(0);
|
|
79
|
+
i := start;
|
|
80
|
+
while runtime((i < end)), {
|
|
81
|
+
ch := s.bytes(i);
|
|
82
|
+
cond(
|
|
83
|
+
((ch >= _ZERO) && (ch <= _NINE)) => {
|
|
84
|
+
val = ((val * u32(10)) + u32((ch - _ZERO)));
|
|
85
|
+
cond(
|
|
86
|
+
(val > u32(65535)) => {
|
|
87
|
+
return .Err(.InvalidPort);
|
|
88
|
+
},
|
|
89
|
+
true => ()
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
true => {
|
|
93
|
+
return .Err(.InvalidPort);
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
i = (i + usize(1));
|
|
97
|
+
};
|
|
98
|
+
.Ok(u16(val))
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
Url :: object(
|
|
102
|
+
_scheme : String,
|
|
103
|
+
_host : Option(String),
|
|
104
|
+
_port : Option(u16),
|
|
105
|
+
_path : String,
|
|
106
|
+
_query : Option(String),
|
|
107
|
+
_fragment : Option(String),
|
|
108
|
+
_userinfo : Option(String)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
impl(Url,
|
|
112
|
+
// Parse a URL string.
|
|
113
|
+
// Supports: scheme://[userinfo@]host[:port]/path[?query][#fragment]
|
|
114
|
+
// Also supports scheme:path (opaque URIs like mailto:user@example.com)
|
|
115
|
+
parse : (fn(s: str) -> Result(Url, UrlError))({
|
|
116
|
+
cond(
|
|
117
|
+
(s.len() == usize(0)) => {
|
|
118
|
+
return .Err(.EmptyInput);
|
|
119
|
+
},
|
|
120
|
+
true => ()
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
pos := usize(0);
|
|
124
|
+
src_len := s.len();
|
|
125
|
+
|
|
126
|
+
// --- Parse scheme ---
|
|
127
|
+
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
128
|
+
scheme_end := usize(0);
|
|
129
|
+
cond(
|
|
130
|
+
(src_len == usize(0)) => {
|
|
131
|
+
return .Err(.MissingScheme);
|
|
132
|
+
},
|
|
133
|
+
true => ()
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// First char must be a letter
|
|
137
|
+
first := s.bytes(usize(0));
|
|
138
|
+
cond(
|
|
139
|
+
((((first >= _LOWER_A) && (first <= _LOWER_Z)) || ((first >= _UPPER_A) && (first <= _UPPER_Z)))) => (),
|
|
140
|
+
true => {
|
|
141
|
+
return .Err(.MissingScheme);
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
i := usize(1);
|
|
146
|
+
found_colon := false;
|
|
147
|
+
while runtime((i < src_len)), {
|
|
148
|
+
ch := s.bytes(i);
|
|
149
|
+
cond(
|
|
150
|
+
(ch == _COLON) => {
|
|
151
|
+
scheme_end = i;
|
|
152
|
+
found_colon = true;
|
|
153
|
+
break;
|
|
154
|
+
},
|
|
155
|
+
(((((first >= _LOWER_A) && (first <= _LOWER_Z)) || ((first >= _UPPER_A) && (first <= _UPPER_Z))) || (((ch >= _LOWER_A) && (ch <= _LOWER_Z)) || ((ch >= _UPPER_A) && (ch <= _UPPER_Z)))) || ((((ch >= _ZERO) && (ch <= _NINE)) || (ch == _PLUS)) || ((ch == _MINUS) || (ch == _DOT)))) => (),
|
|
156
|
+
true => {
|
|
157
|
+
return .Err(.MissingScheme);
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
i = (i + usize(1));
|
|
161
|
+
};
|
|
162
|
+
cond(
|
|
163
|
+
(!(found_colon)) => {
|
|
164
|
+
return .Err(.MissingScheme);
|
|
165
|
+
},
|
|
166
|
+
true => ()
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
scheme_bytes := ArrayList(u8).new();
|
|
170
|
+
j := usize(0);
|
|
171
|
+
while runtime((j < scheme_end)), {
|
|
172
|
+
b := s.bytes(j);
|
|
173
|
+
// Normalize to lowercase
|
|
174
|
+
cond(
|
|
175
|
+
((b >= _UPPER_A) && (b <= _UPPER_Z)) => scheme_bytes.push((b + u8(32))),
|
|
176
|
+
true => scheme_bytes.push(b)
|
|
177
|
+
);
|
|
178
|
+
j = (j + usize(1));
|
|
179
|
+
};
|
|
180
|
+
scheme := String.from_bytes(scheme_bytes);
|
|
181
|
+
pos = (scheme_end + usize(1)); // skip ':'
|
|
182
|
+
|
|
183
|
+
// --- Check for authority (// prefix) ---
|
|
184
|
+
has_authority := false;
|
|
185
|
+
cond(
|
|
186
|
+
(((pos + usize(1)) < src_len) && ((s.bytes(pos) == _SLASH) && (s.bytes((pos + usize(1))) == _SLASH))) => {
|
|
187
|
+
has_authority = true;
|
|
188
|
+
pos = (pos + usize(2)); // skip '//'
|
|
189
|
+
},
|
|
190
|
+
true => ()
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
(host : Option(String)) = .None;
|
|
194
|
+
(port : Option(u16)) = .None;
|
|
195
|
+
(userinfo : Option(String)) = .None;
|
|
196
|
+
path_str := String.new();
|
|
197
|
+
|
|
198
|
+
cond(
|
|
199
|
+
has_authority => {
|
|
200
|
+
// --- Parse authority: [userinfo@]host[:port] ---
|
|
201
|
+
// Find the end of authority (next '/', '?', '#', or end)
|
|
202
|
+
auth_start := pos;
|
|
203
|
+
auth_end := src_len;
|
|
204
|
+
k := pos;
|
|
205
|
+
while runtime((k < src_len)), {
|
|
206
|
+
ch := s.bytes(k);
|
|
207
|
+
cond(
|
|
208
|
+
(((ch == _SLASH) || (ch == _QUESTION)) || (ch == _HASH)) => {
|
|
209
|
+
auth_end = k;
|
|
210
|
+
break;
|
|
211
|
+
},
|
|
212
|
+
true => ()
|
|
213
|
+
);
|
|
214
|
+
k = (k + usize(1));
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Check for userinfo (look for '@')
|
|
218
|
+
host_start := auth_start;
|
|
219
|
+
m := auth_start;
|
|
220
|
+
while runtime((m < auth_end)), {
|
|
221
|
+
cond(
|
|
222
|
+
(s.bytes(m) == _AT) => {
|
|
223
|
+
// Everything before '@' is userinfo
|
|
224
|
+
ui_bytes := ArrayList(u8).new();
|
|
225
|
+
n := auth_start;
|
|
226
|
+
while runtime((n < m)), {
|
|
227
|
+
ui_bytes.push(s.bytes(n));
|
|
228
|
+
n = (n + usize(1));
|
|
229
|
+
};
|
|
230
|
+
userinfo = .Some(String.from_bytes(ui_bytes));
|
|
231
|
+
host_start = (m + usize(1));
|
|
232
|
+
break;
|
|
233
|
+
},
|
|
234
|
+
true => ()
|
|
235
|
+
);
|
|
236
|
+
m = (m + usize(1));
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Parse host[:port]
|
|
240
|
+
// Check for IPv6 literal [...]
|
|
241
|
+
cond(
|
|
242
|
+
((host_start < auth_end) && (s.bytes(host_start) == _LBRACKET)) => {
|
|
243
|
+
// IPv6: find closing ']'
|
|
244
|
+
bracket_end := (host_start + usize(1));
|
|
245
|
+
while runtime(((bracket_end < auth_end) && (s.bytes(bracket_end) != _RBRACKET))), {
|
|
246
|
+
bracket_end = (bracket_end + usize(1));
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
host_bytes := ArrayList(u8).new();
|
|
250
|
+
hh := host_start;
|
|
251
|
+
while runtime((hh <= bracket_end)), {
|
|
252
|
+
cond(
|
|
253
|
+
(hh < auth_end) => { host_bytes.push(s.bytes(hh)); },
|
|
254
|
+
true => ()
|
|
255
|
+
);
|
|
256
|
+
hh = (hh + usize(1));
|
|
257
|
+
};
|
|
258
|
+
host = .Some(String.from_bytes(host_bytes));
|
|
259
|
+
|
|
260
|
+
// Check for port after ']:'
|
|
261
|
+
after_bracket := (bracket_end + usize(1));
|
|
262
|
+
cond(
|
|
263
|
+
((after_bracket < auth_end) && (s.bytes(after_bracket) == _COLON)) => {
|
|
264
|
+
port_result := _parse_port(s, (after_bracket + usize(1)), auth_end);
|
|
265
|
+
cond(
|
|
266
|
+
port_result.is_err() => {
|
|
267
|
+
return .Err(.InvalidPort);
|
|
268
|
+
},
|
|
269
|
+
true => {
|
|
270
|
+
port = .Some(port_result.unwrap());
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
true => ()
|
|
275
|
+
);
|
|
276
|
+
},
|
|
277
|
+
true => {
|
|
278
|
+
// Regular host — find ':' for port
|
|
279
|
+
(colon_pos : Option(usize)) = .None;
|
|
280
|
+
cp := host_start;
|
|
281
|
+
while runtime((cp < auth_end)), {
|
|
282
|
+
cond(
|
|
283
|
+
(s.bytes(cp) == _COLON) => {
|
|
284
|
+
colon_pos = .Some(cp);
|
|
285
|
+
break;
|
|
286
|
+
},
|
|
287
|
+
true => ()
|
|
288
|
+
);
|
|
289
|
+
cp = (cp + usize(1));
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
match(colon_pos,
|
|
293
|
+
.Some(cp_val) => {
|
|
294
|
+
// host is before colon, port is after
|
|
295
|
+
host_b := ArrayList(u8).new();
|
|
296
|
+
hh2 := host_start;
|
|
297
|
+
while runtime((hh2 < cp_val)), {
|
|
298
|
+
host_b.push(s.bytes(hh2));
|
|
299
|
+
hh2 = (hh2 + usize(1));
|
|
300
|
+
};
|
|
301
|
+
host = .Some(String.from_bytes(host_b));
|
|
302
|
+
|
|
303
|
+
port_result := _parse_port(s, (cp_val + usize(1)), auth_end);
|
|
304
|
+
cond(
|
|
305
|
+
port_result.is_err() => {
|
|
306
|
+
return .Err(.InvalidPort);
|
|
307
|
+
},
|
|
308
|
+
true => {
|
|
309
|
+
port = .Some(port_result.unwrap());
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
},
|
|
313
|
+
.None => {
|
|
314
|
+
// No port, entire range is host
|
|
315
|
+
host_b := ArrayList(u8).new();
|
|
316
|
+
hh3 := host_start;
|
|
317
|
+
while runtime((hh3 < auth_end)), {
|
|
318
|
+
host_b.push(s.bytes(hh3));
|
|
319
|
+
hh3 = (hh3 + usize(1));
|
|
320
|
+
};
|
|
321
|
+
h := String.from_bytes(host_b);
|
|
322
|
+
cond(
|
|
323
|
+
(!(h.is_empty())) => {
|
|
324
|
+
host = .Some(h);
|
|
325
|
+
},
|
|
326
|
+
true => ()
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
pos = auth_end;
|
|
334
|
+
},
|
|
335
|
+
true => ()
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// --- Parse path ---
|
|
339
|
+
path_start := pos;
|
|
340
|
+
path_end := src_len;
|
|
341
|
+
pp := pos;
|
|
342
|
+
while runtime((pp < src_len)), {
|
|
343
|
+
ch := s.bytes(pp);
|
|
344
|
+
cond(
|
|
345
|
+
((ch == _QUESTION) || (ch == _HASH)) => {
|
|
346
|
+
path_end = pp;
|
|
347
|
+
break;
|
|
348
|
+
},
|
|
349
|
+
true => ()
|
|
350
|
+
);
|
|
351
|
+
pp = (pp + usize(1));
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
cond(
|
|
355
|
+
(path_start < path_end) => {
|
|
356
|
+
path_bytes := ArrayList(u8).new();
|
|
357
|
+
pb := path_start;
|
|
358
|
+
while runtime((pb < path_end)), {
|
|
359
|
+
path_bytes.push(s.bytes(pb));
|
|
360
|
+
pb = (pb + usize(1));
|
|
361
|
+
};
|
|
362
|
+
path_str = String.from_bytes(path_bytes);
|
|
363
|
+
},
|
|
364
|
+
true => ()
|
|
365
|
+
);
|
|
366
|
+
pos = path_end;
|
|
367
|
+
|
|
368
|
+
// --- Parse query ---
|
|
369
|
+
(query : Option(String)) = .None;
|
|
370
|
+
cond(
|
|
371
|
+
((pos < src_len) && (s.bytes(pos) == _QUESTION)) => {
|
|
372
|
+
pos = (pos + usize(1)); // skip '?'
|
|
373
|
+
q_start := pos;
|
|
374
|
+
q_end := src_len;
|
|
375
|
+
qq := pos;
|
|
376
|
+
while runtime((qq < src_len)), {
|
|
377
|
+
cond(
|
|
378
|
+
(s.bytes(qq) == _HASH) => {
|
|
379
|
+
q_end = qq;
|
|
380
|
+
break;
|
|
381
|
+
},
|
|
382
|
+
true => ()
|
|
383
|
+
);
|
|
384
|
+
qq = (qq + usize(1));
|
|
385
|
+
};
|
|
386
|
+
q_bytes := ArrayList(u8).new();
|
|
387
|
+
qb := q_start;
|
|
388
|
+
while runtime((qb < q_end)), {
|
|
389
|
+
q_bytes.push(s.bytes(qb));
|
|
390
|
+
qb = (qb + usize(1));
|
|
391
|
+
};
|
|
392
|
+
query = .Some(String.from_bytes(q_bytes));
|
|
393
|
+
pos = q_end;
|
|
394
|
+
},
|
|
395
|
+
true => ()
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
// --- Parse fragment ---
|
|
399
|
+
(fragment : Option(String)) = .None;
|
|
400
|
+
cond(
|
|
401
|
+
((pos < src_len) && (s.bytes(pos) == _HASH)) => {
|
|
402
|
+
pos = (pos + usize(1)); // skip '#'
|
|
403
|
+
f_bytes := ArrayList(u8).new();
|
|
404
|
+
fb := pos;
|
|
405
|
+
while runtime((fb < src_len)), {
|
|
406
|
+
f_bytes.push(s.bytes(fb));
|
|
407
|
+
fb = (fb + usize(1));
|
|
408
|
+
};
|
|
409
|
+
fragment = .Some(String.from_bytes(f_bytes));
|
|
410
|
+
},
|
|
411
|
+
true => ()
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
// Ensure authority-based URLs have a path starting with "/" or empty
|
|
415
|
+
cond(
|
|
416
|
+
(has_authority && ((!(path_str.is_empty())) && (path_str.as_bytes().get(usize(0)).unwrap() != _SLASH))) => {
|
|
417
|
+
return .Err(.Other(`authority path must start with '/'`));
|
|
418
|
+
},
|
|
419
|
+
true => ()
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
.Ok(Self(
|
|
423
|
+
_scheme: scheme,
|
|
424
|
+
_host: host,
|
|
425
|
+
_port: port,
|
|
426
|
+
_path: path_str,
|
|
427
|
+
_query: query,
|
|
428
|
+
_fragment: fragment,
|
|
429
|
+
_userinfo: userinfo
|
|
430
|
+
))
|
|
431
|
+
}),
|
|
432
|
+
|
|
433
|
+
// --- Accessors ---
|
|
434
|
+
|
|
435
|
+
scheme : (fn(self: Self) -> String)(
|
|
436
|
+
self._scheme
|
|
437
|
+
),
|
|
438
|
+
|
|
439
|
+
host : (fn(self: Self) -> Option(String))(
|
|
440
|
+
self._host
|
|
441
|
+
),
|
|
442
|
+
|
|
443
|
+
port : (fn(self: Self) -> Option(u16))(
|
|
444
|
+
self._port
|
|
445
|
+
),
|
|
446
|
+
|
|
447
|
+
path : (fn(self: Self) -> String)(
|
|
448
|
+
self._path
|
|
449
|
+
),
|
|
450
|
+
|
|
451
|
+
query : (fn(self: Self) -> Option(String))(
|
|
452
|
+
self._query
|
|
453
|
+
),
|
|
454
|
+
|
|
455
|
+
fragment : (fn(self: Self) -> Option(String))(
|
|
456
|
+
self._fragment
|
|
457
|
+
),
|
|
458
|
+
|
|
459
|
+
userinfo : (fn(self: Self) -> Option(String))(
|
|
460
|
+
self._userinfo
|
|
461
|
+
),
|
|
462
|
+
|
|
463
|
+
// Returns the host:port as a String, or just host if no port
|
|
464
|
+
host_port : (fn(self: Self) -> Option(String))(
|
|
465
|
+
match(self._host,
|
|
466
|
+
.None => .None,
|
|
467
|
+
.Some(h) =>
|
|
468
|
+
match(self._port,
|
|
469
|
+
.None => .Some(h),
|
|
470
|
+
.Some(p) => .Some(`${h}:${p}`)
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
),
|
|
474
|
+
|
|
475
|
+
// Returns scheme + "://" + host_port (the origin)
|
|
476
|
+
origin : (fn(self: Self) -> String)(
|
|
477
|
+
match(self._host,
|
|
478
|
+
.None => self._scheme,
|
|
479
|
+
.Some(h) =>
|
|
480
|
+
match(self._port,
|
|
481
|
+
.None => `${self._scheme}://${h}`,
|
|
482
|
+
.Some(p) => `${self._scheme}://${h}:${p}`
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
// Reconstruct the URL as a String
|
|
489
|
+
impl(Url, ToString(
|
|
490
|
+
to_string : (self -> {
|
|
491
|
+
result := `${self._scheme}:`;
|
|
492
|
+
|
|
493
|
+
match(self._host,
|
|
494
|
+
.Some(h) => {
|
|
495
|
+
result = result.concat(`//`);
|
|
496
|
+
match(self._userinfo,
|
|
497
|
+
.Some(ui) => {
|
|
498
|
+
result = result.concat(ui).concat(`@`);
|
|
499
|
+
},
|
|
500
|
+
.None => ()
|
|
501
|
+
);
|
|
502
|
+
result = result.concat(h);
|
|
503
|
+
match(self._port,
|
|
504
|
+
.Some(p) => {
|
|
505
|
+
result = result.concat(`:${p}`);
|
|
506
|
+
},
|
|
507
|
+
.None => ()
|
|
508
|
+
);
|
|
509
|
+
},
|
|
510
|
+
.None => ()
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
result = result.concat(self._path);
|
|
514
|
+
|
|
515
|
+
match(self._query,
|
|
516
|
+
.Some(q) => {
|
|
517
|
+
result = result.concat(`?`).concat(q);
|
|
518
|
+
},
|
|
519
|
+
.None => ()
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
match(self._fragment,
|
|
523
|
+
.Some(f) => {
|
|
524
|
+
result = result.concat(`#`).concat(f);
|
|
525
|
+
},
|
|
526
|
+
.None => ()
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
result
|
|
530
|
+
})
|
|
531
|
+
));
|
|
532
|
+
|
|
533
|
+
export Url, UrlError;
|
package/std/math/functions.yo
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// std/math/functions.yo - Math utility functions
|
|
2
|
-
//
|
|
3
|
-
// Higher-level math utilities complementing std/libc/math.
|
|
4
|
-
//
|
|
5
|
-
// Example:
|
|
6
|
-
// { min, max, clamp, PI } :: import "std/math/functions";
|
|
7
|
-
|
|
8
|
-
// ============================================================================
|
|
9
|
-
// Constants
|
|
10
|
-
// ============================================================================
|
|
11
|
-
|
|
12
|
-
PI :: f64(3.14159265358979323846);
|
|
13
|
-
E :: f64(2.71828182845904523536);
|
|
14
|
-
TAU :: f64(6.28318530717958647692);
|
|
15
|
-
|
|
16
|
-
export PI, E, TAU;
|
|
17
|
-
|
|
18
|
-
// ============================================================================
|
|
19
|
-
// Generic min / max / clamp
|
|
20
|
-
// ============================================================================
|
|
21
|
-
|
|
22
|
-
// Return the smaller of two values.
|
|
23
|
-
min :: (fn(forall(T : Type), a: T, b: T, where(T <: Ord(T))) -> T)(
|
|
24
|
-
cond((a <= b) => a, true => b)
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
// Return the larger of two values.
|
|
28
|
-
max :: (fn(forall(T : Type), a: T, b: T, where(T <: Ord(T))) -> T)(
|
|
29
|
-
cond((a >= b) => a, true => b)
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
// Clamp `x` to the closed range [lo, hi].
|
|
33
|
-
clamp :: (fn(forall(T : Type), x: T, lo: T, hi: T, where(T <: Ord(T))) -> T)(
|
|
34
|
-
cond((x < lo) => lo, (x > hi) => hi, true => x)
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
export min, max, clamp;
|
|
38
|
-
|
|
39
|
-
// ============================================================================
|
|
40
|
-
// Float utilities
|
|
41
|
-
// ============================================================================
|
|
42
|
-
|
|
43
|
-
// Linear interpolation: a + t*(b-a)
|
|
44
|
-
lerp :: (fn(a: f64, b: f64, t: f64) -> f64)(
|
|
45
|
-
(a + (t * (b - a)))
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
// Map `value` from range [in_min, in_max] to [out_min, out_max].
|
|
49
|
-
map_range :: (fn(value: f64, in_min: f64, in_max: f64, out_min: f64, out_max: f64) -> f64)(
|
|
50
|
-
(out_min + ((value - in_min) / (in_max - in_min)) * (out_max - out_min))
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
export lerp, map_range;
|
|
54
|
-
|
|
55
|
-
// ============================================================================
|
|
56
|
-
// Float classification
|
|
57
|
-
// ============================================================================
|
|
58
|
-
|
|
59
|
-
// True if x is NaN (by IEEE 754 definition: NaN != NaN).
|
|
60
|
-
is_nan :: (fn(x: f64) -> bool)(
|
|
61
|
-
(x != x)
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// True if x is +Inf or -Inf.
|
|
65
|
-
is_inf :: (fn(x: f64) -> bool)(
|
|
66
|
-
((x == (f64(1.0) / f64(0.0))) || (x == (f64(-1.0) / f64(0.0))))
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
// True if x is a finite number (not NaN and not Inf).
|
|
70
|
-
is_finite :: (fn(x: f64) -> bool)(
|
|
71
|
-
((!is_nan(x)) && (!is_inf(x)))
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
export is_nan, is_inf, is_finite;
|
package/std/math/random.yo
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// std/math/random.yo - Non-cryptographic PRNG
|
|
2
|
-
//
|
|
3
|
-
// Uses the xoshiro256** algorithm for fast, high-quality random numbers.
|
|
4
|
-
// NOT suitable for cryptographic use — see std/crypto/random for that.
|
|
5
|
-
//
|
|
6
|
-
// Example:
|
|
7
|
-
// { Rng } :: import "std/math/random";
|
|
8
|
-
//
|
|
9
|
-
// rng := Rng.new(u64(42));
|
|
10
|
-
// x := rng.next_u64();
|
|
11
|
-
|
|
12
|
-
{ ArrayList } :: import "../collections/array_list";
|
|
13
|
-
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// xoshiro256** state
|
|
16
|
-
// ============================================================================
|
|
17
|
-
|
|
18
|
-
Rng :: object(
|
|
19
|
-
_s0 : u64,
|
|
20
|
-
_s1 : u64,
|
|
21
|
-
_s2 : u64,
|
|
22
|
-
_s3 : u64
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
_rotl64 :: (fn(x: u64, k: u64) -> u64)(
|
|
26
|
-
((x << k) | (x >> (u64(64) - k)))
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
// SplitMix64 — used to seed xoshiro from a single u64
|
|
30
|
-
_splitmix64 :: (fn(x: *(u64)) -> u64)({
|
|
31
|
-
x.* = (x.* + u64(0x9e3779b97f4a7c15));
|
|
32
|
-
z := x.*;
|
|
33
|
-
z = ((z ^ (z >> u64(30))) * u64(0xbf58476d1ce4e5b9));
|
|
34
|
-
z = ((z ^ (z >> u64(27))) * u64(0x94d049bb133111eb));
|
|
35
|
-
(z ^ (z >> u64(31)))
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
impl(Rng,
|
|
39
|
-
// Seed the PRNG. Identical seeds produce identical sequences.
|
|
40
|
-
new : (fn(seed: u64) -> Self)({
|
|
41
|
-
s := seed;
|
|
42
|
-
s0 := _splitmix64(&s);
|
|
43
|
-
s1 := _splitmix64(&s);
|
|
44
|
-
s2 := _splitmix64(&s);
|
|
45
|
-
s3 := _splitmix64(&s);
|
|
46
|
-
Self(_s0: s0, _s1: s1, _s2: s2, _s3: s3)
|
|
47
|
-
}),
|
|
48
|
-
|
|
49
|
-
// Generate the next u64 value.
|
|
50
|
-
next_u64 : (fn(self: Self) -> u64)({
|
|
51
|
-
result := (_rotl64((self._s1 * u64(5)), u64(7)) * u64(9));
|
|
52
|
-
t := (self._s1 << u64(17));
|
|
53
|
-
self._s2 = (self._s2 ^ self._s0);
|
|
54
|
-
self._s3 = (self._s3 ^ self._s1);
|
|
55
|
-
self._s1 = (self._s1 ^ self._s2);
|
|
56
|
-
self._s0 = (self._s0 ^ self._s3);
|
|
57
|
-
self._s2 = (self._s2 ^ t);
|
|
58
|
-
self._s3 = _rotl64(self._s3, u64(45));
|
|
59
|
-
result
|
|
60
|
-
}),
|
|
61
|
-
|
|
62
|
-
// Generate the next u32 value.
|
|
63
|
-
next_u32 : (fn(self: Self) -> u32)(
|
|
64
|
-
u32((self.next_u64() >> u64(32)))
|
|
65
|
-
),
|
|
66
|
-
|
|
67
|
-
// Generate a f64 in [0.0, 1.0).
|
|
68
|
-
next_f64 : (fn(self: Self) -> f64)(
|
|
69
|
-
(f64((self.next_u64() >> u64(11))) / f64(9007199254740992.0))
|
|
70
|
-
),
|
|
71
|
-
|
|
72
|
-
// Generate an integer in the half-open range [min, max).
|
|
73
|
-
next_range : (fn(self: Self, min: i64, max: i64) -> i64)({
|
|
74
|
-
span := (max - min);
|
|
75
|
-
cond(
|
|
76
|
-
(span <= i64(0)) => min,
|
|
77
|
-
true => (min + i64((self.next_u64() % u64(span))))
|
|
78
|
-
)
|
|
79
|
-
}),
|
|
80
|
-
|
|
81
|
-
// Shuffle an ArrayList in-place using Fisher-Yates.
|
|
82
|
-
shuffle : (fn(forall(T : Type), self: Self, list: ArrayList(T)) -> unit)({
|
|
83
|
-
i := list.len();
|
|
84
|
-
while (i > usize(1)), (i = (i - usize(1))), {
|
|
85
|
-
j := usize((self.next_u64() % u64(i)));
|
|
86
|
-
a := list.get((i - usize(1))).unwrap();
|
|
87
|
-
b := list.get(j).unwrap();
|
|
88
|
-
list.set((i - usize(1)), b);
|
|
89
|
-
list.set(j, a);
|
|
90
|
-
};
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
export Rng;
|