Support parsing catch expressions.

Fixes #109
diff --git a/src/expr.rs b/src/expr.rs
index 69a3389..3068082 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -176,6 +176,11 @@
 
     /// `expr?`
     Try(Box<Expr>),
+
+    /// A catch expression.
+    ///
+    /// E.g. `do catch { block }`
+    Catch(Block),
 }
 
 /// A field-value pair in a struct literal.
@@ -427,6 +432,8 @@
                 |
                 expr_match
                 |
+                expr_catch
+                |
                 call!(expr_closure, allow_struct)
                 |
                 cond_reduce!(allow_block, expr_block)
@@ -677,6 +684,13 @@
         }))
     ));
 
+    named!(expr_catch -> ExprKind, do_parse!(
+        keyword!("do") >>
+        keyword!("catch") >>
+        catch_block: block >>
+        (ExprKind::Catch(catch_block))
+    ));
+
     fn arm_requires_comma(arm: &Arm) -> bool {
         if let ExprKind::Block(Unsafety::Normal, _) = arm.body.node {
             false
@@ -1337,6 +1351,11 @@
                     tokens.append_all(arms);
                     tokens.append("}");
                 }
+                ExprKind::Catch(ref body) => {
+                    tokens.append("do");
+                    tokens.append("catch");
+                    body.to_tokens(tokens);
+                }
                 ExprKind::Closure(capture, ref decl, ref expr) => {
                     capture.to_tokens(tokens);
                     tokens.append("|");
diff --git a/src/fold.rs b/src/fold.rs
index 2874118..9afda63 100644
--- a/src/fold.rs
+++ b/src/fold.rs
@@ -686,6 +686,9 @@
                     }
                 }))
             }
+            Catch(block) => {
+                Catch(folder.fold_block(block))
+            }
             Closure(capture_by, fn_decl, expr) => {
                 Closure(capture_by,
                         fn_decl.lift(|v| folder.fold_fn_decl(v)),
diff --git a/src/visit.rs b/src/visit.rs
index 8c8645d..879005b 100644
--- a/src/visit.rs
+++ b/src/visit.rs
@@ -502,6 +502,9 @@
                 visitor.visit_expr(body);
             }
         }
+        ExprKind::Catch(ref body) => {
+            walk_list!(visitor, visit_stmt, &body.stmts);
+        }
         ExprKind::Closure(_, ref decl, ref expr) => {
             visitor.visit_fn_decl(decl);
             visitor.visit_expr(expr);
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
new file mode 100644
index 0000000..dca8603
--- /dev/null
+++ b/tests/test_expr.rs
@@ -0,0 +1,136 @@
+extern crate syn;
+use syn::*;
+
+macro_rules! assert_let {
+    ($p:pat = $e:expr) => {
+        assert_let!($p = $e; {})
+    };
+
+    ($p:pat = $e:expr; $body:block) => {
+        if let $p = $e
+            $body
+        else {
+            panic!("Expected to match {} but got {:?}", stringify!($p), $e)
+        }
+    };
+}
+
+#[test]
+#[cfg(feature = "full")]
+fn test_catch_expr() {
+    // Taken from tests/rust/src/test/run-pass/catch-expr.rs
+    let raw = r#"
+        struct catch {}
+
+        pub fn main() {
+            let catch_result = do catch {
+                let x = 5;
+                x
+            };
+            assert_eq!(catch_result, 5);
+
+            let mut catch = true;
+            while catch { catch = false; }
+            assert_eq!(catch, false);
+
+            catch = if catch { false } else { true };
+            assert_eq!(catch, true);
+
+            match catch {
+                _ => {}
+            };
+
+            let catch_err = do catch {
+                Err(22)?;
+                Ok(1)
+            };
+            assert_eq!(catch_err, Err(22));
+
+            let catch_okay: Result<i32, i32> = do catch {
+                if false { Err(25)?; }
+                Ok::<(), i32>(())?;
+                Ok(28)
+            };
+            assert_eq!(catch_okay, Ok(28));
+
+            let catch_from_loop: Result<i32, i32> = do catch {
+                for i in 0..10 {
+                    if i < 5 { Ok::<i32, i32>(i)?; } else { Err(i)?; }
+                }
+                Ok(22)
+            };
+            assert_eq!(catch_from_loop, Err(5));
+
+            let cfg_init;
+            let _res: Result<(), ()> = do catch {
+                cfg_init = 5;
+                Ok(())
+            };
+            assert_eq!(cfg_init, 5);
+
+            let cfg_init_2;
+            let _res: Result<(), ()> = do catch {
+                cfg_init_2 = 6;
+                Err(())?;
+                Ok(())
+            };
+            assert_eq!(cfg_init_2, 6);
+
+            let my_string = "test".to_string();
+            let res: Result<&str, ()> = do catch {
+                Ok(&my_string)
+            };
+            assert_eq!(res, Ok("test"));
+        }
+    "#;
+
+    let actual = parse_crate(raw).unwrap();
+
+    assert_eq!(&actual.items[0].ident, "catch");
+
+    assert_let!(ItemKind::Struct(..) = actual.items[0].node);
+
+    assert_let!(Item { node: ItemKind::Fn(_, _, _, _, _, ref body), .. } = actual.items[1]; {
+        assert_let!(Stmt::Local(ref local) = body.stmts[0]; {
+            assert_let!(Local { init: Some(ref init_expr), .. } = **local; {
+                assert_let!(Expr { node: ExprKind::Catch(..), .. } = **init_expr);
+            });
+        });
+
+        assert_let!(Stmt::Local(ref local) = body.stmts[2]; {
+            assert_let!(Pat::Ident(BindingMode::ByValue(Mutability::Mutable), ref ident, None) = *local.pat; {
+                assert_eq!(ident, "catch");
+            });
+        });
+
+        assert_let!(Stmt::Expr(ref expr) = body.stmts[3]; {
+            assert_let!(Expr { node: ExprKind::While(ref loop_expr, _, None), .. } = **expr; {
+                assert_let!(Expr { node: ExprKind::Path(None, ref loop_var), .. } = **loop_expr; {
+                    assert_eq!(*loop_var, "catch".into());
+                });
+            });
+        });
+
+        assert_let!(Stmt::Semi(ref expr) = body.stmts[5]; {
+            assert_let!(Expr { node: ExprKind::Assign(ref left, ref right), .. } = **expr; {
+                assert_let!(Expr { node: ExprKind::Path(None, ref loop_var), .. } = **left; {
+                    assert_eq!(*loop_var, "catch".into());
+                });
+
+                assert_let!(Expr { node: ExprKind::If(ref if_expr, _, _), .. } = **right; {
+                    assert_let!(Expr { node: ExprKind::Path(None, ref if_var), .. } = **if_expr; {
+                        assert_eq!(*if_var, "catch".into());
+                    });
+                });
+            });
+        });
+
+        assert_let!(Stmt::Semi(ref expr) = body.stmts[7]; {
+            assert_let!(Expr { node: ExprKind::Match(ref match_expr, _), .. } = **expr; {
+                assert_let!(Expr { node: ExprKind::Path(None, ref match_var), .. } = **match_expr; {
+                    assert_eq!(*match_var, "catch".into());
+                });
+            });
+        });
+    });
+}
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
index b1560db..f2a1ba8 100644
--- a/tests/test_round_trip.rs
+++ b/tests/test_round_trip.rs
@@ -74,8 +74,6 @@
         "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
         // not actually a test case
         "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" |
-        // TODO catch expressions
-        "tests/rust/src/test/run-pass/catch-expr.rs" |
         // TODO better support for attributes
         "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
         // TODO 128-bit integer literals