Skip to content

Commit

Permalink
Refactor to keep up with changes to proc_macro
Browse files Browse the repository at this point in the history
Instead of a `kind` field containting a `TokenNode` variant, a
TokenTree is now an enum with variants of different types (Literal, Op,
Term, etc). Note that a TokenTree could be a sequence of TokenTrees if it is a
Group variant.

Other notes:

I'm unsure about the set_span call in Builder::emit_if, but I did not want to
throw away the passed in Span.

Parsing relies on destructuring references to the values associated with
TokenTree enum variants

It doesn't seem as easy to compose/chain TokenStreams as it is to
collect a Vec<TokenTree> into a TokenStream. There is probably some
iterator API I could use instead. See `match_arms` and build.rs

Refs #121
  • Loading branch information
anxiousmodernman committed Apr 12, 2018
1 parent 3d22f90 commit 716824a
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 132 deletions.
4 changes: 2 additions & 2 deletions maud/tests/control_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ fn match_expr() {
for &(input, output) in &[(Some("yay"), "<div>yay</div>"), (None, "oh noes")] {
let s = html! {
@match input {
Some(value) => {
div (value)
Some(valuexxx) => {
div (valuexxx)
},
None => {
"oh noes"
Expand Down
34 changes: 24 additions & 10 deletions maud_macros/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use proc_macro::{Delimiter, Literal, Span, TokenNode, TokenStream, TokenTree};
use proc_macro::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use proc_macro::quote;

use maud_htmlescape::Escaper;

pub struct Builder {
Expand All @@ -24,7 +23,7 @@ impl Builder {
if !self.tail.is_empty() {
let expr = {
let output_ident = self.output_ident.clone();
let string = TokenNode::Literal(Literal::string(&self.tail));
let string = TokenTree::Literal(Literal::string(&self.tail));
quote!($output_ident.push_str($string);)
};
self.stmts.push(expr);
Expand All @@ -34,8 +33,17 @@ impl Builder {

/// Reifies the `Builder` into a raw list of statements.
pub fn build(mut self) -> TokenStream {
// stmts: Vec<TokenStream>
let Builder { stmts, .. } = { self.flush(); self };
stmts.into_iter().collect()

// use a Group here?
let mut tts: Vec<TokenTree> = Vec::new();
for s in stmts.into_iter() {
let i = s.into_iter();
tts.extend(i);
}

tts.into_iter().collect()
}

/// Pushes a statement, flushing the tail buffer in the process.
Expand Down Expand Up @@ -111,14 +119,20 @@ impl Builder {
) {
// If the condition contains an opening brace `{`,
// wrap it in parentheses to avoid parse errors
if cond.clone().into_iter().any(|token| match token.kind {
TokenNode::Group(Delimiter::Brace, _) => true,
if cond.clone().into_iter().any(|token| match token {
TokenTree::Group(grp) => {
if grp.delimiter() == Delimiter::Brace {
true
} else {
false
}
},
_ => false,
}) {
cond = TokenStream::from(TokenTree {
kind: TokenNode::Group(Delimiter::Parenthesis, cond),
span: cond_span,
});
let mut g = Group::new(Delimiter::Parenthesis, cond);
// NOTE: Do we need to do this?
g.set_span(cond_span);
cond = TokenStream::from(TokenTree::Group(g));
}
self.push(quote!(if $cond { $body }));
}
Expand Down
19 changes: 11 additions & 8 deletions maud_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(proc_macro)]
#![feature(pattern_parentheses)]

#![doc(html_root_url = "https://docs.rs/maud_macros/0.17.2")]

Expand All @@ -9,7 +10,7 @@ extern crate proc_macro;
mod parse;
mod build;

use proc_macro::{Literal, Span, Term, TokenNode, TokenStream, TokenTree};
use proc_macro::{Span, Term, TokenStream, TokenTree};
use proc_macro::quote;

type ParseResult<T> = Result<T, String>;
Expand All @@ -27,21 +28,23 @@ pub fn html_debug(input: TokenStream) -> TokenStream {
}

fn expand(input: TokenStream) -> TokenStream {
let output_ident = TokenTree {
kind: TokenNode::Term(Term::intern("__maud_output")),
span: Span::def_site(),
};

// A TokenTree is a sequence of TokenTree variants.
//let output_ident = TokenTree::Term(Term::new("__maud_output", Span::def_site()));
let output_ident = TokenTree::Term(Term::new("__maud_output", Span::def_site()));
// Heuristic: the size of the resulting markup tends to correlate with the
// code size of the template itself
let size_hint = input.to_string().len();
let size_hint = TokenNode::Literal(Literal::u64(size_hint as u64));
//
// NOTE: can't get this to compile inside quote!
//let size_hint = Literal::u64_unsuffixed(size_hint as u64);
let stmts = match parse::parse(input, output_ident.clone()) {
Ok(stmts) => stmts,
Err(e) => panic!(e),
};
quote!({
extern crate maud;
let mut $output_ident = String::with_capacity($size_hint as usize);
//let mut $output_ident = String::with_capacity($size_hint as usize);
let mut $output_ident = String::new();
$stmts
maud::PreEscaped($output_ident)
})
Expand Down
Loading

0 comments on commit 716824a

Please sign in to comment.