use std::fmt::{self, Display};
use std::hash::Hash;
use std::iter;
use std::{borrow::Cow, iter::Peekable};
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
pub struct Tokens(pub TokenStream);
impl Display for Tokens {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl Tokens {
pub fn push(&mut self, token: impl IntoTokens) {
self.extend(token.into_tokens());
}
pub fn inside_of(self, delimiter: Delimiter) -> Group {
Group::new(delimiter, self.0)
}
}
pub trait IntoTokens {
fn into_tokens(self) -> impl Iterator<Item = TokenTree>;
}
impl IntoTokens for Literal {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
iter::once(TokenTree::Literal(self))
}
}
impl IntoTokens for Punct {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
iter::once(TokenTree::Punct(self))
}
}
impl IntoTokens for Group {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
iter::once(TokenTree::Group(self))
}
}
impl IntoTokens for Ident {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
iter::once(TokenTree::Ident(self))
}
}
impl IntoTokens for TokenTree {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
iter::once(self)
}
}
impl<K: IntoTokens> Extend<K> for Tokens {
fn extend<T: IntoIterator<Item = K>>(&mut self, iter: T) {
self.0
.extend(iter.into_iter().flat_map(IntoTokens::into_tokens));
}
}
pub struct TokensIter {
pub stream: Peekable<proc_macro::token_stream::IntoIter>,
pub span: Span,
}
impl Display for TokensIter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.stream.clone().collect::<TokenStream>().fmt(f)
}
}
impl TokensIter {
pub fn compile_error(&self, message: impl Into<Cow<'static, str>>) -> CompileError {
CompileError::new(self.span, message)
}
pub fn eat_until_char(&mut self, f: impl Fn(char) -> bool) {
loop {
match self.tt() {
Some(TokenTree::Punct(punct)) if punct == ',' || punct == ';' => {
if f(punct.as_char()) {
break;
} else {
}
}
None => break,
_ => (),
}
}
}
pub fn peek_tt(&mut self) -> Option<&TokenTree> {
self.stream.peek()
}
pub fn tt(&mut self) -> Option<TokenTree> {
match self.stream.next() {
Some(tt) => {
self.span = tt.span();
Some(tt)
}
None => None,
}
}
pub fn peek_char(&mut self, char: char) -> Option<&Punct> {
match self.stream.peek() {
Some(TokenTree::Punct(punct)) if *punct == char => Some(punct),
_ => None,
}
}
pub fn char(&mut self, char: char) -> Option<Punct> {
match self.peek_tt() {
Some(TokenTree::Punct(punct)) if *punct == char => {
let Some(TokenTree::Punct(punct)) = self.stream.next() else {
unreachable!(".peek() returned `Some(TokenTree::Punct)`")
};
self.span = punct.span();
Some(punct)
}
Some(tt) => {
self.span = tt.span();
None
}
None => None,
}
}
pub fn group(&mut self, delimiter: Delimiter) -> Option<TokenStream> {
match self.peek_tt() {
Some(TokenTree::Group(group)) if group.delimiter() == delimiter => {
let Some(TokenTree::Group(group)) = self.stream.next() else {
unreachable!(".peek() returned `Some(TokenTree::Group)`")
};
self.span = group.span();
Some(group.stream())
}
Some(tt) => {
self.span = tt.span();
None
}
None => None,
}
}
pub fn ident(&mut self) -> Option<Ident> {
match self.stream.peek() {
Some(TokenTree::Ident(_ident)) => {
let Some(TokenTree::Ident(ident)) = self.stream.next() else {
unreachable!(".peek() returned `Some(TokenTree::Ident)`")
};
self.span = ident.span();
Some(ident)
}
Some(tt) => {
self.span = tt.span();
None
}
None => None,
}
}
pub fn path_separator(&mut self) -> Option<PathSeparator> {
match self.peek_tt() {
Some(TokenTree::Punct(colon)) if *colon == ':' => {
let span = colon.span();
self.tt();
match self.tt() {
Some(TokenTree::Punct(colon_colon)) if colon_colon == ':' => {
Some(PathSeparator {
first: span,
second: colon_colon.span(),
})
}
_ => None,
}
}
Some(tt) => {
self.span = tt.span();
None
}
None => None,
}
}
pub fn path(&mut self) -> Result<Path, CompileError> {
let leading_colon = self.path_separator();
let first_component = self.ident().ok_or_else(|| {
self.compile_error(if leading_colon.is_some() {
"expected identifier to form a path like `::std::hash::Hash`"
} else {
"expected identifier or `::` to form a path like `::std::hash::Hash`"
})
})?;
let mut components = Vec::new();
loop {
if self.peek_char(':').is_none() {
break;
}
let separator = self.path_separator().ok_or_else(|| {
self.compile_error("expected `::` to form a path like `::std::hash::Hash`")
})?;
let component = self.ident().ok_or_else(|| {
self.compile_error("expected identifier to form a path like `::std::hash::Hash`")
})?;
components.push((separator, (component)));
}
Ok(Path {
leading_colon,
first_component,
components,
})
}
}
#[derive(Clone, Debug)]
pub struct Path {
pub leading_colon: Option<PathSeparator>,
pub first_component: Ident,
pub components: Vec<(PathSeparator, Ident)>,
}
impl IntoTokens for Path {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
self.leading_colon
.map(PathSeparator::into_tokens)
.into_iter()
.flatten()
.chain([TokenTree::Ident(self.first_component)])
.chain(
self.components
.into_iter()
.flat_map(|(separator, segment)| {
separator
.into_tokens()
.chain(std::iter::once(TokenTree::Ident(segment)))
}),
)
}
}
#[derive(Clone, Debug)]
pub struct PathSeparator {
pub first: Span,
pub second: Span,
}
impl IntoTokens for PathSeparator {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
let mut first = Punct::new(':', Spacing::Joint);
first.set_span(self.first);
let mut second = Punct::new(':', Spacing::Joint);
second.set_span(self.second);
[TokenTree::Punct(first), TokenTree::Punct(second)].into_iter()
}
}
impl Hash for Path {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.leading_colon.is_some().hash(state);
self.first_component.to_string().hash(state);
for (_, component) in &self.components {
component.to_string().hash(state);
}
}
}
impl PartialEq for Path {
fn eq(&self, other: &Self) -> bool {
self.leading_colon.is_some() == other.leading_colon.is_some()
&& self.first_component.to_string() == other.first_component.to_string()
&& self
.components
.iter()
.map(|(_, ident)| ident.to_string())
.eq(other.components.iter().map(|(_, ident)| ident.to_string()))
}
}
impl Eq for Path {}
impl fmt::Display for Path {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.leading_colon.is_some() {
f.write_str("::")?;
}
f.write_str(&self.first_component.to_string())?;
for (_sep, component) in &self.components {
f.write_str("::")?;
f.write_str(&component.to_string())?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct CompileError {
pub span: Span,
pub message: Cow<'static, str>,
}
impl CompileError {
pub fn new(span: Span, message: impl Into<Cow<'static, str>>) -> Self {
Self {
span,
message: message.into(),
}
}
}
impl IntoTokens for CompileError {
fn into_tokens(self) -> impl Iterator<Item = TokenTree> {
[
TokenTree::Ident(Ident::new("compile_error", self.span)),
TokenTree::Punct(Punct::new('!', Spacing::Alone)).with_span(self.span),
TokenTree::Group({
Group::new(Delimiter::Brace, {
TokenStream::from_iter([
TokenTree::Literal(Literal::string(&self.message)).with_span(self.span),
])
})
})
.with_span(self.span),
]
.into_iter()
}
}
trait Spanned {
fn with_span(self, span: Span) -> Self;
}
impl Spanned for TokenTree {
fn with_span(mut self, span: Span) -> Self {
self.set_span(span);
self
}
}