synthphonia/expr/ops/
list.rs

1use std::cmp::min;
2use std::ops::Not;
3
4use derive_more::DebugCustom;
5use crate::galloc::{AllocForStr, AllocForExactSizeIter, TryAllocForExactSizeIter};
6use crate::utils::F64;
7use crate::{new_op1, new_op2, new_op3, new_op2_opt};
8use itertools::izip;
9
10
11
12use super::{Op1, Op3, Op2};
13
14/// Converts an integer to a valid index within a bounded length. 
15/// 
16/// For non-negative input `i`, it returns the minimum of `i` or `len - 1`, ensuring the result does not exceed the upper bound of the available length. 
17/// For negative `i`, it computes an index by subtracting the negated value of `i` from the total length using saturating operations to avoid overflow, effectively wrapping around from the end of the array-like sequence. 
18/// This function handles out-of-bounds indices gracefully, ensuring the resulting index always falls within the valid range of 0 to `len - 1`.
19/// 
20pub fn to_index(len: usize, i: i64) -> usize {
21    if i >= 0 {
22        min(i as usize, len - 1)
23    } else {
24        len.saturating_sub(i.saturating_neg() as usize)
25    }
26}
27
28new_op2_opt!(At, "list.at",
29    (Str, Int) -> Str { |(s1, s2)| {
30        if !s1.is_empty() {
31            let i = to_index(s1.len(), *s2);
32            Some(s1[i..=i].galloc_str())
33        } else { None }
34    }},
35    (ListInt, Int) -> Int { |(s1, s2)| {
36        if !s1.is_empty() {
37            let i = to_index(s1.len(), *s2);
38            Some(s1[i])
39        } else { None }
40    }},
41    (ListStr, Int) -> Str { |(s1, s2)| {
42        if !s1.is_empty() {
43            let i = to_index(s1.len(), *s2);
44            Some(s1[i])
45        } else { None }
46    }},
47    (Str, Float) -> Str { |(s1, s2)| {
48        if !s1.is_empty() {
49            let i = to_index(s1.len(), **s2 as i64);
50            Some(s1[i..=i].galloc_str())
51        } else { None }
52    }},
53    (ListInt, Float) -> Int { |(s1, s2)| {
54        if !s1.is_empty() {
55            let i = to_index(s1.len(), **s2 as i64);
56            Some(s1[i])
57        } else { None }
58    }},
59    (ListStr, Float) -> Str { |(s1, s2)| {
60        if !s1.is_empty() {
61            let i = to_index(s1.len(), **s2 as i64);
62            Some(s1[i])
63        } else { None }
64    }}
65);
66
67new_op2!(StrAt, "str.at",
68    (Str, Int) -> Str { |(s1, s2)| {
69        if !s1.is_empty() {
70            if *s2 >= 0 && (*s2 as usize) < s1.len() {
71                let i = *s2 as usize;
72                s1[i..=i].galloc_str()
73            } else { "" }
74        } else { "" }
75    }}
76);
77
78new_op1!(Len, "list.len", 
79    Str -> Int { |s| s.len() as i64 },
80    ListInt -> Int { |s| s.len() as i64 },
81    ListStr -> Int { |s| s.len() as i64 }
82);
83
84new_op1!(FLen, "list.flen", 
85    Str -> Float { |s| F64::from_usize(s.len()) },
86    ListInt -> Float { |s| F64::from_usize(s.len()) },
87    ListStr -> Float { |s| F64::from_usize(s.len()) }
88);
89
90pub mod map;
91pub use map::Map;
92
93pub mod filter;
94pub use filter::Filter;