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, "&lt;pinkie&gt;");
 }
 
@@ -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");
 }