diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index c69df18..445c3ba 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -40,25 +40,25 @@ macro_rules! ident { ($sp:pat, $x:pat) => (TtToken($sp, token::Ident($x, token::IdentStyle::Plain))) } -pub fn parse(cx: &mut ExtCtxt, input: &[TokenTree], sp: Span) -> P<Expr> { - Renderer::with(cx, |render| { - Parser { - in_attr: false, - input: input, - span: sp, - render: render, - }.markups(); - }) +pub fn parse(cx: &ExtCtxt, input: &[TokenTree], sp: Span) -> P<Expr> { + let mut render = Renderer::new(cx); + Parser { + in_attr: false, + input: input, + span: sp, + render: &mut render, + }.markups(); + render.into_expr() } -struct Parser<'cx: 'r, 's: 'cx, 'i, 'r, 'o: 'r> { +struct Parser<'cx: 'r, 's: 'cx, 'i, 'r> { in_attr: bool, input: &'i [TokenTree], span: Span, - render: &'r mut Renderer<'cx, 's, 'o>, + render: &'r mut Renderer<'cx, 's>, } -impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> { +impl<'cx, 's, 'i, 'r> Parser<'cx, 's, 'i, 'r> { /// Consume `n` items from the input. fn shift(&mut self, n: usize) { self.input = &self.input[n..]; @@ -221,7 +221,7 @@ impl<'cx, 's, 'i, 'r, 'o> Parser<'cx, 's, 'i, 'r, 'o> { } /// Convert a literal to a string. -fn lit_to_string(cx: &mut ExtCtxt, lit: Lit, minus: bool) -> Option<String> { +fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> Option<String> { use syntax::ast::Lit_::*; let mut result = String::new(); if minus { diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs index 9ed6197..8143835 100644 --- a/maud_macros/src/render.rs +++ b/maud_macros/src/render.rs @@ -13,27 +13,23 @@ pub enum Escape { Escape, } -pub struct Renderer<'cx, 's: 'cx, 'o> { - pub cx: &'cx mut ExtCtxt<'s>, - stmts: &'o mut Vec<P<Stmt>>, +pub struct Renderer<'cx, 's: 'cx> { + pub cx: &'cx ExtCtxt<'s>, + stmts: Vec<P<Stmt>>, w: Ident, } -impl<'cx, 's, 'o> Renderer<'cx, 's, 'o> { - pub fn with<F>(cx: &'cx mut ExtCtxt<'s>, f: F) -> P<Expr> where - F: for<'o_> FnOnce(&mut Renderer<'cx, 's, 'o_>) - { - let mut stmts = vec![]; - let w = Ident::new(token::intern("w")); - let cx = { - let mut render = Renderer { - cx: cx, - stmts: &mut stmts, - w: w, - }; - f(&mut render); - render.cx - }; +impl<'cx, 's> Renderer<'cx, 's> { + pub fn new(cx: &'cx ExtCtxt<'s>) -> Renderer<'cx, 's> { + Renderer { + cx: cx, + stmts: vec![], + w: Ident::new(token::intern("w")), + } + } + + pub fn into_expr(self) -> P<Expr> { + let Renderer { cx, stmts, w } = self; quote_expr!(cx, ::maud::rt::make_markup(|&: $w: &mut ::std::fmt::Writer| -> Result<(), ::std::fmt::Error> { $stmts @@ -41,22 +37,29 @@ impl<'cx, 's, 'o> Renderer<'cx, 's, 'o> { })) } - /// Push an expression statement, also wrapping it with `try!`. - fn push(&mut self, expr: P<Expr>) { - let stmt = self.make_stmt(expr); - self.stmts.push(stmt); + fn make_stmts<F>(&self, f: F) -> Vec<P<Stmt>> where + F: FnOnce(&mut Renderer<'cx, 's>) + { + let mut render = Renderer { + cx: self.cx, + stmts: vec![], + w: self.w, + }; + f(&mut render); + render.stmts } - /// Create an expression statement, also wrapping it with `try!`. - fn make_stmt(&mut self, expr: P<Expr>) -> P<Stmt> { - self.cx.stmt_expr(self.cx.expr_try(expr.span, expr)) + /// Push an expression statement, also wrapping it with `try!`. + fn push_try(&mut self, expr: P<Expr>) { + let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr)); + self.stmts.push(stmt); } /// Append a literal pre-escaped string. fn write(&mut self, s: &str) { let w = self.w; let expr = quote_expr!(self.cx, $w.write_str($s)); - self.push(expr); + self.push_try(expr); } /// Append a literal string, with the specified escaping method. @@ -80,7 +83,7 @@ impl<'cx, 's, 'o> Renderer<'cx, 's, 'o> { &mut ::maud::rt::Escaper { inner: $w }, $expr)), }; - self.push(expr); + self.push_try(expr); } pub fn element_open_start(&mut self, name: &str) { @@ -100,21 +103,17 @@ impl<'cx, 's, 'o> Renderer<'cx, 's, 'o> { } pub fn attribute_empty_if(&mut self, name: &str, expr: P<Expr>) { - let s: String = [" ", name].concat(); - let s = &s[]; - let w = self.w; // Silence "unnecessary parentheses" warnings let expr = match expr.node { ExprParen(ref inner) => inner.clone(), _ => expr.clone(), }; - let expr = quote_expr!(self.cx, - if $expr { - $w.write_str($s) - } else { - Ok(()) - }); - self.push(expr); + let stmts = self.make_stmts(|r| { + r.write(" "); + r.write(name); + }); + let stmt = quote_stmt!(self.cx, if $expr { $stmts }); + self.stmts.push(stmt); } pub fn attribute_end(&mut self) {