Pare down the Synom trait

I would like to make it clearer that parsing a string is second-class
functionality compared to parsing tokens.
diff --git a/src/lib.rs b/src/lib.rs
index a19581d..92cb9b2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@
 
 #![cfg_attr(feature = "cargo-clippy", allow(large_enum_variant))]
 
+extern crate proc_macro;
 extern crate proc_macro2;
 extern crate unicode_xid;
 
@@ -62,9 +63,9 @@
                ArgSelf, ArgCaptured};
 
 #[cfg(feature = "full")]
-mod krate;
+mod file;
 #[cfg(feature = "full")]
-pub use krate::Crate;
+pub use file::File;
 
 mod lifetime;
 pub use lifetime::Lifetime;
@@ -100,45 +101,177 @@
 #[cfg(feature = "fold")]
 pub mod fold;
 
+////////////////////////////////////////////////////////////////////////////////
+
 #[cfg(feature = "parsing")]
-mod parsing {
-    use std::str::FromStr;
+pub use synom::ParseError;
 
-    use super::*;
-    use synom::{Synom, ParseError};
-    use proc_macro2::TokenStream;
+#[cfg(feature = "parsing")]
+use synom::{Synom, SynomBuffer};
 
-    macro_rules! traits {
-        ($($ty:ident,)*) => ($(
-            impl From<TokenStream> for $ty {
-                fn from(stream: TokenStream) -> $ty {
-                    $ty::parse_all_unwrap(stream)
-                }
+/// Parse tokens of source code into the chosen syn data type.
+///
+/// This is preferred over parsing a string because tokens are able to preserve
+/// information about where in the user's code they were originally written (the
+/// "span" of the token), possibly allowing the compiler to produce better error
+/// messages.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// extern crate proc_macro;
+/// use proc_macro::TokenStream;
+///
+/// extern crate syn;
+///
+/// #[macro_use]
+/// extern crate quote;
+///
+/// use syn::DeriveInput;
+///
+/// #[proc_macro_derive(MyMacro)]
+/// pub fn my_macro(input: TokenStream) -> TokenStream {
+///     // Parse the tokens into a syntax tree
+///     let ast: DeriveInput = syn::parse(input).unwrap();
+///
+///     // Build the output, possibly using quasi-quotation
+///     let expanded = quote! {
+///         /* ... */
+///     };
+///
+///     // Parse back to a token stream and return it
+///     expanded.parse().unwrap()
+/// }
+/// ```
+#[cfg(feature = "parsing")]
+pub fn parse<T>(tokens: proc_macro::TokenStream) -> Result<T, ParseError>
+    where T: Synom,
+{
+    _parse(tokens.into())
+}
+
+#[cfg(feature = "parsing")]
+fn _parse<T>(tokens: proc_macro2::TokenStream) -> Result<T, ParseError>
+    where T: Synom,
+{
+    let buf = SynomBuffer::new(tokens);
+    let result = T::parse(buf.begin());
+    let err = match result {
+        Ok((rest, t)) => {
+            if rest.eof() {
+                return Ok(t);
+            } else if rest == buf.begin() {
+                // parsed nothing
+                ParseError::new("failed to parse anything")
+            } else {
+                ParseError::new("failed to parse all tokens")
             }
+        }
+        Err(err) => err,
+    };
+    match T::description() {
+        Some(s) => Err(ParseError::new(format!("parsing {}: {}", s, err))),
+        None => Err(err),
+    }
+}
 
-            impl FromStr for $ty {
-                type Err = ParseError;
+/// Parse a string of Rust code into the chosen syn data type.
+///
+/// # Examples
+///
+/// ```rust
+/// extern crate syn;
+/// #
+/// # #[macro_use]
+/// # extern crate error_chain;
+///
+/// use syn::Expr;
+/// #
+/// # error_chain! {
+/// #     foreign_links {
+/// #         Syn(syn::ParseError);
+/// #     }
+/// # }
+///
+/// fn run() -> Result<()> {
+///     let code = "assert_eq!(u8::max_value(), 255)";
+///     let expr = syn::parse_str::<Expr>(code)?;
+///     println!("{:#?}", expr);
+///     Ok(())
+/// }
+/// #
+/// # fn main() { run().unwrap() }
+/// ```
+#[cfg(feature = "parsing")]
+pub fn parse_str<T: Synom>(s: &str) -> Result<T, ParseError> {
+    _parse(s.parse()?)
+}
 
-                fn from_str(s: &str) -> Result<Self, Self::Err> {
-                    $ty::parse_str_all(s)
-                }
-            }
-        )*)
+// FIXME the name parse_file makes it sound like you might pass in a path to a
+// file, rather than the content.
+/// Parse the content of a file of Rust code.
+///
+/// This is different from `syn::parse_str::<File>(content)` in two ways:
+///
+/// - It discards a leading byte order mark `\u{FEFF}` if the file has one.
+/// - It preserves the shebang line of the file, such as `#!/usr/bin/env rustx`.
+///
+/// If present, either of these would be an error using `from_str`.
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// extern crate syn;
+/// #
+/// # #[macro_use]
+/// # extern crate error_chain;
+///
+/// use std::fs::File;
+/// use std::io::Read;
+/// #
+/// # error_chain! {
+/// #     foreign_links {
+/// #         Io(std::io::Error);
+/// #         Syn(syn::ParseError);
+/// #     }
+/// # }
+///
+/// fn run() -> Result<()> {
+///     let mut file = File::open("path/to/code.rs")?;
+///     let mut content = String::new();
+///     file.read_to_string(&mut content)?;
+///
+///     let ast = syn::parse_file(&content)?;
+///     if let Some(shebang) = ast.shebang {
+///         println!("{}", shebang);
+///     }
+///     println!("{} items", ast.items.len());
+///
+///     Ok(())
+/// }
+/// #
+/// # fn main() { run().unwrap() }
+/// ```
+#[cfg(all(feature = "parsing", feature = "full"))]
+pub fn parse_file(mut content: &str) -> Result<File, ParseError> {
+    // Strip the BOM if it is present
+    const BOM: &'static str = "\u{feff}";
+    if content.starts_with(BOM) {
+        content = &content[BOM.len()..];
     }
 
-    traits! {
-        DeriveInput,
-        TyParamBound,
-        Ident,
-        WhereClause,
-        Ty,
-        Lit,
+    let mut shebang = None;
+    if content.starts_with("#!") && !content.starts_with("#![") {
+        if let Some(idx) = content.find('\n') {
+            shebang = Some(content[..idx].to_string());
+            content = &content[idx..];
+        } else {
+            shebang = Some(content.to_string());
+            content = "";
+        }
     }
 
-    #[cfg(feature = "full")]
-    traits! {
-        Expr,
-        Item,
-        Crate,
-    }
+    let mut file: File = parse_str(content)?;
+    file.shebang = shebang;
+    Ok(file)
 }