cycles/
mini.rs

1//! A mini-notation implementation inspired by tidalcycles'.
2
3#[cfg(test)]
4use crate::Pattern;
5
6/// A mini-notation implementation inspired by tidalcycles'.
7#[macro_export]
8macro_rules! m {
9    (atom $i:ident) => {{ $crate::atom(stringify!($i)) }};
10    (atom $l:literal) => {{ $crate::atom($l) }};
11    (atom ~) => {{ $crate::silence() }};
12    (atom ($t:tt) $elongate:literal $($ts:tt)*) => {{
13        let rate = $crate::Rational::new(1, $elongate);
14        let p = $crate::Pattern::into_dyn($crate::Pattern::rate(m!(atom $t), rate));
15        let ps = m!(group $($ts)*);
16        let silence = (0..$elongate-1).map(|_| $crate::Pattern::into_dyn($crate::silence()));
17        let elongated = $crate::mini::Chain(std::iter::once(p).chain(silence));
18        $crate::mini::Chain(elongated.chain(ps))
19    }};
20
21    (group $i:ident($a:literal,$b:literal) $($ts:tt)*) => {{
22        compile_error!("Euclidean patterns not yet supported")
23    }};
24    (group $l:literal($a:literal,$b:literal) $($ts:tt)*) => {{
25        compile_error!("Euclidean patterns not yet supported")
26    }};
27    (group {{$e:expr}} $($ts:tt)*) => {{
28        compile_error!("Expressions unhandled")
29    }};
30    (group $i:ident _ _ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom ($i) 8 $($ts)*) }};
31    (group $i:ident _ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom ($i) 7 $($ts)*) }};
32    (group $i:ident _ _ _ _ _ $($ts:tt)*) => {{ m!(atom ($i) 6 $($ts)*) }};
33    (group $i:ident _ _ _ _ $($ts:tt)*) => {{ m!(atom ($i) 5 $($ts)*) }};
34    (group $i:ident _ _ _ $($ts:tt)*) => {{ m!(atom ($i) 4 $($ts)*) }};
35    (group $i:ident _ _ $($ts:tt)*) => {{ m!(atom ($i) 3 $($ts)*) }};
36    (group $i:ident _ $($ts:tt)*) => {{ m!(atom ($i) 2 $($ts)*) }};
37    (group $i:ident $($ts:tt)*) => {{ m!(atom ($i) 1 $($ts)*) }};
38    (group $l:literal _ _ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom ($l) 8 $($ts)*) }};
39    (group $l:literal _ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom ($l) 7 $($ts)*) }};
40    (group $l:literal _ _ _ _ _ $($ts:tt)*) => {{ m!(atom ($l) 6 $($ts)*) }};
41    (group $l:literal _ _ _ _ $($ts:tt)*) => {{ m!(atom ($l) 5 $($ts)*) }};
42    (group $l:literal _ _ _ $($ts:tt)*) => {{ m!(atom ($l) 4 $($ts)*) }};
43    (group $l:literal _ _ $($ts:tt)*) => {{ m!(atom ($l) 3 $($ts)*) }};
44    (group $l:literal _ $($ts:tt)*) => {{ m!(atom ($l) 2 $($ts)*) }};
45    (group $l:literal $($ts:tt)*) => {{ m!(atom ($l) 1 $($ts)*) }};
46    (group ~ _ _ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom (~) 8 $($ts)*) }};
47    (group ~ _ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom (~) 7 $($ts)*) }};
48    (group ~ _ _ _ _ _ $($ts:tt)*) => {{ m!(atom (~) 6 $($ts)*) }};
49    (group ~ _ _ _ _ $($ts:tt)*) => {{ m!(atom (~) 5 $($ts)*) }};
50    (group ~ _ _ _ $($ts:tt)*) => {{ m!(atom (~) 4 $($ts)*) }};
51    (group ~ _ _ $($ts:tt)*) => {{ m!(atom (~) 3 $($ts)*) }};
52    (group ~ _ $($ts:tt)*) => {{ m!(atom (~) 2 $($ts)*) }};
53    (group ~ $($ts:tt)*) => {{ m!(atom (~) 1 $($ts)*) }};
54    (group $l:literal $($ts:tt)*) => {{
55        let p = $crate::Pattern::into_dyn($crate::atom($l));
56        let ps = m!(group $($ts)*);
57        $crate::mini::Chain(std::iter::once(p).chain(ps))
58    }};
59    (group [$($a:tt)*], [$($b:tt)*]) => {{
60        let stack = $crate::stack([
61            $crate::fastcat(m!(group $($a)*)).into_dyn(),
62            $crate::fastcat(m!(group $($b)*)).into_dyn(),
63        ]).into_dyn();
64        std::iter::once(stack)
65    }};
66    (group [$($fastcat:tt)*] $($ts:tt)*) => {{
67        let p = $crate::Pattern::into_dyn(m!($($fastcat)*));
68        let ps = m!(group $($ts)*);
69        $crate::mini::Chain(std::iter::once(p).chain(ps))
70    }};
71    (group <($($slowcat:tt)*)> $($ts:tt)*) => {{
72        let p = $crate::Pattern::into_dyn($crate::slowcat(m!(group $($slowcat)*)));
73        let ps = m!(group $($ts)*);
74        $crate::mini::Chain(std::iter::once(p).chain(ps))
75    }};
76    (group $($ts:tt)*) => {{
77        []
78    }};
79
80    ($($ts:tt)*) => {{
81        $crate::fastcat(m!(group $($ts)*))
82    }};
83}
84
85// Provides an `ExactSizeIterator` implementation `for std::iter::Chain`
86// The only reason it's omitted from the std library is that the sum of
87// the two lengths might overflow `usize`... To account for this, we check
88// for the upper bound on the size_hint explicitly.
89pub struct Chain<A, B>(pub std::iter::Chain<A, B>);
90
91impl<A, B> Iterator for Chain<A, B>
92where
93    A: Iterator,
94    B: Iterator<Item = A::Item>,
95{
96    type Item = A::Item;
97    fn next(&mut self) -> Option<Self::Item> {
98        self.0.next()
99    }
100    fn size_hint(&self) -> (usize, Option<usize>) {
101        self.0.size_hint()
102    }
103}
104
105impl<A, B> ExactSizeIterator for Chain<A, B>
106where
107    A: Iterator,
108    B: Iterator<Item = A::Item>,
109{
110    fn len(&self) -> usize {
111        let (lower, upper) = self.size_hint();
112        let upper = upper.expect("no upperbound on iterator");
113        assert_eq!(lower, upper);
114        upper
115    }
116}
117
118#[test]
119fn test_ident_atom() {
120    dbg!(m![bd].debug());
121}
122
123#[test]
124fn test_ident_seq() {
125    dbg!(m![bd sn cp td].debug());
126}
127
128#[test]
129fn test_literal_seq() {
130    dbg!(m![0 1 2 3 4].debug());
131}
132
133#[test]
134fn test_literal_fastcat() {
135    dbg!(m![0 [1 2] 3 [4 5]].debug());
136}
137
138#[test]
139fn test_fastcat() {
140    let a = m![bd sn].query_cycle().collect::<Vec<_>>();
141    let b = m![[[[bd sn]]]].query_cycle().collect::<Vec<_>>();
142    assert_eq!(a, b);
143}
144
145#[test]
146fn test_rest() {
147    dbg!(m![0 ~ 2 ~ ~ 6 ~ 8].debug());
148}
149
150#[test]
151fn test_elongate() {
152    dbg!(m![bd _ _ _ sn _].debug());
153    dbg!(m![0 _ _ _ 1 _].debug());
154}
155
156#[test]
157fn test_slowcat() {
158    use crate::span;
159    dbg!(m![a b <(c d)>].debug_span(span!(0 / 1, 4 / 1)));
160}
161
162#[test]
163fn test_stack() {
164    dbg!(m![[bd bd], [sn sn sn]].debug());
165}