1use crate::abi;
11use crate::catalog::MethodCatalog;
12use crate::type_pattern::{CollectionCtor, ScalarType};
13use crate::{MethodEntry, MethodLowering, Purity, TypePattern};
14
15fn vec_of_t() -> TypePattern {
17 TypePattern::Collection {
18 ctor: CollectionCtor::Vec,
19 args: vec![TypePattern::var("T")],
20 }
21}
22
23#[must_use]
29pub fn builtin_catalog() -> MethodCatalog {
30 MethodCatalog::build()
31 .entry(vec_push())
32 .entry(vec_len())
33 .entry(vec_get())
34 .entry(vec_is_empty())
35 .entry(vec_to_text())
36 .entry(deque_push_front())
37 .entry(deque_push_back())
38 .entry(deque_pop_front())
39 .entry(deque_pop_back())
40 .entry(deque_len())
41 .entry(deque_get())
42 .entry(deque_is_empty())
43 .entry(map_insert())
44 .entry(map_get())
45 .entry(map_contains())
46 .entry(map_remove())
47 .entry(map_len())
48 .entry(map_is_empty())
49 .entry(set_insert())
50 .entry(set_remove())
51 .entry(set_contains())
52 .entry(set_len())
53 .entry(set_is_empty())
54 .entry(counter_get())
55 .entry(counter_inc())
56 .entry(counter_len())
57 .entry(counter_is_empty())
58 .entry(max_heap_push())
59 .entry(max_heap_pop())
60 .entry(max_heap_peek())
61 .entry(max_heap_len())
62 .entry(max_heap_is_empty())
63 .entry(min_heap_push())
64 .entry(min_heap_pop())
65 .entry(min_heap_peek())
66 .entry(min_heap_len())
67 .entry(min_heap_is_empty())
68 .entry(bitset_insert())
69 .entry(bitset_remove())
70 .entry(bitset_contains())
71 .entry(bitset_len())
72 .entry(bitset_is_empty())
73 .entry(grid_width())
74 .entry(grid_height())
75 .entry(grid_get())
76 .entry(grid_set())
77 .entry(grid_contains())
78 .entry(grid_neighbors4())
79 .entry(grid_neighbors8())
80 .entry(grid_around4())
81 .entry(grid_around8())
82 .entry(grid_count4())
83 .entry(grid_count8())
84 .entry(grid_count4_where())
85 .entry(grid_count8_where())
86 .entry(grid_positions())
87 .entry(grid_cells())
88 .entry(grid_row())
89 .entry(grid_column())
90 .entry(grid_find())
91 .entry(grid_find_all())
92 .entry(grid_transpose())
93 .entry(grid_rotate_left())
94 .entry(grid_rotate_right())
95 .entry(seq_map())
102 .entry(seq_filter())
103 .entry(seq_fold())
104 .entry(seq_sum())
105 .entry(seq_count())
106 .entry(seq_count_if())
107 .entry(seq_sorted())
110 .entry(seq_sorted_by_key())
111 .entry(seq_unique())
112 .entry(seq_reversed())
113 .entry(seq_frequencies())
114 .entry(seq_join())
115 .entry(seq_chunks())
118 .entry(seq_windows())
119 .entry(seq_take())
122 .entry(seq_skip())
123 .entry(seq_take_while())
124 .entry(seq_enumerate())
125 .entry(seq_zip())
126 .entry(seq_flat_map())
127 .entry(seq_filter_map())
128 .entry(seq_product())
129 .entry(seq_min())
130 .entry(seq_max())
131 .entry(seq_min_by())
132 .entry(seq_max_by())
133 .entry(seq_any())
134 .entry(seq_all())
135 .entry(seq_find())
136 .entry(seq_position())
137 .entry(seq_reduce())
138 .entry(seq_to_vec())
142 .entry(seq_to_set())
143 .entry(seq_to_map())
144 .entry(seq_to_counter())
145 .entry(seq_to_deque())
146 .entry(seq_to_min_heap())
147 .entry(seq_to_max_heap())
148 .entry(seq_to_bitset())
149 .entry(text_len())
150 .entry(text_int())
151 .entry(text_float())
152 .entry(text_is_empty())
153 .entry(text_get())
154 .entry(float_abs())
157 .entry(float_sqrt())
158 .entry(float_floor())
159 .entry(float_ceil())
160 .entry(float_round())
161 .entry(float_sign())
162 .entry(float_to_int())
163 .entry(float_to_text())
164 .entry(float_is_nan())
165 .entry(float_is_infinite())
166 .entry(float_min())
167 .entry(float_max())
168 .entry(int_to_float())
170 .entry(char_to_int())
173 .entry(int_to_char())
174 .entry(int_to_text())
177 .entry(char_to_text())
178 .entry(int_wrapping_add())
179 .entry(int_saturating_add())
180 .entry(int_checked_add())
181 .entry(int_wrapping_sub())
182 .entry(int_saturating_sub())
183 .entry(int_checked_sub())
184 .entry(int_wrapping_mul())
185 .entry(int_saturating_mul())
186 .entry(int_checked_mul())
187 .entry(vec_index())
191 .entry(vec_index_set())
192 .entry(deque_index())
193 .entry(deque_index_set())
194 .entry(text_index())
195 .entry(map_index())
196 .entry(map_index_set())
197 .entry(counter_index())
198 .entry(counter_index_set())
199 .entry(grid_index())
200 .entry(grid_index_set())
201 .entry(map_index_min())
206 .entry(map_index_max())
207 .entry(counter_keys())
209 .entry(counter_values())
210 .entry(map_keys())
211 .entry(map_values())
212 .finish()
213 .expect("built-in catalog must be duplicate-free")
214}
215
216fn counter_keys() -> MethodEntry {
224 MethodEntry {
225 receiver: counter_of_t(),
226 name: "keys",
227 params: vec![],
228 result: TypePattern::Collection {
229 ctor: CollectionCtor::Vec,
230 args: vec![TypePattern::var("T")],
231 },
232 purity: Purity::Pure,
233 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterKeys),
234 doc: "Every key, as a `Vec[T]`, ordered with `values()`.",
235 }
236}
237
238fn counter_values() -> MethodEntry {
239 MethodEntry {
240 receiver: counter_of_t(),
241 name: "values",
242 params: vec![],
243 result: TypePattern::Collection {
244 ctor: CollectionCtor::Vec,
245 args: vec![TypePattern::Scalar(ScalarType::Int)],
246 },
247 purity: Purity::Pure,
248 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterValues),
249 doc: "Every count, as a `Vec[Int]`, ordered with `keys()`.",
250 }
251}
252
253fn map_keys() -> MethodEntry {
254 MethodEntry {
255 receiver: map_of_k_v(),
256 name: "keys",
257 params: vec![],
258 result: TypePattern::Collection {
259 ctor: CollectionCtor::Vec,
260 args: vec![TypePattern::var("K")],
261 },
262 purity: Purity::Pure,
263 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapKeys),
264 doc: "Every key, as a `Vec[K]`, ordered with `values()`.",
265 }
266}
267
268fn map_values() -> MethodEntry {
269 MethodEntry {
270 receiver: map_of_k_v(),
271 name: "values",
272 params: vec![],
273 result: TypePattern::Collection {
274 ctor: CollectionCtor::Vec,
275 args: vec![TypePattern::var("V")],
276 },
277 purity: Purity::Pure,
278 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapValues),
279 doc: "Every value, as a `Vec[V]`, ordered with `keys()`.",
280 }
281}
282
283fn vec_index() -> MethodEntry {
305 MethodEntry {
306 receiver: vec_of_t(),
307 name: crate::catalog::INDEX_READ,
308 params: vec![TypePattern::Scalar(ScalarType::Int)],
309 result: TypePattern::var("T"),
310 purity: Purity::Pure,
311 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
312 doc: "`v[i]` — the element at `i`; faults if out of range.",
313 }
314}
315
316fn vec_index_set() -> MethodEntry {
317 MethodEntry {
318 receiver: vec_of_t(),
319 name: crate::catalog::INDEX_STORE,
320 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
321 result: TypePattern::Unit,
322 purity: Purity::Impure,
323 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSet),
324 doc: "`v[i] = value` — replace the element at `i`; faults if out of range \
325 (it never appends — `push` is the spelling that grows a vector).",
326 }
327}
328
329fn deque_index() -> MethodEntry {
330 MethodEntry {
331 receiver: deque_of_t(),
332 name: crate::catalog::INDEX_READ,
333 params: vec![TypePattern::Scalar(ScalarType::Int)],
334 result: TypePattern::var("T"),
335 purity: Purity::Pure,
336 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
337 doc: "`d[i]` — the element at `i` (0-based from the front); faults if out of range.",
338 }
339}
340
341fn deque_index_set() -> MethodEntry {
342 MethodEntry {
343 receiver: deque_of_t(),
344 name: crate::catalog::INDEX_STORE,
345 params: vec![TypePattern::Scalar(ScalarType::Int), TypePattern::var("T")],
346 result: TypePattern::Unit,
347 purity: Purity::Impure,
348 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeSet),
349 doc: "`d[i] = value` — replace the element at `i` (0-based from the front); \
350 faults if out of range (it never inserts).",
351 }
352}
353
354fn text_index() -> MethodEntry {
355 MethodEntry {
356 receiver: text_receiver(),
357 name: crate::catalog::INDEX_READ,
358 params: vec![TypePattern::Scalar(ScalarType::Int)],
359 result: TypePattern::Scalar(ScalarType::Char),
360 purity: Purity::Pure,
361 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
362 doc: "`t[i]` — the `Char` at `i`, indexing by Unicode scalar value and not \
363 by byte; faults if out of range (ADR-086).",
364 }
365}
366
367fn map_index() -> MethodEntry {
368 MethodEntry {
369 receiver: map_of_k_v(),
370 name: crate::catalog::INDEX_READ,
371 params: vec![TypePattern::var("K")],
372 result: TypePattern::var("V"),
373 purity: Purity::Pure,
374 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIndex),
375 doc: "`m[key]` — the value for `key`; **faults** if absent (§4.7; `.get` is the \
376 spelling that answers with absence).",
377 }
378}
379
380fn map_index_set() -> MethodEntry {
381 MethodEntry {
382 receiver: map_of_k_v(),
383 name: crate::catalog::INDEX_STORE,
384 params: vec![TypePattern::var("K"), TypePattern::var("V")],
385 result: TypePattern::Unit,
386 purity: Purity::Impure,
387 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
388 doc: "`m[key] = value` — set `key`, replacing any prior value.",
389 }
390}
391
392fn map_of_k_int_value() -> TypePattern {
401 TypePattern::Collection {
402 ctor: CollectionCtor::Map,
403 args: vec![
404 TypePattern::var("K"),
405 TypePattern::is_scalar("V", ScalarType::Int),
406 ],
407 }
408}
409
410fn map_index_min() -> MethodEntry {
411 MethodEntry {
412 receiver: map_of_k_int_value(),
413 name: crate::catalog::INDEX_STORE_MIN,
414 params: vec![TypePattern::var("K"), TypePattern::var("V")],
415 result: TypePattern::Unit,
416 purity: Purity::Impure,
417 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMin),
418 doc: "`d[key] min= candidate` — keep the smaller value; an absent entry \
419 accepts the first value.",
420 }
421}
422
423fn map_index_max() -> MethodEntry {
424 MethodEntry {
425 receiver: map_of_k_int_value(),
426 name: crate::catalog::INDEX_STORE_MAX,
427 params: vec![TypePattern::var("K"), TypePattern::var("V")],
428 result: TypePattern::Unit,
429 purity: Purity::Impure,
430 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapUpdateMax),
431 doc: "`b[key] max= score` — keep the larger value; an absent entry accepts \
432 the first value.",
433 }
434}
435
436fn counter_index() -> MethodEntry {
437 MethodEntry {
438 receiver: counter_of_t(),
439 name: crate::catalog::INDEX_READ,
440 params: vec![TypePattern::var("T")],
441 result: TypePattern::Scalar(ScalarType::Int),
442 purity: Purity::Pure,
443 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
444 doc: "`c[key]` — the count for `key`, or zero if absent; never faults.",
445 }
446}
447
448fn counter_index_set() -> MethodEntry {
449 MethodEntry {
450 receiver: counter_of_t(),
451 name: crate::catalog::INDEX_STORE,
452 params: vec![TypePattern::var("T"), TypePattern::Scalar(ScalarType::Int)],
453 result: TypePattern::Unit,
454 purity: Purity::Impure,
455 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterSet),
456 doc: "`c[key] = n` — set the count for `key`.",
457 }
458}
459
460fn grid_index() -> MethodEntry {
461 MethodEntry {
462 receiver: grid_of_t(),
463 name: crate::catalog::INDEX_READ,
464 params: vec![
465 TypePattern::Scalar(ScalarType::Int),
466 TypePattern::Scalar(ScalarType::Int),
467 ],
468 result: TypePattern::var("T"),
469 purity: Purity::Pure,
470 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
471 doc: "`grid[x, y]` — the cell at (x, y); faults if out of range.",
472 }
473}
474
475fn grid_index_set() -> MethodEntry {
476 MethodEntry {
477 receiver: grid_of_t(),
478 name: crate::catalog::INDEX_STORE,
479 params: vec![
480 TypePattern::Scalar(ScalarType::Int),
481 TypePattern::Scalar(ScalarType::Int),
482 TypePattern::var("T"),
483 ],
484 result: TypePattern::Unit,
485 purity: Purity::Impure,
486 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
487 doc: "`grid[x, y] = value` — set the cell at (x, y); faults if out of range.",
488 }
489}
490
491fn text_receiver() -> TypePattern {
494 TypePattern::Scalar(ScalarType::Text)
495}
496
497fn text_len() -> MethodEntry {
498 MethodEntry {
499 receiver: text_receiver(),
500 name: "len",
501 params: vec![],
502 result: TypePattern::Scalar(ScalarType::Int),
503 purity: Purity::Pure,
504 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextLen),
505 doc: "Number of Unicode scalar values (chars) in the text.",
506 }
507}
508
509fn text_int() -> MethodEntry {
520 MethodEntry {
521 receiver: text_receiver(),
522 name: "int",
523 params: vec![],
524 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
525 purity: Purity::Pure,
526 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextInt),
527 doc: "The Int this text spells as `Some(n)`, or `None` if it spells none.",
528 }
529}
530
531fn text_float() -> MethodEntry {
539 MethodEntry {
540 receiver: text_receiver(),
541 name: "float",
542 params: vec![],
543 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Float))),
544 purity: Purity::Pure,
545 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextFloat),
546 doc: "The Float this text spells as `Some(x)`, or `None` if it spells none.",
547 }
548}
549
550fn text_is_empty() -> MethodEntry {
551 MethodEntry {
552 receiver: text_receiver(),
553 name: "is_empty",
554 params: vec![],
555 result: TypePattern::Scalar(ScalarType::Bool),
556 purity: Purity::Pure,
557 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextIsEmpty),
558 doc: "True iff the text has no chars.",
559 }
560}
561
562fn text_get() -> MethodEntry {
563 MethodEntry {
564 receiver: text_receiver(),
565 name: "get",
566 params: vec![TypePattern::Scalar(ScalarType::Int)],
567 result: TypePattern::Scalar(ScalarType::Char),
568 purity: Purity::Pure,
569 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
570 doc: "The `Char` at `index`; faults if out of range. `t[index]` is the \
571 same row and the same answer (ADR-086).",
572 }
573}
574
575fn float_receiver() -> TypePattern {
583 TypePattern::Scalar(ScalarType::Float)
584}
585
586fn float_abs() -> MethodEntry {
587 MethodEntry {
588 receiver: float_receiver(),
589 name: "abs",
590 params: vec![],
591 result: TypePattern::Scalar(ScalarType::Float),
592 purity: Purity::Pure,
593 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatAbs),
594 doc: "Absolute value.",
595 }
596}
597
598fn float_sqrt() -> MethodEntry {
599 MethodEntry {
600 receiver: float_receiver(),
601 name: "sqrt",
602 params: vec![],
603 result: TypePattern::Scalar(ScalarType::Float),
604 purity: Purity::Pure,
605 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSqrt),
606 doc: "Square root. Negative inputs yield NaN (IEEE-754).",
607 }
608}
609
610fn float_floor() -> MethodEntry {
611 MethodEntry {
612 receiver: float_receiver(),
613 name: "floor",
614 params: vec![],
615 result: TypePattern::Scalar(ScalarType::Float),
616 purity: Purity::Pure,
617 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatFloor),
618 doc: "Round toward negative infinity.",
619 }
620}
621
622fn float_ceil() -> MethodEntry {
623 MethodEntry {
624 receiver: float_receiver(),
625 name: "ceil",
626 params: vec![],
627 result: TypePattern::Scalar(ScalarType::Float),
628 purity: Purity::Pure,
629 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatCeil),
630 doc: "Round toward positive infinity.",
631 }
632}
633
634fn float_round() -> MethodEntry {
635 MethodEntry {
636 receiver: float_receiver(),
637 name: "round",
638 params: vec![],
639 result: TypePattern::Scalar(ScalarType::Float),
640 purity: Purity::Pure,
641 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatRound),
642 doc: "Round half away from zero.",
643 }
644}
645
646fn float_sign() -> MethodEntry {
647 MethodEntry {
648 receiver: float_receiver(),
649 name: "sign",
650 params: vec![],
651 result: TypePattern::Scalar(ScalarType::Float),
652 purity: Purity::Pure,
653 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatSign),
654 doc: "Sign as -1.0 / 0.0 / 1.0. NaN yields NaN.",
655 }
656}
657
658fn float_to_int() -> MethodEntry {
659 MethodEntry {
660 receiver: float_receiver(),
661 name: "to_int",
662 params: vec![],
663 result: TypePattern::Scalar(ScalarType::Int),
664 purity: Purity::Pure,
665 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToInt),
666 doc: "Truncate toward zero to an Int. Faults on NaN, ±inf, or out of i64 range.",
667 }
668}
669
670fn float_to_text() -> MethodEntry {
671 MethodEntry {
672 receiver: float_receiver(),
673 name: "to_text",
674 params: vec![],
675 result: TypePattern::Scalar(ScalarType::Text),
676 purity: Purity::Pure,
677 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatToText),
678 doc: "Format as Text (shortest round-trip form; inf/-inf/NaN as literals).",
679 }
680}
681
682fn float_is_nan() -> MethodEntry {
683 MethodEntry {
684 receiver: float_receiver(),
685 name: "is_nan",
686 params: vec![],
687 result: TypePattern::Scalar(ScalarType::Bool),
688 purity: Purity::Pure,
689 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsNan),
690 doc: "True iff NaN.",
691 }
692}
693
694fn float_is_infinite() -> MethodEntry {
695 MethodEntry {
696 receiver: float_receiver(),
697 name: "is_infinite",
698 params: vec![],
699 result: TypePattern::Scalar(ScalarType::Bool),
700 purity: Purity::Pure,
701 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatIsInfinite),
702 doc: "True iff ±infinity.",
703 }
704}
705
706fn float_min() -> MethodEntry {
707 MethodEntry {
708 receiver: float_receiver(),
709 name: "min",
710 params: vec![TypePattern::Scalar(ScalarType::Float)],
711 result: TypePattern::Scalar(ScalarType::Float),
712 purity: Purity::Pure,
713 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMin),
714 doc: "The smaller of two floats. If either is NaN, returns the other.",
715 }
716}
717
718fn float_max() -> MethodEntry {
719 MethodEntry {
720 receiver: float_receiver(),
721 name: "max",
722 params: vec![TypePattern::Scalar(ScalarType::Float)],
723 result: TypePattern::Scalar(ScalarType::Float),
724 purity: Purity::Pure,
725 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::FloatMax),
726 doc: "The larger of two floats. If either is NaN, returns the other.",
727 }
728}
729
730fn int_to_float() -> MethodEntry {
731 MethodEntry {
732 receiver: TypePattern::Scalar(ScalarType::Int),
733 name: "to_float",
734 params: vec![],
735 result: TypePattern::Scalar(ScalarType::Float),
736 purity: Purity::Pure,
737 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToFloat),
738 doc: "Widen to Float (explicit Int→Float conversion, §4.12).",
739 }
740}
741
742fn int_to_text() -> MethodEntry {
771 MethodEntry {
772 receiver: TypePattern::Scalar(ScalarType::Int),
773 name: "to_text",
774 params: vec![],
775 result: TypePattern::Scalar(ScalarType::Text),
776 purity: Purity::Pure,
777 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToText),
778 doc: "Format as Text — the same digits `out` writes (ADR-143).",
779 }
780}
781
782fn char_to_int() -> MethodEntry {
807 MethodEntry {
808 receiver: TypePattern::Scalar(ScalarType::Char),
809 name: "to_int",
810 params: vec![],
811 result: TypePattern::Scalar(ScalarType::Int),
812 purity: Purity::Pure,
813 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToInt),
814 doc: "The Unicode scalar value, as an `Int`. Never faults (ADR-086).",
815 }
816}
817
818fn char_to_text() -> MethodEntry {
819 MethodEntry {
820 receiver: TypePattern::Scalar(ScalarType::Char),
821 name: "to_text",
822 params: vec![],
823 result: TypePattern::Scalar(ScalarType::Text),
824 purity: Purity::Pure,
825 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CharToText),
826 doc: "The one-character Text holding this scalar — the same character \
827 `out` writes. Never faults (ADR-143).",
828 }
829}
830
831fn int_to_char() -> MethodEntry {
832 MethodEntry {
833 receiver: TypePattern::Scalar(ScalarType::Int),
834 name: "to_char",
835 params: vec![],
836 result: TypePattern::Scalar(ScalarType::Char),
837 purity: Purity::Pure,
838 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntToChar),
839 doc: "The `Char` with this Unicode scalar value; **faults** \
840 (`InvalidChar`) if it is negative, above `0x10FFFF`, or a \
841 surrogate. The narrowing half of the pair, as `Float.to_int` is \
842 (ADR-086).",
843 }
844}
845
846fn int_wrapping_add() -> MethodEntry {
855 MethodEntry {
856 receiver: TypePattern::Scalar(ScalarType::Int),
857 name: "wrapping_add",
858 params: vec![TypePattern::Scalar(ScalarType::Int)],
859 result: TypePattern::Scalar(ScalarType::Int),
860 purity: Purity::Pure,
861 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingAdd),
862 doc: "Add with two's-complement wraparound instead of a fault.",
863 }
864}
865
866fn int_saturating_add() -> MethodEntry {
867 MethodEntry {
868 receiver: TypePattern::Scalar(ScalarType::Int),
869 name: "saturating_add",
870 params: vec![TypePattern::Scalar(ScalarType::Int)],
871 result: TypePattern::Scalar(ScalarType::Int),
872 purity: Purity::Pure,
873 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingAdd),
874 doc: "Add, clamping to Int's ends instead of faulting.",
875 }
876}
877
878fn int_checked_add() -> MethodEntry {
879 MethodEntry {
880 receiver: TypePattern::Scalar(ScalarType::Int),
881 name: "checked_add",
882 params: vec![TypePattern::Scalar(ScalarType::Int)],
883 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
884 purity: Purity::Pure,
885 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedAdd),
886 doc: "Add, answering None where the checked `+` would fault.",
887 }
888}
889
890fn int_wrapping_sub() -> MethodEntry {
891 MethodEntry {
892 receiver: TypePattern::Scalar(ScalarType::Int),
893 name: "wrapping_sub",
894 params: vec![TypePattern::Scalar(ScalarType::Int)],
895 result: TypePattern::Scalar(ScalarType::Int),
896 purity: Purity::Pure,
897 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingSub),
898 doc: "Subtract with two's-complement wraparound instead of a fault.",
899 }
900}
901
902fn int_saturating_sub() -> MethodEntry {
903 MethodEntry {
904 receiver: TypePattern::Scalar(ScalarType::Int),
905 name: "saturating_sub",
906 params: vec![TypePattern::Scalar(ScalarType::Int)],
907 result: TypePattern::Scalar(ScalarType::Int),
908 purity: Purity::Pure,
909 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingSub),
910 doc: "Subtract, clamping to Int's ends instead of faulting.",
911 }
912}
913
914fn int_checked_sub() -> MethodEntry {
915 MethodEntry {
916 receiver: TypePattern::Scalar(ScalarType::Int),
917 name: "checked_sub",
918 params: vec![TypePattern::Scalar(ScalarType::Int)],
919 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
920 purity: Purity::Pure,
921 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedSub),
922 doc: "Subtract, answering None where the checked `-` would fault.",
923 }
924}
925
926fn int_wrapping_mul() -> MethodEntry {
927 MethodEntry {
928 receiver: TypePattern::Scalar(ScalarType::Int),
929 name: "wrapping_mul",
930 params: vec![TypePattern::Scalar(ScalarType::Int)],
931 result: TypePattern::Scalar(ScalarType::Int),
932 purity: Purity::Pure,
933 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntWrappingMul),
934 doc: "Multiply with two's-complement wraparound instead of a fault. The \
935 one row here a program could not write for itself: every arithmetic \
936 operator is checked and the language has no bitwise operators.",
937 }
938}
939
940fn int_saturating_mul() -> MethodEntry {
941 MethodEntry {
942 receiver: TypePattern::Scalar(ScalarType::Int),
943 name: "saturating_mul",
944 params: vec![TypePattern::Scalar(ScalarType::Int)],
945 result: TypePattern::Scalar(ScalarType::Int),
946 purity: Purity::Pure,
947 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntSaturatingMul),
948 doc: "Multiply, clamping to Int's ends instead of faulting.",
949 }
950}
951
952fn int_checked_mul() -> MethodEntry {
953 MethodEntry {
954 receiver: TypePattern::Scalar(ScalarType::Int),
955 name: "checked_mul",
956 params: vec![TypePattern::Scalar(ScalarType::Int)],
957 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
958 purity: Purity::Pure,
959 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::IntCheckedMul),
960 doc: "Multiply, answering None where the checked `*` would fault.",
961 }
962}
963
964fn vec_push() -> MethodEntry {
965 MethodEntry {
966 receiver: vec_of_t(),
967 name: "push",
968 params: vec![TypePattern::var("T")],
969 result: TypePattern::Unit,
970 purity: Purity::Impure,
971 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush),
972 doc: "Append a value to the end; returns Unit.",
973 }
974}
975
976fn vec_len() -> MethodEntry {
977 MethodEntry {
978 receiver: vec_of_t(),
979 name: "len",
980 params: vec![],
981 result: TypePattern::Scalar(ScalarType::Int),
982 purity: Purity::Pure,
983 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecLen),
984 doc: "Number of elements in the vector.",
985 }
986}
987
988fn vec_get() -> MethodEntry {
989 MethodEntry {
990 receiver: vec_of_t(),
991 name: "get",
992 params: vec![TypePattern::Scalar(ScalarType::Int)],
993 result: TypePattern::var("T"),
994 purity: Purity::Pure,
995 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecGet),
996 doc: "The element at `index`; faults `IndexOutOfBounds` if out of range.",
997 }
998}
999
1000fn vec_is_empty() -> MethodEntry {
1001 MethodEntry {
1002 receiver: vec_of_t(),
1003 name: "is_empty",
1004 params: vec![],
1005 result: TypePattern::Scalar(ScalarType::Bool),
1006 purity: Purity::Pure,
1007 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecIsEmpty),
1008 doc: "True iff the vector has no elements.",
1009 }
1010}
1011
1012fn vec_of_char() -> TypePattern {
1020 TypePattern::Collection {
1021 ctor: CollectionCtor::Vec,
1022 args: vec![TypePattern::is_scalar("T", ScalarType::Char)],
1023 }
1024}
1025
1026fn vec_to_text() -> MethodEntry {
1039 MethodEntry {
1040 receiver: vec_of_char(),
1041 name: "to_text",
1042 params: vec![],
1043 result: TypePattern::Scalar(ScalarType::Text),
1044 purity: Purity::Pure,
1045 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecToText),
1046 doc: "These Chars as one Text, with nothing between them (ADR-144).",
1047 }
1048}
1049
1050fn deque_of_t() -> TypePattern {
1054 TypePattern::Collection {
1055 ctor: CollectionCtor::Deque,
1056 args: vec![TypePattern::var("T")],
1057 }
1058}
1059
1060fn deque_push_front() -> MethodEntry {
1061 MethodEntry {
1062 receiver: deque_of_t(),
1063 name: "push_front",
1064 params: vec![TypePattern::var("T")],
1065 result: TypePattern::Unit,
1066 purity: Purity::Impure,
1067 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushFront),
1068 doc: "Prepend a value to the front; returns Unit.",
1069 }
1070}
1071
1072fn deque_push_back() -> MethodEntry {
1073 MethodEntry {
1074 receiver: deque_of_t(),
1075 name: "push_back",
1076 params: vec![TypePattern::var("T")],
1077 result: TypePattern::Unit,
1078 purity: Purity::Impure,
1079 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePushBack),
1080 doc: "Append a value to the back; returns Unit.",
1081 }
1082}
1083
1084fn deque_pop_front() -> MethodEntry {
1085 MethodEntry {
1086 receiver: deque_of_t(),
1087 name: "pop_front",
1088 params: vec![],
1089 result: TypePattern::var("T"),
1090 purity: Purity::Impure,
1091 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopFront),
1092 doc: "Remove and return the front element; faults if empty.",
1093 }
1094}
1095
1096fn deque_pop_back() -> MethodEntry {
1097 MethodEntry {
1098 receiver: deque_of_t(),
1099 name: "pop_back",
1100 params: vec![],
1101 result: TypePattern::var("T"),
1102 purity: Purity::Impure,
1103 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequePopBack),
1104 doc: "Remove and return the back element; faults if empty.",
1105 }
1106}
1107
1108fn deque_len() -> MethodEntry {
1109 MethodEntry {
1110 receiver: deque_of_t(),
1111 name: "len",
1112 params: vec![],
1113 result: TypePattern::Scalar(ScalarType::Int),
1114 purity: Purity::Pure,
1115 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeLen),
1116 doc: "Number of elements in the deque.",
1117 }
1118}
1119
1120fn deque_get() -> MethodEntry {
1121 MethodEntry {
1122 receiver: deque_of_t(),
1123 name: "get",
1124 params: vec![TypePattern::Scalar(ScalarType::Int)],
1125 result: TypePattern::var("T"),
1126 purity: Purity::Pure,
1127 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeGet),
1128 doc: "The element at `index` (0-based from the front); faults if out of range.",
1129 }
1130}
1131
1132fn deque_is_empty() -> MethodEntry {
1133 MethodEntry {
1134 receiver: deque_of_t(),
1135 name: "is_empty",
1136 params: vec![],
1137 result: TypePattern::Scalar(ScalarType::Bool),
1138 purity: Purity::Pure,
1139 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::DequeIsEmpty),
1140 doc: "True iff the deque has no elements.",
1141 }
1142}
1143
1144fn map_of_k_v() -> TypePattern {
1148 TypePattern::Collection {
1149 ctor: CollectionCtor::Map,
1150 args: vec![TypePattern::var("K"), TypePattern::var("V")],
1151 }
1152}
1153
1154fn set_of_t() -> TypePattern {
1156 TypePattern::Collection {
1157 ctor: CollectionCtor::Set,
1158 args: vec![TypePattern::var("T")],
1159 }
1160}
1161
1162fn counter_of_t() -> TypePattern {
1164 TypePattern::Collection {
1165 ctor: CollectionCtor::Counter,
1166 args: vec![TypePattern::var("T")],
1167 }
1168}
1169
1170fn map_insert() -> MethodEntry {
1171 MethodEntry {
1172 receiver: map_of_k_v(),
1173 name: "insert",
1174 params: vec![TypePattern::var("K"), TypePattern::var("V")],
1175 result: TypePattern::Unit,
1176 purity: Purity::Impure,
1177 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapInsert),
1178 doc: "Set `key` to `value`, replacing any prior value; returns Unit.",
1179 }
1180}
1181
1182fn map_get() -> MethodEntry {
1183 MethodEntry {
1184 receiver: map_of_k_v(),
1185 name: "get",
1186 params: vec![TypePattern::var("K")],
1187 result: TypePattern::Option(Box::new(TypePattern::var("V"))),
1191 purity: Purity::Pure,
1192 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapGet),
1193 doc: "The value for `key` as `Some(value)`, or `None` if absent.",
1194 }
1195}
1196
1197fn map_contains() -> MethodEntry {
1198 MethodEntry {
1199 receiver: map_of_k_v(),
1200 name: "contains",
1201 params: vec![TypePattern::var("K")],
1202 result: TypePattern::Scalar(ScalarType::Bool),
1203 purity: Purity::Pure,
1204 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapContains),
1205 doc: "True iff `key` is present in the map.",
1206 }
1207}
1208
1209fn map_remove() -> MethodEntry {
1210 MethodEntry {
1211 receiver: map_of_k_v(),
1212 name: "remove",
1213 params: vec![TypePattern::var("K")],
1214 result: TypePattern::Unit,
1215 purity: Purity::Impure,
1216 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapRemove),
1217 doc: "Remove `key` if present; returns Unit.",
1218 }
1219}
1220
1221fn map_len() -> MethodEntry {
1222 MethodEntry {
1223 receiver: map_of_k_v(),
1224 name: "len",
1225 params: vec![],
1226 result: TypePattern::Scalar(ScalarType::Int),
1227 purity: Purity::Pure,
1228 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapLen),
1229 doc: "Number of entries in the map.",
1230 }
1231}
1232
1233fn map_is_empty() -> MethodEntry {
1234 MethodEntry {
1235 receiver: map_of_k_v(),
1236 name: "is_empty",
1237 params: vec![],
1238 result: TypePattern::Scalar(ScalarType::Bool),
1239 purity: Purity::Pure,
1240 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MapIsEmpty),
1241 doc: "True iff the map has no entries.",
1242 }
1243}
1244
1245fn set_insert() -> MethodEntry {
1246 MethodEntry {
1247 receiver: set_of_t(),
1248 name: "insert",
1249 params: vec![TypePattern::var("T")],
1250 result: TypePattern::Unit,
1251 purity: Purity::Impure,
1252 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetInsert),
1253 doc: "Add `value` to the set; returns Unit.",
1254 }
1255}
1256
1257fn set_remove() -> MethodEntry {
1258 MethodEntry {
1259 receiver: set_of_t(),
1260 name: "remove",
1261 params: vec![TypePattern::var("T")],
1262 result: TypePattern::Unit,
1263 purity: Purity::Impure,
1264 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetRemove),
1265 doc: "Remove `value` if present; returns Unit.",
1266 }
1267}
1268
1269fn set_contains() -> MethodEntry {
1270 MethodEntry {
1271 receiver: set_of_t(),
1272 name: "contains",
1273 params: vec![TypePattern::var("T")],
1274 result: TypePattern::Scalar(ScalarType::Bool),
1275 purity: Purity::Pure,
1276 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetContains),
1277 doc: "True iff `value` is in the set.",
1278 }
1279}
1280
1281fn set_len() -> MethodEntry {
1282 MethodEntry {
1283 receiver: set_of_t(),
1284 name: "len",
1285 params: vec![],
1286 result: TypePattern::Scalar(ScalarType::Int),
1287 purity: Purity::Pure,
1288 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetLen),
1289 doc: "Number of elements in the set.",
1290 }
1291}
1292
1293fn set_is_empty() -> MethodEntry {
1294 MethodEntry {
1295 receiver: set_of_t(),
1296 name: "is_empty",
1297 params: vec![],
1298 result: TypePattern::Scalar(ScalarType::Bool),
1299 purity: Purity::Pure,
1300 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::SetIsEmpty),
1301 doc: "True iff the set has no elements.",
1302 }
1303}
1304
1305fn counter_get() -> MethodEntry {
1306 MethodEntry {
1307 receiver: counter_of_t(),
1308 name: "get",
1309 params: vec![TypePattern::var("T")],
1310 result: TypePattern::Scalar(ScalarType::Int),
1311 purity: Purity::Pure,
1312 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterGet),
1313 doc: "The count for `key`, or zero if absent (never faults).",
1314 }
1315}
1316
1317fn counter_inc() -> MethodEntry {
1318 MethodEntry {
1319 receiver: counter_of_t(),
1320 name: "inc",
1321 params: vec![TypePattern::var("T")],
1322 result: TypePattern::Unit,
1323 purity: Purity::Impure,
1324 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterInc),
1325 doc: "Increment the count for `key` by one; returns Unit.",
1326 }
1327}
1328
1329fn counter_len() -> MethodEntry {
1330 MethodEntry {
1331 receiver: counter_of_t(),
1332 name: "len",
1333 params: vec![],
1334 result: TypePattern::Scalar(ScalarType::Int),
1335 purity: Purity::Pure,
1336 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterLen),
1337 doc: "Number of distinct keys in the counter.",
1338 }
1339}
1340
1341fn counter_is_empty() -> MethodEntry {
1342 MethodEntry {
1343 receiver: counter_of_t(),
1344 name: "is_empty",
1345 params: vec![],
1346 result: TypePattern::Scalar(ScalarType::Bool),
1347 purity: Purity::Pure,
1348 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::CounterIsEmpty),
1349 doc: "True iff the counter has no keys.",
1350 }
1351}
1352
1353fn min_heap_of_t() -> TypePattern {
1356 TypePattern::Collection {
1357 ctor: CollectionCtor::MinHeap,
1358 args: vec![TypePattern::var("T")],
1359 }
1360}
1361
1362fn max_heap_of_t() -> TypePattern {
1363 TypePattern::Collection {
1364 ctor: CollectionCtor::MaxHeap,
1365 args: vec![TypePattern::var("T")],
1366 }
1367}
1368
1369fn max_heap_push() -> MethodEntry {
1370 MethodEntry {
1371 receiver: max_heap_of_t(),
1372 name: "push",
1373 params: vec![TypePattern::var("T")],
1374 result: TypePattern::Unit,
1375 purity: Purity::Impure,
1376 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPush),
1377 doc: "Push a value onto the max-heap; returns Unit.",
1378 }
1379}
1380
1381fn max_heap_pop() -> MethodEntry {
1382 MethodEntry {
1383 receiver: max_heap_of_t(),
1384 name: "pop",
1385 params: vec![],
1386 result: TypePattern::var("T"),
1387 purity: Purity::Impure,
1388 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPop),
1389 doc: "Remove and return the largest element; faults if empty.",
1390 }
1391}
1392
1393fn max_heap_peek() -> MethodEntry {
1394 MethodEntry {
1395 receiver: max_heap_of_t(),
1396 name: "peek",
1397 params: vec![],
1398 result: TypePattern::var("T"),
1399 purity: Purity::Pure,
1400 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapPeek),
1401 doc: "The largest element without removing it; faults if empty.",
1402 }
1403}
1404
1405fn max_heap_len() -> MethodEntry {
1406 MethodEntry {
1407 receiver: max_heap_of_t(),
1408 name: "len",
1409 params: vec![],
1410 result: TypePattern::Scalar(ScalarType::Int),
1411 purity: Purity::Pure,
1412 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapLen),
1413 doc: "Number of elements in the max-heap.",
1414 }
1415}
1416
1417fn max_heap_is_empty() -> MethodEntry {
1418 MethodEntry {
1419 receiver: max_heap_of_t(),
1420 name: "is_empty",
1421 params: vec![],
1422 result: TypePattern::Scalar(ScalarType::Bool),
1423 purity: Purity::Pure,
1424 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MaxHeapIsEmpty),
1425 doc: "True iff the max-heap has no elements.",
1426 }
1427}
1428
1429fn min_heap_push() -> MethodEntry {
1430 MethodEntry {
1431 receiver: min_heap_of_t(),
1432 name: "push",
1433 params: vec![TypePattern::var("T")],
1434 result: TypePattern::Unit,
1435 purity: Purity::Impure,
1436 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPush),
1437 doc: "Push a value onto the min-heap; returns Unit.",
1438 }
1439}
1440
1441fn min_heap_pop() -> MethodEntry {
1442 MethodEntry {
1443 receiver: min_heap_of_t(),
1444 name: "pop",
1445 params: vec![],
1446 result: TypePattern::var("T"),
1447 purity: Purity::Impure,
1448 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPop),
1449 doc: "Remove and return the smallest element; faults if empty.",
1450 }
1451}
1452
1453fn min_heap_peek() -> MethodEntry {
1454 MethodEntry {
1455 receiver: min_heap_of_t(),
1456 name: "peek",
1457 params: vec![],
1458 result: TypePattern::var("T"),
1459 purity: Purity::Pure,
1460 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapPeek),
1461 doc: "The smallest element without removing it; faults if empty.",
1462 }
1463}
1464
1465fn min_heap_len() -> MethodEntry {
1466 MethodEntry {
1467 receiver: min_heap_of_t(),
1468 name: "len",
1469 params: vec![],
1470 result: TypePattern::Scalar(ScalarType::Int),
1471 purity: Purity::Pure,
1472 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapLen),
1473 doc: "Number of elements in the min-heap.",
1474 }
1475}
1476
1477fn min_heap_is_empty() -> MethodEntry {
1478 MethodEntry {
1479 receiver: min_heap_of_t(),
1480 name: "is_empty",
1481 params: vec![],
1482 result: TypePattern::Scalar(ScalarType::Bool),
1483 purity: Purity::Pure,
1484 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::MinHeapIsEmpty),
1485 doc: "True iff the min-heap has no elements.",
1486 }
1487}
1488
1489fn bitset_receiver() -> TypePattern {
1493 TypePattern::Collection {
1494 ctor: CollectionCtor::BitSet,
1495 args: vec![],
1496 }
1497}
1498
1499fn bitset_insert() -> MethodEntry {
1500 MethodEntry {
1501 receiver: bitset_receiver(),
1502 name: "insert",
1503 params: vec![TypePattern::Scalar(ScalarType::Int)],
1504 result: TypePattern::Unit,
1505 purity: Purity::Impure,
1506 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetInsert),
1507 doc: "Set the bit for a non-negative integer; returns Unit.",
1508 }
1509}
1510
1511fn bitset_remove() -> MethodEntry {
1512 MethodEntry {
1513 receiver: bitset_receiver(),
1514 name: "remove",
1515 params: vec![TypePattern::Scalar(ScalarType::Int)],
1516 result: TypePattern::Unit,
1517 purity: Purity::Impure,
1518 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetRemove),
1519 doc: "Clear the bit for an integer; returns Unit.",
1520 }
1521}
1522
1523fn bitset_contains() -> MethodEntry {
1524 MethodEntry {
1525 receiver: bitset_receiver(),
1526 name: "contains",
1527 params: vec![TypePattern::Scalar(ScalarType::Int)],
1528 result: TypePattern::Scalar(ScalarType::Bool),
1529 purity: Purity::Pure,
1530 lowering: MethodLowering::ScalarPrimitive(abi::RuntimeSymbol::BitsetContains),
1534 doc: "True iff the bit for the integer is set.",
1535 }
1536}
1537
1538fn bitset_len() -> MethodEntry {
1539 MethodEntry {
1540 receiver: bitset_receiver(),
1541 name: "len",
1542 params: vec![],
1543 result: TypePattern::Scalar(ScalarType::Int),
1544 purity: Purity::Pure,
1545 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetLen),
1546 doc: "Number of set bits (popcount).",
1547 }
1548}
1549
1550fn bitset_is_empty() -> MethodEntry {
1551 MethodEntry {
1552 receiver: bitset_receiver(),
1553 name: "is_empty",
1554 params: vec![],
1555 result: TypePattern::Scalar(ScalarType::Bool),
1556 purity: Purity::Pure,
1557 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::BitsetIsEmpty),
1558 doc: "True iff no bits are set.",
1559 }
1560}
1561
1562fn grid_of_t() -> TypePattern {
1566 TypePattern::Collection {
1567 ctor: CollectionCtor::Grid,
1568 args: vec![TypePattern::var("T")],
1569 }
1570}
1571
1572fn point_pattern() -> TypePattern {
1574 TypePattern::Tuple(vec![
1575 TypePattern::Scalar(ScalarType::Int),
1576 TypePattern::Scalar(ScalarType::Int),
1577 ])
1578}
1579
1580fn grid_width() -> MethodEntry {
1581 MethodEntry {
1582 receiver: grid_of_t(),
1583 name: "width",
1584 params: vec![],
1585 result: TypePattern::Scalar(ScalarType::Int),
1586 purity: Purity::Pure,
1587 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridWidth),
1588 doc: "The number of columns.",
1589 }
1590}
1591
1592fn grid_height() -> MethodEntry {
1593 MethodEntry {
1594 receiver: grid_of_t(),
1595 name: "height",
1596 params: vec![],
1597 result: TypePattern::Scalar(ScalarType::Int),
1598 purity: Purity::Pure,
1599 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridHeight),
1600 doc: "The number of rows.",
1601 }
1602}
1603
1604fn grid_get() -> MethodEntry {
1605 MethodEntry {
1606 receiver: grid_of_t(),
1607 name: "get",
1608 params: vec![
1609 TypePattern::Scalar(ScalarType::Int),
1610 TypePattern::Scalar(ScalarType::Int),
1611 ],
1612 result: TypePattern::var("T"),
1613 purity: Purity::Pure,
1614 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridGet),
1615 doc: "The cell at (x, y); faults if out of range.",
1616 }
1617}
1618
1619fn grid_set() -> MethodEntry {
1620 MethodEntry {
1621 receiver: grid_of_t(),
1622 name: "set",
1623 params: vec![
1624 TypePattern::Scalar(ScalarType::Int),
1625 TypePattern::Scalar(ScalarType::Int),
1626 TypePattern::var("T"),
1627 ],
1628 result: TypePattern::Unit,
1629 purity: Purity::Impure,
1630 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridSet),
1631 doc: "Set the cell at (x, y); faults if out of range.",
1632 }
1633}
1634
1635fn grid_contains() -> MethodEntry {
1636 MethodEntry {
1637 receiver: grid_of_t(),
1638 name: "contains",
1639 params: vec![
1640 TypePattern::Scalar(ScalarType::Int),
1641 TypePattern::Scalar(ScalarType::Int),
1642 ],
1643 result: TypePattern::Scalar(ScalarType::Bool),
1644 purity: Purity::Pure,
1645 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridContains),
1646 doc: "True iff (x, y) is within the grid.",
1647 }
1648}
1649
1650fn grid_neighbors4() -> MethodEntry {
1651 MethodEntry {
1652 receiver: grid_of_t(),
1653 name: "neighbors4",
1654 params: vec![point_pattern()],
1655 result: TypePattern::Collection {
1656 ctor: CollectionCtor::Vec,
1657 args: vec![point_pattern()],
1658 },
1659 purity: Purity::Pure,
1660 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors4),
1661 doc: "The 4 orthogonal in-bounds neighbors of a point, as a Vec of (x, y).",
1662 }
1663}
1664
1665fn grid_neighbors8() -> MethodEntry {
1666 MethodEntry {
1667 receiver: grid_of_t(),
1668 name: "neighbors8",
1669 params: vec![point_pattern()],
1670 result: TypePattern::Collection {
1671 ctor: CollectionCtor::Vec,
1672 args: vec![point_pattern()],
1673 },
1674 purity: Purity::Pure,
1675 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridNeighbors8),
1676 doc: "The 8 in-bounds neighbors of a point, as a Vec of (x, y).",
1677 }
1678}
1679
1680fn maybe_point() -> TypePattern {
1684 TypePattern::Option(Box::new(point_pattern()))
1685}
1686
1687fn around4_pattern() -> TypePattern {
1698 TypePattern::Record {
1699 name: "Around4",
1700 fields: vec![
1701 ("up", maybe_point()),
1702 ("left", maybe_point()),
1703 ("right", maybe_point()),
1704 ("down", maybe_point()),
1705 ],
1706 }
1707}
1708
1709fn around8_pattern() -> TypePattern {
1712 TypePattern::Record {
1713 name: "Around8",
1714 fields: vec![
1715 ("up_left", maybe_point()),
1716 ("up", maybe_point()),
1717 ("up_right", maybe_point()),
1718 ("left", maybe_point()),
1719 ("right", maybe_point()),
1720 ("down_left", maybe_point()),
1721 ("down", maybe_point()),
1722 ("down_right", maybe_point()),
1723 ],
1724 }
1725}
1726
1727fn grid_around4() -> MethodEntry {
1737 MethodEntry {
1738 receiver: grid_of_t(),
1739 name: "around4",
1740 params: vec![point_pattern()],
1741 result: around4_pattern(),
1742 purity: Purity::Pure,
1743 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridAround4),
1744 doc: "The 4 orthogonal neighbors by name: { up, left, right, down }, \
1745 each Some((x, y)) or None off the grid.",
1746 }
1747}
1748
1749fn grid_around8() -> MethodEntry {
1751 MethodEntry {
1752 receiver: grid_of_t(),
1753 name: "around8",
1754 params: vec![point_pattern()],
1755 result: around8_pattern(),
1756 purity: Purity::Pure,
1757 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridAround8),
1758 doc: "The 8 neighbors by name, in reading order: { up_left, up, up_right, \
1759 left, right, down_left, down, down_right }, each Some((x, y)) or None.",
1760 }
1761}
1762
1763fn grid_count4() -> MethodEntry {
1770 MethodEntry {
1771 receiver: grid_of_t(),
1772 name: "count4",
1773 params: vec![point_pattern(), TypePattern::var("T")],
1774 result: TypePattern::Scalar(ScalarType::Int),
1775 purity: Purity::Pure,
1776 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount4),
1777 doc: "How many of the 4 orthogonal neighbors hold `value`. \
1778 A neighbor off the grid has no cell and is not counted.",
1779 }
1780}
1781
1782fn grid_count8() -> MethodEntry {
1785 MethodEntry {
1786 receiver: grid_of_t(),
1787 name: "count8",
1788 params: vec![point_pattern(), TypePattern::var("T")],
1789 result: TypePattern::Scalar(ScalarType::Int),
1790 purity: Purity::Pure,
1791 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount8),
1792 doc: "How many of the 8 neighbors hold `value`. \
1793 A neighbor off the grid has no cell and is not counted.",
1794 }
1795}
1796
1797fn grid_count4_where() -> MethodEntry {
1804 MethodEntry {
1805 receiver: grid_of_t(),
1806 name: "count4_where",
1807 params: vec![
1808 point_pattern(),
1809 TypePattern::Function {
1810 params: vec![TypePattern::var("T")],
1811 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
1812 },
1813 ],
1814 result: TypePattern::Scalar(ScalarType::Int),
1815 purity: Purity::Pure,
1816 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount4Where),
1817 doc: "How many of the 4 orthogonal neighbors hold a cell the closure accepts. \
1818 A neighbor off the grid has no cell, so the closure never sees one.",
1819 }
1820}
1821
1822fn grid_count8_where() -> MethodEntry {
1825 MethodEntry {
1826 receiver: grid_of_t(),
1827 name: "count8_where",
1828 params: vec![
1829 point_pattern(),
1830 TypePattern::Function {
1831 params: vec![TypePattern::var("T")],
1832 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
1833 },
1834 ],
1835 result: TypePattern::Scalar(ScalarType::Int),
1836 purity: Purity::Pure,
1837 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCount8Where),
1838 doc: "How many of the 8 neighbors hold a cell the closure accepts. \
1839 A neighbor off the grid has no cell, so the closure never sees one.",
1840 }
1841}
1842
1843fn grid_positions() -> MethodEntry {
1844 MethodEntry {
1845 receiver: grid_of_t(),
1846 name: "positions",
1847 params: vec![],
1848 result: TypePattern::Collection {
1849 ctor: CollectionCtor::Vec,
1850 args: vec![point_pattern()],
1851 },
1852 purity: Purity::Pure,
1853 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridPositions),
1854 doc: "All (x, y) positions in row-major order, as a Vec.",
1855 }
1856}
1857
1858fn grid_cells() -> MethodEntry {
1859 MethodEntry {
1860 receiver: grid_of_t(),
1861 name: "cells",
1862 params: vec![],
1863 result: TypePattern::Collection {
1864 ctor: CollectionCtor::Vec,
1865 args: vec![TypePattern::var("T")],
1866 },
1867 purity: Purity::Pure,
1868 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridCells),
1869 doc: "All cells in row-major order, as a Vec.",
1870 }
1871}
1872
1873fn grid_row() -> MethodEntry {
1874 MethodEntry {
1875 receiver: grid_of_t(),
1876 name: "row",
1877 params: vec![TypePattern::Scalar(ScalarType::Int)],
1878 result: TypePattern::Collection {
1879 ctor: CollectionCtor::Vec,
1880 args: vec![TypePattern::var("T")],
1881 },
1882 purity: Purity::Pure,
1883 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRow),
1884 doc: "Row `y` as a Vec; faults if out of range.",
1885 }
1886}
1887
1888fn grid_column() -> MethodEntry {
1889 MethodEntry {
1890 receiver: grid_of_t(),
1891 name: "column",
1892 params: vec![TypePattern::Scalar(ScalarType::Int)],
1893 result: TypePattern::Collection {
1894 ctor: CollectionCtor::Vec,
1895 args: vec![TypePattern::var("T")],
1896 },
1897 purity: Purity::Pure,
1898 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridColumn),
1899 doc: "Column `x` as a Vec; faults if out of range.",
1900 }
1901}
1902
1903fn grid_find() -> MethodEntry {
1904 MethodEntry {
1905 receiver: grid_of_t(),
1906 name: "find",
1907 params: vec![TypePattern::var("T")],
1908 result: TypePattern::Option(Box::new(point_pattern())),
1912 purity: Purity::Pure,
1913 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFind),
1914 doc: "The first (x, y) whose cell equals `value` as `Some((x, y))`, or `None`.",
1915 }
1916}
1917
1918fn grid_find_all() -> MethodEntry {
1919 MethodEntry {
1920 receiver: grid_of_t(),
1921 name: "find_all",
1922 params: vec![TypePattern::var("T")],
1923 result: TypePattern::Collection {
1924 ctor: CollectionCtor::Vec,
1925 args: vec![point_pattern()],
1926 },
1927 purity: Purity::Pure,
1928 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridFindAll),
1929 doc: "All (x, y) positions whose cell equals `value`, as a Vec.",
1930 }
1931}
1932
1933fn grid_transpose() -> MethodEntry {
1934 MethodEntry {
1935 receiver: grid_of_t(),
1936 name: "transpose",
1937 params: vec![],
1938 result: grid_of_t(),
1939 purity: Purity::Pure,
1940 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridTranspose),
1941 doc: "A transposed copy (rows ↔ columns).",
1942 }
1943}
1944
1945fn grid_rotate_left() -> MethodEntry {
1946 MethodEntry {
1947 receiver: grid_of_t(),
1948 name: "rotate_left",
1949 params: vec![],
1950 result: grid_of_t(),
1951 purity: Purity::Pure,
1952 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateLeft),
1953 doc: "A copy rotated 90° counter-clockwise.",
1954 }
1955}
1956
1957fn grid_rotate_right() -> MethodEntry {
1958 MethodEntry {
1959 receiver: grid_of_t(),
1960 name: "rotate_right",
1961 params: vec![],
1962 result: grid_of_t(),
1963 purity: Purity::Pure,
1964 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::GridRotateRight),
1965 doc: "A copy rotated 90° clockwise.",
1966 }
1967}
1968
1969fn iterable_of_t() -> TypePattern {
1989 TypePattern::iterable(TypePattern::var("T"))
1990}
1991
1992fn t_to_u() -> TypePattern {
1994 TypePattern::Function {
1995 params: vec![TypePattern::var("T")],
1996 result: Box::new(TypePattern::var("U")),
1997 }
1998}
1999
2000fn t_to_bool() -> TypePattern {
2002 TypePattern::Function {
2003 params: vec![TypePattern::var("T")],
2004 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
2005 }
2006}
2007
2008fn t_to_option_u() -> TypePattern {
2015 TypePattern::Function {
2016 params: vec![TypePattern::var("T")],
2017 result: Box::new(TypePattern::Option(Box::new(TypePattern::var("U")))),
2018 }
2019}
2020
2021fn acc_t_to_acc() -> TypePattern {
2023 TypePattern::Function {
2024 params: vec![TypePattern::var("Acc"), TypePattern::var("T")],
2025 result: Box::new(TypePattern::var("Acc")),
2026 }
2027}
2028
2029fn t_t_to_t() -> TypePattern {
2038 TypePattern::Function {
2039 params: vec![TypePattern::var("T"), TypePattern::var("T")],
2040 result: Box::new(TypePattern::var("T")),
2041 }
2042}
2043
2044fn seq_map() -> MethodEntry {
2045 MethodEntry {
2046 receiver: iterable_of_t(),
2047 name: "map",
2048 params: vec![t_to_u()],
2049 result: vec_of_u(),
2050 purity: Purity::Pure,
2051 lowering: MethodLowering::Intrinsic("seq_map"),
2052 doc: "Apply a function to each element, collecting into a Vec.",
2053 }
2054}
2055
2056fn vec_of_u() -> TypePattern {
2059 TypePattern::Collection {
2060 ctor: CollectionCtor::Vec,
2061 args: vec![TypePattern::var("U")],
2062 }
2063}
2064
2065fn seq_filter() -> MethodEntry {
2066 MethodEntry {
2067 receiver: iterable_of_t(),
2068 name: "filter",
2069 params: vec![t_to_bool()],
2070 result: vec_of_t(),
2071 purity: Purity::Pure,
2072 lowering: MethodLowering::Intrinsic("seq_filter"),
2073 doc: "Keep elements satisfying a predicate, collecting into a Vec.",
2074 }
2075}
2076
2077fn seq_fold() -> MethodEntry {
2078 MethodEntry {
2079 receiver: iterable_of_t(),
2080 name: "fold",
2081 params: vec![TypePattern::var("Acc"), acc_t_to_acc()],
2082 result: TypePattern::var("Acc"),
2083 purity: Purity::Pure,
2084 lowering: MethodLowering::Intrinsic("seq_fold"),
2085 doc: "Reduce elements left-to-right with an accumulator and combining closure.",
2086 }
2087}
2088
2089fn seq_sum() -> MethodEntry {
2090 MethodEntry {
2091 receiver: iterable_of_int_elem(),
2092 name: "sum",
2093 params: vec![],
2094 result: TypePattern::Scalar(ScalarType::Int),
2095 purity: Purity::Pure,
2096 lowering: MethodLowering::Intrinsic("seq_sum"),
2097 doc: "Sum the (Int) elements.",
2098 }
2099}
2100
2101fn seq_count() -> MethodEntry {
2102 MethodEntry {
2103 receiver: iterable_of_t(),
2104 name: "count",
2105 params: vec![],
2106 result: TypePattern::Scalar(ScalarType::Int),
2107 purity: Purity::Pure,
2108 lowering: MethodLowering::Intrinsic("seq_count"),
2109 doc: "Number of elements.",
2110 }
2111}
2112
2113fn seq_count_if() -> MethodEntry {
2119 MethodEntry {
2120 receiver: iterable_of_t(),
2121 name: "count",
2122 params: vec![t_to_bool()],
2123 result: TypePattern::Scalar(ScalarType::Int),
2124 purity: Purity::Pure,
2125 lowering: MethodLowering::Intrinsic("seq_count"),
2126 doc: "Number of elements satisfying the predicate.",
2127 }
2128}
2129
2130fn seq_sorted() -> MethodEntry {
2198 MethodEntry {
2199 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2200 name: "sorted",
2201 params: vec![],
2202 result: vec_of_t(),
2203 purity: Purity::Pure,
2204 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSorted),
2205 doc: "A new Vec holding these elements in ascending order.",
2206 }
2207}
2208
2209fn seq_unique() -> MethodEntry {
2216 MethodEntry {
2217 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2218 name: "unique",
2219 params: vec![],
2220 result: vec_of_t(),
2221 purity: Purity::Pure,
2222 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecUnique),
2223 doc: "A new Vec with duplicate elements removed, keeping first occurrences.",
2224 }
2225}
2226
2227fn seq_reversed() -> MethodEntry {
2239 MethodEntry {
2240 receiver: iterable_of_t(),
2241 name: "reversed",
2242 params: vec![],
2243 result: vec_of_t(),
2244 purity: Purity::Pure,
2245 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed),
2246 doc: "A new Vec holding these elements in reverse order.",
2247 }
2248}
2249
2250fn vec_of_vec_of_t() -> TypePattern {
2257 TypePattern::Collection {
2258 ctor: CollectionCtor::Vec,
2259 args: vec![vec_of_t()],
2260 }
2261}
2262
2263fn seq_chunks() -> MethodEntry {
2283 MethodEntry {
2284 receiver: iterable_of_t(),
2285 name: "chunks",
2286 params: vec![TypePattern::Scalar(ScalarType::Int)],
2287 result: vec_of_vec_of_t(),
2288 purity: Purity::Pure,
2289 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecChunks),
2290 doc: "Consecutive non-overlapping runs of n; the last may be shorter. \
2291 Faults if n is not positive.",
2292 }
2293}
2294
2295fn seq_windows() -> MethodEntry {
2309 MethodEntry {
2310 receiver: iterable_of_t(),
2311 name: "windows",
2312 params: vec![TypePattern::Scalar(ScalarType::Int)],
2313 result: vec_of_vec_of_t(),
2314 purity: Purity::Pure,
2315 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecWindows),
2316 doc: "Every consecutive run of exactly n, sliding by one. Empty if n \
2317 exceeds the length; faults if n is not positive.",
2318 }
2319}
2320
2321fn iterable_of_text_elem() -> TypePattern {
2326 TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Text))
2327}
2328
2329fn seq_join() -> MethodEntry {
2342 MethodEntry {
2343 receiver: iterable_of_text_elem(),
2344 name: "join",
2345 params: vec![TypePattern::Scalar(ScalarType::Text)],
2346 result: TypePattern::Scalar(ScalarType::Text),
2347 purity: Purity::Pure,
2348 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecJoin),
2349 doc: "These Text items concatenated with `sep` between them.",
2350 }
2351}
2352
2353fn seq_frequencies() -> MethodEntry {
2362 MethodEntry {
2363 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2364 name: "frequencies",
2365 params: vec![],
2366 result: counter_of_t(),
2367 purity: Purity::Pure,
2368 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecFrequencies),
2369 doc: "A Counter holding how many times each element occurs.",
2370 }
2371}
2372
2373fn t_t_to_bool() -> TypePattern {
2380 TypePattern::Function {
2381 params: vec![TypePattern::var("T"), TypePattern::var("T")],
2382 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
2383 }
2384}
2385
2386fn t_to_vec_u() -> TypePattern {
2388 TypePattern::Function {
2389 params: vec![TypePattern::var("T")],
2390 result: Box::new(vec_of_u()),
2391 }
2392}
2393
2394fn seq_take() -> MethodEntry {
2397 MethodEntry {
2398 receiver: iterable_of_t(),
2399 name: "take",
2400 params: vec![TypePattern::Scalar(ScalarType::Int)],
2401 result: vec_of_t(),
2402 purity: Purity::Pure,
2403 lowering: MethodLowering::Intrinsic("seq_take"),
2404 doc: "Keep at most the first n elements.",
2405 }
2406}
2407
2408fn seq_skip() -> MethodEntry {
2409 MethodEntry {
2410 receiver: iterable_of_t(),
2411 name: "skip",
2412 params: vec![TypePattern::Scalar(ScalarType::Int)],
2413 result: vec_of_t(),
2414 purity: Purity::Pure,
2415 lowering: MethodLowering::Intrinsic("seq_skip"),
2416 doc: "Drop the first n elements.",
2417 }
2418}
2419
2420fn seq_take_while() -> MethodEntry {
2421 MethodEntry {
2422 receiver: iterable_of_t(),
2423 name: "take_while",
2424 params: vec![t_to_bool()],
2425 result: vec_of_t(),
2426 purity: Purity::Pure,
2427 lowering: MethodLowering::Intrinsic("seq_take_while"),
2428 doc: "Keep elements until the predicate is false.",
2429 }
2430}
2431
2432fn vec_of_index_and_t() -> TypePattern {
2434 TypePattern::Collection {
2435 ctor: CollectionCtor::Vec,
2436 args: vec![TypePattern::Tuple(vec![
2437 TypePattern::Scalar(ScalarType::Int),
2438 TypePattern::var("T"),
2439 ])],
2440 }
2441}
2442
2443fn vec_of_t_and_u() -> TypePattern {
2446 TypePattern::Collection {
2447 ctor: CollectionCtor::Vec,
2448 args: vec![TypePattern::Tuple(vec![
2449 TypePattern::var("T"),
2450 TypePattern::var("U"),
2451 ])],
2452 }
2453}
2454
2455fn seq_enumerate() -> MethodEntry {
2456 MethodEntry {
2457 receiver: iterable_of_t(),
2458 name: "enumerate",
2459 params: vec![],
2460 result: vec_of_index_and_t(),
2461 purity: Purity::Pure,
2462 lowering: MethodLowering::Intrinsic("seq_enumerate"),
2463 doc: "Pair each element with its index.",
2464 }
2465}
2466
2467fn seq_zip() -> MethodEntry {
2468 MethodEntry {
2469 receiver: iterable_of_t(),
2470 name: "zip",
2471 params: vec![vec_of_u()],
2472 result: vec_of_t_and_u(),
2473 purity: Purity::Pure,
2474 lowering: MethodLowering::Intrinsic("seq_zip"),
2475 doc: "Pair elements with another sequence, stopping at the shorter length.",
2476 }
2477}
2478
2479fn seq_flat_map() -> MethodEntry {
2480 MethodEntry {
2481 receiver: iterable_of_t(),
2482 name: "flat_map",
2483 params: vec![t_to_vec_u()],
2484 result: vec_of_u(),
2485 purity: Purity::Pure,
2486 lowering: MethodLowering::Intrinsic("seq_flat_map"),
2487 doc: "Map each element to a Vec and concatenate the results.",
2488 }
2489}
2490
2491fn seq_filter_map() -> MethodEntry {
2492 MethodEntry {
2493 receiver: iterable_of_t(),
2494 name: "filter_map",
2495 params: vec![t_to_option_u()],
2496 result: vec_of_u(),
2497 purity: Purity::Pure,
2498 lowering: MethodLowering::Intrinsic("seq_filter_map"),
2499 doc: "Map each element to an Option and keep the Some payloads.",
2500 }
2501}
2502
2503fn iterable_of_int_elem() -> TypePattern {
2523 TypePattern::iterable(TypePattern::is_scalar("T", ScalarType::Int))
2524}
2525
2526fn seq_product() -> MethodEntry {
2527 MethodEntry {
2528 receiver: iterable_of_int_elem(),
2529 name: "product",
2530 params: vec![],
2531 result: TypePattern::Scalar(ScalarType::Int),
2532 purity: Purity::Pure,
2533 lowering: MethodLowering::Intrinsic("seq_product"),
2534 doc: "Multiply the (Int) elements.",
2535 }
2536}
2537
2538fn seq_min() -> MethodEntry {
2539 MethodEntry {
2540 receiver: iterable_of_int_elem(),
2541 name: "min",
2542 params: vec![],
2543 result: TypePattern::Scalar(ScalarType::Int),
2544 purity: Purity::Pure,
2545 lowering: MethodLowering::Intrinsic("seq_min"),
2546 doc: "Smallest (Int) element. Faults on an empty sequence (D1).",
2547 }
2548}
2549
2550fn seq_max() -> MethodEntry {
2551 MethodEntry {
2552 receiver: iterable_of_int_elem(),
2553 name: "max",
2554 params: vec![],
2555 result: TypePattern::Scalar(ScalarType::Int),
2556 purity: Purity::Pure,
2557 lowering: MethodLowering::Intrinsic("seq_max"),
2558 doc: "Largest (Int) element. Faults on an empty sequence (D1).",
2559 }
2560}
2561
2562fn seq_min_by() -> MethodEntry {
2563 MethodEntry {
2564 receiver: iterable_of_t(),
2565 name: "min_by",
2566 params: vec![t_t_to_bool()],
2567 result: TypePattern::var("T"),
2568 purity: Purity::Pure,
2569 lowering: MethodLowering::Intrinsic("seq_min_by"),
2570 doc: "Smallest element per a (T,T)->Bool \"less-than\" comparator.",
2571 }
2572}
2573
2574fn seq_max_by() -> MethodEntry {
2575 MethodEntry {
2576 receiver: iterable_of_t(),
2577 name: "max_by",
2578 params: vec![t_t_to_bool()],
2579 result: TypePattern::var("T"),
2580 purity: Purity::Pure,
2581 lowering: MethodLowering::Intrinsic("seq_max_by"),
2582 doc: "Largest element per a (T,T)->Bool \"less-than\" comparator.",
2583 }
2584}
2585
2586fn seq_any() -> MethodEntry {
2587 MethodEntry {
2588 receiver: iterable_of_t(),
2589 name: "any",
2590 params: vec![t_to_bool()],
2591 result: TypePattern::Scalar(ScalarType::Bool),
2592 purity: Purity::Pure,
2593 lowering: MethodLowering::Intrinsic("seq_any"),
2594 doc: "True if any element satisfies the predicate (short-circuits).",
2595 }
2596}
2597
2598fn seq_all() -> MethodEntry {
2599 MethodEntry {
2600 receiver: iterable_of_t(),
2601 name: "all",
2602 params: vec![t_to_bool()],
2603 result: TypePattern::Scalar(ScalarType::Bool),
2604 purity: Purity::Pure,
2605 lowering: MethodLowering::Intrinsic("seq_all"),
2606 doc: "True if all elements satisfy the predicate (short-circuits).",
2607 }
2608}
2609
2610fn seq_find() -> MethodEntry {
2611 MethodEntry {
2612 receiver: iterable_of_t(),
2613 name: "find",
2614 params: vec![t_to_bool()],
2615 result: TypePattern::Option(Box::new(TypePattern::var("T"))),
2616 purity: Purity::Pure,
2617 lowering: MethodLowering::Intrinsic("seq_find"),
2618 doc: "The first matching element, or None.",
2619 }
2620}
2621
2622fn seq_position() -> MethodEntry {
2623 MethodEntry {
2624 receiver: iterable_of_t(),
2625 name: "position",
2626 params: vec![t_to_bool()],
2627 result: TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int))),
2628 purity: Purity::Pure,
2629 lowering: MethodLowering::Intrinsic("seq_position"),
2630 doc: "The index of the first matching element, or None.",
2631 }
2632}
2633
2634fn seq_reduce() -> MethodEntry {
2635 MethodEntry {
2636 receiver: iterable_of_t(),
2637 name: "reduce",
2638 params: vec![t_t_to_t()],
2639 result: TypePattern::var("T"),
2640 purity: Purity::Pure,
2641 lowering: MethodLowering::Intrinsic("seq_reduce"),
2642 doc: "Reduce left-to-right, seeded with the first element.",
2643 }
2644}
2645
2646fn seq_sorted_by_key() -> MethodEntry {
2662 MethodEntry {
2663 receiver: iterable_of_t(),
2664 name: "sorted_by_key",
2665 params: vec![TypePattern::Function {
2666 params: vec![TypePattern::var("T")],
2667 result: Box::new(TypePattern::of_kind("K", crate::CapKind::Ord)),
2668 }],
2669 result: vec_of_t(),
2670 purity: Purity::Pure,
2671 lowering: MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecSortedByKey),
2672 doc: "A new Vec ordered by the key the closure extracts.",
2673 }
2674}
2675
2676fn seq_to_vec() -> MethodEntry {
2713 MethodEntry {
2714 receiver: iterable_of_t(),
2715 name: "to_vec",
2716 params: vec![],
2717 result: vec_of_t(),
2718 purity: Purity::Pure,
2719 lowering: MethodLowering::Intrinsic("seq_to_vec"),
2720 doc: "The items as a Vec. On a Vec receiver this is the receiver itself.",
2721 }
2722}
2723
2724fn seq_to_set() -> MethodEntry {
2736 MethodEntry {
2737 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::HashStable)),
2738 name: "to_set",
2739 params: vec![],
2740 result: set_of_t(),
2741 purity: Purity::Pure,
2742 lowering: MethodLowering::Intrinsic("seq_to_set"),
2743 doc: "A Set holding these items, duplicates dropped.",
2744 }
2745}
2746
2747fn seq_to_map() -> MethodEntry {
2756 MethodEntry {
2757 receiver: TypePattern::iterable(TypePattern::Tuple(vec![
2758 TypePattern::of_kind("K", crate::CapKind::HashStable),
2759 TypePattern::var("V"),
2760 ])),
2761 name: "to_map",
2762 params: vec![],
2763 result: map_of_k_v(),
2764 purity: Purity::Pure,
2765 lowering: MethodLowering::Intrinsic("seq_to_map"),
2766 doc: "A Map built from (key, value) pairs. Duplicate keys: last wins.",
2767 }
2768}
2769
2770fn seq_to_counter() -> MethodEntry {
2778 MethodEntry {
2779 receiver: TypePattern::iterable(TypePattern::Tuple(vec![
2780 TypePattern::of_kind("T", crate::CapKind::HashStable),
2781 TypePattern::Scalar(ScalarType::Int),
2782 ])),
2783 name: "to_counter",
2784 params: vec![],
2785 result: counter_of_t(),
2786 purity: Purity::Pure,
2787 lowering: MethodLowering::Intrinsic("seq_to_counter"),
2788 doc: "A Counter built from (key, count) pairs. Duplicate keys: last wins.",
2789 }
2790}
2791
2792fn seq_to_deque() -> MethodEntry {
2793 MethodEntry {
2794 receiver: iterable_of_t(),
2795 name: "to_deque",
2796 params: vec![],
2797 result: deque_of_t(),
2798 purity: Purity::Pure,
2799 lowering: MethodLowering::Intrinsic("seq_to_deque"),
2800 doc: "A Deque holding these items, in order.",
2801 }
2802}
2803
2804fn seq_to_min_heap() -> MethodEntry {
2809 MethodEntry {
2810 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2811 name: "to_min_heap",
2812 params: vec![],
2813 result: min_heap_of_t(),
2814 purity: Purity::Pure,
2815 lowering: MethodLowering::Intrinsic("seq_to_min_heap"),
2816 doc: "A MinHeap holding these items.",
2817 }
2818}
2819
2820fn seq_to_max_heap() -> MethodEntry {
2821 MethodEntry {
2822 receiver: TypePattern::iterable(TypePattern::of_kind("T", crate::CapKind::Ord)),
2823 name: "to_max_heap",
2824 params: vec![],
2825 result: max_heap_of_t(),
2826 purity: Purity::Pure,
2827 lowering: MethodLowering::Intrinsic("seq_to_max_heap"),
2828 doc: "A MaxHeap holding these items.",
2829 }
2830}
2831
2832fn seq_to_bitset() -> MethodEntry {
2838 MethodEntry {
2839 receiver: iterable_of_int_elem(),
2840 name: "to_bitset",
2841 params: vec![],
2842 result: bitset_receiver(),
2843 purity: Purity::Pure,
2844 lowering: MethodLowering::Intrinsic("seq_to_bitset"),
2845 doc: "A BitSet holding these (Int) items. Faults on a negative or oversized member.",
2846 }
2847}
2848
2849#[cfg(test)]
2850mod tests {
2851 use super::*;
2852
2853 #[test]
2862 fn every_catalog_row_documents_itself() {
2863 for e in builtin_catalog().entries() {
2864 let what = format!("{}.{}/{}", e.receiver, e.name, e.arity());
2865 assert!(e.doc.len() > 8, "{what} has no real documentation");
2866 assert!(
2867 e.doc.ends_with('.') || e.doc.ends_with(')'),
2868 "{what}'s doc is not a sentence: {:?}",
2869 e.doc
2870 );
2871 assert!(
2875 e.doc
2876 .chars()
2877 .next()
2878 .is_some_and(|c| c.is_uppercase() || c == '`'),
2879 "{what}'s doc does not open a sentence: {:?}",
2880 e.doc
2881 );
2882 }
2883 }
2884
2885 #[test]
2886 fn builtin_catalog_has_vec_methods() {
2887 let cat = builtin_catalog();
2888 assert!(cat.len() >= 4);
2889 let vec_pat = vec_of_t();
2890 let push_hits: Vec<_> = cat.by_receiver_and_name(&vec_pat, "push").collect();
2891 assert_eq!(push_hits.len(), 1);
2892 assert_eq!(
2893 push_hits[0].lowering,
2894 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecPush)
2895 );
2896 }
2897
2898 #[test]
2899 fn builtin_catalog_get_can_fault() {
2900 let cat = builtin_catalog();
2901 let vec_pat = vec_of_t();
2902 let get = cat
2903 .by_receiver_and_name(&vec_pat, "get")
2904 .next()
2905 .expect("vec.get exists");
2906 assert!(get.can_fault());
2907 let bitset_pat = bitset_receiver();
2911 let insert = cat
2912 .by_receiver_and_name(&bitset_pat, "insert")
2913 .next()
2914 .expect("bitset.insert exists");
2915 assert!(insert.can_fault());
2916 }
2917
2918 #[test]
2925 fn a_keyed_collection_enumerates_and_count_has_two_arities() {
2926 let cat = builtin_catalog();
2927 let map_pat = map_of_k_v();
2928 let counter_pat = counter_of_t();
2929
2930 for (pat, what) in [(map_pat.clone(), "Map"), (counter_pat.clone(), "Counter")] {
2932 for name in ["keys", "values"] {
2933 let hits: Vec<_> = cat.by_receiver_and_name(&pat, name).collect();
2934 assert_eq!(hits.len(), 1, "{what}.{name}()");
2935 assert_eq!(hits[0].arity(), 0, "{what}.{name}() takes no arguments");
2936 assert!(
2937 matches!(
2938 hits[0].result,
2939 TypePattern::Collection {
2940 ctor: CollectionCtor::Vec,
2941 ..
2942 }
2943 ),
2944 "{what}.{name}() answers a Vec so every §6.3 combinator applies"
2945 );
2946 }
2947 }
2948
2949 let counter_values = cat
2953 .by_receiver_and_name(&counter_pat, "values")
2954 .next()
2955 .expect("Counter.values");
2956 assert_eq!(
2957 counter_values.result,
2958 TypePattern::Collection {
2959 ctor: CollectionCtor::Vec,
2960 args: vec![TypePattern::Scalar(ScalarType::Int)]
2961 }
2962 );
2963 let map_values = cat
2964 .by_receiver_and_name(&map_pat, "values")
2965 .next()
2966 .expect("Map.values");
2967 assert_eq!(
2968 map_values.result,
2969 TypePattern::Collection {
2970 ctor: CollectionCtor::Vec,
2971 args: vec![TypePattern::var("V")]
2972 }
2973 );
2974 let map_keys = cat
2976 .by_receiver_and_name(&map_pat, "keys")
2977 .next()
2978 .expect("Map.keys");
2979 assert_eq!(
2980 map_keys.result,
2981 TypePattern::Collection {
2982 ctor: CollectionCtor::Vec,
2983 args: vec![TypePattern::var("K")]
2984 }
2985 );
2986
2987 let arities: Vec<usize> = cat
2990 .by_receiver_and_name(&iterable_of_t(), "count")
2991 .map(|e| e.arity())
2992 .collect();
2993 assert_eq!(arities.len(), 2, "count has two rows");
2994 assert!(arities.contains(&0) && arities.contains(&1));
2995 }
2996
2997 #[test]
3004 fn every_pipeline_combinator_is_one_row_on_the_generic_receiver() {
3005 let cat = builtin_catalog();
3006 let combinators = [
3014 "map",
3015 "filter",
3016 "filter_map",
3017 "flat_map",
3018 "take",
3019 "skip",
3020 "take_while",
3021 "enumerate",
3022 "zip",
3023 "fold",
3024 "reduce",
3025 "sum",
3026 "product",
3027 "count",
3028 "any",
3029 "all",
3030 "find",
3031 "position",
3032 "min",
3033 "max",
3034 "min_by",
3035 "max_by",
3036 "sorted",
3037 "sorted_by_key",
3038 "unique",
3039 "reversed",
3040 "frequencies",
3041 "join",
3042 "to_vec",
3043 "to_set",
3044 "to_map",
3045 "to_counter",
3046 "to_deque",
3047 "to_min_heap",
3048 "to_max_heap",
3049 "to_bitset",
3050 ];
3051 for name in combinators {
3052 let rows: Vec<_> = cat.entries().iter().filter(|e| e.name == name).collect();
3053 assert!(!rows.is_empty(), "`{name}` has no row at all");
3054 let mut generic: Vec<usize> = rows
3058 .iter()
3059 .filter(|e| matches!(e.receiver, TypePattern::Iterable { .. }))
3060 .map(|e| e.arity())
3061 .collect();
3062 assert!(!generic.is_empty(), "`{name}` has no generic row");
3063 let seen = generic.len();
3064 generic.sort_unstable();
3065 generic.dedup();
3066 assert_eq!(
3067 generic.len(),
3068 seen,
3069 "`{name}` has two generic rows at one arity — one receiver \
3070 getting a feature ten should have"
3071 );
3072 for row in rows {
3079 assert!(
3080 matches!(row.receiver, TypePattern::Iterable { .. })
3081 || !crate::is_pipeline_receiver(&row.receiver),
3082 "`{name}` also has a row on {}, which the generic row \
3083 accepts — both would match, and which one a call resolves \
3084 to would be insertion order",
3085 row.receiver
3086 );
3087 }
3088 }
3089
3090 assert!(
3095 !crate::PIPELINE_RECEIVERS.contains(&CollectionCtor::Grid),
3096 "a generic `map` would claim §6.4's name and answer a `Vec`"
3097 );
3098
3099 for e in cat.entries() {
3101 assert!(
3102 !matches!(
3103 e.receiver,
3104 TypePattern::Collection {
3105 ctor: CollectionCtor::Seq,
3106 ..
3107 }
3108 ),
3109 "`{}` is still on a `Seq`, which has no values",
3110 e.name
3111 );
3112 }
3113 }
3114
3115 #[test]
3124 fn a_conversion_exists_for_every_collection_that_can_be_constructed() {
3125 let cat = builtin_catalog();
3126 for (name, ctor) in [
3127 ("to_vec", CollectionCtor::Vec),
3128 ("to_set", CollectionCtor::Set),
3129 ("to_map", CollectionCtor::Map),
3130 ("to_counter", CollectionCtor::Counter),
3131 ("to_deque", CollectionCtor::Deque),
3132 ("to_min_heap", CollectionCtor::MinHeap),
3133 ("to_max_heap", CollectionCtor::MaxHeap),
3134 ("to_bitset", CollectionCtor::BitSet),
3135 ] {
3136 let row = cat
3137 .entries()
3138 .iter()
3139 .find(|e| e.name == name)
3140 .unwrap_or_else(|| panic!("`{name}` has no row"));
3141 assert_eq!(row.arity(), 0, "`{name}` takes no arguments");
3142 let built = match &row.result {
3143 TypePattern::Collection { ctor, .. } => *ctor,
3144 other => panic!("`{name}` answers {other}, not a collection"),
3145 };
3146 assert_eq!(built, ctor, "`{name}` builds the collection it names");
3147 }
3148 assert!(
3149 cat.entries().iter().all(|e| e.name != "to_grid"),
3150 "a grid needs a width, and an item sequence does not carry one"
3151 );
3152
3153 for name in ["to_map", "to_counter"] {
3157 let row = cat.entries().iter().find(|e| e.name == name).unwrap();
3158 let TypePattern::Iterable { item } = &row.receiver else {
3159 panic!("`{name}` is not on the generic receiver")
3160 };
3161 assert!(
3162 matches!(**item, TypePattern::Tuple(ref els) if els.len() == 2),
3163 "`{name}` accepts a `Map` or a `Counter` by saying its item is a pair"
3164 );
3165 }
3166 }
3167
3168 #[test]
3177 fn the_subscript_rows_are_a_closed_set_no_program_can_name() {
3178 let cat = builtin_catalog();
3179 let of = |ctor: CollectionCtor, args: usize| TypePattern::Collection {
3180 ctor,
3181 args: (0..args).map(|_| TypePattern::var("T")).collect(),
3182 };
3183 let map_pat = map_of_k_v();
3184
3185 for (pat, indices, what) in [
3187 (of(CollectionCtor::Vec, 1), 1, "Vec"),
3188 (of(CollectionCtor::Deque, 1), 1, "Deque"),
3189 (map_pat.clone(), 1, "Map"),
3190 (of(CollectionCtor::Counter, 1), 1, "Counter"),
3191 (of(CollectionCtor::Grid, 1), 2, "Grid"),
3192 (text_receiver(), 1, "Text"),
3193 ] {
3194 let hits: Vec<_> = cat
3195 .by_receiver_and_name(&pat, crate::catalog::INDEX_READ)
3196 .collect();
3197 assert_eq!(hits.len(), 1, "{what} reads through exactly one row");
3198 assert_eq!(hits[0].arity(), indices, "{what} indexes at {indices}");
3199 }
3200
3201 for (pat, args, what) in [
3205 (of(CollectionCtor::Vec, 1), 2, "Vec"),
3206 (of(CollectionCtor::Deque, 1), 2, "Deque"),
3207 (map_pat.clone(), 2, "Map"),
3208 (of(CollectionCtor::Counter, 1), 2, "Counter"),
3209 (of(CollectionCtor::Grid, 1), 3, "Grid"),
3210 ] {
3211 let hits: Vec<_> = cat
3212 .by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
3213 .collect();
3214 assert_eq!(hits.len(), 1, "{what} stores through exactly one row");
3215 assert_eq!(
3216 hits[0].arity(),
3217 args,
3218 "{what}'s store takes its indices and then the value"
3219 );
3220 }
3221
3222 for (pat, what) in [
3224 (of(CollectionCtor::Set, 1), "Set"),
3225 (of(CollectionCtor::MinHeap, 1), "MinHeap"),
3226 (of(CollectionCtor::MaxHeap, 1), "MaxHeap"),
3227 (of(CollectionCtor::BitSet, 0), "BitSet"),
3228 ] {
3229 for name in [crate::catalog::INDEX_READ, crate::catalog::INDEX_STORE] {
3230 assert_eq!(
3231 cat.by_receiver_and_name(&pat, name).count(),
3232 0,
3233 "{what} has no `{name}`"
3234 );
3235 }
3236 }
3237 assert_eq!(
3238 cat.by_receiver_and_name(&text_receiver(), crate::catalog::INDEX_STORE)
3239 .count(),
3240 0,
3241 "a `Text` is immutable: it reads through a subscript and has \
3242 no element store"
3243 );
3244
3245 for (pat, push, what) in [
3250 (of(CollectionCtor::Vec, 1), "push", "Vec"),
3251 (of(CollectionCtor::Deque, 1), "push_back", "Deque"),
3252 ] {
3253 let store = cat
3254 .by_receiver_and_name(&pat, crate::catalog::INDEX_STORE)
3255 .next()
3256 .unwrap_or_else(|| panic!("{what}'s store"));
3257 let appender = cat
3258 .by_receiver_and_name(&pat, push)
3259 .next()
3260 .unwrap_or_else(|| panic!("{what}.{push}"));
3261 assert_ne!(
3262 store.lowering, appender.lowering,
3263 "{what}'s store replaces; `{push}` appends"
3264 );
3265 assert!(
3266 store.can_fault(),
3267 "{what}'s store reports an index it does not hold"
3268 );
3269 }
3270
3271 let get = cat
3275 .by_receiver_and_name(&map_pat, "get")
3276 .next()
3277 .expect("Map.get");
3278 let index = cat
3279 .by_receiver_and_name(&map_pat, crate::catalog::INDEX_READ)
3280 .next()
3281 .expect("Map's subscript");
3282 assert_ne!(get.lowering, index.lowering);
3283 assert!(
3284 index.can_fault() && !get.can_fault(),
3285 "indexing faults where `.get` answers"
3286 );
3287
3288 let map_int_value = map_of_k_int_value();
3292 let plain_store = cat
3293 .by_receiver_and_name(&map_pat, crate::catalog::INDEX_STORE)
3294 .next()
3295 .expect("Map's store")
3296 .lowering
3297 .clone();
3298 for (name, what) in [
3299 (crate::catalog::INDEX_STORE_MIN, "min="),
3300 (crate::catalog::INDEX_STORE_MAX, "max="),
3301 ] {
3302 let hits: Vec<_> = cat.by_receiver_and_name(&map_int_value, name).collect();
3303 assert_eq!(hits.len(), 1, "`{what}` is one row on a Map");
3304 assert_eq!(hits[0].arity(), 2, "`{what}` takes its key and its value");
3305 assert_ne!(
3306 hits[0].lowering, plain_store,
3307 "`{what}` must not lower to the plain store"
3308 );
3309 for (pat, other) in [
3312 (of(CollectionCtor::Counter, 1), "Counter"),
3313 (of(CollectionCtor::Grid, 1), "Grid"),
3314 (of(CollectionCtor::Vec, 1), "Vec"),
3315 (of(CollectionCtor::Set, 1), "Set"),
3316 ] {
3317 assert_eq!(
3318 cat.by_receiver_and_name(&pat, name).count(),
3319 0,
3320 "{other} has no `{what}`"
3321 );
3322 }
3323 }
3324 assert_ne!(
3327 cat.by_receiver_and_name(&map_int_value, crate::catalog::INDEX_STORE_MIN)
3328 .next()
3329 .expect("min=")
3330 .lowering,
3331 cat.by_receiver_and_name(&map_int_value, crate::catalog::INDEX_STORE_MAX)
3332 .next()
3333 .expect("max=")
3334 .lowering,
3335 );
3336
3337 for name in [
3340 crate::catalog::INDEX_READ,
3341 crate::catalog::INDEX_STORE,
3342 crate::catalog::INDEX_STORE_MIN,
3343 crate::catalog::INDEX_STORE_MAX,
3344 ] {
3345 assert!(
3346 !name
3347 .chars()
3348 .next()
3349 .is_some_and(|c| c.is_alphabetic() || c == '_'),
3350 "`{name}` must not be spellable as a method name"
3351 );
3352 }
3353 }
3354
3355 #[test]
3359 fn catalog_covers_every_collection_kind() {
3360 let cat = builtin_catalog();
3361 for (ctor, name) in [
3364 (CollectionCtor::Vec, "Vec"),
3365 (CollectionCtor::Deque, "Deque"),
3366 (CollectionCtor::Set, "Set"),
3367 (CollectionCtor::Counter, "Counter"),
3368 (CollectionCtor::MinHeap, "MinHeap"),
3369 (CollectionCtor::MaxHeap, "MaxHeap"),
3370 (CollectionCtor::BitSet, "BitSet"),
3371 ] {
3372 let args: Vec<TypePattern> = match ctor.arity() {
3373 0 => Vec::new(),
3374 n => (0..n).map(|_| TypePattern::var("T")).collect(),
3375 };
3376 let pat = TypePattern::Collection { ctor, args };
3377 let len = cat.by_receiver_and_name(&pat, "len").count();
3378 let is_empty = cat.by_receiver_and_name(&pat, "is_empty").count();
3379 assert!(len >= 1, "{name} missing len method");
3380 assert!(is_empty >= 1, "{name} missing is_empty method");
3381 }
3382 let map_pat = map_of_k_v();
3385 assert!(cat.by_receiver_and_name(&map_pat, "len").count() >= 1);
3386 assert!(cat.by_receiver_and_name(&map_pat, "is_empty").count() >= 1);
3387 let grid_pat = grid_of_t();
3389 assert!(cat.by_receiver_and_name(&grid_pat, "width").count() >= 1);
3390 assert!(cat.by_receiver_and_name(&grid_pat, "neighbors4").count() >= 1);
3391 }
3392
3393 #[test]
3406 fn the_neighbourhood_records_field_order_is_reading_order() {
3407 let cat = builtin_catalog();
3408 let grid_pat = grid_of_t();
3409 for (method, expected) in [
3410 ("around4", &["up", "left", "right", "down"][..]),
3411 (
3412 "around8",
3413 &[
3414 "up_left",
3415 "up",
3416 "up_right",
3417 "left",
3418 "right",
3419 "down_left",
3420 "down",
3421 "down_right",
3422 ][..],
3423 ),
3424 ] {
3425 let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
3426 assert_eq!(rows.len(), 1, "`Grid[T].{method}` is one row");
3427 let TypePattern::Record { name, fields } = &rows[0].result else {
3428 panic!("`Grid[T].{method}` answers a nominal record");
3429 };
3430 assert_eq!(
3431 *name,
3432 if method == "around4" {
3433 "Around4"
3434 } else {
3435 "Around8"
3436 }
3437 );
3438 let names: Vec<&str> = fields.iter().map(|(n, _)| *n).collect();
3439 assert_eq!(names, expected, "`{name}`'s fields are its slot order");
3440 for (fname, fpat) in fields {
3441 assert_eq!(
3442 *fpat,
3443 TypePattern::Option(Box::new(point_pattern())),
3444 "`{name}.{fname}` is an Option[(Int, Int)]"
3445 );
3446 }
3447 assert_eq!(rows[0].result.to_string(), *name);
3450 }
3451 }
3452
3453 #[test]
3462 fn the_clipped_vec_rows_still_answer_a_vec() {
3463 let cat = builtin_catalog();
3464 let grid_pat = grid_of_t();
3465 let vec_of_points = TypePattern::Collection {
3466 ctor: CollectionCtor::Vec,
3467 args: vec![point_pattern()],
3468 };
3469 for method in ["neighbors4", "neighbors8"] {
3470 let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
3471 assert_eq!(rows.len(), 1);
3472 assert_eq!(
3473 rows[0].result, vec_of_points,
3474 "`{method}` answers the clipped Vec a graph walk consumes"
3475 );
3476 }
3477 }
3478
3479 #[test]
3489 fn only_the_counts_that_run_a_closure_can_fault() {
3490 let cat = builtin_catalog();
3491 let grid_pat = grid_of_t();
3492 let predicate = TypePattern::Function {
3493 params: vec![TypePattern::var("T")],
3494 result: Box::new(TypePattern::Scalar(ScalarType::Bool)),
3495 };
3496 for (method, second, can_fault) in [
3497 ("count4", TypePattern::var("T"), false),
3498 ("count8", TypePattern::var("T"), false),
3499 ("count4_where", predicate.clone(), true),
3500 ("count8_where", predicate, true),
3501 ] {
3502 let rows: Vec<_> = cat.by_receiver_and_name(&grid_pat, method).collect();
3503 assert_eq!(rows.len(), 1, "`Grid[T].{method}` is one row");
3504 let row = rows[0];
3505 assert_eq!(
3506 row.params,
3507 vec![point_pattern(), second],
3508 "`{method}` takes the position and then what to count"
3509 );
3510 assert_eq!(row.result, TypePattern::Scalar(ScalarType::Int));
3511 assert_eq!(
3512 row.can_fault(),
3513 can_fault,
3514 "`{method}`: only a row that calls back into the program \
3515 needs a fault check after it"
3516 );
3517 }
3518 }
3519
3520 #[test]
3545 fn a_non_faulting_row_with_a_value_result_cannot_answer_the_unit_sentinel() {
3546 let cat = builtin_catalog();
3547 let mut checked = 0;
3548 for entry in cat.entries() {
3549 let MethodLowering::RuntimeSymbol(sym) = entry.lowering else {
3552 continue;
3553 };
3554 checked += 1;
3555 let ret = sym.sig().ret;
3556 let result_is_unit = entry.result == TypePattern::Unit;
3557 match (ret, result_is_unit) {
3558 (abi::AbiRet::GcUnit, true) | (abi::AbiRet::Gc, false) => {}
3559 _ => panic!(
3560 "{}.{} declares `{}` and lowers to `{}`, whose manifest return is \
3561 {ret:?}. A wrapper answers either a value (`AbiRet::Gc`, \
3562 non-`Unit` result) or nothing (`AbiRet::GcUnit`, `Unit` \
3563 result); an answer that is sometimes absent is spelled \
3564 `Option[T]`, never a Unit sentinel under a value type.",
3565 entry.receiver,
3566 entry.name,
3567 entry.result,
3568 sym.name(),
3569 ),
3570 }
3571 }
3572 assert!(
3575 checked >= 40,
3576 "expected the sweep to reach most of the catalog, it reached {checked} rows"
3577 );
3578 }
3579
3580 #[test]
3594 fn a_scalar_primitive_row_answers_the_scalar_channel_and_a_scalar_type() {
3595 let cat = builtin_catalog();
3596 let mut rows = 0;
3597 for entry in cat.entries() {
3598 let MethodLowering::ScalarPrimitive(sym) = entry.lowering else {
3599 continue;
3600 };
3601 rows += 1;
3602 assert_eq!(
3603 sym.sig().ret,
3604 abi::AbiRet::RawI64,
3605 "{}.{} lowers as a scalar primitive, so `{}` must answer the \
3606 scalar channel; a `-> Gc` row here is a box the caller would \
3607 have to unwrap again, which is the whole thing this arm exists \
3608 to remove",
3609 entry.receiver,
3610 entry.name,
3611 sym.name(),
3612 );
3613 assert!(
3614 matches!(entry.result, TypePattern::Scalar(_)),
3615 "{}.{} answers `{}`, which the scalar channel cannot carry",
3616 entry.receiver,
3617 entry.name,
3618 entry.result,
3619 );
3620 assert!(
3623 !entry.allocates(),
3624 "{}.{} allocates, so it cannot be a non-safepoint instruction",
3625 entry.receiver,
3626 entry.name,
3627 );
3628 }
3629 assert_eq!(
3630 rows, 1,
3631 "`BitSet.contains` is the only scalar-primitive row today; a second \
3632 one wants an arm in `praxis-mir`'s `lower_scalar_primitive` before \
3633 this number moves"
3634 );
3635 }
3636
3637 #[test]
3641 fn map_get_and_grid_find_answer_an_option() {
3642 let cat = builtin_catalog();
3643 let map_pat = map_of_k_v();
3644 let get = cat
3645 .by_receiver_and_name(&map_pat, "get")
3646 .next()
3647 .expect("map.get exists");
3648 assert_eq!(
3649 get.result,
3650 TypePattern::Option(Box::new(TypePattern::var("V"))),
3651 "§5.7 writes `Map[K,V].get(K) -> Option[V]`"
3652 );
3653
3654 let grid_pat = grid_of_t();
3655 let find = cat
3656 .by_receiver_and_name(&grid_pat, "find")
3657 .next()
3658 .expect("grid.find exists");
3659 assert_eq!(find.result, TypePattern::Option(Box::new(point_pattern())));
3660
3661 let counter_pat = counter_of_t();
3664 let counter_get = cat
3665 .by_receiver_and_name(&counter_pat, "get")
3666 .next()
3667 .expect("counter.get exists");
3668 assert_eq!(
3669 counter_get.result,
3670 TypePattern::Scalar(ScalarType::Int),
3671 "§6.2: a Counter's absent values read as zero, deliberately"
3672 );
3673 }
3674
3675 #[test]
3688 fn text_int_and_float_answer_options() {
3689 let cat = builtin_catalog();
3690 for (name, scalar) in [("int", ScalarType::Int), ("float", ScalarType::Float)] {
3691 let entry = cat
3692 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Text), name)
3693 .next()
3694 .unwrap_or_else(|| panic!("`Text.{name}()` exists"));
3695 assert_eq!(
3696 entry.result,
3697 TypePattern::Option(Box::new(TypePattern::Scalar(scalar))),
3698 "a text that is not a number is absence, not a fault (\u{00a7}4.7)"
3699 );
3700 assert!(entry.params.is_empty(), "`{name}` takes no arguments");
3701 }
3702 }
3703
3704 #[test]
3711 fn the_two_text_reads_answer_a_char() {
3712 let cat = builtin_catalog();
3713 let text = text_receiver();
3714
3715 for name in [crate::catalog::INDEX_READ, "get"] {
3716 let row = cat
3717 .by_receiver_and_name(&text, name)
3718 .next()
3719 .unwrap_or_else(|| panic!("Text.{name} exists"));
3720 assert_eq!(
3721 row.result,
3722 TypePattern::Scalar(ScalarType::Char),
3723 "ADR-086: `Text.{name}` answers a Char, not the char's scalar value"
3724 );
3725 assert_eq!(
3728 row.lowering,
3729 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::TextGet),
3730 "`Text.{name}` lowers through the one text read"
3731 );
3732 }
3733 }
3734
3735 #[test]
3740 fn char_and_int_convert_both_ways() {
3741 let cat = builtin_catalog();
3742
3743 let to_int = cat
3744 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Char), "to_int")
3745 .next()
3746 .expect("Char.to_int exists");
3747 assert_eq!(to_int.result, TypePattern::Scalar(ScalarType::Int));
3748 assert_eq!(to_int.purity, Purity::Pure);
3749
3750 let to_char = cat
3751 .by_receiver_and_name(&TypePattern::Scalar(ScalarType::Int), "to_char")
3752 .next()
3753 .expect("Int.to_char exists");
3754 assert_eq!(to_char.result, TypePattern::Scalar(ScalarType::Char));
3755
3756 assert!(
3760 to_char.can_fault(),
3761 "not every Int is a Unicode scalar value, so the narrowing faults"
3762 );
3763 assert!(
3764 !to_int.can_fault(),
3765 "every Unicode scalar value fits an Int, so the widening cannot"
3766 );
3767 }
3768
3769 #[test]
3776 fn the_to_text_family_is_int_float_and_char() {
3777 let cat = builtin_catalog();
3778 for scalar in [ScalarType::Int, ScalarType::Float, ScalarType::Char] {
3779 let receiver = TypePattern::Scalar(scalar);
3780 let row = cat
3781 .by_receiver_and_name(&receiver, "to_text")
3782 .next()
3783 .unwrap_or_else(|| panic!("{scalar:?}.to_text exists"));
3784 assert_eq!(row.result, TypePattern::Scalar(ScalarType::Text));
3785 assert_eq!(row.purity, Purity::Pure);
3786 assert!(row.allocates(), "{scalar:?}.to_text answers a fresh Text");
3787 assert!(!row.can_fault(), "{scalar:?}.to_text cannot fail");
3791 }
3792 for scalar in [ScalarType::Bool, ScalarType::Byte, ScalarType::Text] {
3793 let receiver = TypePattern::Scalar(scalar);
3794 assert_eq!(
3795 cat.by_receiver_and_name(&receiver, "to_text").count(),
3796 0,
3797 "the `to_text` family is three scalars; {scalar:?} is not one of \
3798 them, and a universal `to_text` is §8.1 interpolation's question"
3799 );
3800 }
3801 }
3802
3803 #[test]
3813 fn join_is_one_row_and_a_sequence_of_chars_has_its_own_name() {
3814 let cat = builtin_catalog();
3815
3816 let join: Vec<_> = cat.entries().iter().filter(|e| e.name == "join").collect();
3817 assert_eq!(join.len(), 1, "`join` is one row");
3818 assert_eq!(join[0].receiver, iterable_of_text_elem());
3819 assert_eq!(join[0].params, vec![TypePattern::Scalar(ScalarType::Text)]);
3820 assert_eq!(join[0].result, TypePattern::Scalar(ScalarType::Text));
3821
3822 assert!(
3826 !cat.entries()
3827 .iter()
3828 .any(|e| e.name == "to_text" && matches!(e.receiver, TypePattern::Iterable { .. })),
3829 "an `Iterable.to_text` would collide with the scalar `to_text` rows"
3830 );
3831 let chars_pat = vec_of_char();
3832 let chars = cat
3833 .by_receiver_and_name(&chars_pat, "to_text")
3834 .next()
3835 .expect("Vec[Char].to_text exists");
3836 assert_eq!(chars.result, TypePattern::Scalar(ScalarType::Text));
3837 assert!(chars.params.is_empty());
3838 }
3839
3840 #[test]
3849 fn reversed_is_a_barrier_with_no_bound_on_its_element() {
3850 let cat = builtin_catalog();
3851 let receiver = iterable_of_t();
3852 let row = cat
3853 .by_receiver_and_name(&receiver, "reversed")
3854 .next()
3855 .expect("Iterable.reversed exists");
3856 assert_eq!(row.result, vec_of_t());
3857 assert_eq!(row.purity, Purity::Pure);
3858 assert!(row.bounds().is_empty(), "reversal reads no callback");
3859 assert!(
3860 !row.can_fault(),
3861 "there is no element `reversed` can be handed that it cannot reverse"
3862 );
3863 assert!(
3864 matches!(
3865 row.lowering,
3866 MethodLowering::RuntimeSymbol(abi::RuntimeSymbol::VecReversed)
3867 ),
3868 "a barrier is a runtime call, not a fused stage: reversal cannot \
3869 answer its first element until it has seen the last"
3870 );
3871 }
3872
3873 #[test]
3891 fn a_grouping_answers_a_nested_vec_with_no_bound_and_can_fault() {
3892 let cat = builtin_catalog();
3893 let receiver = iterable_of_t();
3894 for (name, symbol) in [
3895 ("chunks", abi::RuntimeSymbol::VecChunks),
3896 ("windows", abi::RuntimeSymbol::VecWindows),
3897 ] {
3898 let row = cat
3899 .by_receiver_and_name(&receiver, name)
3900 .next()
3901 .unwrap_or_else(|| panic!("Iterable.{name} exists"));
3902 assert_eq!(
3903 row.result,
3904 vec_of_vec_of_t(),
3905 "`{name}` groups without flattening"
3906 );
3907 assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
3908 assert_eq!(row.purity, Purity::Pure);
3909 assert!(
3910 row.bounds().is_empty(),
3911 "a grouping reads no descriptor callback ({name})"
3912 );
3913 assert!(
3914 row.can_fault(),
3915 "`{name}(0)` names no run, and the program has to be able to see that"
3916 );
3917 assert!(
3918 matches!(row.lowering, MethodLowering::RuntimeSymbol(s) if s == symbol),
3919 "a grouping is a barrier: it cannot answer its first group from \
3920 one element ({name})"
3921 );
3922 }
3923 }
3924
3925 #[test]
3938 fn the_overflow_alternative_family_is_three_modes_over_three_operators() {
3939 let cat = builtin_catalog();
3940 let int = TypePattern::Scalar(ScalarType::Int);
3941
3942 for mode in ["wrapping", "saturating", "checked"] {
3943 for op in ["add", "sub", "mul"] {
3944 let name = format!("{mode}_{op}");
3945 let row = cat
3946 .by_receiver_and_name(&int, &name)
3947 .next()
3948 .unwrap_or_else(|| panic!("§4.12's family includes `Int.{name}`"));
3949 assert_eq!(row.params, vec![TypePattern::Scalar(ScalarType::Int)]);
3950 assert!(!row.can_fault(), "`{name}` is an alternative to faulting");
3954
3955 let want = if mode == "checked" {
3956 TypePattern::Option(Box::new(TypePattern::Scalar(ScalarType::Int)))
3957 } else {
3958 TypePattern::Scalar(ScalarType::Int)
3959 };
3960 assert_eq!(row.result, want, "`{name}`'s result");
3961 }
3962 }
3963
3964 for absent in [
3966 "wrapping_div",
3967 "saturating_div",
3968 "checked_div",
3969 "wrapping_rem",
3970 "checked_rem",
3971 "wrapping_neg",
3972 "checked_neg",
3973 "saturating_abs",
3974 ] {
3975 assert!(
3976 cat.by_receiver_and_name(&int, absent).next().is_none(),
3977 "§4.12 closes the family before `{absent}`: division's escape \
3978 hatch is closed by \"Division by zero always faults\", and \
3979 `_neg`/`_abs` are spelled with `0.wrapping_sub(x)`"
3980 );
3981 }
3982 }
3983}