Optimize token peek
We avoid formatting error messages in the very common case that a peek
is not matched. This is around 3x performance improvement on parsing the
rust-lang/rust repo, down from 20 seconds to 7 seconds.
diff --git a/src/token.rs b/src/token.rs
index be9b68e..685fc47 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -211,8 +211,6 @@
}
}
- impl_token!($name concat!("`", $token, "`"));
-
impl std::default::Default for $name {
fn default() -> Self {
$name {
@@ -258,6 +256,20 @@
})
}
}
+
+ #[cfg(feature = "parsing")]
+ impl Token for $name {
+ fn peek(cursor: Cursor) -> bool {
+ parsing::peek_keyword(cursor, $token)
+ }
+
+ fn display() -> &'static str {
+ concat!("`", $token, "`")
+ }
+ }
+
+ #[cfg(feature = "parsing")]
+ impl private::Sealed for $name {}
)*
};
}
@@ -284,8 +296,6 @@
}
}
- impl_token!($name concat!("`", $token, "`"));
-
impl std::default::Default for $name {
fn default() -> Self {
$name {
@@ -341,6 +351,20 @@
})
}
}
+
+ #[cfg(feature = "parsing")]
+ impl Token for $name {
+ fn peek(cursor: Cursor) -> bool {
+ parsing::peek_punct(cursor, $token)
+ }
+
+ fn display() -> &'static str {
+ concat!("`", $token, "`")
+ }
+ }
+
+ #[cfg(feature = "parsing")]
+ impl private::Sealed for $name {}
)*
};
}
@@ -439,6 +463,26 @@
}
#[cfg(feature = "parsing")]
+impl Token for Underscore {
+ fn peek(cursor: Cursor) -> bool {
+ if let Some((ident, _rest)) = cursor.ident() {
+ return ident == "_";
+ }
+ if let Some((punct, _rest)) = cursor.punct() {
+ return punct.as_char() == '_'
+ }
+ false
+ }
+
+ fn display() -> &'static str {
+ "`_`"
+ }
+}
+
+#[cfg(feature = "parsing")]
+impl private::Sealed for Underscore {}
+
+#[cfg(feature = "parsing")]
impl Token for Paren {
fn peek(cursor: Cursor) -> bool {
lookahead::is_delimiter(cursor, Delimiter::Parenthesis)
@@ -688,6 +732,7 @@
mod parsing {
use proc_macro2::{Spacing, Span};
+ use buffer::Cursor;
use error::{Error, Result};
use parse::ParseStream;
use span::FromSpans;
@@ -703,6 +748,14 @@
})
}
+ pub fn peek_keyword(cursor: Cursor, token: &str) -> bool {
+ if let Some((ident, _rest)) = cursor.ident() {
+ ident == token
+ } else {
+ false
+ }
+ }
+
pub fn punct<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
let mut spans = [input.cursor().span(); 3];
punct_helper(input, token, &mut spans)?;
@@ -734,6 +787,25 @@
Err(Error::new(spans[0], format!("expected `{}`", token)))
})
}
+
+ pub fn peek_punct(mut cursor: Cursor, token: &str) -> bool {
+ for (i, ch) in token.chars().enumerate() {
+ match cursor.punct() {
+ Some((punct, rest)) => {
+ if punct.as_char() != ch {
+ break;
+ } else if i == token.len() - 1 {
+ return true;
+ } else if punct.spacing() != Spacing::Joint {
+ break;
+ }
+ cursor = rest;
+ }
+ None => break,
+ }
+ }
+ false
+ }
}
#[cfg(feature = "printing")]