Provide a trait to invoke parser functions
diff --git a/src/lib.rs b/src/lib.rs
index afe414f..98aea1d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -509,9 +509,7 @@
////////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "parsing")]
-use synom::Synom;
-#[cfg(feature = "parsing")]
-use buffer::TokenBuffer;
+use synom::{Synom, Parser};
#[cfg(feature = "parsing")]
mod error;
@@ -594,25 +592,13 @@
where
T: Synom,
{
- let buf = TokenBuffer::new2(tokens);
- let result = T::parse(buf.begin());
- let err = match result {
- Ok((t, rest)) => {
- 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")
- }
+ let parser = T::parse;
+ parser.parse2(tokens).map_err(|err| {
+ match T::description() {
+ Some(s) => ParseError::new(format!("failed to parse {}: {}", s, err)),
+ None => err,
}
- Err(err) => err,
- };
- match T::description() {
- Some(s) => Err(ParseError::new(format!("failed to parse {}: {}", s, err))),
- None => Err(err),
- }
+ })
}
/// Parse a string of Rust code into the chosen syntax tree node.
diff --git a/src/synom.rs b/src/synom.rs
index d14c3f8..5db92d3 100644
--- a/src/synom.rs
+++ b/src/synom.rs
@@ -8,14 +8,115 @@
//! Parsing interface for parsing a token stream into a syntax tree node.
//!
-//! All syntax tree nodes that can be parsed from a token stream need to
-//! implement the [`Synom`] trait. This trait is usually implemented using the
-//! [`nom`]-style parser combinator macros provided by Syn, but may also be
-//! implemented without macros be using the low-level [`Cursor`] API directly.
+//! Parsing in Syn is built on parser functions that take in a [`Cursor`] and
+//! produce a [`PResult<T>`] where `T` is some syntax tree node. `Cursor` is a
+//! cheaply copyable cursor over a range of tokens in a token stream, and
+//! `PResult` is a result that packages together a parsed syntax tree node `T`
+//! with a stream of remaining unparsed tokens after `T` represented as another
+//! `Cursor`, or a [`ParseError`] if parsing failed.
//!
-//! [`Synom`]: trait.Synom.html
-//! [`nom`]: https://github.com/Geal/nom
//! [`Cursor`]: ../buffer/index.html
+//! [`PResult<T>`]: type.PResult.html
+//! [`ParseError`]: struct.ParseError.html
+//!
+//! This `Cursor`- and `PResult`-based interface is convenient for parser
+//! combinators and parser implementations, but not necessarily when you just
+//! have some tokens that you want to parse. For that we expose the following
+//! two entry points.
+//!
+//! ## The `syn::parse*` functions
+//!
+//! The [`syn::parse`], [`syn::parse2`], and [`syn::parse_str`] functions serve
+//! as an entry point for parsing syntax tree nodes that can be parsed in an
+//! obvious default way. These functions can return any syntax tree node that
+//! implements the [`Synom`] trait, which includes most types in Syn.
+//!
+//! [`syn::parse`]: ../fn.parse.html
+//! [`syn::parse2`]: ../fn.parse2.html
+//! [`syn::parse_str`]: ../fn.parse_str.html
+//! [`Synom`]: trait.Synom.html
+//!
+//! ```
+//! use syn::Type;
+//!
+//! # fn run_parser() -> Result<(), syn::synom::ParseError> {
+//! let t: Type = syn::parse_str("std::collections::HashMap<String, Value>")?;
+//! # Ok(())
+//! # }
+//! #
+//! # fn main() {
+//! # run_parser().unwrap();
+//! # }
+//! ```
+//!
+//! ## The `Parser` trait
+//!
+//! Some types can be parsed in several ways depending on context. For example
+//! an [`Attribute`] can be either "outer" like `#[...]` or "inner" like
+//! `#![...]` and parsing the wrong one would be a bug. Similarly [`Punctuated`]
+//! may or may not allow trailing punctuation, and parsing it the wrong way
+//! would either reject valid input or accept invalid input.
+//!
+//! [`Attribute`]: ../struct.Attribute.html
+//! [`Punctuated`]: ../punctuated/index.html
+//!
+//! The `Synom` trait is not implemented in these cases because there is no good
+//! behavior to consider the default.
+//!
+//! ```ignore
+//! // Can't parse `Punctuated` without knowing whether trailing punctuation
+//! // should be allowed in this context.
+//! let path: Punctuated<PathSegment, Token![::]> = syn::parse(tokens)?;
+//! ```
+//!
+//! In these cases the types provide a choice of parser functions rather than a
+//! single `Synom` implementation, and those parser functions can be invoked
+//! through the [`Parser`] trait.
+//!
+//! [`Parser`]: trait.Parser.html
+//!
+//! ```
+//! # #[macro_use]
+//! # extern crate syn;
+//! #
+//! # extern crate proc_macro2;
+//! # use proc_macro2::TokenStream;
+//! #
+//! use syn::synom::Parser;
+//! use syn::punctuated::Punctuated;
+//! use syn::{PathSegment, Expr, Attribute};
+//!
+//! # fn run_parsers() -> Result<(), syn::synom::ParseError> {
+//! # let tokens = TokenStream::empty().into();
+//! // Parse a nonempty sequence of path segments separated by `::` punctuation
+//! // with no trailing punctuation.
+//! let parser = Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty;
+//! let path = parser.parse(tokens)?;
+//!
+//! # let tokens = TokenStream::empty().into();
+//! // Parse a possibly empty sequence of expressions terminated by commas with
+//! // an optional trailing punctuation.
+//! let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
+//! let args = parser.parse(tokens)?;
+//!
+//! # let tokens = TokenStream::empty().into();
+//! // Parse zero or more outer attributes but not inner attributes.
+//! named!(outer_attrs -> Vec<Attribute>, many0!(Attribute::parse_outer));
+//! let attrs = outer_attrs.parse(tokens)?;
+//! #
+//! # Ok(())
+//! # }
+//! #
+//! # fn main() {}
+//! ```
+//!
+//! # Implementing a parser function
+//!
+//! Parser functions are usually implemented using the [`nom`]-style parser
+//! combinator macros provided by Syn, but may also be implemented without
+//! macros be using the low-level [`Cursor`] API directly.
+//!
+//! [`nom`]: https://github.com/Geal/nom
//!
//! The following parser combinator macros are available and a `Synom` parsing
//! example is provided for each one.
@@ -44,14 +145,15 @@
//!
//! *This module is available if Syn is built with the `"parsing"` feature.*
-use proc_macro2::TokenStream;
+use proc_macro;
+use proc_macro2;
pub use error::{PResult, ParseError};
-use buffer::Cursor;
+use buffer::{Cursor, TokenBuffer};
-/// Parsing interface implemented by all types that can be parsed from a token
-/// stream.
+/// Parsing interface implemented by all types that can be parsed in a default
+/// way from a token stream.
///
/// Refer to the [module documentation] for details about parsing in Syn.
///
@@ -66,7 +168,7 @@
}
}
-impl Synom for TokenStream {
+impl Synom for proc_macro2::TokenStream {
fn parse(input: Cursor) -> PResult<Self> {
Ok((input.token_stream(), Cursor::empty()))
}
@@ -75,3 +177,44 @@
Some("arbitrary token stream")
}
}
+
+/// Parser that can parse Rust tokens into a particular syntax tree node.
+///
+/// Refer to the [module documentation] for details about parsing in Syn.
+///
+/// [module documentation]: index.html
+///
+/// *This trait is available if Syn is built with the `"parsing"` feature.*
+pub trait Parser: Sized {
+ type Output;
+
+ fn parse2(self, tokens: proc_macro2::TokenStream) -> Result<Self::Output, ParseError>;
+
+ fn parse(self, tokens: proc_macro::TokenStream) -> Result<Self::Output, ParseError> {
+ self.parse2(tokens.into())
+ }
+
+ fn parse_str(self, s: &str) -> Result<Self::Output, ParseError> {
+ match s.parse() {
+ Ok(tts) => self.parse2(tts),
+ Err(_) => Err(ParseError::new("error while lexing input string")),
+ }
+ }
+}
+
+impl<F, T> Parser for F where F: FnOnce(Cursor) -> PResult<T> {
+ type Output = T;
+
+ fn parse2(self, tokens: proc_macro2::TokenStream) -> Result<T, ParseError> {
+ let buf = TokenBuffer::new2(tokens);
+ let (t, rest) = self(buf.begin())?;
+ if rest.eof() {
+ Ok(t)
+ } else if rest == buf.begin() {
+ // parsed nothing
+ Err(ParseError::new("failed to parse anything"))
+ } else {
+ Err(ParseError::new("failed to parse all tokens"))
+ }
+ }
+}