use crate::lax::hypergraph::Hyperedge;
use crate::lax::open_hypergraph::OpenHypergraph;
use super::var::*;
use std::cell::RefCell;
use std::ops::*;
use std::rc::Rc;
pub fn operation<O: Clone, A: HasVar>(
builder: &Rc<RefCell<OpenHypergraph<O, A>>>,
vars: &[Var<O, A>],
result_types: Vec<O>, op: A,
) -> Vec<Var<O, A>> {
let mut nodes = Vec::with_capacity(vars.len());
for v in vars {
nodes.push(v.new_target());
}
let result_vars: Vec<Var<O, A>> = result_types
.into_iter()
.map(|t| Var::new(builder.clone(), t))
.collect();
let result_nodes = result_vars.iter().map(|v| v.new_source()).collect();
let mut term = builder.borrow_mut();
let _ = term.new_edge(
op,
Hyperedge {
sources: nodes,
targets: result_nodes,
},
);
result_vars
}
pub fn fn_operation<O: Clone, A: HasVar>(
builder: &Rc<RefCell<OpenHypergraph<O, A>>>,
vars: &[Var<O, A>],
result_type: O,
op: A,
) -> Var<O, A> {
let vs = operation(builder, vars, vec![result_type], op);
assert_eq!(vs.len(), 1);
vs.into_iter().next().unwrap()
}
pub trait HasBitXor<O, A> {
fn bitxor(lhs_type: O, rhs_type: O) -> (O, A);
}
impl<O: Clone, A: HasVar + HasBitXor<O, A>> BitXor for Var<O, A> {
type Output = Var<O, A>;
fn bitxor(self, rhs: Self) -> Self::Output {
let (result_label, op) = A::bitxor(self.label.clone(), rhs.label.clone());
fn_operation(&self.state.clone(), &[self, rhs], result_label, op)
}
}
macro_rules! define_binary_op {
($trait_name:ident, $fn_name:ident, $has_trait_name:ident) => {
#[doc = r" Vars support this operator when the underlying signature has the appropriate operation."]
pub trait $has_trait_name<O, A> {
fn $fn_name(lhs_type: O, rhs_type: O) -> (O, A);
}
impl<O: Clone, A: HasVar + $has_trait_name<O, A>> $trait_name for Var<O, A> {
type Output = Var<O, A>;
fn $fn_name(self, rhs: Self) -> Self::Output {
let (result_label, op) = A::$fn_name(self.label.clone(), rhs.label.clone());
fn_operation(&self.state.clone(), &[self, rhs], result_label, op)
}
}
};
}
macro_rules! define_unary_op {
($trait_name:ident, $fn_name:ident, $has_trait_name:ident) => {
#[doc = r" Vars support this unary operator when the underlying signature has the appropriate operation."]
pub trait $has_trait_name<O, A> {
fn $fn_name(operand_type: O) -> (O, A);
}
impl<O: Clone, A: HasVar + $has_trait_name<O, A>> $trait_name for Var<O, A> {
type Output = Var<O, A>;
fn $fn_name(self) -> Self::Output {
let (result_label, op) = A::$fn_name(self.label.clone());
fn_operation(&self.state.clone(), &[self], result_label, op)
}
}
};
}
define_binary_op!(BitAnd, bitand, HasBitAnd);
define_binary_op!(BitOr, bitor, HasBitOr);
define_binary_op!(Shl, shl, HasShl);
define_binary_op!(Shr, shr, HasShr);
define_unary_op!(Not, not, HasNot);
define_binary_op!(Add, add, HasAdd);
define_binary_op!(Mul, mul, HasMul);
define_binary_op!(Sub, sub, HasSub);
define_binary_op!(Div, div, HasDiv);
define_unary_op!(Neg, neg, HasNeg);