Parse paths
diff --git a/src/data.rs b/src/data.rs
index b87eaf1..36ba136 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -303,13 +303,7 @@
}
fn parse_crate(input: ParseStream) -> Result<Self> {
- let followed_by_colons = {
- let ahead = input.fork();
- ahead.parse::<Token![crate]>()?;
- ahead.peek(Token![::])
- };
-
- if followed_by_colons {
+ if input.peek2(Token![::]) {
Ok(Visibility::Inherited)
} else {
Ok(Visibility::Crate(VisCrate {
diff --git a/src/derive.rs b/src/derive.rs
index 7a1e406..7a8b94b 100644
--- a/src/derive.rs
+++ b/src/derive.rs
@@ -183,7 +183,13 @@
}
}
- fn data_enum(input: ParseStream) -> Result<(Option<WhereClause>, token::Brace, Punctuated<Variant, Token![,]>)> {
+ fn data_enum(
+ input: ParseStream,
+ ) -> Result<(
+ Option<WhereClause>,
+ token::Brace,
+ Punctuated<Variant, Token![,]>,
+ )> {
let where_clause = if input.peek(Token![where]) {
Some(input.parse_synom(WhereClause::parse)?)
} else {
diff --git a/src/ident.rs b/src/ident.rs
new file mode 100644
index 0000000..df4386b
--- /dev/null
+++ b/src/ident.rs
@@ -0,0 +1,11 @@
+#[cfg(feature = "parsing")]
+use lookahead;
+
+pub use proc_macro2::Ident;
+
+#[cfg(feature = "parsing")]
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub fn Ident(marker: lookahead::TokenMarker) -> Ident {
+ match marker {}
+}
diff --git a/src/lib.rs b/src/lib.rs
index 54d81b2..266d7b8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -316,7 +316,8 @@
#[macro_use]
pub mod token;
-pub use proc_macro2::Ident;
+mod ident;
+pub use ident::Ident;
#[cfg(any(feature = "full", feature = "derive"))]
mod attr;
diff --git a/src/lifetime.rs b/src/lifetime.rs
index 4710ec5..c8101eb 100644
--- a/src/lifetime.rs
+++ b/src/lifetime.rs
@@ -13,6 +13,8 @@
use proc_macro2::{Ident, Span};
use unicode_xid::UnicodeXID;
+#[cfg(feature = "parsing")]
+use lookahead;
use token::Apostrophe;
/// A Rust lifetime: `'a`.
@@ -107,6 +109,13 @@
}
#[cfg(feature = "parsing")]
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
+ match marker {}
+}
+
+#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
use parse::{Error, Parse, ParseStream, Result};
diff --git a/src/lit.rs b/src/lit.rs
index 299704d..a25d2a3 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -22,6 +22,9 @@
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
+#[cfg(feature = "parsing")]
+use lookahead;
+
ast_enum_of_structs! {
/// A Rust literal such as a string or integer or boolean.
///
@@ -411,45 +414,49 @@
}
#[cfg(feature = "parsing")]
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub fn Lit(marker: lookahead::TokenMarker) -> Lit {
+ match marker {}
+}
+
+#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use buffer::Cursor;
+ use parse::{Parse, ParseStream, Result};
use parse_error;
- use synom::PResult;
use synom::Synom;
- impl Synom for Lit {
- fn parse(input: Cursor) -> PResult<Self> {
- match input.literal() {
- Some((lit, rest)) => {
- if lit.to_string().starts_with('/') {
- // Doc comment literal which is not a Syn literal
- parse_error()
- } else {
- Ok((Lit::new(lit), rest))
+ impl Parse for Lit {
+ fn parse(input: ParseStream) -> Result<Self> {
+ input.step_cursor(|cursor| {
+ match cursor.literal() {
+ Some((lit, rest)) => {
+ if lit.to_string().starts_with('/') {
+ // Doc comment literal which is not a Syn literal
+ parse_error()
+ } else {
+ Ok((Lit::new(lit), rest))
+ }
}
+ _ => match cursor.ident() {
+ Some((ident, rest)) => Ok((
+ Lit::Bool(LitBool {
+ value: if ident == "true" {
+ true
+ } else if ident == "false" {
+ false
+ } else {
+ return parse_error();
+ },
+ span: ident.span(),
+ }),
+ rest,
+ )),
+ _ => parse_error(),
+ },
}
- _ => match input.ident() {
- Some((ident, rest)) => Ok((
- Lit::Bool(LitBool {
- value: if ident == "true" {
- true
- } else if ident == "false" {
- false
- } else {
- return parse_error();
- },
- span: ident.span(),
- }),
- rest,
- )),
- _ => parse_error(),
- },
- }
- }
-
- fn description() -> Option<&'static str> {
- Some("literal")
+ })
}
}
diff --git a/src/parse.rs b/src/parse.rs
index aa3043d..c833bb7 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -13,6 +13,7 @@
use error;
use punctuated::Punctuated;
use synom::PResult;
+use token::Token;
pub use error::{Error, Result};
pub use lookahead::{Lookahead1, Peek};
@@ -79,8 +80,6 @@
unsafe { mem::transmute::<Cursor<'c>, Cursor<'a>>(other) }
}
- // Not public API.
- #[doc(hidden)]
pub fn error<T: Display>(self, message: T) -> Error {
error::new_at(self.scope, self.cursor, message)
}
@@ -124,6 +123,25 @@
self.lookahead1().peek(token)
}
+ pub fn peek2<T: Peek>(&self, token: T) -> bool {
+ if self.is_empty() {
+ return false;
+ }
+ let ahead = self.fork();
+ ahead.step_cursor(|cursor| Ok(cursor.token_tree().unwrap())).unwrap();
+ ahead.peek(token)
+ }
+
+ pub fn peek3<T: Peek>(&self, token: T) -> bool {
+ if self.is_empty() {
+ return false;
+ }
+ let ahead = self.fork();
+ ahead.step_cursor(|cursor| Ok(cursor.token_tree().unwrap())).unwrap();
+ ahead.step_cursor(|cursor| Ok(cursor.token_tree().unwrap())).unwrap();
+ ahead.peek(token)
+ }
+
pub fn parse_terminated<T, P: Parse>(
&self,
parser: fn(ParseStream) -> Result<T>,
@@ -135,6 +153,10 @@
self.clone()
}
+ pub fn error<T: Display>(&self, message: T) -> Error {
+ error::new_at(self.scope, self.cursor(), message)
+ }
+
// Not public API.
#[doc(hidden)]
pub fn step_cursor<F, R>(&self, function: F) -> Result<R>
@@ -200,14 +222,12 @@
}
}
-// In reality the impl would be for Punctuated.
-impl<T: Parse> Parse for Vec<T> {
+impl<T: Parse + Token> Parse for Option<T> {
fn parse(input: ParseStream) -> Result<Self> {
- let mut vec = Vec::new();
- while !input.is_empty() {
- let t = input.parse::<T>()?;
- vec.push(t);
+ if T::peek(&input.lookahead1()) {
+ Ok(Some(input.parse()?))
+ } else {
+ Ok(None)
}
- Ok(vec)
}
}
diff --git a/src/path.rs b/src/path.rs
index a848944..12c7549 100644
--- a/src/path.rs
+++ b/src/path.rs
@@ -227,122 +227,120 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
+ use parse::{Parse, ParseStream, Result};
+ use synom::ext::IdentExt;
use synom::Synom;
- impl Synom for Path {
- named!(parse -> Self, do_parse!(
- colon: option!(punct!(::)) >>
- segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
- cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn")) >>
- (Path {
- leading_colon: colon,
- segments: segments,
+ impl Parse for Path {
+ fn parse(input: ParseStream) -> Result<Self> {
+ if input.peek(Token![dyn]) {
+ return Err(input.error("expected path"));
+ }
+
+ Ok(Path {
+ leading_colon: input.parse()?,
+ segments: input.parse_synom(Punctuated::parse_separated_nonempty)?,
})
- ));
-
- fn description() -> Option<&'static str> {
- Some("path")
}
}
- #[cfg(not(feature = "full"))]
- impl Synom for GenericArgument {
- named!(parse -> Self, alt!(
- call!(ty_no_eq_after) => { GenericArgument::Type }
- |
- syn!(Lifetime) => { GenericArgument::Lifetime }
- |
- syn!(Binding) => { GenericArgument::Binding }
- ));
- }
+ impl Parse for GenericArgument {
+ fn parse(input: ParseStream) -> Result<Self> {
+ if input.peek(Lifetime) && !input.peek3(Token![+]) {
+ return Ok(GenericArgument::Lifetime(input.parse()?));
+ }
- #[cfg(feature = "full")]
- impl Synom for GenericArgument {
- named!(parse -> Self, alt!(
- call!(ty_no_eq_after) => { GenericArgument::Type }
- |
- syn!(Lifetime) => { GenericArgument::Lifetime }
- |
- syn!(Binding) => { GenericArgument::Binding }
- |
- syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
- |
- syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
- ));
+ if input.peek(Ident) && input.peek2(Token![=]) {
+ return Ok(GenericArgument::Binding(input.parse()?));
+ }
- fn description() -> Option<&'static str> {
- Some("generic argument")
+ #[cfg(feature = "full")]
+ {
+ if input.peek(Lit) {
+ let lit = input.parse_synom(ExprLit::parse)?;
+ return Ok(GenericArgument::Const(Expr::Lit(lit)));
+ }
+
+ if input.peek(token::Brace) {
+ let block = input.parse_synom(ExprBlock::parse)?;
+ return Ok(GenericArgument::Const(Expr::Block(block)));
+ }
+ }
+
+ Ok(GenericArgument::Type(input.parse_synom(ty_no_eq_after)?))
}
}
- impl Synom for AngleBracketedGenericArguments {
- named!(parse -> Self, do_parse!(
- colon2: option!(punct!(::)) >>
- lt: punct!(<) >>
- args: call!(Punctuated::parse_terminated) >>
- gt: punct!(>) >>
- (AngleBracketedGenericArguments {
- colon2_token: colon2,
- lt_token: lt,
- args: args,
- gt_token: gt,
+ impl Parse for AngleBracketedGenericArguments {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Ok(AngleBracketedGenericArguments {
+ colon2_token: input.parse()?,
+ lt_token: input.parse()?,
+ args: {
+ let mut args = Punctuated::new();
+ loop {
+ if input.peek(Token![>]) {
+ break;
+ }
+ let value = input.parse()?;
+ args.push_value(value);
+ if input.peek(Token![>]) {
+ break;
+ }
+ let punct = input.parse()?;
+ args.push_punct(punct);
+ }
+ args
+ },
+ gt_token: input.parse()?,
})
- ));
-
- fn description() -> Option<&'static str> {
- Some("angle bracketed generic arguments")
}
}
- impl Synom for ParenthesizedGenericArguments {
- named!(parse -> Self, do_parse!(
- data: parens!(Punctuated::parse_terminated) >>
- output: call!(ReturnType::without_plus) >>
- (ParenthesizedGenericArguments {
- paren_token: data.0,
- inputs: data.1,
- output: output,
+ impl Parse for ParenthesizedGenericArguments {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let content;
+ Ok(ParenthesizedGenericArguments {
+ paren_token: parenthesized!(content in input),
+ inputs: content.parse_synom(Punctuated::parse_terminated)?,
+ output: input.parse_synom(ReturnType::without_plus)?,
})
- ));
-
- fn description() -> Option<&'static str> {
- Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
}
}
- impl Synom for PathSegment {
- named!(parse -> Self, alt!(
- do_parse!(
- ident: syn!(Ident) >>
- arguments: syn!(AngleBracketedGenericArguments) >>
- (PathSegment {
+ impl Parse for PathSegment {
+ fn parse(input: ParseStream) -> Result<Self> {
+ if input.peek(Token![super])
+ || input.peek(Token![self])
+ || input.peek(Token![Self])
+ || input.peek(Token![crate])
+ || input.peek(Token![extern])
+ {
+ let ident = input.parse_synom(Ident::parse_any)?;
+ return Ok(PathSegment::from(ident));
+ }
+
+ let ident = input.parse()?;
+ if input.peek(Token![<]) && !input.peek2(Token![=])
+ || input.peek(Token![::]) && input.peek3(Token![<])
+ {
+ Ok(PathSegment {
ident: ident,
- arguments: PathArguments::AngleBracketed(arguments),
+ arguments: PathArguments::AngleBracketed(input.parse()?),
})
- )
- |
- mod_style_path_segment
- ));
-
- fn description() -> Option<&'static str> {
- Some("path segment")
+ } else {
+ Ok(PathSegment::from(ident))
+ }
}
}
- impl Synom for Binding {
- named!(parse -> Self, do_parse!(
- id: syn!(Ident) >>
- eq: punct!(=) >>
- ty: syn!(Type) >>
- (Binding {
- ident: id,
- eq_token: eq,
- ty: ty,
+ impl Parse for Binding {
+ fn parse(input: ParseStream) -> Result<Self> {
+ Ok(Binding {
+ ident: input.parse()?,
+ eq_token: input.parse()?,
+ ty: input.parse_synom(Type::parse)?,
})
- ));
-
- fn description() -> Option<&'static str> {
- Some("associated type binding")
}
}
diff --git a/src/token.rs b/src/token.rs
index 0dd00a2..3ffe827 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -122,6 +122,10 @@
#[cfg(feature = "parsing")]
use error::Result;
#[cfg(feature = "parsing")]
+use lifetime::Lifetime;
+#[cfg(feature = "parsing")]
+use lit::Lit;
+#[cfg(feature = "parsing")]
use lookahead;
#[cfg(feature = "parsing")]
use parse::{Lookahead1, Parse, ParseBuffer, ParseStream};
@@ -147,7 +151,7 @@
}
macro_rules! impl_token {
- ($token:tt $name:ident) => {
+ ($name:ident $display:expr) => {
#[cfg(feature = "parsing")]
impl Token for $name {
fn peek(lookahead: &Lookahead1) -> bool {
@@ -161,7 +165,7 @@
}
fn display() -> String {
- concat!("`", $token, "`").to_owned()
+ $display.to_owned()
}
}
@@ -170,6 +174,10 @@
};
}
+impl_token!(Ident "identifier");
+impl_token!(Lifetime "lifetime");
+impl_token!(Lit "literal");
+
macro_rules! define_keywords {
($($token:tt pub struct $name:ident #[$doc:meta])*) => {
$(
@@ -192,7 +200,7 @@
}
}
- impl_token!($token $name);
+ impl_token!($name concat!("`", $token, "`"));
impl std::default::Default for $name {
fn default() -> Self {
@@ -261,7 +269,7 @@
}
}
- impl_token!($token $name);
+ impl_token!($name concat!("`", $token, "`"));
impl std::default::Default for $name {
fn default() -> Self {