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