Switch to 3rd party quote
macro (#201)
* Add proc_macro2 and quote dependencies * Completly switch over to proc_macro2 where possible * Remove unessesary ::from casts * Remove pendantic cast that i missed * Make Builder::push_tokens accept TokenStream * Remove stray commented out code * Reword unclear comment on `Span` to a todo item * Move comment closer to its intended line * Use into instead of explicit conversion
This commit is contained in:
parent
ee82847d8f
commit
a10130a9ac
3 changed files with 43 additions and 36 deletions
maud_macros
|
@ -14,6 +14,8 @@ edition = "2018"
|
||||||
syn = "1.0.8"
|
syn = "1.0.8"
|
||||||
matches = "0.1.8"
|
matches = "0.1.8"
|
||||||
maud_htmlescape = { version = "0.17.0", path = "../maud_htmlescape" }
|
maud_htmlescape = { version = "0.17.0", path = "../maud_htmlescape" }
|
||||||
|
quote = "1.0.7"
|
||||||
|
proc-macro2 = "1.0.18"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "maud_macros"
|
name = "maud_macros"
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use matches::matches;
|
use matches::matches;
|
||||||
use maud_htmlescape::Escaper;
|
use maud_htmlescape::Escaper;
|
||||||
use proc_macro::{
|
use proc_macro2::{
|
||||||
Delimiter,
|
Delimiter,
|
||||||
Group,
|
Group,
|
||||||
Literal,
|
Literal,
|
||||||
quote,
|
|
||||||
Span,
|
Span,
|
||||||
Ident,
|
Ident,
|
||||||
TokenStream,
|
TokenStream,
|
||||||
TokenTree,
|
TokenTree,
|
||||||
};
|
};
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
|
||||||
|
@ -48,10 +48,10 @@ impl Generator {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Markup::Literal { content, .. } => build.push_escaped(&content),
|
Markup::Literal { content, .. } => build.push_escaped(&content),
|
||||||
Markup::Symbol { symbol } => self.name(symbol, build),
|
Markup::Symbol { symbol } => self.name(symbol.into(), build),
|
||||||
Markup::Splice { expr, .. } => build.push_tokens(self.splice(expr)),
|
Markup::Splice { expr, .. } => build.push_tokens(self.splice(expr.into())),
|
||||||
Markup::Element { name, attrs, body } => self.element(name, attrs, body, build),
|
Markup::Element { name, attrs, body } => self.element(name.into(), attrs, body, build),
|
||||||
Markup::Let { tokens, .. } => build.push_tokens(tokens),
|
Markup::Let { tokens, .. } => build.push_tokens(tokens.into()),
|
||||||
Markup::Special { segments } => {
|
Markup::Special { segments } => {
|
||||||
for segment in segments {
|
for segment in segments {
|
||||||
build.push_tokens(self.special(segment));
|
build.push_tokens(self.special(segment));
|
||||||
|
@ -64,8 +64,9 @@ impl Generator {
|
||||||
.map(|arm| self.match_arm(arm))
|
.map(|arm| self.match_arm(arm))
|
||||||
.collect();
|
.collect();
|
||||||
let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body));
|
let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body));
|
||||||
body.set_span(arms_span);
|
body.set_span(arms_span.into());
|
||||||
quote!($head $body)
|
let head: TokenStream = head.into();
|
||||||
|
quote!(#head #body)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,7 @@ impl Generator {
|
||||||
let mut build = self.builder();
|
let mut build = self.builder();
|
||||||
self.markups(markups, &mut build);
|
self.markups(markups, &mut build);
|
||||||
let mut block = TokenTree::Group(Group::new(Delimiter::Brace, build.finish()));
|
let mut block = TokenTree::Group(Group::new(Delimiter::Brace, build.finish()));
|
||||||
block.set_span(outer_span);
|
block.set_span(outer_span.into());
|
||||||
TokenStream::from(block)
|
TokenStream::from(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ impl Generator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: maud::Render> Render for T {}
|
impl<T: maud::Render> Render for T {}
|
||||||
$expr.__maud_render_to(&mut $output_ident);
|
#expr.__maud_render_to(&mut #output_ident);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,23 +123,23 @@ impl Generator {
|
||||||
match attr_type {
|
match attr_type {
|
||||||
AttrType::Normal { value } => {
|
AttrType::Normal { value } => {
|
||||||
build.push_str(" ");
|
build.push_str(" ");
|
||||||
self.name(name, build);
|
self.name(name.into(), build);
|
||||||
build.push_str("=\"");
|
build.push_str("=\"");
|
||||||
self.markup(value, build);
|
self.markup(value, build);
|
||||||
build.push_str("\"");
|
build.push_str("\"");
|
||||||
},
|
},
|
||||||
AttrType::Empty { toggler: None } => {
|
AttrType::Empty { toggler: None } => {
|
||||||
build.push_str(" ");
|
build.push_str(" ");
|
||||||
self.name(name, build);
|
self.name(name.into(), build);
|
||||||
},
|
},
|
||||||
AttrType::Empty { toggler: Some(toggler) } => {
|
AttrType::Empty { toggler: Some(toggler) } => {
|
||||||
let head = desugar_toggler(toggler);
|
let head = desugar_toggler(toggler);
|
||||||
build.push_tokens({
|
build.push_tokens({
|
||||||
let mut build = self.builder();
|
let mut build = self.builder();
|
||||||
build.push_str(" ");
|
build.push_str(" ");
|
||||||
self.name(name, &mut build);
|
self.name(name.into(), &mut build);
|
||||||
let body = build.finish();
|
let body = build.finish();
|
||||||
quote!($head { $body })
|
quote!(#head { #body })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -147,12 +148,14 @@ impl Generator {
|
||||||
|
|
||||||
fn special(&self, Special { head, body, .. }: Special) -> TokenStream {
|
fn special(&self, Special { head, body, .. }: Special) -> TokenStream {
|
||||||
let body = self.block(body);
|
let body = self.block(body);
|
||||||
quote!($head $body)
|
let head: TokenStream = head.into();
|
||||||
|
quote!(#head #body)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_arm(&self, MatchArm { head, body }: MatchArm) -> TokenStream {
|
fn match_arm(&self, MatchArm { head, body }: MatchArm) -> TokenStream {
|
||||||
let body = self.block(body);
|
let body = self.block(body);
|
||||||
quote!($head $body)
|
let head: TokenStream = head.into();
|
||||||
|
quote!(#head #body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,15 +204,15 @@ fn desugar_classes_or_ids(
|
||||||
};
|
};
|
||||||
let head = desugar_toggler(toggler);
|
let head = desugar_toggler(toggler);
|
||||||
markups.push(Markup::Special {
|
markups.push(Markup::Special {
|
||||||
segments: vec![Special { at_span: Span::call_site(), head, body }],
|
segments: vec![Special { at_span: proc_macro::Span::call_site(), head: head.into(), body }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(Attribute {
|
Some(Attribute {
|
||||||
name: TokenStream::from(TokenTree::Ident(Ident::new(attr_name, Span::call_site()))),
|
name: TokenStream::from(TokenTree::Ident(Ident::new(attr_name, Span::call_site()))).into(),
|
||||||
attr_type: AttrType::Normal {
|
attr_type: AttrType::Normal {
|
||||||
value: Markup::Block(Block {
|
value: Markup::Block(Block {
|
||||||
markups,
|
markups,
|
||||||
outer_span: Span::call_site(),
|
outer_span: proc_macro::Span::call_site(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -228,7 +231,8 @@ fn prepend_leading_space(name: Markup, leading_space: &mut bool) -> Vec<Markup>
|
||||||
markups
|
markups
|
||||||
}
|
}
|
||||||
|
|
||||||
fn desugar_toggler(Toggler { mut cond, cond_span }: Toggler) -> TokenStream {
|
fn desugar_toggler(Toggler { cond, cond_span }: Toggler) -> TokenStream {
|
||||||
|
let mut cond = TokenStream::from(cond);
|
||||||
// If the expression contains an opening brace `{`,
|
// If the expression contains an opening brace `{`,
|
||||||
// wrap it in parentheses to avoid parse errors
|
// wrap it in parentheses to avoid parse errors
|
||||||
if cond.clone().into_iter().any(|token| match token {
|
if cond.clone().into_iter().any(|token| match token {
|
||||||
|
@ -236,10 +240,10 @@ fn desugar_toggler(Toggler { mut cond, cond_span }: Toggler) -> TokenStream {
|
||||||
_ => false,
|
_ => false,
|
||||||
}) {
|
}) {
|
||||||
let mut wrapped_cond = TokenTree::Group(Group::new(Delimiter::Parenthesis, cond));
|
let mut wrapped_cond = TokenTree::Group(Group::new(Delimiter::Parenthesis, cond));
|
||||||
wrapped_cond.set_span(cond_span);
|
wrapped_cond.set_span(cond_span.into());
|
||||||
cond = TokenStream::from(wrapped_cond);
|
cond = TokenStream::from(wrapped_cond);
|
||||||
}
|
}
|
||||||
quote!(if $cond)
|
quote!(if #cond)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
|
@ -268,7 +272,7 @@ impl Builder {
|
||||||
Escaper::new(&mut self.tail).write_str(string).unwrap();
|
Escaper::new(&mut self.tail).write_str(string).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_tokens<T: IntoIterator<Item=TokenTree>>(&mut self, tokens: T) {
|
fn push_tokens(&mut self, tokens: TokenStream) {
|
||||||
self.cut();
|
self.cut();
|
||||||
self.tokens.extend(tokens);
|
self.tokens.extend(tokens);
|
||||||
}
|
}
|
||||||
|
@ -280,7 +284,7 @@ impl Builder {
|
||||||
let push_str_expr = {
|
let push_str_expr = {
|
||||||
let output_ident = self.output_ident.clone();
|
let output_ident = self.output_ident.clone();
|
||||||
let string = TokenTree::Literal(Literal::string(&self.tail));
|
let string = TokenTree::Literal(Literal::string(&self.tail));
|
||||||
quote!($output_ident.push_str($string);)
|
quote!(#output_ident.push_str(#string);)
|
||||||
};
|
};
|
||||||
self.tail.clear();
|
self.tail.clear();
|
||||||
self.tokens.extend(push_str_expr);
|
self.tokens.extend(push_str_expr);
|
||||||
|
|
|
@ -15,38 +15,39 @@ mod ast;
|
||||||
mod generate;
|
mod generate;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
||||||
use proc_macro::{Literal, Span, Ident, TokenStream, TokenTree};
|
use proc_macro2::{Literal, Ident, TokenStream, TokenTree};
|
||||||
use proc_macro::quote;
|
use quote::quote;
|
||||||
|
|
||||||
type ParseResult<T> = Result<T, ()>;
|
type ParseResult<T> = Result<T, ()>;
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn html(input: TokenStream) -> TokenStream {
|
pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
expand(input)
|
expand(input.into()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn html_debug(input: TokenStream) -> TokenStream {
|
pub fn html_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let expr = expand(input);
|
let expr = expand(input.into());
|
||||||
println!("expansion:\n{}", expr);
|
println!("expansion:\n{}", expr);
|
||||||
expr
|
expr.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand(input: TokenStream) -> TokenStream {
|
fn expand(input: TokenStream) -> TokenStream {
|
||||||
let output_ident = TokenTree::Ident(Ident::new("__maud_output", Span::mixed_site()));
|
// TODO: call `proc_macro2::Span::mixed_site()` directly when Rust 1.45 is stable
|
||||||
|
let output_ident = TokenTree::Ident(Ident::new("__maud_output", proc_macro::Span::mixed_site().into()));
|
||||||
// Heuristic: the size of the resulting markup tends to correlate with the
|
// Heuristic: the size of the resulting markup tends to correlate with the
|
||||||
// code size of the template itself
|
// code size of the template itself
|
||||||
let size_hint = input.to_string().len();
|
let size_hint = input.to_string().len();
|
||||||
let size_hint = TokenTree::Literal(Literal::u64_unsuffixed(size_hint as u64));
|
let size_hint = TokenTree::Literal(Literal::u64_unsuffixed(size_hint as u64));
|
||||||
let markups = match parse::parse(input) {
|
let markups = match parse::parse(input.into()) {
|
||||||
Ok(markups) => markups,
|
Ok(markups) => markups,
|
||||||
Err(()) => Vec::new(),
|
Err(()) => Vec::new(),
|
||||||
};
|
};
|
||||||
let stmts = generate::generate(markups, output_ident.clone());
|
let stmts = generate::generate(markups, output_ident.clone());
|
||||||
quote!({
|
quote!({
|
||||||
extern crate maud;
|
extern crate maud;
|
||||||
let mut $output_ident = ::std::string::String::with_capacity($size_hint);
|
let mut #output_ident = ::std::string::String::with_capacity(#size_hint);
|
||||||
$stmts
|
#stmts
|
||||||
maud::PreEscaped($output_ident)
|
maud::PreEscaped(#output_ident)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue