Skip to main content

sva_samples/machine/
ops.rs

1// Concern: the postfix op array one node renderer lowers to, and the width each slot holds | Non-concern: lowering into it or running it (mod.rs) | IO: (&NodeRenderer, &Layout) -> Vec<Op> + Vec<usize>
2
3use crate::error::SampleError;
4use crate::machine::renderer::{Binary, BufId, NodeRenderer, Site, SiteId, Unary};
5
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub(crate) enum Op {
8    Const(f64),
9    Time,
10    Read {
11        id: BufId,
12        shift: i64,
13    },
14    SelfAt {
15        steps: u32,
16    },
17    Add(usize),
18    Mul(usize),
19    Sub,
20    Div,
21    Pow,
22    Map(Unary),
23    Zip(Binary),
24    Crop {
25        a: f64,
26        b: f64,
27        rise: f64,
28        fall: f64,
29    },
30    Join(usize),
31    Channel(usize),
32    Filter(SiteId),
33    Physics(SiteId),
34}
35
36/// What the engine already knows from typing: how wide the node is, how wide each collapsed
37/// read is, and which call sites the renderer opens.
38#[derive(Clone, Debug, PartialEq)]
39pub struct Layout {
40    pub width: usize,
41    pub read_widths: Vec<usize>,
42    pub sites: Vec<Site>,
43}
44
45/// Equal widths pass, a mono side widens, anything else refuses.
46fn meet(a: usize, b: usize) -> Result<usize, SampleError> {
47    match (a, b) {
48        (a, b) if a == b => Ok(a),
49        (1, b) => Ok(b),
50        (a, 1) => Ok(a),
51        (left, right) => Err(SampleError::WidthMismatch { left, right }),
52    }
53}
54
55/// Postfix, so the runner needs no recursion and every operand's width is already settled
56/// by the time the op that consumes it is reached.
57pub(crate) fn lower(
58    renderer: &NodeRenderer,
59    layout: &Layout,
60    ops: &mut Vec<Op>,
61    widths: &mut Vec<usize>,
62) -> Result<usize, SampleError> {
63    let push = |op: Op, width: usize, ops: &mut Vec<Op>, widths: &mut Vec<usize>| {
64        ops.push(op);
65        widths.push(width);
66        width
67    };
68    let w = match renderer {
69        NodeRenderer::Const(v) => push(Op::Const(*v), 1, ops, widths),
70        NodeRenderer::Time => push(Op::Time, 1, ops, widths),
71        NodeRenderer::Buffer { id, shift } => {
72            let width = layout.read_widths[id.0 as usize];
73            push(
74                Op::Read {
75                    id: *id,
76                    shift: *shift,
77                },
78                width,
79                ops,
80                widths,
81            )
82        }
83        NodeRenderer::SelfAt { steps } => {
84            push(Op::SelfAt { steps: *steps }, layout.width, ops, widths)
85        }
86        NodeRenderer::Add(parts) | NodeRenderer::Mul(parts) => {
87            let mut width = 1;
88            for p in parts {
89                width = meet(width, lower(p, layout, ops, widths)?)?;
90            }
91            let op = match renderer {
92                NodeRenderer::Add(_) => Op::Add(parts.len()),
93                _ => Op::Mul(parts.len()),
94            };
95            push(op, width, ops, widths)
96        }
97        NodeRenderer::Sub(a, b) | NodeRenderer::Div(a, b) | NodeRenderer::Pow(a, b) => {
98            let wa = lower(a, layout, ops, widths)?;
99            let wb = lower(b, layout, ops, widths)?;
100            let op = match renderer {
101                NodeRenderer::Sub(..) => Op::Sub,
102                NodeRenderer::Div(..) => Op::Div,
103                _ => Op::Pow,
104            };
105            push(op, meet(wa, wb)?, ops, widths)
106        }
107        NodeRenderer::Map(f, x) => {
108            let w = lower(x, layout, ops, widths)?;
109            push(Op::Map(*f), w, ops, widths)
110        }
111        NodeRenderer::Zip(f, a, b) => {
112            let wa = lower(a, layout, ops, widths)?;
113            let wb = lower(b, layout, ops, widths)?;
114            push(Op::Zip(*f), meet(wa, wb)?, ops, widths)
115        }
116        NodeRenderer::Crop {
117            x,
118            a,
119            b,
120            rise,
121            fall,
122        } => {
123            let w = lower(x, layout, ops, widths)?;
124            let crop = Op::Crop {
125                a: *a,
126                b: *b,
127                rise: *rise,
128                fall: *fall,
129            };
130            push(crop, w, ops, widths)
131        }
132        NodeRenderer::Join(parts) => {
133            let mut width = 0;
134            for p in parts {
135                width += lower(p, layout, ops, widths)?;
136            }
137            push(Op::Join(parts.len()), width, ops, widths)
138        }
139        NodeRenderer::Channel { x, k } => {
140            let w = lower(x, layout, ops, widths)?;
141            if *k >= w {
142                return Err(SampleError::ChannelOutOfRange { k: *k, width: w });
143            }
144            push(Op::Channel(*k), 1, ops, widths)
145        }
146        NodeRenderer::Filter {
147            site,
148            x,
149            cutoff,
150            q,
151            gain,
152        } => {
153            let mut width = lower(x, layout, ops, widths)?;
154            for arg in [cutoff, q, gain] {
155                width = meet(width, lower(arg, layout, ops, widths)?)?;
156            }
157            push(Op::Filter(*site), width, ops, widths)
158        }
159        NodeRenderer::Physics { site } => push(Op::Physics(*site), 1, ops, widths),
160    };
161    Ok(w)
162}