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