blob: 789bf829106045b85e75cb131b4d4d2aa5c8972d [file] [log] [blame]
#[cfg(feature = "extra-traits")]
use std::fmt;
use super::*;
use proc_macro2::{TokenKind, Delimiter};
ast_struct! {
/// Represents a macro invocation. The Path indicates which macro
/// is being invoked, and the vector of token-trees contains the source
/// of the macro invocation.
///
/// NB: the additional ident for a `macro_rules`-style macro is actually
/// stored in the enclosing item. Oog.
pub struct Mac {
pub path: Path,
pub bang_token: tokens::Bang,
pub tokens: Vec<TokenTree>,
}
}
#[cfg_attr(feature = "clone-impls", derive(Clone))]
pub struct TokenTree(pub proc_macro2::TokenTree);
impl Mac {
pub fn is_braced(&self) -> bool {
match self.tokens.last() {
Some(t) => t.is_braced(),
None => false,
}
}
}
impl TokenTree {
pub fn is_braced(&self) -> bool {
match self.0.kind {
TokenKind::Sequence(Delimiter::Brace, _) => true,
_ => false,
}
}
}
#[cfg(feature = "extra-traits")]
impl PartialEq for TokenTree {
fn eq(&self, other: &TokenTree) -> bool {
use proc_macro2::OpKind;
match (&self.0.kind, &other.0.kind) {
(&TokenKind::Sequence(d1, ref s1), &TokenKind::Sequence(d2, ref s2)) => {
match (d1, d2) {
(Delimiter::Parenthesis, Delimiter::Parenthesis) |
(Delimiter::Brace, Delimiter::Brace) |
(Delimiter::Bracket, Delimiter::Bracket) => {}
(Delimiter::None, Delimiter::None) => {}
_ => return false,
}
let s1 = s1.clone().into_iter();
let mut s2 = s2.clone().into_iter();
for item1 in s1 {
let item2 = match s2.next() {
Some(item) => item,
None => return false,
};
if TokenTree(item1) != TokenTree(item2) {
return false
}
}
s2.next().is_none()
}
(&TokenKind::Op(o1, k1), &TokenKind::Op(o2, k2)) => {
o1 == o2 && match (k1, k2) {
(OpKind::Alone, OpKind::Alone) |
(OpKind::Joint, OpKind::Joint) => true,
_ => false,
}
}
(&TokenKind::Literal(ref l1), &TokenKind::Literal(ref l2)) => {
l1.to_string() == l2.to_string()
}
(&TokenKind::Word(ref s1), &TokenKind::Word(ref s2)) => {
s1.as_str() == s2.as_str()
}
_ => false,
}
}
}
#[cfg(feature = "extra-traits")]
impl Eq for TokenTree {}
#[cfg(feature = "extra-traits")]
impl ::std::hash::Hash for TokenTree {
fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) {
use proc_macro2::OpKind;
match self.0.kind {
TokenKind::Sequence(delim, ref stream) => {
0u8.hash(h);
match delim {
Delimiter::Parenthesis => 0u8.hash(h),
Delimiter::Brace => 1u8.hash(h),
Delimiter::Bracket => 2u8.hash(h),
Delimiter::None => 3u8.hash(h),
}
for item in stream.clone().into_iter() {
TokenTree(item).hash(h);
}
0xffu8.hash(h); // terminator w/ a variant we don't normally hash
}
TokenKind::Op(op, kind) => {
1u8.hash(h);
op.hash(h);
match kind {
OpKind::Alone => 0u8.hash(h),
OpKind::Joint => 1u8.hash(h),
}
}
TokenKind::Literal(ref lit) => (2u8, lit.to_string()).hash(h),
TokenKind::Word(ref word) => (3u8, word.as_str()).hash(h),
}
}
}
#[cfg(feature = "extra-traits")]
impl fmt::Debug for TokenTree {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.to_string().fmt(f)
}
}
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
use {Lifetime};
use generics::parsing::lifetime;
use ident::parsing::word;
use lit::parsing::lit;
use synom::IResult;
use synom::space::{block_comment, whitespace, skip_whitespace};
use ty::parsing::path;
use proc_macro2::{self, TokenStream, TokenKind, Delimiter, OpKind, Literal};
fn tt(kind: TokenKind) -> TokenTree {
TokenTree(proc_macro2::TokenTree {
kind: kind,
span: Default::default(),
})
}
named!(pub mac -> Mac, do_parse!(
what: path >>
punct!("!") >>
body: delimited >>
(Mac {
path: what,
bang_token: tokens::Bang::default(),
tokens: vec![body],
})
));
named!(pub token_trees -> Vec<TokenTree>, many0!(token_tree));
named!(pub token_stream -> TokenStream,
map!(token_trees, |t: Vec<TokenTree>| t.into_iter().map(|t| t.0).collect()));
named!(pub delimited -> TokenTree, alt!(
delimited!(
punct!("("),
token_stream,
punct!(")")
) => { |ts| tt(TokenKind::Sequence(Delimiter::Parenthesis, ts)) }
|
delimited!(
punct!("["),
token_stream,
punct!("]")
) => { |ts| tt(TokenKind::Sequence(Delimiter::Bracket, ts)) }
|
delimited!(
punct!("{"),
token_stream,
punct!("}")
) => { |ts| tt(TokenKind::Sequence(Delimiter::Brace, ts)) }
));
named!(pub token_tree -> TokenTree, alt!(
token
|
delimited
));
macro_rules! punct1 {
($i:expr, $punct:expr) => {
punct1($i, $punct)
}
}
fn punct1<'a>(input: &'a str, token: &'static str) -> IResult<&'a str, char> {
let input = skip_whitespace(input);
if input.starts_with(token) {
IResult::Done(&input[1..], token.chars().next().unwrap())
} else {
IResult::Error
}
}
named!(token -> TokenTree, alt!(
keyword!("_") => { |_| tt(TokenKind::Op('_', OpKind::Alone)) }
|
punct1!("&&") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before BinOp
|
punct1!("||") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before BinOp
|
punct1!("->") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before BinOp
|
punct1!("<-") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before Lt
|
punct1!("=>") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before Eq
|
punct1!("...") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before DotDot
|
punct1!("..") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) } // must be before Dot
|
punct1!(".") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
// must be before bin_op
map!(doc_comment, |s: String| tt(TokenKind::Literal(Literal::doccomment(&s))))
|
bin_op_eq // must be before bin_op
|
bin_op
|
map!(lit, |l: Lit| l.into_token_tree())
|
map!(word, |w: Ident| tt(TokenKind::Word(w.sym)))
|
map!(lifetime, |lt: Lifetime| tt(TokenKind::Word(lt.ident.sym)))
|
punct1!("<=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("==") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("!=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!(">=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("::") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("=") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("<") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!(">") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("!") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("~") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("@") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!(",") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!(";") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!(":") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("#") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("$") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("?") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
));
named!(bin_op -> TokenTree, alt!(
punct1!("+") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("-") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("*") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("/") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("%") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("^") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("&") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("|") => { |c| tt(TokenKind::Op(c, OpKind::Alone)) }
|
punct1!("<<") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!(">>") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
));
named!(bin_op_eq -> TokenTree, alt!(
punct1!("+=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("-=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("*=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("/=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("%=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("^=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("&=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("|=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!("<<=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
|
punct1!(">>=") => { |c| tt(TokenKind::Op(c, OpKind::Joint)) }
));
named!(doc_comment -> String, alt!(
do_parse!(
punct!("//!") >>
content: take_until!("\n") >>
(format!("//!{}", content))
)
|
do_parse!(
option!(whitespace) >>
peek!(tag!("/*!")) >>
com: block_comment >>
(com.to_owned())
)
|
do_parse!(
punct!("///") >>
not!(tag!("/")) >>
content: take_until!("\n") >>
(format!("///{}", content))
)
|
do_parse!(
option!(whitespace) >>
peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
com: block_comment >>
(com.to_owned())
)
));
}
#[cfg(feature = "printing")]
mod printing {
use super::*;
use quote::{Tokens, ToTokens};
impl ToTokens for Mac {
fn to_tokens(&self, tokens: &mut Tokens) {
self.path.to_tokens(tokens);
self.bang_token.to_tokens(tokens);
tokens.append_all(&self.tokens);
}
}
impl ToTokens for TokenTree {
fn to_tokens(&self, tokens: &mut Tokens) {
self.0.to_tokens(tokens);
}
}
}