1use crate::diag::ByteSpan;
4
5pub const JOIN: &str = "join";
6pub const SERIES: &str = "sum";
7pub const INDEX: &str = "idx";
8pub const FLOOR: &str = "floor";
9pub const CEIL: &str = "ceil";
10pub const DEGREE: &str = "deg";
11
12#[derive(Clone, Copy, Debug)]
13pub enum BinOp {
14 Add,
15 Sub,
16 Mul,
17 Div,
18 Mod,
19}
20
21impl PartialEq for BinOp {
22 fn eq(&self, other: &Self) -> bool {
23 matches!(
24 (self, other),
25 (BinOp::Add, BinOp::Add)
26 | (BinOp::Sub, BinOp::Sub)
27 | (BinOp::Mul, BinOp::Mul)
28 | (BinOp::Div, BinOp::Div)
29 | (BinOp::Mod, BinOp::Mod)
30 )
31 }
32}
33impl Eq for BinOp {}
34
35#[derive(Clone, Debug)]
38pub enum Literal {
39 Num(f64),
40 Bars(f64),
41 Samples(f64),
42 Str(String),
43 Path(String),
44 Recorded(std::sync::Arc<crate::recording::Recording>),
45}
46
47impl PartialEq for Literal {
48 fn eq(&self, other: &Self) -> bool {
49 match (self, other) {
50 (Literal::Num(a), Literal::Num(b))
51 | (Literal::Bars(a), Literal::Bars(b))
52 | (Literal::Samples(a), Literal::Samples(b)) => a.to_bits() == b.to_bits(),
53 (Literal::Str(a), Literal::Str(b)) | (Literal::Path(a), Literal::Path(b)) => a == b,
54 (Literal::Recorded(a), Literal::Recorded(b)) => a == b,
55 _ => false,
56 }
57 }
58}
59impl Eq for Literal {}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum Arg {
63 Pos(Expr),
64 Named(String, Expr),
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub enum Address {
70 Time,
71 Index,
72}
73
74#[derive(Clone, Debug)]
78pub enum Expr {
79 Lit(Literal),
80 Var(String),
81 Bin(BinOp, Box<Expr>, Box<Expr>),
82 Call {
83 name: String,
84 args: Vec<Arg>,
85 span: ByteSpan,
86 },
87 Ref {
88 path: String,
89 arg: Box<Expr>,
90 binds: Vec<(String, Expr)>,
91 address: Address,
92 span: ByteSpan,
93 },
94 SelfRef {
95 arg: Box<Expr>,
96 address: Address,
97 span: ByteSpan,
98 },
99 Indexed {
101 name: String,
102 arg: Box<Expr>,
103 span: ByteSpan,
104 },
105}
106
107impl PartialEq for Expr {
108 fn eq(&self, other: &Self) -> bool {
109 match (self, other) {
110 (Expr::Lit(a), Expr::Lit(b)) => a == b,
111 (Expr::Var(a), Expr::Var(b)) => a == b,
112 (Expr::Bin(o1, l1, r1), Expr::Bin(o2, l2, r2)) => o1 == o2 && l1 == l2 && r1 == r2,
113 (
114 Expr::Call {
115 name: n1, args: a1, ..
116 },
117 Expr::Call {
118 name: n2, args: a2, ..
119 },
120 ) => n1 == n2 && a1 == a2,
121 (
122 Expr::Ref {
123 path: p1,
124 arg: a1,
125 binds: b1,
126 address: x1,
127 ..
128 },
129 Expr::Ref {
130 path: p2,
131 arg: a2,
132 binds: b2,
133 address: x2,
134 ..
135 },
136 ) => p1 == p2 && a1 == a2 && b1 == b2 && x1 == x2,
137 (
138 Expr::SelfRef {
139 arg: a1,
140 address: x1,
141 ..
142 },
143 Expr::SelfRef {
144 arg: a2,
145 address: x2,
146 ..
147 },
148 ) => a1 == a2 && x1 == x2,
149 (
150 Expr::Indexed {
151 name: n1, arg: a1, ..
152 },
153 Expr::Indexed {
154 name: n2, arg: a2, ..
155 },
156 ) => n1 == n2 && a1 == a2,
157 _ => false,
158 }
159 }
160}
161impl Eq for Expr {}
162
163#[derive(Clone, Copy, Debug, PartialEq, Eq)]
165pub enum Binds {
166 Keep,
167 Substitute,
168}
169
170pub fn children(e: &Expr, binds: Binds) -> Vec<&Expr> {
172 match e {
173 Expr::Lit(_) | Expr::Var(_) => Vec::new(),
174 Expr::Bin(_, l, r) => vec![l, r],
175 Expr::SelfRef { arg, .. } | Expr::Indexed { arg, .. } => vec![arg],
176 Expr::Ref { arg, binds: bs, .. } => {
177 let mut out = vec![arg.as_ref()];
178 if binds == Binds::Substitute {
179 out.extend(bs.iter().map(|(_, v)| v));
180 }
181 out
182 }
183 Expr::Call { args, .. } => args
184 .iter()
185 .map(|a| {
186 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
187 x
188 })
189 .collect(),
190 }
191}
192
193pub fn map_children<E>(
195 e: &Expr,
196 binds: Binds,
197 mut f: impl FnMut(&Expr) -> Result<Expr, E>,
198) -> Result<Expr, E> {
199 Ok(match e {
200 Expr::Lit(_) | Expr::Var(_) => e.clone(),
201 Expr::Bin(op, l, r) => Expr::Bin(*op, Box::new(f(l)?), Box::new(f(r)?)),
202 Expr::SelfRef { arg, address, span } => Expr::SelfRef {
203 arg: Box::new(f(arg)?),
204 address: *address,
205 span: *span,
206 },
207 Expr::Indexed { name, arg, span } => Expr::Indexed {
208 name: name.clone(),
209 arg: Box::new(f(arg)?),
210 span: *span,
211 },
212 Expr::Ref {
213 path,
214 arg,
215 binds: bs,
216 address,
217 span,
218 } => {
219 let arg = Box::new(f(arg)?);
220 let mut rebuilt = Vec::with_capacity(bs.len());
221 for (k, v) in bs {
222 rebuilt.push((
223 k.clone(),
224 if binds == Binds::Substitute {
225 f(v)?
226 } else {
227 v.clone()
228 },
229 ));
230 }
231 Expr::Ref {
232 path: path.clone(),
233 arg,
234 binds: rebuilt,
235 address: *address,
236 span: *span,
237 }
238 }
239 Expr::Call { name, args, span } => {
240 let mut rebuilt = Vec::with_capacity(args.len());
241 for a in args {
242 rebuilt.push(match a {
243 Arg::Pos(x) => Arg::Pos(f(x)?),
244 Arg::Named(k, x) => Arg::Named(k.clone(), f(x)?),
245 });
246 }
247 Expr::Call {
248 name: name.clone(),
249 args: rebuilt,
250 span: *span,
251 }
252 }
253 })
254}
255
256pub(crate) fn map_children_ok(e: &Expr, binds: Binds, mut f: impl FnMut(&Expr) -> Expr) -> Expr {
257 let mapped: Result<Expr, ()> = map_children(e, binds, |c| Ok(f(c)));
258 mapped.expect("an infallible map never refuses")
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn structural_equality_ignores_spans() {
267 let a = Expr::Ref {
268 path: "kick".to_string(),
269 arg: Box::new(Expr::Var("t".to_string())),
270 binds: Vec::new(),
271 address: Address::Time,
272 span: ByteSpan::new(0, 4),
273 };
274 let b = Expr::Ref {
275 path: "kick".to_string(),
276 arg: Box::new(Expr::Var("t".to_string())),
277 binds: Vec::new(),
278 address: Address::Time,
279 span: ByteSpan::new(99, 200),
280 };
281 assert_eq!(a, b);
282 }
283
284 #[test]
285 fn float_literals_compare_by_bit_pattern() {
286 assert_eq!(Literal::Num(0.0), Literal::Num(0.0));
287 assert_ne!(Literal::Num(0.0), Literal::Num(-0.0), "-0.0 != 0.0 exactly");
288 assert_eq!(Literal::Num(f64::NAN), Literal::Num(f64::NAN));
289 }
290}