use crate::{invoke_nested, new_group, Result};
use proc_macro::{token_stream::IntoIter, Delimiter, Ident, Spacing, Span, TokenStream, TokenTree};
use std::{
collections::VecDeque,
fmt::{Debug, Formatter},
iter::FromIterator,
};
#[derive(Debug, Clone)]
pub enum Token
{
Simple(TokenTree),
Group(Delimiter, TokenIter, Span),
}
impl Token
{
fn is_punct(t: &TokenTree, c: char) -> bool
{
if let TokenTree::Punct(p) = t
{
p.as_char() == c && p.spacing() == Spacing::Alone
}
else
{
false
}
}
pub fn is_semicolon(t: &TokenTree) -> bool
{
Self::is_punct(t, ';')
}
pub fn is_ident(t: &TokenTree, comp: Option<&str>) -> bool
{
if let TokenTree::Ident(id) = t
{
comp.map_or(true, |comp| comp == id.to_string())
}
else
{
false
}
}
pub fn get_ident(t: TokenTree) -> Option<Ident>
{
if let TokenTree::Ident(id) = t
{
Some(id)
}
else
{
None
}
}
}
impl From<Token> for TokenTree
{
fn from(t: Token) -> Self
{
match t
{
Token::Simple(t) => t,
Token::Group(d, iter, span) =>
{
TokenTree::Group(new_group(d, iter.to_token_stream(), span))
},
}
}
}
#[derive(Clone)]
pub struct TokenIter
{
raw_tokens: IntoIter,
unconsumed: VecDeque<Token>,
last_span: Span,
}
impl TokenIter
{
fn fetch(&mut self) -> Result<bool>
{
if let Some(t) = self.raw_tokens.next()
{
match t
{
TokenTree::Group(g) =>
{
self.unconsumed.push_back(Token::Group(
g.delimiter(),
TokenIter::from(g.stream()),
g.span(),
))
},
TokenTree::Ident(id) if id.to_string() == "duplicate" =>
{
if let Some(TokenTree::Punct(p)) = self.raw_tokens.next()
{
if Token::is_punct(&TokenTree::Punct(p.clone()), '!')
{
let stream = invoke_nested(
&mut TokenStream::from_iter(self.raw_tokens.next().into_iter())
.into(),
)?;
self.unconsumed.push_back(Token::Group(
Delimiter::None,
TokenIter::from(stream),
p.span(),
));
}
else
{
self.unconsumed
.push_back(Token::Simple(TokenTree::Ident(id)));
self.unconsumed
.push_back(Token::Simple(TokenTree::Punct(p)));
}
}
else
{
self.unconsumed
.push_back(Token::Simple(TokenTree::Ident(id)));
}
},
_ => self.unconsumed.push_back(Token::Simple(t)),
}
Ok(true)
}
else
{
Ok(false)
}
}
fn next_unconsumed(&mut self) -> Result<Option<Token>>
{
self.unconsumed.pop_front().map_or(Ok(None), |t| {
match t
{
Token::Group(del, mut iter, span) if del == Delimiter::None =>
{
match iter.next_fallible()
{
Ok(Some(t)) =>
{
self.unconsumed.push_front(Token::Group(del, iter, span));
Ok(Some(t))
},
Ok(None) => self.next_fallible(),
err => err,
}
},
t => Ok(Some(t)),
}
})
}
pub fn next_fallible(&mut self) -> Result<Option<Token>>
{
self.fetch()?;
self.next_unconsumed()
}
pub fn extract_simple<R, P: FnOnce(&TokenTree) -> bool, F: FnOnce(TokenTree) -> R>(
&mut self,
p: P,
f: F,
expected: Option<&str>,
) -> Result<R>
{
let create_error = |error: &str| {
let mut msg: String = error.into();
if let Some(expected_string) = expected
{
msg.push_str(". Expected ");
msg.push_str(expected_string);
msg.push_str(" instead.");
}
else
{
msg.push('.');
}
msg
};
match self.peek()?
{
Some(Token::Simple(t)) if p(&t) =>
{
self.last_span = t.span();
Ok(f(self.next_fallible().unwrap().unwrap().into()))
},
Some(Token::Simple(t)) => Err((t.span(), create_error("Unexpected token"))),
Some(Token::Group(_, _, span)) =>
{
Err((span.clone(), create_error("Unexpected delimiter")))
},
None => Err((Span::call_site(), create_error("Unexpected end of code"))),
}
}
pub fn extract_identifier(&mut self, expected: Option<&str>) -> Result<Ident>
{
self.extract_simple(
|t| Token::is_ident(t, None),
|t| Token::get_ident(t).unwrap(),
expected,
)
}
pub fn expect_simple<P: FnOnce(&TokenTree) -> bool>(
&mut self,
p: P,
expected: Option<&str>,
) -> Result<()>
{
self.extract_simple(p, |_| (), expected)
}
pub fn expect_comma(&mut self) -> Result<()>
{
self.expect_simple(|t| Token::is_punct(t, ','), Some(","))
}
pub fn expect_semicolon(&mut self) -> Result<()>
{
self.expect_simple(Token::is_semicolon, Some(";"))
}
pub fn next_group(
&mut self,
expected: Option<Delimiter>,
hint: &str,
) -> Result<(TokenIter, Span)>
{
assert_ne!(
Some(Delimiter::None),
expected,
"should only be used with non-None delimiters"
);
let left_delimiter = |d| {
match d
{
Some(Delimiter::Bracket) => "'['",
Some(Delimiter::Brace) => "'{'",
Some(Delimiter::Parenthesis) => "'('",
None => "'{', '[', or '('",
_ => unreachable!(),
}
};
let error = || format!("Expected {}.\n{}", left_delimiter(expected), hint);
match self.peek()?
{
Some(Token::Group(del, _, span)) if *del != Delimiter::None =>
{
if let Some(exp_del) = expected
{
if exp_del != *del
{
return Err((span.clone(), error()));
}
}
if let Token::Group(_, iter, span) = self.next_fallible()?.unwrap()
{
self.last_span = span;
Ok((iter, span))
}
else
{
unreachable!()
}
},
_ => Err((self.last_span, error())),
}
}
pub fn to_token_tree_iter(self) -> impl Iterator<Item = TokenTree>
{
self.map(|t| t.into())
}
pub fn to_token_stream(self) -> TokenStream
{
TokenStream::from_iter(self.to_token_tree_iter())
}
pub fn has_next(&mut self) -> Result<bool>
{
self.peek().map_or_else(|e| Err(e), |t| Ok(t.is_some()))
}
pub fn peek(&mut self) -> Result<Option<&Token>>
{
let (pop_front, should_fetch, new_front) = match self.unconsumed.front_mut()
{
Some(Token::Group(del, iter, _)) if *del == Delimiter::None =>
{
if let Some(t) = iter.next_fallible()?
{
(false, false, Some(t))
}
else
{
(true, true, None)
}
},
None => (false, true, None),
_ => (false, false, None),
};
if pop_front
{
self.unconsumed.pop_front();
}
if should_fetch
{
if self.fetch()?
{
return self.peek();
}
}
new_front.map_or((), |t| self.unconsumed.push_front(t));
Ok(self.unconsumed.front())
}
pub fn push_front(&mut self, token: Token)
{
self.unconsumed.push_front(token)
}
}
impl Debug for TokenIter
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
f.write_str("TokenIter{")?;
self.raw_tokens.clone().collect::<Vec<_>>().fmt(f)?;
f.write_str(", ")?;
self.unconsumed.fmt(f)?;
f.write_str(",...}")
}
}
impl From<TokenStream> for TokenIter
{
fn from(stream: TokenStream) -> Self
{
Self {
raw_tokens: stream.into_iter(),
unconsumed: VecDeque::new(),
last_span: Span::call_site(),
}
}
}
impl Iterator for TokenIter
{
type Item = Token;
fn next(&mut self) -> Option<Self::Item>
{
self.next_fallible().unwrap()
}
}