minor refactor

This commit is contained in:
Michael Zhang 2020-02-09 22:51:17 -06:00
parent 4e7fc9c790
commit ebcb5f818b
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
2 changed files with 263 additions and 262 deletions

View file

@ -5,271 +5,16 @@ extern crate maplit;
extern crate proc_macro; extern crate proc_macro;
mod symbol; mod symbol;
mod visitor;
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use bimap::BiHashMap; use petgraph::{dot::Dot, graph::Graph};
use petgraph::{dot::Dot, graph::Graph, graphmap::DiGraphMap, visit::Dfs}; use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream, TokenTree}; use syn::Expr;
use quote::ToTokens;
use syn::{punctuated::Punctuated, Expr, ExprPath, Ident, Path, PathArguments, PathSegment};
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::visitor::{Elem, Rsx, TagLhs, Visitor};
type Id = Symbol;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum TagLhs {
Bind(String),
On(String),
Plain(String),
}
#[derive(Debug, Default)]
struct Elem<T> {
tag: String,
attrs: HashMap<TagLhs, String>,
inner: Vec<T>,
}
#[derive(Debug)]
enum Rsx {
Elem(Elem<Rsx>),
Code(Expr),
Text(String),
}
#[derive(Debug)]
enum TaggedRsx {
Elem(Id, Elem<TaggedRsx>),
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<DepNode, ()>);
impl DependencyGraph {
fn new() -> Self {
let graph = DiGraphMap::new();
DependencyGraph(graph)
}
}
#[derive(Default, Debug)]
struct Visitor {
idx: u32,
deps: DependencyGraph,
model: HashMap<Id, String>,
impl_code: TokenStream,
elem_attr_map: HashMap<Id, HashSet<Id>>,
// symbol maps
model_bimap: BiHashMap<Id, String>,
}
impl Visitor {
fn new() -> Visitor {
Visitor {
..Default::default()
}
}
fn load_model(&mut self, model: &HashMap<String, String>) {
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<TaggedRsx> {
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<String> {
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<Symbol> {
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
}
}
fn process( fn process(
name: impl AsRef<str>, name: impl AsRef<str>,
@ -286,7 +31,7 @@ fn process(
// println!("{:?}", visitor); // println!("{:?}", visitor);
println!("DOT:"); println!("DOT:");
let graph: Graph<_, _, _> = visitor.deps.0.clone().into_graph(); let graph: Graph<_, _, _> = visitor.deps.clone().into_graph();
println!("{:?}", Dot::new(&graph)); println!("{:?}", Dot::new(&graph));
let name = format_ident!("{}", name); let name = format_ident!("{}", name);

View file

@ -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<T> {
pub tag: String,
pub attrs: HashMap<TagLhs, String>,
pub inner: Vec<T>,
}
#[derive(Debug)]
pub enum Rsx {
Elem(Elem<Rsx>),
Code(Expr),
Text(String),
}
#[derive(Debug)]
pub enum TaggedRsx {
Elem(Id, Elem<TaggedRsx>),
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<DepNode, ()>;
#[derive(Default, Debug)]
pub struct Visitor {
idx: u32,
pub(crate) deps: DependencyGraph,
model: HashMap<Id, String>,
pub(crate) impl_code: TokenStream,
elem_attr_map: HashMap<Id, HashSet<Id>>,
// symbol maps
model_bimap: BiHashMap<Id, String>,
}
impl Visitor {
pub fn new() -> Visitor {
Visitor {
..Default::default()
}
}
pub fn load_model(&mut self, model: &HashMap<String, String>) {
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<TaggedRsx> {
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<String> {
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<Symbol> {
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
}
}