Repository: dtolnay/syn
Pull request #13 (2016-09-24 00:33:58)

Expressions


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index c11e2bff..e7287fce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
 [features]
 default = ["parsing", "printing"]
 aster = []
+full = []
 parsing = []
 printing = ["quote"]
 visit = []
diff --git a/src/attr.rs b/src/attr.rs
index 2cf98259..e08d9c7f 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -22,14 +22,15 @@ pub enum MetaItem {
     /// Name value meta item.
     ///
     /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
-    NameValue(Ident, String),
+    NameValue(Ident, Lit),
 }
 
 #[cfg(feature = "parsing")]
 pub mod parsing {
     use super::*;
-    use escape::escaped_string;
     use ident::parsing::ident;
+    use lit::{Lit, StrStyle};
+    use lit::parsing::lit;
     use nom::multispace;
 
     named!(pub attribute -> Attribute, alt!(
@@ -51,7 +52,10 @@ pub mod parsing {
             (Attribute {
                 value: MetaItem::NameValue(
                     "doc".into(),
-                    format!("///{}{}", space, content),
+                    Lit::Str(
+                        format!("///{}{}", space, content),
+                        StrStyle::Cooked,
+                    ),
                 ),
                 is_sugared_doc: true,
             })
@@ -68,20 +72,14 @@ pub mod parsing {
         )
         |
         do_parse!(
-            id: ident >>
+            name: ident >>
             punct!("=") >>
-            string: quoted >>
-            (MetaItem::NameValue(id, string))
+            value: lit >>
+            (MetaItem::NameValue(name, value))
         )
         |
         map!(ident, MetaItem::Word)
     ));
-
-    named!(quoted -> String, delimited!(
-        punct!("\""),
-        escaped_string,
-        tag!("\"")
-    ));
 }
 
 #[cfg(feature = "printing")]
diff --git a/src/expr.rs b/src/expr.rs
new file mode 100644
index 00000000..5c31ab92
--- /dev/null
+++ b/src/expr.rs
@@ -0,0 +1,351 @@
+use super::*;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Expr {
+    /// A `box x` expression.
+    Box(Box<Expr>),
+    /// First expr is the place; second expr is the value.
+    InPlace(Box<Expr>, Box<Expr>),
+    /// An array (`[a, b, c, d]`)
+    Vec(Vec<Expr>),
+    /// A function call
+    ///
+    /// The first field resolves to the function itself,
+    /// and the second field is the list of arguments
+    Call(Box<Expr>, Vec<Expr>),
+    /// A method call (`x.foo::<Bar, Baz>(a, b, c, d)`)
+    ///
+    /// The `Ident` is the identifier for the method name.
+    /// The vector of `Ty`s are the ascripted type parameters for the method
+    /// (within the angle brackets).
+    ///
+    /// The first element of the vector of `Expr`s is the expression that evaluates
+    /// to the object on which the method is being called on (the receiver),
+    /// and the remaining elements are the rest of the arguments.
+    ///
+    /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
+    /// `ExprKind::MethodCall(foo, [Bar, Baz], [x, a, b, c, d])`.
+    MethodCall(Ident, Vec<Ty>, Vec<Expr>),
+    /// A tuple (`(a, b, c, d)`)
+    Tup(Vec<Expr>),
+    /// A binary operation (For example: `a + b`, `a * b`)
+    Binary(BinOp, Box<Expr>, Box<Expr>),
+    /// A unary operation (For example: `!x`, `*x`)
+    Unary(UnOp, Box<Expr>),
+    /// A literal (For example: `1`, `"foo"`)
+    Lit(Lit),
+    /// A cast (`foo as f64`)
+    Cast(Box<Expr>, Box<Ty>),
+    Type(Box<Expr>, Box<Ty>),
+    /// An `if` block, with an optional else block
+    ///
+    /// `if expr { block } else { expr }`
+    If(Box<Expr>, Box<Block>, Option<Box<Expr>>),
+    /// An `if let` expression with an optional else block
+    ///
+    /// `if let pat = expr { block } else { expr }`
+    ///
+    /// This is desugared to a `match` expression.
+    IfLet(Box<Pat>, Box<Expr>, Box<Block>, Option<Box<Expr>>),
+    /// A while loop, with an optional label
+    ///
+    /// `'label: while expr { block }`
+    While(Box<Expr>, Box<Block>, Option<Ident>),
+    /// A while-let loop, with an optional label
+    ///
+    /// `'label: while let pat = expr { block }`
+    ///
+    /// This is desugared to a combination of `loop` and `match` expressions.
+    WhileLet(Box<Pat>, Box<Expr>, Box<Block>, Option<Ident>),
+    /// A for loop, with an optional label
+    ///
+    /// `'label: for pat in expr { block }`
+    ///
+    /// This is desugared to a combination of `loop` and `match` expressions.
+    ForLoop(Box<Pat>, Box<Expr>, Box<Block>, Option<Ident>),
+    /// Conditionless loop (can be exited with break, continue, or return)
+    ///
+    /// `'label: loop { block }`
+    Loop(Box<Block>, Option<Ident>),
+    /// A `match` block.
+    Match(Box<Expr>, Vec<Arm>),
+    /// A closure (for example, `move |a, b, c| {a + b + c}`)
+    Closure(CaptureBy, Box<FnDecl>, Box<Block>),
+    /// A block (`{ ... }`)
+    Block(Box<Block>),
+
+    /// An assignment (`a = foo()`)
+    Assign(Box<Expr>, Box<Expr>),
+    /// An assignment with an operator
+    ///
+    /// For example, `a += 1`.
+    AssignOp(BinOp, Box<Expr>, Box<Expr>),
+    /// Access of a named struct field (`obj.foo`)
+    Field(Box<Expr>, Ident),
+    /// Access of an unnamed field of a struct or tuple-struct
+    ///
+    /// For example, `foo.0`.
+    TupField(Box<Expr>, usize),
+    /// An indexing operation (`foo[2]`)
+    Index(Box<Expr>, Box<Expr>),
+    /// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`)
+    Range(Option<Box<Expr>>, Option<Box<Expr>>, RangeLimits),
+
+    /// Variable reference, possibly containing `::` and/or type
+    /// parameters, e.g. foo::bar::<baz>.
+    ///
+    /// Optionally "qualified",
+    /// E.g. `<Vec<T> as SomeTrait>::SomeType`.
+    Path(Option<QSelf>, Path),
+
+    /// A referencing operation (`&a` or `&mut a`)
+    AddrOf(Mutability, Box<Expr>),
+    /// A `break`, with an optional label to break
+    Break(Option<Ident>),
+    /// A `continue`, with an optional label
+    Continue(Option<Ident>),
+    /// A `return`, with an optional value to be returned
+    Ret(Option<Box<Expr>>),
+
+    /// A macro invocation; pre-expansion
+    Mac(Mac),
+
+    /// A struct literal expression.
+    ///
+    /// For example, `Foo {x: 1, y: 2}`, or
+    /// `Foo {x: 1, .. base}`, where `base` is the `Option<Expr>`.
+    Struct(Path, Vec<Field>, Option<Box<Expr>>),
+
+    /// An array literal constructed from one repeated element.
+    ///
+    /// For example, `[1; 5]`. The first expression is the element
+    /// to be repeated; the second is the number of times to repeat it.
+    Repeat(Box<Expr>, Box<Expr>),
+
+    /// No-op: used solely so we can pretty-print faithfully
+    Paren(Box<Expr>),
+
+    /// `expr?`
+    Try(Box<Expr>),
+}
+
+/// A Block (`{ .. }`).
+///
+/// E.g. `{ .. }` as in `fn foo() { .. }`
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Block {
+    /// Statements in a block
+    pub stmts: Vec<Stmt>,
+    /// Distinguishes between `unsafe { ... }` and `{ ... }`
+    pub rules: BlockCheckMode,
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum BlockCheckMode {
+    Default,
+    Unsafe,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Stmt {
+    /// A local (let) binding.
+    Local(Box<Local>),
+
+    /// An item definition.
+    Item(Box<Item>),
+
+    /// Expr without trailing semi-colon.
+    Expr(Box<Expr>),
+
+    Semi(Box<Expr>),
+
+    Mac(Box<(Mac, MacStmtStyle, Vec<Attribute>)>),
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum MacStmtStyle {
+    /// The macro statement had a trailing semicolon, e.g. `foo! { ... };`
+    /// `foo!(...);`, `foo![...];`
+    Semicolon,
+    /// The macro statement had braces; e.g. foo! { ... }
+    Braces,
+    /// The macro statement had parentheses or brackets and no semicolon; e.g.
+    /// `foo!(...)`. All of these will end up being converted into macro
+    /// expressions.
+    NoBraces,
+}
+
+/// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Local {
+    pub pat: Box<Pat>,
+    pub ty: Option<Box<Ty>>,
+    /// Initializer expression to set the value, if any
+    pub init: Option<Box<Expr>>,
+    pub attrs: Vec<Attribute>,
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum BinOp {
+    /// The `+` operator (addition)
+    Add,
+    /// The `-` operator (subtraction)
+    Sub,
+    /// The `*` operator (multiplication)
+    Mul,
+    /// The `/` operator (division)
+    Div,
+    /// The `%` operator (modulus)
+    Rem,
+    /// The `&&` operator (logical and)
+    And,
+    /// The `||` operator (logical or)
+    Or,
+    /// The `^` operator (bitwise xor)
+    BitXor,
+    /// The `&` operator (bitwise and)
+    BitAnd,
+    /// The `|` operator (bitwise or)
+    BitOr,
+    /// The `<<` operator (shift left)
+    Shl,
+    /// The `>>` operator (shift right)
+    Shr,
+    /// The `==` operator (equality)
+    Eq,
+    /// The `<` operator (less than)
+    Lt,
+    /// The `<=` operator (less than or equal to)
+    Le,
+    /// The `!=` operator (not equal to)
+    Ne,
+    /// The `>=` operator (greater than or equal to)
+    Ge,
+    /// The `>` operator (greater than)
+    Gt,
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum UnOp {
+    /// The `*` operator for dereferencing
+    Deref,
+    /// The `!` operator for logical inversion
+    Not,
+    /// The `-` operator for negation
+    Neg,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Pat {
+    /// Represents a wildcard pattern (`_`)
+    Wild,
+
+    /// A `PatKind::Ident` may either be a new bound variable (`ref mut binding @ OPT_SUBPATTERN`),
+    /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
+    /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
+    /// during name resolution.
+    Ident(BindingMode, Ident, Option<Box<Pat>>),
+
+    /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
+    /// The `bool` is `true` in the presence of a `..`.
+    Struct(Path, Vec<FieldPat>, bool),
+
+    /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    TupleStruct(Path, Vec<Pat>, Option<usize>),
+
+    /// A possibly qualified path pattern.
+    /// Unquailfied path patterns `A::B::C` can legally refer to variants, structs, constants
+    /// or associated constants. Quailfied path patterns `<A>::B::C`/`<A as Trait>::B::C` can
+    /// only legally refer to associated constants.
+    Path(Option<QSelf>, Path),
+
+    /// A tuple pattern `(a, b)`.
+    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    Tuple(Vec<Pat>, Option<usize>),
+    /// A `box` pattern
+    Box(Box<Pat>),
+    /// A reference pattern, e.g. `&mut (a, b)`
+    Ref(Box<Pat>, Mutability),
+    /// A literal
+    Lit(Box<Expr>),
+    /// A range pattern, e.g. `1...2`
+    Range(Box<Expr>, Box<Expr>),
+    /// `[a, b, ..i, y, z]` is represented as:
+    ///     `PatKind::Vec(box [a, b], Some(i), box [y, z])`
+    Vec(Vec<Pat>, Option<Box<Pat>>, Vec<Pat>),
+    /// A macro pattern; pre-expansion
+    Mac(Mac),
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Arm {
+    pub attrs: Vec<Attribute>,
+    pub pats: Vec<Pat>,
+    pub guard: Option<Box<Expr>>,
+    pub body: Box<Expr>,
+}
+
+/// A capture clause
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum CaptureBy {
+    Value,
+    Ref,
+}
+
+/// Limit types of a range (inclusive or exclusive)
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum RangeLimits {
+    /// Inclusive at the beginning, exclusive at the end
+    HalfOpen,
+    /// Inclusive at the beginning and end
+    Closed,
+}
+
+/// A single field in a struct pattern
+///
+/// Patterns like the fields of Foo `{ x, ref y, ref mut z }`
+/// are treated the same as` x: x, y: ref y, z: ref mut z`,
+/// except is_shorthand is true
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct FieldPat {
+    /// The identifier for the field
+    pub ident: Ident,
+    /// The pattern the field is destructured to
+    pub pat: Box<Pat>,
+    pub is_shorthand: bool,
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum BindingMode {
+    ByRef(Mutability),
+    ByValue(Mutability),
+}
+
+#[cfg(feature = "parsing")]
+pub mod parsing {
+    use super::*;
+    use lit::parsing::lit;
+
+    named!(expr -> Expr, alt!(
+        // TODO other expressions
+        lit => { Expr::Lit }
+    ));
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+    use super::*;
+    use quote::{Tokens, ToTokens};
+
+    impl ToTokens for Expr {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            match *self {
+                Expr::Lit(ref lit) => lit.to_tokens(tokens),
+                _ => unimplemented!(),
+            }
+        }
+    }
+}
diff --git a/src/item.rs b/src/item.rs
index 3ed5f8ad..4a8a394b 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -20,6 +20,8 @@ pub struct Variant {
     pub ident: Ident,
     pub attrs: Vec<Attribute>,
     pub data: VariantData,
+    /// Explicit discriminant, e.g. `Foo = 1`
+    pub discriminant: Option<Discriminant>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -53,12 +55,19 @@ pub enum Visibility {
     Inherited,
 }
 
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct Discriminant {
+    pub value: u64,
+    pub ty: IntTy,
+}
+
 #[cfg(feature = "parsing")]
 pub mod parsing {
     use super::*;
     use attr::parsing::attribute;
     use generics::parsing::generics;
     use ident::parsing::ident;
+    use lit::parsing::int;
     use ty::parsing::ty;
     use nom::multispace;
 
@@ -116,10 +125,12 @@ pub mod parsing {
             |
             epsilon!() => { |_| VariantData::Unit }
         ) >>
+        disr: option!(preceded!(punct!("="), discriminant)) >>
         (Variant {
             ident: id,
             attrs: attrs,
             data: data,
+            discriminant: disr,
         })
     ));
 
@@ -174,11 +185,20 @@ pub mod parsing {
         |
         epsilon!() => { |_| Visibility::Inherited }
     ));
+
+    named!(discriminant -> Discriminant, map!(
+        int,
+        |(value, ty)| Discriminant {
+            value: value,
+            ty: ty,
+        }
+    ));
 }
 
 #[cfg(feature = "printing")]
 mod printing {
     use super::*;
+    use lit::Lit;
     use quote::{Tokens, ToTokens};
 
     impl ToTokens for Item {
@@ -230,6 +250,10 @@ mod printing {
             }
             self.ident.to_tokens(tokens);
             variant_data_to_tokens(&self.data, tokens);
+            if let Some(ref disr) = self.discriminant {
+                tokens.append("=");
+                disr.to_tokens(tokens);
+            }
         }
     }
 
@@ -249,6 +273,12 @@ mod printing {
         }
     }
 
+    impl ToTokens for Discriminant {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            Lit::Int(self.value, self.ty).to_tokens(tokens);
+        }
+    }
+
     fn variant_data_to_tokens(data: &VariantData, tokens: &mut Tokens) {
         match *data {
             VariantData::Struct(ref fields) => {
diff --git a/src/lib.rs b/src/lib.rs
index 2c1be2f9..9ebe7286 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,6 +16,26 @@ pub use attr::{
     MetaItem,
 };
 
+#[cfg(feature = "full")]
+mod expr;
+#[cfg(feature = "full")]
+pub use expr::{
+    Arm,
+    BinOp,
+    BindingMode,
+    Block,
+    BlockCheckMode,
+    CaptureBy,
+    Expr,
+    FieldPat,
+    Local,
+    MacStmtStyle,
+    Pat,
+    RangeLimits,
+    Stmt,
+    UnOp,
+};
+
 mod generics;
 pub use generics::{
     Generics,
@@ -38,6 +58,7 @@ pub use ident::{
 mod item;
 pub use item::{
     Body,
+    Discriminant,
     Field,
     Item,
     Variant,
@@ -45,6 +66,27 @@ pub use item::{
     Visibility,
 };
 
+mod lit;
+pub use lit::{
+    FloatTy,
+    IntTy,
+    Lit,
+    StrStyle,
+};
+
+#[cfg(feature = "full")]
+mod mac;
+#[cfg(feature = "full")]
+pub use mac::{
+    BinOpToken,
+    DelimToken,
+    Delimited,
+    Mac,
+    SequenceRepetition,
+    Token,
+    TokenTree,
+};
+
 mod ty;
 pub use ty::{
     AngleBracketedParameterData,
diff --git a/src/lit.rs b/src/lit.rs
new file mode 100644
index 00000000..92d53399
--- /dev/null
+++ b/src/lit.rs
@@ -0,0 +1,167 @@
+/// Literal kind.
+///
+/// E.g. `"foo"`, `42`, `12.34` or `bool`
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Lit {
+    /// A string literal (`"foo"`)
+    Str(String, StrStyle),
+    /// A byte string (`b"foo"`)
+    ByteStr(Vec<u8>),
+    /// A byte char (`b'f'`)
+    Byte(u8),
+    /// A character literal (`'a'`)
+    Char(char),
+    /// An integer literal (`1`)
+    Int(u64, IntTy),
+    /// A float literal (`1f64` or `1E10f64` or `1.0E10`)
+    Float(String, FloatTy),
+    /// A boolean literal
+    Bool(bool),
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum StrStyle {
+    /// A regular string, like `"foo"`
+    Cooked,
+    /// A raw string, like `r##"foo"##`
+    ///
+    /// The uint is the number of `#` symbols used
+    Raw(usize)
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum IntTy {
+    Isize,
+    I8,
+    I16,
+    I32,
+    I64,
+    Usize,
+    U8,
+    U16,
+    U32,
+    U64,
+    Unsuffixed
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum FloatTy {
+    F32,
+    F64,
+    Unsuffixed,
+}
+
+#[cfg(feature = "parsing")]
+pub mod parsing {
+    use super::*;
+    use escape::escaped_string;
+    use nom::{IResult, multispace};
+
+    named!(pub lit -> Lit, alt!(
+        // TODO other literals
+        quoted => { |q| Lit::Str(q, StrStyle::Cooked) }
+        |
+        int => { |(value, ty)| Lit::Int(value, ty) }
+    ));
+
+    named!(quoted -> String, delimited!(
+        punct!("\""),
+        escaped_string,
+        tag!("\"")
+    ));
+
+    named!(pub int -> (u64, IntTy), preceded!(
+        option!(multispace),
+        tuple!(
+            digits,
+            alt!(
+                tag!("isize") => { |_| IntTy::Isize }
+                |
+                tag!("i8") => { |_| IntTy::I8 }
+                |
+                tag!("i16") => { |_| IntTy::I16 }
+                |
+                tag!("i32") => { |_| IntTy::I32 }
+                |
+                tag!("i64") => { |_| IntTy::I64 }
+                |
+                tag!("usize") => { |_| IntTy::Usize }
+                |
+                tag!("u8") => { |_| IntTy::U8 }
+                |
+                tag!("u16") => { |_| IntTy::U16 }
+                |
+                tag!("u32") => { |_| IntTy::U32 }
+                |
+                tag!("u64") => { |_| IntTy::U64 }
+                |
+                epsilon!() => { |_| IntTy::Unsuffixed }
+            )
+        )
+    ));
+
+    fn digits(input: &str) -> IResult<&str, u64> {
+        let mut value = 0u64;
+        let mut len = 0;
+        let mut bytes = input.bytes().peekable();
+        while let Some(&b) = bytes.peek() {
+            match b {
+                b'0' ... b'9' => {
+                    value = match value.checked_mul(10) {
+                        Some(value) => value,
+                        None => return IResult::Error,
+                    };
+                    value = match value.checked_add((b - b'0') as u64) {
+                        Some(value) => value,
+                        None => return IResult::Error,
+                    };
+                    bytes.next();
+                    len += 1;
+                }
+                _ => {
+                    return if len > 0 {
+                        IResult::Done(&input[len..], value)
+                    } else {
+                        IResult::Error
+                    };
+                },
+            }
+        }
+        IResult::Error
+    }
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+    use super::*;
+    use quote::{Tokens, ToTokens};
+    use std::fmt::{self, Display};
+
+    impl ToTokens for Lit {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            match *self {
+                Lit::Str(ref s, StrStyle::Cooked) => s.to_tokens(tokens),
+                Lit::Int(value, ty) => tokens.append(&format!("{}{}", value, ty)),
+                _ => unimplemented!(),
+            }
+        }
+    }
+
+    impl Display for IntTy {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+            match *self {
+                IntTy::Isize => formatter.write_str("isize"),
+                IntTy::I8 => formatter.write_str("i8"),
+                IntTy::I16 => formatter.write_str("i16"),
+                IntTy::I32 => formatter.write_str("i32"),
+                IntTy::I64 => formatter.write_str("i64"),
+                IntTy::Usize => formatter.write_str("usize"),
+                IntTy::U8 => formatter.write_str("u8"),
+                IntTy::U16 => formatter.write_str("u16"),
+                IntTy::U32 => formatter.write_str("u32"),
+                IntTy::U64 => formatter.write_str("u64"),
+                IntTy::Unsuffixed => Ok(()),
+            }
+        }
+    }
+}
diff --git a/src/mac.rs b/src/mac.rs
new file mode 100644
index 00000000..42d116fd
--- /dev/null
+++ b/src/mac.rs
@@ -0,0 +1,148 @@
+use super::*;
+
+/// Represents a macro invocation. The Path indicates which macro
+/// is being invoked, and the vector of token-trees contains the source
+/// of the macro invocation.
+///
+/// NB: the additional ident for a macro_rules-style macro is actually
+/// stored in the enclosing item. Oog.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Mac {
+    pub path: Path,
+    pub tts: Vec<TokenTree>,
+}
+
+/// When the main rust parser encounters a syntax-extension invocation, it
+/// parses the arguments to the invocation as a token-tree. This is a very
+/// loose structure, such that all sorts of different AST-fragments can
+/// be passed to syntax extensions using a uniform type.
+///
+/// If the syntax extension is an MBE macro, it will attempt to match its
+/// LHS token tree against the provided token tree, and if it finds a
+/// match, will transcribe the RHS token tree, splicing in any captured
+/// macro_parser::matched_nonterminals into the `SubstNt`s it finds.
+///
+/// The RHS of an MBE macro is the only place `SubstNt`s are substituted.
+/// Nothing special happens to misnamed or misplaced `SubstNt`s.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum TokenTree {
+    /// A single token
+    Token(Token),
+    /// A delimited sequence of token trees
+    Delimited(Delimited),
+
+    // This only makes sense in MBE macros.
+    /// A kleene-style repetition sequence with a span
+    Sequence(SequenceRepetition),
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Delimited {
+    /// The type of delimiter
+    pub delim: DelimToken,
+    /// The delimited sequence of token trees
+    pub tts: Vec<TokenTree>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct SequenceRepetition {
+    /// The sequence of token trees
+    pub tts: Vec<TokenTree>,
+    /// The optional separator
+    pub separator: Option<Token>,
+    /// Whether the sequence can be repeated zero (*), or one or more times (+)
+    pub op: KleeneOp,
+    /// The number of `MatchNt`s that appear in the sequence (and subsequences)
+    pub num_captures: usize,
+}
+
+/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
+/// for token sequences.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum KleeneOp {
+    ZeroOrMore,
+    OneOrMore,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Token {
+    /* Expression-operator symbols. */
+    Eq,
+    Lt,
+    Le,
+    EqEq,
+    Ne,
+    Ge,
+    Gt,
+    AndAnd,
+    OrOr,
+    Not,
+    Tilde,
+    BinOp(BinOpToken),
+    BinOpEq(BinOpToken),
+
+    /* Structural symbols */
+    At,
+    Dot,
+    DotDot,
+    DotDotDot,
+    Comma,
+    Semi,
+    Colon,
+    ModSep,
+    RArrow,
+    LArrow,
+    FatArrow,
+    Pound,
+    Dollar,
+    Question,
+    /// An opening delimiter, eg. `{`
+    OpenDelim(DelimToken),
+    /// A closing delimiter, eg. `}`
+    CloseDelim(DelimToken),
+
+    /* Literals */
+    Literal(Lit, Option<String>),
+
+    /* Name components */
+    Ident(Ident),
+    Underscore,
+    Lifetime(Ident),
+
+    // Can be expanded into several tokens.
+    /// Doc comment
+    DocComment(String),
+    // In left-hand-sides of MBE macros:
+    /// Parse a nonterminal (name to bind, name of NT)
+    MatchNt(Ident, Ident),
+    // In right-hand-sides of MBE macros:
+    /// A syntactic variable that will be filled in by macro expansion.
+    SubstNt(Ident),
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum BinOpToken {
+    Plus,
+    Minus,
+    Star,
+    Slash,
+    Percent,
+    Caret,
+    And,
+    Or,
+    Shl,
+    Shr,
+}
+
+/// A delimiter token
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum DelimToken {
+    /// A round parenthesis: `(` or `)`
+    Paren,
+    /// A square bracket: `[` or `]`
+    Bracket,
+    /// A curly brace: `{` or `}`
+    Brace,
+    /// An empty delimiter
+    NoDelim,
+}
diff --git a/src/ty.rs b/src/ty.rs
index e87f7409..cdfa6977 100644
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -40,7 +40,7 @@ pub struct MutTy {
     pub mutability: Mutability,
 }
 
-#[derive(Debug, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum Mutability {
     Mutable,
     Immutable,


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index c82533a6..86a06ca3 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -87,6 +87,7 @@ fn test_enum() {
         pub enum Result<T, E> {
             Ok(T),
             Err(E),
+            Surprise = 0isize,
         }
     ";
 
@@ -97,7 +98,10 @@ fn test_enum() {
             Attribute {
                 value: MetaItem::NameValue(
                     "doc".into(),
-                    "/// See the std::result module documentation for details.".into(),
+                    Lit::Str(
+                        "/// See the std::result module documentation for details.".into(),
+                        StrStyle::Cooked,
+                    ),
                 ),
                 is_sugared_doc: true,
             },
@@ -136,6 +140,7 @@ fn test_enum() {
                         ty: simple_ty("T"),
                     },
                 ]),
+                discriminant: None,
             },
             Variant {
                 ident: "Err".into(),
@@ -148,6 +153,16 @@ fn test_enum() {
                         ty: simple_ty("E"),
                     },
                 ]),
+                discriminant: None,
+            },
+            Variant {
+                ident: "Surprise".into(),
+                attrs: Vec::new(),
+                data: VariantData::Unit,
+                discriminant: Some(Discriminant {
+                    value: 0,
+                    ty: IntTy::Isize,
+                }),
             },
         ]),
     };