#[derive(Clone, Debug, PartialEq)]
pub enum Token {
Ident(String),
Integer(i128),
Float(f64),
ByteChar(char),
Char(char),
ByteString(String),
String(String),
Plus,
Minus,
Slash,
Star,
At,
Ampersand,
Semi,
Colon,
GreaterThan,
LessThan,
OpenBrace,
CloseBrace,
OpenBracket,
CloseBracket,
OpenParen,
CloseParen,
Comma,
SingleQuote,
DoubleQuote,
Bang,
Question,
Dot,
Tilde,
Percent,
Caret,
Pipe,
Hash,
Dollar,
Equal,
None,
Literal(String),
}
#[derive(Clone, Debug)]
pub struct SpannedToken {
token: Token,
span: proc_macro2::Span,
}
impl SpannedToken {
pub fn token(&self) -> &Token {
&self.token
}
pub fn span(&self) -> &proc_macro2::Span {
&self.span
}
}
#[derive(Clone, Debug)]
pub struct TokenStream {
tokens: Vec<SpannedToken>,
iter_ptr: usize,
}
impl Iterator for TokenStream {
type Item = SpannedToken;
fn next(&mut self) -> Option<Self::Item> {
let x = self.tokens.get(self.iter_ptr).cloned();
self.iter_ptr += 1;
x
}
}
impl TokenStream {
pub fn peek(&mut self, ahead: usize) -> Option<SpannedToken> {
self.tokens.get(self.iter_ptr + ahead).cloned()
}
}
impl From<proc_macro2::TokenStream> for TokenStream {
fn from(value: proc_macro2::TokenStream) -> Self {
recursive_convert(value)
}
}
fn recursive_convert(tokens: proc_macro2::TokenStream) -> TokenStream {
let mut tokens_output = vec![];
let tokens = tokens.into_iter();
for token in tokens {
if let proc_macro2::TokenTree::Group(group) = token {
tokens_output.push(SpannedToken {
token: match group.delimiter() {
proc_macro2::Delimiter::Parenthesis => Token::OpenParen,
proc_macro2::Delimiter::Brace => Token::OpenBrace,
proc_macro2::Delimiter::Bracket => Token::OpenBracket,
proc_macro2::Delimiter::None => Token::None,
},
span: group.span(),
});
tokens_output.extend(recursive_convert(group.stream()).tokens);
tokens_output.push(SpannedToken {
token: match group.delimiter() {
proc_macro2::Delimiter::Parenthesis => Token::CloseParen,
proc_macro2::Delimiter::Brace => Token::CloseBrace,
proc_macro2::Delimiter::Bracket => Token::CloseBracket,
proc_macro2::Delimiter::None => Token::None,
},
span: group.span(),
});
} else {
match token {
proc_macro2::TokenTree::Group(..) => unreachable!(),
proc_macro2::TokenTree::Ident(ident) => {
tokens_output.push(SpannedToken {
token: Token::Ident(ident.to_string()),
span: ident.span(),
});
}
proc_macro2::TokenTree::Punct(punct) => {
let tok = match punct.as_char() {
'+' => Token::Plus,
'-' => Token::Minus,
'>' => Token::GreaterThan,
'<' => Token::LessThan,
'@' => Token::At,
'/' => Token::Slash,
'*' => Token::Star,
'&' => Token::Ampersand,
';' => Token::Semi,
':' => Token::Colon,
'"' => Token::DoubleQuote,
'\'' => Token::SingleQuote,
'?' => Token::Question,
'!' => Token::Bang,
',' => Token::Comma,
'.' => Token::Dot,
'~' => Token::Tilde,
'%' => Token::Percent,
'^' => Token::Caret,
'|' => Token::Pipe,
'#' => Token::Hash,
'$' => Token::Dollar,
'=' => Token::Equal,
_ => unreachable!(),
};
tokens_output.push(SpannedToken {
token: tok,
span: punct.span(),
});
}
proc_macro2::TokenTree::Literal(literal) => {
let mut panic = true;
if let Ok(int_value) = literal.to_string().parse::<i128>() {
panic = false;
tokens_output.push(SpannedToken {
token: Token::Integer(int_value),
span: literal.span(),
});
}
if let Ok(float_value) = literal.to_string().parse::<f64>() {
panic = false;
tokens_output.push(SpannedToken {
token: Token::Float(float_value),
span: literal.span(),
});
}
let str_value = literal.to_string();
if str_value.starts_with("0x") {
if let Ok(int_value) =
i128::from_str_radix(str_value.trim_start_matches("0x"), 16)
{
panic = false;
tokens_output.push(SpannedToken {
token: Token::Integer(int_value),
span: literal.span(),
});
}
}
if str_value.starts_with("b'") && str_value.ends_with('\'') {
panic = false;
let as_char = str_value
.trim_start_matches("b\'")
.trim_end_matches('\'')
.parse::<char>()
.expect("infallible - guaranteed to be a char");
tokens_output.push(SpannedToken {
token: Token::ByteChar(as_char),
span: literal.span(),
});
}
if str_value.starts_with('\'') && str_value.ends_with('\'') {
panic = false;
let as_char = str_value
.trim_matches('\'')
.trim()
.parse::<char>()
.expect("infallible - guaranteed to be a char");
tokens_output.push(SpannedToken {
token: Token::Char(as_char),
span: literal.span(),
});
}
if str_value.starts_with('"') && str_value.ends_with('"') {
panic = false;
tokens_output.push(SpannedToken {
token: Token::String(str_value.trim_matches('"').to_string()),
span: literal.span(),
});
}
if str_value.starts_with("b\"") && str_value.ends_with('"') {
panic = false;
tokens_output.push(SpannedToken {
token: Token::String(
str_value
.trim_end_matches('"')
.trim_start_matches("b\"")
.to_string(),
),
span: literal.span(),
});
}
if panic {
tokens_output.push(SpannedToken {
token: Token::Literal(str_value),
span: literal.span(),
});
}
}
}
}
}
TokenStream {
tokens: tokens_output,
iter_ptr: 0,
}
}