use crate::{spanned::Spans, utils::Peekable2, Error};
use proc_macro2::{
token_stream::IntoIter, Delimiter, Group, Ident, Punct, Span, TokenStream as TokenStream2,
TokenTree as TokenTree2,
};
use std::{cmp::PartialEq, ops::Range};
pub type ParseStream<'a> = &'a mut ParseBuffer;
pub struct ParseBuffer {
iter: Peekable2<IntoIter>,
}
impl ParseBuffer {
pub fn new(ts: TokenStream2) -> Self {
let iter = Peekable2::new(ts);
Self { iter }
}
pub fn is_empty(&mut self) -> bool {
self.iter.is_empty()
}
pub fn peek(&mut self) -> Option<&TokenTree2> {
self.iter.peek()
}
pub fn peek2(&mut self) -> Option<&TokenTree2> {
self.iter.peek2()
}
pub fn parse_punct(&mut self, c: char) -> Result<Punct, crate::Error> {
match self.next() {
Some(TokenTree2::Punct(x)) if x.as_char() == c => Ok(x),
Some(x) => Err(Error::new(x.span(), &format!("Expected a '{}' token", c))),
None => Err(Error::new(
Span::mixed_site(),
&format!("Expected a '{}' token", c),
)),
}
}
pub fn parse_opt_punct(&mut self, c: char) -> Result<Option<Punct>, crate::Error> {
match self.next() {
Some(TokenTree2::Punct(x)) if x.as_char() == c => Ok(Some(x)),
Some(x) => Err(Error::new(x.span(), &format!("Expected a '{}' token", c))),
None => Ok(None),
}
}
pub fn parse_ident(&mut self) -> Result<Ident, crate::Error> {
match self.next() {
Some(TokenTree2::Ident(x)) => Ok(x),
Some(x) => Err(Error::new(x.span(), "Expected an identifier")),
None => Err(Error::new(Span::mixed_site(), "Expected an identifier")),
}
}
pub fn parse_paren(&mut self) -> Result<Parentheses, crate::Error> {
match self.next() {
Some(TokenTree2::Group(group)) if group.delimiter() == Delimiter::Parenthesis => {
Ok(Parentheses {
paren_span: group.span(),
contents: group.stream(),
})
}
Some(x) => Err(Error::new(
x.span(),
&format!("Expected parentheses: found {}", x),
)),
None => Err(Error::new(
Span::mixed_site(),
"Expected parentheses, found nothing",
)),
}
}
pub fn parse_unwrap_paren<F, T>(&mut self, f: F) -> Result<T, crate::Error>
where
F: FnOnce(ParseStream<'_>) -> Result<T, crate::Error>,
{
if matches!(self.peek(), Some(TokenTree2::Group(x)) if x.delimiter() == Delimiter::Parenthesis )
{
if let Some(TokenTree2::Group(group)) = self.next() {
ParseBuffer::new(group.stream()).parse_unwrap_tt(f)
} else {
unreachable!("But I peeked for a Parenthesis delimited TokenTree::Group!!")
}
} else {
f(self)
}
}
pub fn parse_unwrap_group<F, T>(&mut self, f: F) -> Result<T, crate::Error>
where
F: FnOnce(ParseStream<'_>) -> Result<T, crate::Error>,
{
if let Some(TokenTree2::Group(group)) = self.next() {
ParseBuffer::new(group.stream()).parse_unwrap_tt(f)
} else {
f(self)
}
}
pub fn parse_token_stream_and_span(&mut self) -> (TokenStream2, Spans) {
let mut start = match self.peek() {
Some(x) => x.span(),
None => Span::call_site(),
};
let mut end = start;
let ts = self
.inspect(|tt| {
end = tt.span();
if let Some(next) = start.join(end) {
start = next;
}
})
.collect::<TokenStream2>();
(ts, Spans { start, end })
}
pub fn parse_unwrap_tt<F, T>(&mut self, f: F) -> Result<T, crate::Error>
where
F: FnOnce(ParseStream<'_>) -> Result<T, crate::Error>,
{
if matches!(self.peek(), Some(TokenTree2::Group(x)) if x.delimiter() == Delimiter::None ) {
if let Some(TokenTree2::Group(group)) = self.next() {
ParseBuffer::new(group.stream()).parse_unwrap_tt(f)
} else {
unreachable!("But I peeked for a None delimited TokenTree::Group!!")
}
} else {
f(self)
}
}
}
impl Iterator for ParseBuffer {
type Item = TokenTree2;
fn next(&mut self) -> Option<TokenTree2> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub struct Parentheses {
#[allow(dead_code)]
pub paren_span: Span,
pub contents: TokenStream2,
}
pub struct LitStr {
value: String,
pub rawness: StrRawness,
pub inside_lit: Range<usize>,
pub span: Span,
}
impl LitStr {
pub fn value(&self) -> &str {
&self.value[self.inside_lit.clone()]
}
pub(crate) fn parse_from_literal(literal: &proc_macro2::Literal) -> Result<Self, Error> {
let mut value = literal.to_string();
let mut range = 1..value.len() - 1;
let span = literal.span();
let is_raw = if let Some(suffix) = value.strip_prefix('r') {
let hashes = suffix.bytes().take_while(|x| *x == b'#').count();
if value.as_bytes()[1 + hashes] != b'"' {
return Err(Error::new(
span,
&format!("Expected a string literal, found: {}", literal),
));
}
range.start += 1 + hashes;
range.end -= hashes;
Some(hashes as u32)
} else {
let mut matches = value.match_indices(r#"\u"#).peekable();
if matches.peek().is_some() {
let mut prev_end = 0;
let mut new = String::with_capacity(value.len());
for (pos, _) in matches {
new.push_str(&value[prev_end..pos]);
let past_open = pos + 3;
let off_close = value[pos..].find('}').unwrap();
let c = &value[past_open..pos + off_close];
let c = u32::from_str_radix(c, 16).unwrap();
let c = std::char::from_u32(c).unwrap();
new.push(c);
prev_end = pos + off_close + 1;
}
new.push_str(&value[prev_end..]);
value = new;
}
range = 1..value.len() - 1;
None
};
Ok(Self {
value,
rawness: StrRawness { is_raw, span },
inside_lit: range,
span,
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct StrRawness {
is_raw: Option<u32>,
span: Span,
}
impl PartialEq for StrRawness {
fn eq(&self, other: &Self) -> bool {
self.is_raw == other.is_raw
}
}
impl StrRawness {
#[cfg(test)]
pub fn dummy() -> Self {
Self {
is_raw: Some(4),
span: Span::mixed_site(),
}
}
pub fn span(&self) -> Span {
self.span
}
pub fn tokenize_sub(&self, str: &str) -> TokenStream2 {
let mut buffer = String::new();
match self.is_raw {
Some(hashes) => {
let hashes = hashes as usize;
buffer.reserve(3 + hashes + str.len() + hashes);
buffer.push('r');
let hashes = (0..hashes).map(|_| '#');
buffer.extend(hashes.clone());
buffer.push('"');
buffer.push_str(str);
buffer.push('"');
buffer.extend(hashes);
}
None => {
buffer.reserve(2 + str.len());
buffer.push('"');
buffer.push_str(str);
buffer.push('"');
}
}
buffer
.parse::<TokenStream2>()
.unwrap()
.set_span_recursive(self.span)
}
}
pub trait TokenTreeExt: Sized {
fn as_token_tree(&self) -> &TokenTree2;
fn into_token_tree(self) -> TokenTree2;
fn is_punct(&self, c: char) -> bool {
matches!(self.as_token_tree(), TokenTree2::Punct(p) if p.as_char() == c)
}
fn is_paren(&self) -> bool {
matches!(
self.as_token_tree(),
TokenTree2::Group(g) if g.delimiter() == Delimiter::Parenthesis
)
}
fn is_ident(&self, ident: &str) -> bool {
matches!(self.as_token_tree(), TokenTree2::Ident(x) if x == ident)
}
fn set_span_recursive(self, span: Span) -> TokenTree2 {
let mut tt = self.into_token_tree();
tt.set_span(span);
if let TokenTree2::Group(group) = tt {
let delim = group.delimiter();
let stream = group.stream().set_span_recursive(span);
tt = TokenTree2::Group(Group::new(delim, stream));
}
tt.set_span(span);
tt
}
}
impl TokenTreeExt for TokenTree2 {
fn as_token_tree(&self) -> &TokenTree2 {
self
}
fn into_token_tree(self) -> TokenTree2 {
self
}
}
pub trait TokenStream2Ext: Sized {
fn into_token_stream(self) -> TokenStream2;
fn set_span_recursive(self, span: Span) -> TokenStream2 {
self.into_token_stream()
.into_iter()
.map(|tt| tt.set_span_recursive(span))
.collect()
}
}
impl TokenStream2Ext for TokenStream2 {
fn into_token_stream(self) -> TokenStream2 {
self
}
}
pub trait MyParse: Sized {
fn parse(input: ParseStream<'_>) -> Result<Self, crate::Error>;
fn parse_token_stream_1(input: proc_macro::TokenStream) -> Result<Self, crate::Error> {
Self::parse(&mut ParseBuffer::new(TokenStream2::from(input)))
}
fn parse_token_stream_2(input: TokenStream2) -> Result<Self, crate::Error> {
Self::parse(&mut ParseBuffer::new(input))
}
}