@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.
Files changed (97) hide show
  1. package/out/cjs/index.cjs +560 -520
  2. package/out/cjs/yo-cli.cjs +598 -558
  3. package/out/esm/index.mjs +551 -511
  4. package/out/types/src/codegen/exprs/arc.d.ts +5 -0
  5. package/out/types/src/codegen/types/generation.d.ts +2 -0
  6. package/out/types/src/codegen/utils/index.d.ts +8 -1
  7. package/out/types/src/evaluator/builtins/rc-fns.d.ts +5 -0
  8. package/out/types/src/evaluator/calls/arc.d.ts +15 -0
  9. package/out/types/src/evaluator/calls/trait-type.d.ts +8 -1
  10. package/out/types/src/evaluator/context.d.ts +11 -1
  11. package/out/types/src/evaluator/types/utils.d.ts +8 -3
  12. package/out/types/src/evaluator/utils/closure.d.ts +7 -1
  13. package/out/types/src/evaluator/values/impl.d.ts +8 -0
  14. package/out/types/src/expr.d.ts +6 -0
  15. package/out/types/src/test-runner.d.ts +2 -0
  16. package/out/types/src/types/creators.d.ts +2 -1
  17. package/out/types/src/types/definitions.d.ts +10 -0
  18. package/out/types/src/types/guards.d.ts +2 -1
  19. package/out/types/src/types/tags.d.ts +1 -0
  20. package/out/types/tsconfig.tsbuildinfo +1 -1
  21. package/package.json +1 -1
  22. package/std/collections/array_list.yo +80 -10
  23. package/std/collections/btree_map.yo +120 -2
  24. package/std/collections/deque.yo +98 -6
  25. package/std/collections/hash_map.yo +137 -1
  26. package/std/collections/hash_set.yo +85 -1
  27. package/std/collections/linked_list.yo +61 -1
  28. package/std/collections/priority_queue.yo +70 -1
  29. package/std/crypto/md5.yo +4 -4
  30. package/std/crypto/random.yo +3 -3
  31. package/std/crypto/sha256.yo +8 -7
  32. package/std/encoding/base64.yo +6 -6
  33. package/std/encoding/hex.yo +27 -22
  34. package/std/encoding/json.yo +185 -186
  35. package/std/encoding/utf16.yo +2 -2
  36. package/std/fmt/display.yo +1 -1
  37. package/std/fmt/writer.yo +2 -4
  38. package/std/fs/dir.yo +5 -5
  39. package/std/fs/file.yo +21 -13
  40. package/std/fs/metadata.yo +4 -4
  41. package/std/fs/temp.yo +3 -3
  42. package/std/fs/types.yo +1 -1
  43. package/std/fs/walker.yo +1 -1
  44. package/std/net/addr.yo +2 -2
  45. package/std/net/dns.yo +21 -20
  46. package/std/net/errors.yo +1 -1
  47. package/std/net/tcp.yo +134 -100
  48. package/std/net/udp.yo +51 -42
  49. package/std/os/env.yo +21 -23
  50. package/std/os/signal.yo +10 -9
  51. package/std/prelude.yo +158 -22
  52. package/std/string/string.yo +99 -1
  53. package/std/sync/once.yo +1 -1
  54. package/std/{io → sys}/advise.yo +1 -1
  55. package/std/sys/bufio/buf_reader.yo +300 -0
  56. package/std/sys/bufio/buf_writer.yo +168 -0
  57. package/std/{io → sys}/clock.yo +1 -1
  58. package/std/{io → sys}/constants.yo +1 -1
  59. package/std/{io → sys}/copy.yo +1 -1
  60. package/std/{io → sys}/dir.yo +6 -6
  61. package/std/{io → sys}/dns.yo +3 -3
  62. package/std/{io → sys}/errors.yo +1 -1
  63. package/std/{io → sys}/events.yo +1 -1
  64. package/std/{io → sys}/externs.yo +1 -1
  65. package/std/{io → sys}/fallocate.yo +1 -1
  66. package/std/{io → sys}/fcntl.yo +1 -1
  67. package/std/{io → sys}/file.yo +1 -1
  68. package/std/{io → sys}/future.yo +1 -1
  69. package/std/{io → sys}/iov.yo +1 -1
  70. package/std/{io → sys}/lock.yo +1 -1
  71. package/std/{io → sys}/mmap.yo +1 -1
  72. package/std/{io → sys}/path.yo +1 -1
  73. package/std/{io → sys}/perm.yo +3 -3
  74. package/std/{io → sys}/pipe.yo +1 -1
  75. package/std/{io → sys}/process.yo +1 -1
  76. package/std/{io → sys}/seek.yo +1 -1
  77. package/std/{io → sys}/signal.yo +1 -1
  78. package/std/{io → sys}/signals.yo +1 -1
  79. package/std/{io → sys}/socket.yo +1 -1
  80. package/std/{io → sys}/socketpair.yo +1 -1
  81. package/std/{io → sys}/sockinfo.yo +1 -1
  82. package/std/{io → sys}/statfs.yo +2 -2
  83. package/std/{io → sys}/statx.yo +1 -1
  84. package/std/{io → sys}/sysinfo.yo +1 -1
  85. package/std/{io → sys}/tcp.yo +3 -3
  86. package/std/{io → sys}/temp.yo +1 -1
  87. package/std/{io → sys}/time.yo +2 -2
  88. package/std/{io → sys}/timer.yo +1 -1
  89. package/std/{io → sys}/tty.yo +1 -1
  90. package/std/{io → sys}/udp.yo +4 -4
  91. package/std/{io → sys}/umask.yo +1 -1
  92. package/std/{io → sys}/unix.yo +1 -1
  93. package/std/time/datetime.yo +18 -23
  94. package/std/time/instant.yo +13 -11
  95. package/std/url/url.yo +533 -0
  96. package/std/math/functions.yo +0 -74
  97. 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;
@@ -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;
@@ -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;