Nom nom
diff --git a/src/lib.rs b/src/lib.rs
index c9e6e52..1d36389 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,7 @@
 extern crate nom;
 
 use nom::IResult;
-use nom::multispace as space;
+use nom::{digit, multispace as space};
 
 use std::str;
 
@@ -255,7 +255,7 @@
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct MutTy {
     pub ty: Ty,
-    pub mutbl: Mutability,
+    pub mutability: Mutability,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -320,6 +320,10 @@
     Ty(Ty),
 }
 
+pub fn epsilon<T>(input: T) -> IResult<T, ()> {
+    IResult::Done(input, ())
+}
+
 pub fn escaped_string(input: &str) -> IResult<&str, String> {
     let mut s = String::new();
     let mut chars = input.char_indices().peekable();
@@ -357,81 +361,119 @@
     IResult::Error(nom::Err::Position(nom::ErrorKind::Escaped, input))
 }
 
+macro_rules! punct {
+    ($i:expr, $ch:expr) => {
+        tuple!($i, opt!(space), tag_s!($ch))
+    };
+}
+
 named!(quoted<&str, String>, delimited!(
-    tag_s!("\""),
+    punct!("\""),
     escaped_string,
     tag_s!("\"")
 ));
 
-named!(meta_item<&str, MetaItem>, chain!(
-    space? ~
-    meta_item: alt!(
-        chain!(
-            ident: word ~
-            space? ~
-            tag_s!("(") ~
-            inner: separated_list!(tag_s!(","), meta_item) ~
-            space? ~
-            tag_s!(")"),
-            move || MetaItem::List(ident, inner)
-        )
-        |
-        chain!(
-            ident: word ~
-            space? ~
-            tag_s!("=") ~
-            space? ~
-            string: quoted,
-            move || MetaItem::NameValue(ident, string)
-        )
-        |
-        map!(word, MetaItem::Word)
-    ),
-    move || meta_item
+named!(meta_item<&str, MetaItem>, alt!(
+    chain!(
+        ident: word ~
+        punct!("(") ~
+        inner: separated_list!(punct!(","), meta_item) ~
+        punct!(")"),
+        move || MetaItem::List(ident, inner)
+    )
+    |
+    chain!(
+        ident: word ~
+        punct!("=") ~
+        string: quoted,
+        move || MetaItem::NameValue(ident, string)
+    )
+    |
+    map!(word, MetaItem::Word)
 ));
 
 named!(attribute<&str, Attribute>, chain!(
-    space? ~
-    tag_s!("#") ~
-    space? ~
-    tag_s!("[") ~
+    punct!("#") ~
+    punct!("[") ~
     meta_item: meta_item ~
-    space? ~
-    tag_s!("]"),
+    punct!("]"),
     move || Attribute {
         value: meta_item,
         is_sugared_doc: false,
     }
 ));
 
-named!(visibility<&str, Visibility>,
-    map!(opt!(terminated!(tag_s!("pub"), space)), |tag| match tag {
-        Some(_) => Visibility::Public,
-        None => Visibility::Inherited,
-    })
-);
+named!(mutability<&str, Mutability>, preceded!(
+    opt!(space),
+    alt!(
+        terminated!(tag_s!("mut"), space) => { |_| Mutability::Mutable }
+        |
+        epsilon => { |_| Mutability::Immutable }
+    )
+));
+
+named!(visibility<&str, Visibility>, preceded!(
+    opt!(space),
+    alt!(
+        terminated!(tag_s!("pub"), space) => { |_| Visibility::Public }
+        |
+        epsilon => { |_| Visibility::Inherited }
+    )
+));
 
 fn ident_ch(ch: char) -> bool {
     ch.is_alphanumeric() || ch == '_'
 }
 
-named!(word<&str, Ident>, map!(take_while1_s!(ident_ch), String::from));
+named!(word<&str, Ident>, preceded!(
+    opt!(space),
+    map!(take_while1_s!(ident_ch), String::from)
+));
+
+macro_rules! opt_vec (
+    ($i:expr, $submac:ident!( $($args:tt)* )) => ({
+        match $submac!($i, $($args)*) {
+            IResult::Done(i, o) => IResult::Done(i, o),
+            IResult::Error(_) => IResult::Done($i, Vec::new()),
+            IResult::Incomplete(i) => IResult::Incomplete(i)
+        }
+    });
+);
+
+named!(type_binding<&str, TypeBinding>, chain!(
+    ident: word ~
+    punct!("=") ~
+    ty: ty,
+    move || TypeBinding {
+        ident: ident,
+        ty: ty,
+    }
+));
 
 named!(path_segment<&str, PathSegment>, alt!(
     chain!(
         ident: word ~
-        space? ~
-        tag_s!("<") ~
-        types: many0!(ty) ~
-        space? ~
-        tag_s!(">"),
+        punct!("<") ~
+        lifetimes: separated_list!(punct!(","), lifetime) ~
+        types: opt_vec!(preceded!(
+            cond!(!lifetimes.is_empty(), punct!(",")),
+            separated_nonempty_list!(
+                punct!(","),
+                terminated!(ty, not!(peek!(punct!("="))))
+            )
+        )) ~
+        bindings: opt_vec!(preceded!(
+            cond!(!lifetimes.is_empty() || !types.is_empty(), punct!(",")),
+            separated_nonempty_list!(punct!(","), type_binding)
+        )) ~
+        punct!(">"),
         move || PathSegment {
             ident: ident,
             parameters: PathParameters::AngleBracketed(
                 AngleBracketedParameterData {
-                    lifetimes: Vec::new(),
+                    lifetimes: lifetimes,
                     types: types,
-                    bindings: Vec::new(),
+                    bindings: bindings,
                 }
             ),
         }
@@ -440,56 +482,146 @@
     map!(word, PathSegment::ident)
 ));
 
-named!(ty<&str, Ty>, chain!(
-    global: tag_s!("::")? ~
-    segments: separated_nonempty_list!(tag_s!("::"), path_segment),
-    move || Ty::Path(None, Path {
+named!(path<&str, Path>, chain!(
+    global: punct!("::")? ~
+    segments: separated_nonempty_list!(punct!("::"), path_segment),
+    move || Path {
         global: global.is_some(),
         segments: segments,
-    })
+    }
 ));
 
-/*
-    /// A variable-length array (`[T]`)
-    Vec(Box<Ty>),
-    /// A fixed length array (`[T; n]`)
-    FixedLengthVec(Box<Ty>, usize),
-    /// A raw pointer (`*const T` or `*mut T`)
-    Ptr(Box<MutTy>),
-    /// A reference (`&'a T` or `&'a mut T`)
-    Rptr(Option<Lifetime>, Box<MutTy>),
-    /// A bare function (e.g. `fn(usize) -> bool`)
-    BareFn(Box<BareFnTy>),
-    /// The never type (`!`)
-    Never,
-    /// A tuple (`(A, B, C, D, ...)`)
-    Tup(Vec<Ty>),
-    /// A path (`module::module::...::Type`), optionally
-    /// "qualified", e.g. `<Vec<T> as SomeTrait>::SomeType`.
-    ///
-    /// Type parameters are stored in the Path itself
-    Path(Option<QSelf>, Path),
-    /// Something like `A+B`. Note that `B` must always be a path.
-    ObjectSum(Box<Ty>, Vec<TyParamBound>),
-    /// A type like `for<'a> Foo<&'a Bar>`
-    PolyTraitRef(Vec<TyParamBound>),
-    /// An `impl TraitA+TraitB` type.
-    ImplTrait(Vec<TyParamBound>),
-    /// No-op; kept solely so that we can pretty-print faithfully
-    Paren(Box<Ty>),
-    /// TyKind::Infer means the type should be inferred instead of it having been
-    /// specified. This can appear anywhere in a type.
-    Infer,
-*/
+named!(fn_arg<&str, Arg>, chain!(
+    pat: opt!(terminated!(word, punct!(":"))) ~
+    ty: ty,
+    move || Arg {
+        pat: pat,
+        ty: ty,
+    }
+));
+
+named!(ty<&str, Ty>, alt!(
+    delimited!(
+        punct!("["),
+        ty,
+        punct!("]")
+    ) => { |elem| Ty::Vec(Box::new(elem)) }
+    |
+    chain!(
+        punct!("[") ~
+        elem: ty ~
+        punct!(";") ~
+        space? ~
+        size: map_res!(digit, str::parse),
+        move || Ty::FixedLengthVec(Box::new(elem), size)
+    )
+    |
+    chain!(
+        punct!("*") ~
+        mutability: alt!(
+            punct!("const") => { |_| Mutability::Immutable }
+            |
+            punct!("mut") => { |_| Mutability::Mutable }
+        ) ~
+        target: ty,
+        move || Ty::Ptr(Box::new(MutTy {
+            ty: target,
+            mutability: mutability,
+        }))
+    )
+    |
+    chain!(
+        punct!("&") ~
+        life: lifetime? ~
+        mutability: mutability ~
+        target: ty,
+        move || Ty::Rptr(life, Box::new(MutTy {
+            ty: target,
+            mutability: mutability,
+        }))
+    )
+    |
+    chain!(
+        punct!("fn") ~
+        space ~
+        lifetimes: opt_vec!(delimited!(
+            punct!("<"),
+            separated_list!(punct!(","), lifetime_def),
+            punct!(">")
+        )) ~
+        punct!("(") ~
+        inputs: separated_list!(punct!(","), fn_arg) ~
+        punct!(")") ~
+        output: opt!(preceded!(
+            punct!("->"),
+            ty
+        )),
+        move || Ty::BareFn(Box::new(BareFnTy {
+            lifetimes: lifetimes,
+            decl: FnDecl {
+                inputs: inputs,
+                output: match output {
+                    Some(ty) => FunctionRetTy::Ty(ty),
+                    None => FunctionRetTy::Default,
+                },
+            },
+        }))
+    )
+    |
+    punct!("!") => { |_| Ty::Never }
+    |
+    delimited!(
+        punct!("("),
+        separated_list!(punct!(","), ty),
+        punct!(")")
+    ) => { Ty::Tup }
+    |
+    path => { |p| Ty::Path(None, p) }
+    |
+    chain!(
+        punct!("<") ~
+        this: map!(ty, Box::new) ~
+        path: opt!(preceded!(
+            tuple!(punct!("as"), space),
+            path
+        )) ~
+        punct!(">") ~
+        punct!("::") ~
+        rest: separated_nonempty_list!(punct!("::"), path_segment),
+        move || {
+            match path {
+                Some(mut path) => {
+                    let pos = path.segments.len();
+                    path.segments.extend(rest);
+                    Ty::Path(Some(QSelf { ty: this, position: pos }), path)
+                }
+                None => {
+                    Ty::Path(Some(QSelf { ty: this, position: 0 }), Path {
+                        global: false,
+                        segments: rest,
+                    })
+                }
+            }
+        }
+    )
+    |
+    preceded!(
+        tuple!(punct!("impl"), space),
+        separated_nonempty_list!(punct!("+"), ty_param_bound)
+    ) => { Ty::ImplTrait }
+    |
+    delimited!(
+        punct!("("),
+        ty,
+        punct!(")")
+    ) => { |inner| Ty::Paren(Box::new(inner)) }
+));
 
 named!(struct_field<&str, Field>, chain!(
     attrs: many0!(attribute) ~
-    space? ~
     vis: visibility ~
     ident: word ~
-    space? ~
-    tag_s!(":") ~
-    space? ~
+    punct!(":") ~
     ty: ty,
     move || Field {
         ident: Some(ident),
@@ -501,7 +633,6 @@
 
 named!(tuple_field<&str, Field>, chain!(
     attrs: many0!(attribute) ~
-    space? ~
     vis: visibility ~
     ty: ty,
     move || Field {
@@ -512,36 +643,40 @@
     }
 ));
 
+named!(struct_like_body<&str, Vec<Field> >, chain!(
+    punct!("{") ~
+    fields: separated_list!(punct!(","), struct_field) ~
+    punct!(",")? ~
+    punct!("}"),
+    move || fields
+));
+
+named!(tuple_like_body<&str, Vec<Field> >, chain!(
+    punct!("(") ~
+    fields: separated_list!(punct!(","), tuple_field) ~
+    punct!(",")? ~
+    punct!(")"),
+    move || fields
+));
+
 named!(struct_body<&str, (Style, Vec<Field>)>, alt!(
-    chain!(
-        tag_s!("{") ~
-        fields: separated_list!(tag_s!(","), struct_field) ~
-        space? ~
-        tag_s!(",")? ~
-        space? ~
-        tag_s!("}"),
-        move || (Style::Struct, fields)
-    )
+    struct_like_body => { |fields| (Style::Struct, fields) }
     |
-    chain!(
-        tag_s!("(") ~
-        fields: separated_list!(tag_s!(","), tuple_field) ~
-        space? ~
-        tag_s!(",")? ~
-        space? ~
-        tag_s!(")"),
-        move || (Style::Tuple, fields)
-    )
+    terminated!(tuple_like_body, punct!(";")) => { |fields| (Style::Tuple, fields) }
     |
-    map!(tag_s!(";"), |_| (Style::Unit, Vec::new()))
+    punct!(";") => { |_| (Style::Unit, Vec::new()) }
 ));
 
 named!(variant<&str, Variant>, chain!(
     attrs: many0!(attribute) ~
-    space? ~
     ident: word ~
-    space? ~
-    body: struct_body,
+    body: alt!(
+        struct_like_body => { |fields| (Style::Struct, fields) }
+        |
+        tuple_like_body => { |fields| (Style::Tuple, fields) }
+        |
+        epsilon => { |_| (Style::Unit, Vec::new()) }
+    ),
     move || Variant {
         ident: ident,
         attrs: attrs,
@@ -551,90 +686,131 @@
 ));
 
 named!(enum_body<&str, Body>, chain!(
-    tag_s!("{") ~
-    variants: separated_list!(tag_s!(","), variant) ~
-    space? ~
-    tag_s!(",")? ~
-    space? ~
-    tag_s!("}"),
+    punct!("{") ~
+    variants: separated_list!(punct!(","), variant) ~
+    punct!(",")? ~
+    punct!("}"),
     move || Body::Enum(variants)
 ));
 
 named!(lifetime<&str, Lifetime>, preceded!(
-    tag_s!("'"),
-    map!(word, |n| Lifetime { ident: n })
+    punct!("'"),
+    map!(word, |ident| Lifetime { ident: ident })
 ));
 
-named!(where_predicate<&str, WherePredicate>, preceded!(
-    opt!(space),
-    alt!(
-        map!(lifetime, |_| unimplemented!())
-        |
-        map!(word, |_| unimplemented!())
+named!(bound_lifetimes<&str, Vec<LifetimeDef> >, opt_vec!(chain!(
+    punct!("for") ~
+    punct!("<") ~
+    lifetimes: separated_list!(punct!(","), lifetime_def) ~
+    punct!(">"),
+    move || lifetimes
+)));
+
+named!(poly_trait_ref<&str, PolyTraitRef>, chain!(
+    bound_lifetimes: bound_lifetimes ~
+    trait_ref: path,
+    move || PolyTraitRef {
+        bound_lifetimes: bound_lifetimes,
+        trait_ref: trait_ref,
+    }
+));
+
+named!(ty_param_bound<&str, TyParamBound>, alt!(
+    tuple!(punct!("?"), punct!("Sized")) => { |_| TyParamBound::MaybeSized }
+    |
+    lifetime => { TyParamBound::Region }
+    |
+    poly_trait_ref => { TyParamBound::Trait }
+));
+
+named!(where_predicate<&str, WherePredicate>, alt!(
+    chain!(
+        ident: lifetime ~
+        punct!(":") ~
+        bounds: separated_nonempty_list!(punct!("+"), lifetime),
+        move || WherePredicate::RegionPredicate(WhereRegionPredicate {
+            lifetime: ident,
+            bounds: bounds,
+        })
+    )
+    |
+    chain!(
+        bound_lifetimes: bound_lifetimes ~
+        bounded_ty: ty ~
+        punct!(":") ~
+        bounds: separated_nonempty_list!(punct!("+"), ty_param_bound),
+        move || WherePredicate::BoundPredicate(WhereBoundPredicate {
+            bound_lifetimes: bound_lifetimes,
+            bounded_ty: bounded_ty,
+            bounds: bounds,
+        })
     )
 ));
 
 named!(lifetime_def<&str, LifetimeDef>, chain!(
-    lifetime: lifetime,
+    life: lifetime ~
+    bounds: opt_vec!(preceded!(
+        punct!(":"),
+        separated_nonempty_list!(punct!(","), lifetime)
+    )),
     move || LifetimeDef {
-        lifetime: lifetime,
-        bounds: Vec::new(),
+        lifetime: life,
+        bounds: bounds,
     }
 ));
 
 named!(ty_param<&str, TyParam>, chain!(
-    space? ~
-    ident: word,
+    ident: word ~
+    bounds: opt_vec!(preceded!(
+        punct!(":"),
+        separated_nonempty_list!(punct!("+"), ty_param_bound)
+    )) ~
+    default: opt!(preceded!(
+        punct!("="),
+        ty
+    )) ~
     move || TyParam {
         ident: ident,
-        bounds: Vec::new(),
-        default: None,
+        bounds: bounds,
+        default: default,
     }
 ));
 
 named!(generics<&str, Generics>, chain!(
-    bracketed: map!(
-        opt!(chain!(
-            space? ~
-            tag_s!("<") ~
-            lifetimes: separated_list!(tag_s!(","), lifetime_def) ~
-            ty_params: opt!(chain!(
-                space? ~
-                cond!(!lifetimes.is_empty(), tag_s!(",")) ~
-                ty_params: separated_nonempty_list!(tag_s!(","), ty_param),
-                move || ty_params
+    bracketed: alt!(
+        chain!(
+            punct!("<") ~
+            lifetimes: separated_list!(punct!(","), lifetime_def) ~
+            ty_params: opt_vec!(preceded!(
+                cond!(!lifetimes.is_empty(), punct!(",")),
+                separated_nonempty_list!(punct!(","), ty_param)
             )) ~
-            space? ~
-            tag_s!(">"),
-            move || (lifetimes, ty_params.unwrap_or_else(Vec::new))
-        )),
-        |opt: Option<_>| opt.unwrap_or_else(|| (Vec::new(), Vec::new()))
+            punct!(">"),
+            move || (lifetimes, ty_params)
+        )
+        |
+        epsilon => { |_| (Vec::new(), Vec::new()) }
     ) ~
-    where_clause: opt!(chain!(
-        tag_s!("where") ~
+    where_clause: opt_vec!(chain!(
+        punct!("where") ~
         space ~
-        predicates: separated_nonempty_list!(tag_s!(","), where_predicate) ~
-        space? ~
-        tag_s!(",")?,
+        predicates: separated_nonempty_list!(punct!(","), where_predicate) ~
+        punct!(",")? ~
         move || predicates
     )),
     move || Generics {
         lifetimes: bracketed.0,
         ty_params: bracketed.1,
-        where_clause: where_clause.unwrap_or_else(Vec::new),
+        where_clause: where_clause,
     }
 ));
 
 named!(item<&str, Item>, chain!(
     attrs: many0!(attribute) ~
-    space? ~
     vis: visibility ~
     which: alt!(tag_s!("struct") | tag_s!("enum")) ~
-    space ~
     ident: word ~
-    space? ~
     generics: generics ~
-    space? ~
     item: switch!(value!(which),
         "struct" => map!(struct_body, move |(style, fields)| Item {
             ident: ident,