diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index f6ca258..4cc502f 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -1,6 +1,6 @@ use std::mem; use std::rc::Rc; -use syntax::ast::{Expr, ExprKind, Lit, LitKind, Stmt}; +use syntax::ast::{Expr, Lit, LitKind, Stmt}; use syntax::ext::quote::rt::ToTokens; use syntax::codemap::Span; use syntax::errors::{DiagnosticBuilder, FatalError}; @@ -64,9 +64,6 @@ macro_rules! minus { macro_rules! slash { () => (TokenTree::Token(_, Token::BinOp(BinOpToken::Slash))) } -macro_rules! caret { - () => (TokenTree::Token(_, Token::BinOp(BinOpToken::Caret))) -} macro_rules! literal { () => (TokenTree::Token(_, Token::Literal(..))) } @@ -190,24 +187,24 @@ impl<'cx, 'a, 'i> Parser<'cx, 'a, 'i> { let func = self.splice(tt.get_span())?; self.render.emit_call(func); }, - // Splice - [ref tt @ caret!(), ..] => { - self.shift(1); - let expr = self.splice(tt.get_span())?; - self.render.splice(expr); - }, // Element [ident!(sp, _), ..] => { let name = self.namespaced_name().unwrap(); self.element(sp, &name)?; }, + // Splice + [TokenTree::Delimited(_, ref d), ..] if d.delim == DelimToken::Paren => { + self.shift(1); + let expr = self.with_rust_parser(d.tts.clone(), RustParser::parse_expr)?; + self.render.splice(expr); + } // Block [TokenTree::Delimited(_, ref d), ..] if d.delim == DelimToken::Brace => { self.shift(1); { // Parse the contents of the block, emitting the // result inline - let mut i = &*d.tts; + let mut i = &d.tts[..]; mem::swap(&mut self.input, &mut i); self.markups()?; mem::swap(&mut self.input, &mut i); @@ -502,21 +499,23 @@ impl<'cx, 'a, 'i> Parser<'cx, 'a, 'i> { (Ok(name), &[question!(), ..]) => { // Empty attribute self.shift(1); - if let [ref tt @ eq!(), ..] = *self.input { - // Toggle the attribute based on a boolean expression - self.shift(1); - let cond = self.splice(tt.get_span())?; - // Silence "unnecessary parentheses" warnings - let cond = strip_outer_parens(cond).to_tokens(self.render.cx); - let body = { - let mut r = self.render.fork(); - r.attribute_empty(&name); - r.into_stmts() - }; - self.render.emit_if(cond, body, None); - } else { - // Write the attribute unconditionally - self.render.attribute_empty(&name); + match *self.input { + [TokenTree::Delimited(_, ref d), ..] if d.delim == DelimToken::Paren => { + // Toggle the attribute based on a boolean expression + self.shift(1); + let cond = self.with_rust_parser(d.tts.clone(), RustParser::parse_expr)?; + let cond = cond.to_tokens(self.render.cx); + let body = { + let mut r = self.render.fork(); + r.attribute_empty(&name); + r.into_stmts() + }; + self.render.emit_if(cond, body, None); + }, + _ => { + // Write the attribute unconditionally + self.render.attribute_empty(&name); + }, } }, (Err(_), &[dot!(), ident!(_, _), ..]) => { @@ -619,11 +618,3 @@ fn lit_to_string(cx: &ExtCtxt, lit: Lit, minus: bool) -> PResult<String> { }; Ok(result) } - -/// If the expression is wrapped in parentheses, strip them off. -fn strip_outer_parens(expr: P<Expr>) -> P<Expr> { - expr.and_then(|expr| match expr { - Expr { node: ExprKind::Paren(inner), .. } => inner, - expr => P(expr), - }) -} diff --git a/maud_macros/tests/control_structures.rs b/maud_macros/tests/control_structures.rs index 8d0d6c6..62eae74 100644 --- a/maud_macros/tests/control_structures.rs +++ b/maud_macros/tests/control_structures.rs @@ -30,7 +30,7 @@ fn if_let() { let mut s = String::new(); html!(s, { @if let Some(value) = input { - ^value + (value) } @else { "oh noes" } @@ -45,7 +45,7 @@ fn for_expr() { let mut s = String::new(); html!(s, { ul @for pony in &ponies { - li ^pony + li (pony) } }).unwrap(); assert_eq!(s, concat!( @@ -63,7 +63,7 @@ fn match_expr() { html!(s, { @match input { Some(value) => { - div { ^value } + div (value) }, None => { "oh noes" @@ -80,8 +80,8 @@ fn match_expr_without_delims() { let mut s = String::new(); html!(s, { @match input { - Some(value) => ^value, - None => span { "oh noes" }, + Some(value) => (value), + None => span "oh noes", } }).unwrap(); assert_eq!(s, output); @@ -95,7 +95,7 @@ fn match_expr_with_guards() { html!(s, { @match input { Some(value) if value == 1 => "one", - Some(value) => ^value, + Some(value) => (value), None => "none", } }).unwrap(); @@ -112,7 +112,7 @@ fn match_in_attribute() { 1 => "one", 2 => "two", _ => "many", - } { ^input } + } { (input) } }).unwrap(); assert_eq!(s, output); } @@ -141,7 +141,7 @@ fn call() { fn assert_cute<'a>(name: &'a str) -> impl maud::Template + 'a { template! { p { - ^name " is the cutest" + (name) " is the cutest" } } } diff --git a/maud_macros/tests/misc.rs b/maud_macros/tests/misc.rs index 9f82a9d..113ca6e 100644 --- a/maud_macros/tests/misc.rs +++ b/maud_macros/tests/misc.rs @@ -16,7 +16,7 @@ fn html_utf8() { fn issue_13() { let owned = String::from("yay"); let mut s = String::new(); - html!(s, ^owned).unwrap(); + html!(s, (owned)).unwrap(); // Make sure the `html!` call didn't move it let _owned = owned; } @@ -27,7 +27,7 @@ fn issue_21() { () => ({ let mut result = String::new(); let name = "Pinkie Pie"; - html!(result, p { "Hello, " ^name "!" }).map(|()| result) + html!(result, p { "Hello, " (name) "!" }).map(|()| result) }) } @@ -40,7 +40,7 @@ fn issue_21_2() { macro_rules! greet { ($name:expr) => ({ let mut result = String::new(); - html!(result, p { "Hello, " ^$name "!" }).map(|()| result) + html!(result, p { "Hello, " ($name) "!" }).map(|()| result) }) } @@ -59,7 +59,7 @@ fn issue_23() { } let name = "Lyra"; - let s = to_string!(p { "Hi, " ^name "!" }); + let s = to_string!(p { "Hi, " (name) "!" }); assert_eq!(s, "<p>Hi, Lyra!</p>"); } @@ -74,7 +74,7 @@ fn issue_26() { } let name = "Lyra"; - let s = to_string!(p { "Hi, " ^(name) "!" }); + let s = to_string!(p { "Hi, " (name) "!" }); assert_eq!(s, "<p>Hi, Lyra!</p>"); } @@ -89,22 +89,7 @@ fn issue_26_2() { } let name = "Lyra"; - let s = to_string!(p { "Hi, " ^("person called ".to_string() + name) "!" }); - assert_eq!(s, "<p>Hi, person called Lyra!</p>"); -} - -#[test] -fn issue_26_3() { - macro_rules! to_string { - ($($x:tt)*) => {{ - let mut s = String::new(); - html!(s, $($x)*).unwrap(); - s - }} - } - - let name = "Lyra"; - let s = to_string!(p { "Hi, " ^{"person called ".to_string() + name} "!" }); + let s = to_string!(p { "Hi, " ("person called ".to_string() + name) "!" }); assert_eq!(s, "<p>Hi, person called Lyra!</p>"); } @@ -121,8 +106,8 @@ fn render_impl() { let r = R("pinkie"); // Since `R` is not `Copy`, this shows that Maud will auto-ref splice // arguments to find a `Render` impl - html!(s, ^r).unwrap(); - html!(s, ^r).unwrap(); + html!(s, (r)).unwrap(); + html!(s, (r)).unwrap(); assert_eq!(s, "pinkiepinkie"); } @@ -137,6 +122,6 @@ fn render_once_impl() { let mut s = String::new(); let once = Once(String::from("pinkie")); - html!(s, ^once).unwrap(); + html!(s, (once)).unwrap(); assert_eq!(s, "pinkie"); } diff --git a/maud_macros/tests/splices.rs b/maud_macros/tests/splices.rs index e57972f..cbc18bc 100644 --- a/maud_macros/tests/splices.rs +++ b/maud_macros/tests/splices.rs @@ -6,7 +6,7 @@ extern crate maud; #[test] fn literals() { let mut s = String::new(); - html!(s, ^"<pinkie>").unwrap(); + html!(s, ("<pinkie>")).unwrap(); assert_eq!(s, "<pinkie>"); } @@ -14,7 +14,7 @@ fn literals() { fn raw_literals() { use maud::PreEscaped; let mut s = String::new(); - html!(s, ^PreEscaped("<pinkie>")).unwrap(); + html!(s, (PreEscaped("<pinkie>"))).unwrap(); assert_eq!(s, "<pinkie>"); } @@ -22,13 +22,13 @@ fn raw_literals() { fn blocks() { let mut s = String::new(); html!(s, { - ^{ + ({ let mut result = 1i32; for i in 2..11 { result *= i; } result - } + }) }).unwrap(); assert_eq!(s, "3628800"); } @@ -38,10 +38,10 @@ fn attributes() { let rocks = true; let mut s = String::new(); html!(s, { - input checked?=true / - input checked?=false / - input checked?=rocks / - input checked?=(!rocks) / + input checked?(true) / + input checked?(false) / + input checked?(rocks) / + input checked?(!rocks) / }).unwrap(); assert_eq!(s, concat!( r#"<input checked>"#, @@ -55,7 +55,7 @@ static BEST_PONY: &'static str = "Pinkie Pie"; #[test] fn statics() { let mut s = String::new(); - html!(s, ^BEST_PONY).unwrap(); + html!(s, (BEST_PONY)).unwrap(); assert_eq!(s, "Pinkie Pie"); } @@ -63,7 +63,7 @@ fn statics() { fn locals() { let best_pony = "Pinkie Pie"; let mut s = String::new(); - html!(s, ^best_pony).unwrap(); + html!(s, (best_pony)).unwrap(); assert_eq!(s, "Pinkie Pie"); } @@ -90,7 +90,7 @@ fn structs() { }; let mut s = String::new(); html!(s, { - "Name: " ^pinkie.name ". Rating: " ^pinkie.repugnance() + "Name: " (pinkie.name) ". Rating: " (pinkie.repugnance()) }).unwrap(); assert_eq!(s, "Name: Pinkie Pie. Rating: 1"); } @@ -99,7 +99,7 @@ fn structs() { fn tuple_accessors() { let mut s = String::new(); let a = ("ducks", "geese"); - html!(s, ^a.0).unwrap(); + html!(s, (a.0)).unwrap(); assert_eq!(s, "ducks"); } @@ -111,7 +111,7 @@ fn splice_with_path() { } } let mut s = String::new(); - html!(s, ^inner::name()).unwrap(); + html!(s, (inner::name())).unwrap(); assert_eq!(s, "Maud"); } @@ -119,6 +119,6 @@ fn splice_with_path() { fn nested_macro_invocation() { let best_pony = "Pinkie Pie"; let mut s = String::new(); - html!(s, ^(format!("{}", best_pony))).unwrap(); + html!(s, (format!("{}", best_pony))).unwrap(); assert_eq!(s, "Pinkie Pie"); }