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