diff --git a/enterprise-compiler/src/lib.rs b/enterprise-compiler/src/lib.rs index 7de22c9..9b837f3 100644 --- a/enterprise-compiler/src/lib.rs +++ b/enterprise-compiler/src/lib.rs @@ -5,271 +5,16 @@ extern crate maplit; extern crate proc_macro; mod symbol; +mod visitor; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; -use bimap::BiHashMap; -use petgraph::{dot::Dot, graph::Graph, graphmap::DiGraphMap, visit::Dfs}; -use proc_macro2::{Span, TokenStream, TokenTree}; -use quote::ToTokens; -use syn::{punctuated::Punctuated, Expr, ExprPath, Ident, Path, PathArguments, PathSegment}; +use petgraph::{dot::Dot, graph::Graph}; +use proc_macro2::TokenStream; +use syn::Expr; use crate::symbol::Symbol; - -type Id = Symbol; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -enum TagLhs { - Bind(String), - On(String), - Plain(String), -} - -#[derive(Debug, Default)] -struct Elem { - tag: String, - attrs: HashMap, - inner: Vec, -} - -#[derive(Debug)] -enum Rsx { - Elem(Elem), - Code(Expr), - Text(String), -} - -#[derive(Debug)] -enum TaggedRsx { - Elem(Id, Elem), - Code(Id, Expr), - Text(Id, String), -} - -impl TaggedRsx { - pub fn get_id(&self) -> Id { - match self { - TaggedRsx::Elem(id, _) | TaggedRsx::Code(id, _) | TaggedRsx::Text(id, _) => *id, - } - } -} - -#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] -enum DepNode { - // This is an attribute on an element - // Not read-only - RsxAttr(Symbol, Symbol), - // This is a text node (innertext) - // These are read-only - RsxSpan(Symbol), - // This is something in the model - ModelValue(Symbol), -} - -#[derive(Debug)] -enum DepActions { - Updates, -} - -#[derive(Default, Debug)] -struct DependencyGraph(DiGraphMap); - -impl DependencyGraph { - fn new() -> Self { - let graph = DiGraphMap::new(); - DependencyGraph(graph) - } -} - -#[derive(Default, Debug)] -struct Visitor { - idx: u32, - deps: DependencyGraph, - model: HashMap, - impl_code: TokenStream, - elem_attr_map: HashMap>, - - // symbol maps - model_bimap: BiHashMap, -} - -impl Visitor { - fn new() -> Visitor { - Visitor { - ..Default::default() - } - } - - fn load_model(&mut self, model: &HashMap) { - for (key, value) in model { - let id = Symbol::gensym(); - self.model_bimap.insert(id, key.clone()); - self.model.insert(id, value.clone()); - } - // self.model.extend(model.clone()); - } - - fn make_graph(&mut self, nodes: &[Rsx]) -> Vec { - let mut new_nodes = Vec::new(); - for node in nodes { - let node_id = Symbol::gensym(); - let new_node = match node { - Rsx::Elem(Elem { tag, attrs, inner }) => { - let tag_inner = self.make_graph(&inner); - for (lhs, rhs) in attrs { - if let TagLhs::Bind(attr) = lhs { - if let Some(id) = self.model_bimap.get_by_right(rhs) { - let from = DepNode::RsxAttr(node_id, Symbol::from(attr)); - let to = DepNode::ModelValue(*id); - self.deps.0.add_edge(from, to, ()); - if let Some(set) = self.elem_attr_map.get_mut(&node_id) { - set.insert(Symbol::from(attr)); - } else { - let mut set = HashSet::new(); - set.insert(Symbol::from(attr)); - self.elem_attr_map.insert(node_id, set); - } - } - } - } - TaggedRsx::Elem( - node_id, - Elem { - tag: tag.to_string(), - attrs: attrs.clone(), - inner: tag_inner, - }, - ) - } - Rsx::Code(expr) => { - let deps = self.extract_model_dependencies(expr); - for dep in deps { - let from = DepNode::ModelValue(dep); - let to = DepNode::RsxSpan(node_id); - self.deps.0.add_edge(from, to, ()); - } - - TaggedRsx::Code(node_id, expr.clone()) - } - Rsx::Text(literal) => TaggedRsx::Text(node_id, literal.clone()), - }; - new_nodes.push(new_node); - } - new_nodes - } - - fn gen_code(&mut self, nodes: &[TaggedRsx]) -> Vec { - let mut names = Vec::new(); - for node in nodes { - let node_str = node.get_id().as_str(); - let make_node_id = format_ident!("make_{}", node_str); - match node { - TaggedRsx::Elem(node_id, Elem { tag, attrs, inner }) => { - let mut updates = TokenStream::new(); - if let Some(this_attrs) = self.elem_attr_map.get(node_id) { - for attr in this_attrs { - let starting = DepNode::RsxAttr(*node_id, *attr); - let mut dfs = Dfs::new(&self.deps.0, starting); - let mut update_func = TokenStream::new(); - while let Some(nx) = dfs.next(&self.deps.0) { - if nx != starting { - println!("NX: {:?}", nx); - match nx { - DepNode::ModelValue(sym) => { - let sym_name = format_ident!( - "{}", - self.model_bimap.get_by_left(&sym).unwrap() - ); - let inner_lock = format_ident!( - "inner_lock_{}", - Symbol::gensym().as_str() - ); - updates.extend(quote! { - let #inner_lock = self.#sym_name.clone(); - }); - update_func.extend(quote! { - { - let mut locked = #inner_lock.lock(); - *locked = new_value.clone(); - } - }); - } - DepNode::RsxSpan(id) => { - let id_str = id.as_str(); - update_func.extend(quote! { - { - if let Some(target) = document().get_element_by_id(#id_str) { - target.set_text_content(&new_value.clone()); - } - } - }); - } - _ => (), - } - } - } - let attr_name = attr.as_str(); - updates.extend(quote! { - let inner_el = el.clone(); - el.add_event_listener(move |evt: stdweb::web::event::InputEvent| { - let new_value = InputElement::try_from(inner_el.clone()).unwrap().raw_value(); - js! { console.log("bruh", @{inner_el.clone()}, @{&new_value}); } - #update_func - }); - }); - } - } - self.impl_code.extend(quote! { - fn #make_node_id(&self) -> impl stdweb::web::INode { - let el = document().create_element(#tag).unwrap(); - el.set_attribute("id", #node_str); - #updates - el - } - }); - self.gen_code(&inner); - } - TaggedRsx::Code(node_id, expr) => { - self.impl_code.extend(quote! { - #[inline] - fn #make_node_id(&self) -> impl stdweb::web::INode { - let el = document().create_element("span").expect("shouldn't fail"); - el.set_attribute("id", #node_str); - el - } - }); - } - TaggedRsx::Text(node_id, literal) => { - self.impl_code.extend(quote! { - #[inline] - fn #make_node_id(&self) -> impl stdweb::web::INode { - document().create_text_node(#literal) - } - }); - } - _ => (), - } - names.push(format!("{}", make_node_id)); - } - names - } - - /// This is using a really dumb heuristic - fn extract_model_dependencies(&self, expr: &Expr) -> HashSet { - let tokens = expr.to_token_stream(); - let mut result = HashSet::new(); - - for token in tokens.into_iter() { - if let TokenTree::Ident(ident) = token { - if let Some(id) = self.model_bimap.get_by_right(&ident.to_string()) { - result.insert(*id); - } - // result.insert(format!("{}", ident)); - } - } - result - } -} +use crate::visitor::{Elem, Rsx, TagLhs, Visitor}; fn process( name: impl AsRef, @@ -286,7 +31,7 @@ fn process( // println!("{:?}", visitor); println!("DOT:"); - let graph: Graph<_, _, _> = visitor.deps.0.clone().into_graph(); + let graph: Graph<_, _, _> = visitor.deps.clone().into_graph(); println!("{:?}", Dot::new(&graph)); let name = format_ident!("{}", name); diff --git a/enterprise-compiler/src/visitor.rs b/enterprise-compiler/src/visitor.rs new file mode 100644 index 0000000..6a28e1b --- /dev/null +++ b/enterprise-compiler/src/visitor.rs @@ -0,0 +1,256 @@ +use std::collections::HashMap; +use std::collections::HashSet; + +use bimap::BiHashMap; +use petgraph::graphmap::DiGraphMap; +use petgraph::visit::Dfs; +use proc_macro2::{TokenStream, TokenTree}; +use quote::ToTokens; +use syn::Expr; + +use crate::Symbol; + +type Id = Symbol; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TagLhs { + Bind(String), + On(String), + Plain(String), +} + +#[derive(Debug, Default)] +pub struct Elem { + pub tag: String, + pub attrs: HashMap, + pub inner: Vec, +} + +#[derive(Debug)] +pub enum Rsx { + Elem(Elem), + Code(Expr), + Text(String), +} + +#[derive(Debug)] +pub enum TaggedRsx { + Elem(Id, Elem), + Code(Id, Expr), + Text(Id, String), +} + +impl TaggedRsx { + pub fn get_id(&self) -> Id { + match self { + TaggedRsx::Elem(id, _) | TaggedRsx::Code(id, _) | TaggedRsx::Text(id, _) => *id, + } + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub enum DepNode { + // This is an attribute on an element + // Not read-only + RsxAttr(Symbol, Symbol), + // This is a text node (innertext) + // These are read-only + RsxSpan(Symbol), + // This is something in the model + ModelValue(Symbol), +} + +#[derive(Debug)] +enum DepActions { + Updates, +} + +type DependencyGraph = DiGraphMap; + +#[derive(Default, Debug)] +pub struct Visitor { + idx: u32, + pub(crate) deps: DependencyGraph, + model: HashMap, + pub(crate) impl_code: TokenStream, + elem_attr_map: HashMap>, + + // symbol maps + model_bimap: BiHashMap, +} + +impl Visitor { + pub fn new() -> Visitor { + Visitor { + ..Default::default() + } + } + + pub fn load_model(&mut self, model: &HashMap) { + for (key, value) in model { + let id = Symbol::gensym(); + self.model_bimap.insert(id, key.clone()); + self.model.insert(id, value.clone()); + } + // self.model.extend(model.clone()); + } + + pub fn make_graph(&mut self, nodes: &[Rsx]) -> Vec { + let mut new_nodes = Vec::new(); + for node in nodes { + let node_id = Symbol::gensym(); + let new_node = match node { + Rsx::Elem(Elem { tag, attrs, inner }) => { + let tag_inner = self.make_graph(&inner); + for (lhs, rhs) in attrs { + if let TagLhs::Bind(attr) = lhs { + if let Some(id) = self.model_bimap.get_by_right(rhs) { + let from = DepNode::RsxAttr(node_id, Symbol::from(attr)); + let to = DepNode::ModelValue(*id); + self.deps.add_edge(from, to, ()); + if let Some(set) = self.elem_attr_map.get_mut(&node_id) { + set.insert(Symbol::from(attr)); + } else { + let mut set = HashSet::new(); + set.insert(Symbol::from(attr)); + self.elem_attr_map.insert(node_id, set); + } + } + } + } + TaggedRsx::Elem( + node_id, + Elem { + tag: tag.to_string(), + attrs: attrs.clone(), + inner: tag_inner, + }, + ) + } + Rsx::Code(expr) => { + let deps = self.extract_model_dependencies(expr); + for dep in deps { + let from = DepNode::ModelValue(dep); + let to = DepNode::RsxSpan(node_id); + self.deps.add_edge(from, to, ()); + } + + TaggedRsx::Code(node_id, expr.clone()) + } + Rsx::Text(literal) => TaggedRsx::Text(node_id, literal.clone()), + }; + new_nodes.push(new_node); + } + new_nodes + } + + pub fn gen_code(&mut self, nodes: &[TaggedRsx]) -> Vec { + let mut names = Vec::new(); + for node in nodes { + let node_str = node.get_id().as_str(); + let make_node_id = format_ident!("make_{}", node_str); + match node { + TaggedRsx::Elem(node_id, Elem { tag, attrs, inner }) => { + let mut updates = TokenStream::new(); + if let Some(this_attrs) = self.elem_attr_map.get(node_id) { + for attr in this_attrs { + let starting = DepNode::RsxAttr(*node_id, *attr); + let mut dfs = Dfs::new(&self.deps, starting); + let mut update_func = TokenStream::new(); + while let Some(nx) = dfs.next(&self.deps) { + if nx != starting { + println!("NX: {:?}", nx); + match nx { + DepNode::ModelValue(sym) => { + let sym_name = format_ident!( + "{}", + self.model_bimap.get_by_left(&sym).unwrap() + ); + let inner_lock = format_ident!( + "inner_lock_{}", + Symbol::gensym().as_str() + ); + updates.extend(quote! { + let #inner_lock = self.#sym_name.clone(); + }); + update_func.extend(quote! { + { + let mut locked = #inner_lock.lock(); + *locked = new_value.clone(); + } + }); + } + DepNode::RsxSpan(id) => { + let id_str = id.as_str(); + update_func.extend(quote! { + { + if let Some(target) = document().get_element_by_id(#id_str) { + target.set_text_content(&new_value.clone()); + } + } + }); + } + _ => (), + } + } + } + updates.extend(quote! { + let inner_el = el.clone(); + el.add_event_listener(move |evt: stdweb::web::event::InputEvent| { + let new_value = InputElement::try_from(inner_el.clone()).unwrap().raw_value(); + #update_func + }); + }); + } + } + self.impl_code.extend(quote! { + fn #make_node_id(&self) -> impl stdweb::web::INode { + let el = document().create_element(#tag).unwrap(); + el.set_attribute("id", #node_str); + #updates + el + } + }); + self.gen_code(&inner); + } + TaggedRsx::Code(node_id, expr) => { + self.impl_code.extend(quote! { + #[inline] + fn #make_node_id(&self) -> impl stdweb::web::INode { + let el = document().create_element("span").expect("shouldn't fail"); + el.set_attribute("id", #node_str); + el + } + }); + } + TaggedRsx::Text(node_id, literal) => { + self.impl_code.extend(quote! { + #[inline] + fn #make_node_id(&self) -> impl stdweb::web::INode { + document().create_text_node(#literal) + } + }); + } + _ => (), + } + names.push(format!("{}", make_node_id)); + } + names + } + + /// This is using a really dumb heuristic + fn extract_model_dependencies(&self, expr: &Expr) -> HashSet { + let tokens = expr.to_token_stream(); + let mut result = HashSet::new(); + + for token in tokens.into_iter() { + if let TokenTree::Ident(ident) = token { + if let Some(id) = self.model_bimap.get_by_right(&ident.to_string()) { + result.insert(*id); + } + // result.insert(format!("{}", ident)); + } + } + result + } +}