Skip to content

Instantly share code, notes, and snippets.

@yupferris
Last active February 1, 2020 01:21
Show Gist options
  • Save yupferris/41b308e845a8c5e638f6105db7871bb6 to your computer and use it in GitHub Desktop.
Save yupferris/41b308e845a8c5e638f6105db7871bb6 to your computer and use it in GitHub Desktop.
I feel dirty.
/// **UNSTABLE:** Provides a convenient way to write conditional combinational logic.
///
/// # Panics
///
/// Since this macro rewrites the referenced variable assignments using [`mux`], any panic conditions from that method apply to the generated code as well.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// let m = c.module("my_module");
/// let i = m.input("i", 1);
/// let invert = m.input("invert", 1);
/// let mut o = i;
/// kaze_sugar! {
/// if (invert) {
/// o = !o; // Equivalent to `o = invert.mux(!o, o);`
/// }
/// }
/// m.output("o", o);
/// ```
///
/// [`mux`]: ./struct.Signal.html#method.mux
#[macro_export(local_inner_macros)]
macro_rules! kaze_sugar {
($($contents:tt)*) => {
kaze_sugar_impl!([], [ $($contents)* ])
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! kaze_sugar_impl {
// [if], [token stream]
// No if cases
([], [ $name:ident = $value:expr; $($rest:tt)* ]) => {
$name = $value;
kaze_sugar_impl!([], [ $($rest)* ]);
};
([], [ if ($cond:expr) { $($inner:tt)* } $($rest:tt)* ]) => {
{
let mut if_ = If::new(None, $cond);
kaze_sugar_impl!([ if_ ], [ $($inner)* ]);
}
kaze_sugar_impl!([], [ $($rest)* ]);
};
([], []) => {};
// If cases
([ $if:expr ], [ $name:ident = $value:expr; $($rest:tt)* ]) => {
$if.assign(&mut $name, $value);
kaze_sugar_impl!([ $if ], [ $($rest)* ]);
};
([ $if:expr ], [ if ($cond:expr) { $($inner:tt)* } $($rest:tt)* ]) => {
{
let mut if_ = If::new(Some(&mut $if), $cond);
kaze_sugar_impl!([ if_ ], [ $($inner)* ]);
}
kaze_sugar_impl!([ $if ], [ $($rest)* ]);
};
([ $_:expr ], []) => {};
}
use super::signal::*;
use std::collections::HashMap;
use std::ptr;
#[doc(hidden)]
pub struct If<'a> {
// TODO: *shudders*
parent: Option<* mut If<'a>>,
cond: &'a Signal<'a>,
// TODO: Look into using references as keys, not pointers, for safety (can lifetimes be checked correctly then?)
old_values: HashMap<*mut &'a Signal<'a>, &'a Signal<'a>>,
}
impl<'a> If<'a> {
pub fn new(parent: Option<* mut If<'a>>, cond: &'a Signal<'a>) -> If<'a> {
If {
parent,
cond,
old_values: HashMap::new(),
}
}
pub fn assign(&mut self, target: *mut &'a Signal<'a>, value: &'a Signal<'a>) {
// TODO: Entry API?
if !self.old_values.contains_key(&target) {
// TODO: lol
self.old_values.insert(target, unsafe { *target });
}
if let Some(parent) = &mut self.parent {
// TODO: asdfasdf
let parent = unsafe { &mut **parent };
parent.assign(target, value);
}
// TODO: LLOOOLLLL
*unsafe { &mut *target } = value;
}
}
impl<'a> Drop for If<'a> {
fn drop(&mut self) {
for (target, old_value) in self.old_values.iter() {
// TODO: :D
let target = unsafe { &mut **target };
let new_value = *target;
if !ptr::eq(new_value, *old_value) {
let muxed_value = self.cond.mux(new_value, old_value);
*target = muxed_value;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment