spade_mir/
macros.rs

1/// Macros for writing mir.
2/// Requires the crate to be in scope, i.e. `use spade_mir;`
3
4#[macro_export]
5macro_rules! value_name {
6    (n($id:expr, $debug_name:expr)) => {
7        spade_mir::ValueName::_test_named($id, $debug_name.to_string())
8    };
9    (e($id:expr)) => {
10        spade_mir::ValueName::Expr(spade_common::id_tracker::ExprID($id))
11    };
12}
13
14#[macro_export]
15macro_rules! if_tracing {
16    () => {None};
17    ($traced_kind:ident $traced_name:tt) => {Some(spade_mir::value_name!($traced_kind $traced_name))}
18}
19
20#[macro_export]
21macro_rules! optional_reset {
22    () => {None};
23    (($rst_trig_kind:ident $rst_trig_name:tt, $rst_val_kind:ident $rst_val_name:tt)) => {
24        Some((
25            spade_mir::value_name!($rst_trig_kind $rst_trig_name),
26            spade_mir::value_name!($rst_val_kind $rst_val_name)
27        ))
28    }
29}
30
31#[macro_export]
32macro_rules! optional_initial {
33    () => {
34        None
35    };
36    (($val:expr)) => {
37        Some($val)
38    };
39}
40
41#[macro_export]
42macro_rules! statement {
43    // Normal constants
44    (
45        const $id:expr; $ty:expr; $value:expr
46    ) => {
47        spade_mir::Statement::Constant(spade_common::id_tracker::ExprID($id), $ty, $value)
48    };
49    // Bindings
50    (
51        $name_kind:ident $name:tt;
52        $type:expr;
53        $operator:ident $(($operator_args:tt))?$({$operator_struct_args:tt})?;
54        $($arg_kind:ident $arg_name:tt),*
55    ) => {
56        spade_mir::Statement::Binding(spade_mir::Binding {
57            name: spade_mir::value_name!($name_kind $name),
58            operator: spade_mir::Operator::$operator$($operator_args)?,
59            operands: vec![
60                $(spade_mir::value_name!($arg_kind $arg_name)),*
61            ],
62            ty: $type,
63            loc: None,
64        })
65    };
66    //register with async reset
67    (
68        $(traced($traced_kind:ident $traced_name:tt))?
69        reg $name_kind:ident $name:tt;
70        $type:expr;
71        clock ($clk_name_kind:ident $clk_name:tt);
72        $(reset $reset:tt)?
73        $(initial $initial:tt)?;
74        $val_kind:ident $val_name:tt
75    ) => {
76        spade_mir::Statement::Register(spade_mir::Register {
77            name: spade_mir::value_name!($name_kind $name),
78            ty: $type,
79            clock: spade_mir::value_name!($clk_name_kind $clk_name),
80            reset: spade_mir::optional_reset!($($reset)?),
81            initial: spade_mir::optional_initial!($($initial)?),
82            value: spade_mir::value_name!($val_kind $val_name),
83            loc: None,
84            traced: spade_mir::if_tracing!($($traced_kind $traced_name)?)
85        })
86    };
87    // Register without reset
88    (
89        $(traced($traced_kind:ident $traced_name:tt))?
90        reg $name_kind:ident $name:tt;
91        $type:expr;
92        clock ($clk_name_kind:ident $clk_name:tt);
93        $val_kind:ident $val_name:tt
94    ) => {
95        spade_mir::Statement::Register(spade_mir::Register {
96            name: spade_mir::value_name!($name_kind $name),
97            ty: $type,
98            clock: spade_mir::value_name!($clk_name_kind $clk_name),
99            reset: None,
100            initial: None,
101            value: spade_mir::value_name!($val_kind $val_name),
102            loc: None,
103            traced: spade_mir::if_tracing!($($traced_kind $traced_name)?)
104        })
105    };
106    // Set statement
107    (set; $lhs_kind:ident $lhs_name:tt; $rhs_kind:ident $rhs_name:tt) => {
108        spade_mir::Statement::Set{
109            target: spade_mir::value_name!($lhs_kind $lhs_name).nowhere(),
110            value: spade_mir::value_name!($rhs_kind $rhs_name).nowhere()
111        }
112    };
113    (
114        assert; $name_kind:ident $name:tt
115    ) => {
116        spade_mir::Statement::Assert(spade_mir::value_name!($name_kind $name).nowhere())
117    };
118    (wal_trace ($name_kind:ident $name_name:tt, $val_kind:ident $val_name:tt, $suffix:expr, $ty:expr) ) => {
119        spade_mir::Statement::WalTrace{
120            name: spade_mir::value_name!($name_kind $name_name),
121            val: spade_mir::value_name!($val_kind $val_name),
122            suffix: $suffix.into(),
123            ty: $ty
124        }
125    }
126}
127
128/// Example
129/// ```
130/// use spade_mir as mir;
131/// use spade_mir::entity;
132/// use spade_mir::types::Type;
133/// entity!("pong"; ("_i_clk", n(0, "clk"), Type::Bool) -> Type::int(6); {
134///     (e(0); Type::int(6); Add; n(1, "value"));
135///     (const 0; Type::int(10); ConstantValue::Int(6));
136///     (reg n(1, "value"); Type::int(6); clock (n(0, "clk")); e(0))
137/// } => n(1, "value"));
138/// ```
139#[macro_export]
140macro_rules! entity {
141    ($name:expr; (
142            $( $arg_name:expr, $arg_intern_kind:ident $arg_intern_name:tt, $arg_type:expr ),* $(,)?
143        ) -> $output_type:expr; {
144            $( $statement:tt );*
145            $(;)?
146        } => $output_name_kind:ident $output_name:tt
147    ) => {
148        spade_mir::Entity {
149            name: spade_mir::unit_name::IntoUnitName::_test_into_unit_name($name),
150            inputs: vec![
151                $(
152                    spade_mir::MirInput {
153                        name: $arg_name.to_string(),
154                        val_name: spade_mir::value_name!($arg_intern_kind $arg_intern_name),
155                        ty: $arg_type,
156                        no_mangle: None
157                    }
158                ),*
159            ],
160            output: spade_mir::value_name!($output_name_kind $output_name),
161            output_type: $output_type,
162            statements: vec![
163                $( spade_mir::statement! $statement ),*
164            ],
165        }
166    }
167}
168
169#[macro_export]
170macro_rules! assert_same_mir {
171    ($got:expr, $expected:expr) => {{
172        let mut var_map = spade_mir::diff::VarMap::new();
173
174        if !spade_mir::diff::compare_entity($got, $expected, &mut var_map) {
175            let (got, expected) =
176                spade_mir::diff_printing::translated_strings($got, $expected, &var_map);
177
178            println!("{}:\n{}", "got".red(), got);
179            println!("{}", "==============================================".red());
180            println!("{}:\n{}", "expected".green(), expected);
181            println!(
182                "{}",
183                "==============================================".green()
184            );
185            println!("{}", prettydiff::diff_chars(&got, &expected));
186            println!(
187                "{}",
188                "==============================================".yellow()
189            );
190            panic!("Code mismatch")
191        }
192    }};
193}
194
195#[cfg(test)]
196mod tests {
197    use spade_common::id_tracker::ExprID;
198    use spade_common::name::{NameID, Path};
199    use spade_mir::unit_name::UnitNameKind;
200
201    use crate::{self as spade_mir, MirInput, UnitName};
202    use crate::{types::Type, Binding, ConstantValue, Operator, Register, Statement, ValueName};
203
204    #[test]
205    fn value_name_parsing_works() {
206        assert_eq!(
207            value_name!(n(0, "test")),
208            ValueName::_test_named(0, "test".to_string())
209        );
210        assert_eq!(value_name!(e(0)), ValueName::Expr(ExprID(0)));
211    }
212
213    #[test]
214    fn binding_parsing_works() {
215        let expected = Statement::Binding(Binding {
216            name: ValueName::Expr(ExprID(0)),
217            operator: Operator::Add,
218            operands: vec![
219                ValueName::Expr(ExprID(1)),
220                ValueName::_test_named(1, "test".to_string()),
221            ],
222            ty: Type::Bool,
223            loc: None,
224        });
225
226        assert_eq!(
227            statement!(e(0); Type::Bool; Add; e(1), n(1, "test")),
228            expected
229        );
230    }
231
232    #[test]
233    fn named_parsing_works() {
234        let expected = Statement::Binding(Binding {
235            name: ValueName::_test_named(0, "string".to_string()),
236            operator: Operator::Add,
237            operands: vec![
238                ValueName::Expr(ExprID(1)),
239                ValueName::_test_named(1, "test".to_string()),
240            ],
241            ty: Type::Bool,
242            loc: None,
243        });
244
245        assert_eq!(
246            statement!(n(0, "string"); Type::Bool; Add; e(1), n(1, "test")),
247            expected
248        );
249    }
250
251    #[test]
252    fn register_parsing_works() {
253        let expected = Statement::Register(Register {
254            name: ValueName::_test_named(0, "test".into()),
255            ty: Type::int(5),
256            clock: ValueName::_test_named(1, "clk".into()),
257            reset: None,
258            initial: None,
259            value: ValueName::Expr(ExprID(0)),
260            loc: None,
261            traced: Some(ValueName::Expr(ExprID(2))),
262        });
263
264        assert_eq!(
265            statement!(traced(e(2)) reg n(0, "test"); Type::int(5); clock (n(1, "clk")); e(0)),
266            expected
267        );
268    }
269
270    #[test]
271    fn register_with_reset_parsing_works() {
272        let expected = Statement::Register(Register {
273            name: ValueName::_test_named(0, "test".into()),
274            ty: Type::int(5),
275            clock: ValueName::_test_named(1, "clk".into()),
276            reset: Some((ValueName::Expr(ExprID(1)), ValueName::Expr(ExprID(2)))),
277            initial: None,
278            value: ValueName::Expr(ExprID(0)),
279            loc: None,
280            traced: None,
281        });
282
283        assert_eq!(
284            statement!(reg n(0, "test"); Type::int(5); clock (n(1, "clk")); reset (e(1), e(2)); e(0)),
285            expected
286        );
287    }
288
289    #[test]
290    fn entity_parsing_works() {
291        let expected = crate::Entity {
292            name: UnitName {
293                kind: UnitNameKind::Escaped {
294                    name: "pong".to_string(),
295                    path: vec!["pong".to_string()],
296                },
297                source: NameID(0, Path::from_strs(&["pong"])),
298            },
299            inputs: vec![MirInput {
300                name: "_i_clk".to_string(),
301                val_name: ValueName::_test_named(0, "clk".to_string()),
302                ty: Type::Bool,
303                no_mangle: None,
304            }],
305            output: ValueName::_test_named(1, "value".to_string()),
306            output_type: Type::int(6),
307            statements: vec![
308                statement!(e(0); Type::int(6); Add; n(1, "value")),
309                statement!(reg n(1, "value"); Type::int(6); clock (n(0, "clk")); e(0)),
310            ],
311        };
312
313        let result = entity!(&["pong"]; ("_i_clk", n(0, "clk"), Type::Bool) -> Type::int(6); {
314                (e(0); Type::int(6); Add; n(1, "value"));
315                (reg n(1, "value"); Type::int(6); clock (n(0, "clk")); e(0))
316            } => n(1, "value"));
317
318        assert_eq!(result, expected);
319    }
320
321    #[test]
322    fn constant_parsing_works() {
323        let expected = Statement::Constant(ExprID(0), Type::int(10), ConstantValue::int(6));
324
325        let result = statement!(const 0; Type::int(10); ConstantValue::int(6));
326
327        assert_eq!(result, expected);
328    }
329}