use crate::parser::{ReBlock, ReClosure, ReExpr, ReLocal};
use petgraph::{graph::NodeIndex, Graph};
use syn::{Error, Result, Type};
use super::{
ChangedNode, EvtNode, Family, FilterNode, FoldNode, MapNode, NameNode, NodeData, ReData,
ReEdge, ReNode, VarNode,
};
pub struct ReVisitor<'ast> {
pub graph: Graph<ReNode<'ast>, ReEdge>,
name_nodes: Vec<(NameNode<'ast>, NodeIndex)>,
node_count: u32,
}
impl<'ast> ReVisitor<'ast> {
pub fn visit_reblock(&mut self, i: &'ast ReBlock) -> Result<()> {
for local in &i.stmts {
self.visit_relocal(local)?;
}
Ok(())
}
fn visit_relocal(&mut self, i: &'ast ReLocal) -> Result<()> {
let (mut last_idxs, last_fam) = self.visit_reexpr(&i.init)?;
let name = &i.ident;
let name_str = name.ident.to_string();
if self.name_nodes.iter().any(|(n, _)| n.id.ident == name_str) {
return Err(Error::new(
i.ident.ident.span(),
"identifier already occupied",
));
}
let last_len = last_idxs.len();
let (last_idx, last_ty) = last_idxs.remove(0);
let last_node = self.graph.node_weight_mut(last_idx).unwrap();
let pin = if i.pin_token.is_some() {
if last_len != 1 {
return Err(Error::new(
i.pin_token.unwrap().span,
"cannot pin group of reactives",
));
}
if let ReNode::Name(namenode) = last_node {
return Err(Error::new(
namenode.id.ident.span(),
"pin original signal instead",
));
}
*last_node.pin_mut() = true;
true
} else if last_node.pin() {
true
} else {
false
};
let name_node = NameNode {
id: name,
data: ReData {
id: self.next_idx(),
family: last_fam,
ty: last_ty.clone(),
pin,
},
};
let new_node = ReNode::Name(name_node.clone());
let node_idx = self.graph.add_node(new_node);
self.name_nodes.push((name_node, node_idx));
let edge = ReEdge { ty: last_ty };
self.graph.add_edge(last_idx, node_idx, edge);
Ok(())
}
fn visit_reexpr(&mut self, i: &'ast ReExpr) -> Result<(Vec<(NodeIndex, Type)>, Family)> {
match i {
ReExpr::Group(groupexpr) => {
let mut incoming_nodes = Vec::new();
let mut family = Family::Variable;
for expr in &groupexpr.exprs {
let (mut nodes, fam) = self.visit_reexpr(expr)?;
assert!(nodes.len() == 1);
incoming_nodes.push(nodes.remove(0));
if fam == Family::Event {
family = Family::Event;
}
}
Ok((incoming_nodes, family))
}
ReExpr::Var(varexpr) => {
let data = ReData {
pin: true,
ty: varexpr.ty.clone(),
family: Family::Variable,
id: self.next_idx(),
};
let node = ReNode::Var(VarNode {
initial: &varexpr.expr,
data,
});
let idx = self.graph.add_node(node);
Ok((vec![(idx, varexpr.ty.clone())], Family::Variable))
}
ReExpr::Evt(evtexpr) => {
let node = ReNode::Evt(EvtNode {
data: ReData {
pin: true,
ty: evtexpr.ty.clone(),
id: self.next_idx(),
family: Family::Event,
},
});
let idx = self.graph.add_node(node);
Ok((vec![(idx, evtexpr.ty.clone())], Family::Event))
}
ReExpr::Ident(identexpr) => {
let idx = self
.name_nodes
.iter()
.find(|(n, _)| identexpr.ident == n.id.ident);
match idx {
Some((name, idx)) => Ok((vec![(*idx, name.ty().clone())], name.family())),
None => Err(Error::new(identexpr.ident.span(), "unknown reactive")),
}
}
ReExpr::Fold(foldexpr) => {
let (incoming, fam) = self.visit_reexpr(&foldexpr.left_expr)?;
let ty = self.visit_reclosure(&foldexpr.closure)?.clone();
if fam != Family::Event {
return Err(Error::new(
foldexpr.fold_token.span,
"incoming node must be event",
));
}
let node = ReNode::Fold(FoldNode {
initial: &foldexpr.init_expr,
update_expr: &foldexpr.closure,
data: ReData {
ty: ty.clone(),
id: self.next_idx(),
family: fam,
pin: true,
},
});
let idx = self.graph.add_node(node);
for (node, ty) in incoming {
let edge = ReEdge { ty: ty.clone() };
self.graph.add_edge(node, idx, edge);
}
Ok((vec![(idx, ty)], Family::Variable))
}
ReExpr::Map(mapexpr) => {
let (incoming, incoming_fam) = self.visit_reexpr(&mapexpr.left_expr)?;
let ty = self.visit_reclosure(&mapexpr.closure)?.clone();
let node = ReNode::Map(MapNode {
update_expr: &mapexpr.closure,
data: ReData {
pin: false,
ty: ty.clone(),
id: self.next_idx(),
family: incoming_fam,
},
});
let idx = self.graph.add_node(node);
for (node, ty) in incoming {
let edge = ReEdge { ty: ty.clone() };
self.graph.add_edge(node, idx, edge);
}
Ok((vec![(idx, ty)], incoming_fam))
}
ReExpr::Filter(filterexpr) => {
let (mut incoming, incoming_fam) = self.visit_reexpr(&filterexpr.left_expr)?;
self.visit_reclosure(&filterexpr.closure)?;
let (idx, ty) = incoming.remove(0); let node_fam = self.graph.node_weight(idx).unwrap().family();
if incoming_fam != Family::Event || node_fam != Family::Event {
return Err(Error::new(
filterexpr.filter_token.span,
"filter only valid on events",
));
}
let node = ReNode::Filter(FilterNode {
filter_expr: &filterexpr.closure,
data: ReData {
pin: false,
ty: ty.clone(),
id: self.next_idx(),
family: incoming_fam,
},
});
let idx_filter = self.graph.add_node(node);
let edge = ReEdge { ty: ty.clone() };
self.graph.add_edge(idx, idx_filter, edge);
Ok((vec![(idx, ty)], incoming_fam))
}
ReExpr::Changed(changedexpr) => {
let (mut incoming, incoming_fam) = self.visit_reexpr(&changedexpr.left_expr)?;
assert!(incoming.len() == 1);
let (idx, ty) = incoming.remove(0);
if incoming_fam == Family::Event {
return Err(Error::new(
changedexpr.changed_token.span,
"changed only valid on variables",
));
}
let incoming = self.graph.node_weight_mut(idx).unwrap();
*incoming.pin_mut() = true;
let node = ReNode::Changed(ChangedNode {
data: ReData {
pin: false,
ty: ty.clone(),
id: self.next_idx(),
family: Family::Event,
},
});
let idx_changed = self.graph.add_node(node);
let edge = ReEdge { ty: ty.clone() };
self.graph.add_edge(idx, idx_changed, edge);
Ok((vec![(idx, ty)], Family::Event))
}
}
}
fn next_idx(&mut self) -> u32 {
let res = self.node_count;
self.node_count += 1;
res
}
fn visit_reclosure(&mut self, i: &'ast ReClosure) -> Result<&'ast Type> {
Ok(&i.return_type)
}
pub fn reactive_graph(self) -> Graph<ReNode<'ast>, ReEdge> {
self.graph
}
pub fn new() -> Self {
Self {
graph: Graph::new(),
name_nodes: Vec::new(),
node_count: 0u32,
}
}
}