minor refactor
This commit is contained in:
parent
4e7fc9c790
commit
ebcb5f818b
2 changed files with 263 additions and 262 deletions
|
@ -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<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
|
||||
}
|
||||
}
|
||||
use crate::visitor::{Elem, Rsx, TagLhs, Visitor};
|
||||
|
||||
fn process(
|
||||
name: impl AsRef<str>,
|
||||
|
@ -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);
|
||||
|
|
256
enterprise-compiler/src/visitor.rs
Normal file
256
enterprise-compiler/src/visitor.rs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue