#[cfg(feature = "proc-macro")]
use proc_macro;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, TokenStream, TokenTree};
use error::parse_error;
pub use error::{PResult, ParseError};
use buffer::{Cursor, TokenBuffer};
pub trait Synom: Sized {
fn parse(input: Cursor) -> PResult<Self>;
fn description() -> Option<&'static str> {
None
}
}
impl Synom for TokenStream {
fn parse(input: Cursor) -> PResult<Self> {
Ok((input.token_stream(), Cursor::empty()))
}
fn description() -> Option<&'static str> {
Some("arbitrary token stream")
}
}
impl Synom for TokenTree {
fn parse(input: Cursor) -> PResult<Self> {
match input.token_tree() {
Some((tt, rest)) => Ok((tt, rest)),
None => parse_error(),
}
}
fn description() -> Option<&'static str> {
Some("token tree")
}
}
impl Synom for Group {
fn parse(input: Cursor) -> PResult<Self> {
for delim in &[Delimiter::Parenthesis, Delimiter::Brace, Delimiter::Bracket] {
if let Some((inside, span, rest)) = input.group(*delim) {
let mut group = Group::new(*delim, inside.token_stream());
group.set_span(span);
return Ok((group, rest));
}
}
parse_error()
}
fn description() -> Option<&'static str> {
Some("group token")
}
}
impl Synom for Ident {
fn parse(input: Cursor) -> PResult<Self> {
let (ident, rest) = match input.ident() {
Some(ident) => ident,
_ => return parse_error(),
};
match &ident.to_string()[..] {
"_"
| "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const"
| "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final"
| "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match"
| "mod" | "move" | "mut" | "offsetof" | "override" | "priv" | "proc" | "pub"
| "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct"
| "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use"
| "virtual" | "where" | "while" | "yield" => return parse_error(),
_ => {}
}
Ok((ident, rest))
}
fn description() -> Option<&'static str> {
Some("identifier")
}
}
impl Synom for Punct {
fn parse(input: Cursor) -> PResult<Self> {
match input.punct() {
Some((punct, rest)) => Ok((punct, rest)),
None => parse_error(),
}
}
fn description() -> Option<&'static str> {
Some("punctuation token")
}
}
impl Synom for Literal {
fn parse(input: Cursor) -> PResult<Self> {
match input.literal() {
Some((literal, rest)) => Ok((literal, rest)),
None => parse_error(),
}
}
fn description() -> Option<&'static str> {
Some("literal token")
}
}
pub trait Parser: Sized {
type Output;
fn parse2(self, tokens: TokenStream) -> Result<Self::Output, ParseError>;
#[cfg(feature = "proc-macro")]
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: 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() {
Err(ParseError::new("failed to parse anything"))
} else {
Err(ParseError::new("failed to parse all tokens"))
}
}
}
pub mod ext {
use super::*;
use proc_macro2::Ident;
pub trait IdentExt: Sized + private::Sealed {
fn parse_any(input: Cursor) -> PResult<Self>;
}
impl IdentExt for Ident {
fn parse_any(input: Cursor) -> PResult<Self> {
input.ident().map_or_else(parse_error, Ok)
}
}
mod private {
use proc_macro2::Ident;
pub trait Sealed {}
impl Sealed for Ident {}
}
}