use enum_dispatch::enum_dispatch;
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;
use quote::quote;
use crate::analysis::{Family, NameNode, NodeData, ReEdge, ReNode};
use petgraph::{graph::NodeIndex, visit::Topo, Graph};
mod reactives;
mod sources;
pub fn generate(graph: &Graph<ReNode, ReEdge>) -> TokenStream {
let mut topo_visitor = Topo::new(graph);
let mut tks_state = TokenStream::new();
let mut tks_function = TokenStream::new();
let mut tks_update = TokenStream::new();
let mut tks_observers = TokenStream::new();
let mut tks_input_struct = TokenStream::new();
let mut tks_notify = TokenStream::new();
let mut tks_card_structs = TokenStream::new();
let mut tks_slots = TokenStream::new();
let mut tks_sink_fn = TokenStream::new();
let mut tks_input_fn = TokenStream::new();
let mut tks_slot_check = quote! {true};
let mut tks_take_all = TokenStream::new();
let mut tks_slot_init = TokenStream::new();
let mut tks_initialize = TokenStream::new();
let mut tks_initialize_struct = TokenStream::new();
while let Some(nodeidx) = topo_visitor.next(graph) {
let incoming = &get_incoming_weights(graph, nodeidx);
let weight = graph.node_weight(nodeidx).expect("expect valid node index");
let tokens = weight.generate_interface(incoming);
tks_card_structs.extend(tokens.card_struct);
tks_slots.extend(tokens.slot_part);
tks_sink_fn.extend(tokens.sink_fn);
tks_input_fn.extend(tokens.input_fn);
tks_take_all.extend(tokens.take_all);
tks_slot_check.extend(tokens.check_input);
tks_slot_init.extend(tokens.slot_init);
tks_state.extend(tokens.state_struct);
tks_input_struct.extend(tokens.input_struct_part);
tks_function.extend(tokens.functions);
tks_update.extend(tokens.update_part);
tks_notify.extend(tokens.notify_part);
tks_observers.extend(tokens.observer_struct);
tks_initialize.extend(tokens.initialize);
tks_initialize_struct.extend(tokens.initialize_struct);
}
quote! {
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;
use std::sync::mpsc::*;
use std::mem;
use if_chain::if_chain;
#[derive(Clone)]
struct Variable<T> {
value: T,
change: bool,
}
#[derive(Clone)]
enum Event<T> {
Some(T),
None,
}
#[derive(Clone)]
pub struct State {
#tks_state
}
#[derive(Default)]
struct Observers {
#tks_observers
}
pub struct Program {
state: State,
observers: Observers,
receiver: Receiver<Input>,
sink: Sink,
}
#[derive(Default, Clone)]
pub struct Input {
#tks_input_struct
}
impl Input {
#tks_input_fn
}
struct Phantom {}
#tks_card_structs
#[derive(Default)]
struct Slots {
#tks_slots
}
impl Slots {
fn new(data: Weak<Phantom>) -> Self {
Self {
#tks_slot_init
}
}
}
pub struct Sink {
slots: Slots,
channel_sender: Sender<Input>,
id: Rc<Phantom>,
}
impl Clone for Sink {
fn clone(&self) -> Self {
Self { slots: Slots::default(), channel_sender: self.channel_sender.clone(), id: self.id.clone()}
}
}
impl Sink {
#tks_sink_fn
pub fn take_all(&mut self, other: &mut Self) {
#tks_take_all
}
pub fn send(&mut self, input: Input) {
if #tks_slot_check
{
self.channel_sender.send(input).unwrap();
} else
{
panic!("Slot empty or from another program instance");
}
}
fn new(sender: Sender<Input>) -> Self {
let id = Rc::new(Phantom {});
Self {
slots: Slots::new(Rc::downgrade(&id)),
channel_sender: sender,
id,
}
}
}
impl Default for State {
fn default() -> Self {
Program::default_state()
}
}
impl Program {
pub fn update(state: &mut State, mut inputs: Input) {
#tks_update
}
fn notify(observers: &mut Observers, state: &mut State) {
#tks_notify
}
pub fn default_state() -> State {
#tks_initialize
State { #tks_initialize_struct }
}
pub fn run(&mut self) {
let Program {state, observers, receiver, sink} = self;
let result = receiver.try_recv();
match result {
Ok(inputs) => {
Self::update(state, inputs);
Self::notify(observers, state);
}
Err(recv_error) => {
println!("Queue error: {:?}", recv_error);
}
}
}
pub fn init(&mut self) {
let Program { state, observers, receiver, sink } = self;
Self::notify(observers, state);
}
pub fn new() -> Self {
let (send,recv) = channel();
Self {
state: State::default(),
observers: Observers::default(),
receiver: recv,
sink: Sink::new(send),
}
}
pub fn sink(&mut self) -> Sink {
let mut new_sink = self.sink.clone();
new_sink.take_all(&mut self.sink);
new_sink
}
#tks_function
}
}
}
fn get_incoming_weights<'ast>(
graph: &'ast Graph<ReNode<'ast>, ReEdge>,
idx: NodeIndex,
) -> Vec<&'ast ReNode<'ast>> {
let incoming_nodes = graph.neighbors_directed(idx, petgraph::Incoming);
let mut weights = Vec::new();
for node_idx in incoming_nodes {
let node = graph.node_weight(node_idx).expect("invalid node index");
if let ReNode::Name(_) = node {
weights.extend(get_incoming_weights(graph, node_idx));
} else {
weights.push(node);
}
}
weights
}
#[derive(Default)]
pub struct InterfaceTokens {
pub input_struct_part: TokenStream,
pub card_struct: TokenStream,
pub slot_part: TokenStream,
pub sink_fn: TokenStream,
pub input_fn: TokenStream,
pub check_input: TokenStream,
pub take_all: TokenStream,
pub slot_init: TokenStream,
pub functions: TokenStream,
pub update_part: TokenStream,
pub state_struct: TokenStream,
pub observer_struct: TokenStream,
pub notify_part: TokenStream,
pub initialize: TokenStream,
pub initialize_struct: TokenStream,
}
pub fn change_prefix(ident: &Ident) -> Ident {
format_ident!("change_{}", ident)
}
pub fn temp_prefix(ident: &Ident) -> Ident {
format_ident!("temp_{}", ident)
}
pub fn val_prefix(ident: &Ident) -> Ident {
format_ident!("val_{}", ident)
}
#[enum_dispatch]
pub trait Generate {
fn generate_interface(&self, incoming: &Vec<&ReNode>) -> InterfaceTokens;
fn ident(&self) -> Ident;
}
impl Generate for NameNode<'_> {
fn ident(&self) -> Ident {
self.id.ident.clone()
}
fn generate_interface(&self, incoming: &Vec<&ReNode>) -> InterfaceTokens {
let name = self.ident();
let ty = self.ty();
let ident = self.ident();
let income = incoming[0].ident();
let parent_node = incoming[0];
let pin = self.pin();
let family = self.family();
let mut ift = InterfaceTokens::default();
if pin {
ift.observer_struct = quote! {
#name: Vec<Weak<RefCell<dyn FnMut(&#ty)>>>,
};
let observer_ident = format_ident!("observe_{}", ident);
ift.functions = quote! {
pub fn #observer_ident(&mut self, observer: Weak<RefCell<dyn FnMut(&#ty)>>) {
self.observers.#name.push(observer);
}
};
match family {
Family::Event => {
ift.notify_part = quote! {
if let Event::Some(value) = &state.#income {
observers.#ident.retain(|lst| {
if let Some(cb) = Weak::upgrade(lst) {
(&mut *cb.borrow_mut())(value);
true
} else {
false
}
});
}
state.#income = Event::None;
};
}
Family::Variable => {
ift.notify_part = quote! {
if state.#income.change {
observers.#ident.retain(|lst| {
if let Some(cb) = Weak::upgrade(lst) {
(&mut *cb.borrow_mut())(&state.#income.value);
true
} else {
false
}
});
}
state.#income.change = false;
};
}
}
}
match parent_node {
ReNode::Var(_) | ReNode::Evt(_) => {
let input_fn_name = format_ident!("set_{}", name);
ift.input_fn = quote! {
pub fn #input_fn_name(&mut self, value: #ty) {
self.#income = Some(value);
}
};
let card_name = format_ident!("Card{}", name);
ift.card_struct = quote! {
pub struct #card_name {
data: Weak<Phantom>,
}
};
ift.slot_part = quote! {
#name: Option<#card_name>,
};
ift.slot_init = quote! {
#name: Some(#card_name {data: data.clone()}),
};
let push_card = format_ident!("push_{}", name);
let pull_card = format_ident!("pull_{}", name);
let send_single = format_ident!("send_{}", name);
ift.sink_fn = quote! {
pub fn #push_card(&mut self, card: #card_name) {
let id_self = Rc::downgrade(&self.id);
if id_self.ptr_eq(&card.data) {
self.slots.#name = Some(card);
} else {
panic!("Card has incorrect id");
}
}
pub fn #pull_card(&mut self) -> Option<#card_name> {
self.slots.#name.take()
}
pub fn #send_single(&mut self, value: #ty) {
let mut input = Input::default();
input.#input_fn_name(value);
self.send(input);
}
};
ift.take_all = quote! {
if other.slots.#name.is_some() {
mem::swap(&mut self.slots.#name, &mut other.slots.#name);
}
};
ift.check_input = quote! {
&& (self.slots.#name.is_some() || input.#income.is_none())
};
}
_ => {}
};
ift
}
}