use proc_macro::{Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
use crate::{Diagnostic, Expected, IntoTokens, Pattern, Result, ToSpan, TokenQueue};
struct Stream<T, I> {
seen_buffer: Vec<T>,
seen_idx: SeenIdx,
iter: I,
}
impl<T: Clone, I: Iterator<Item = T>> Stream<T, I> {
fn take_in(&mut self) {
self.iter
.next()
.inspect(|tok| self.seen_buffer.push(tok.clone()));
}
pub fn with_capacity(iter: I, len: u32) -> Self {
assert!(len < u32::MAX, "stream length cannot be u32::MAX");
let mut this = Self {
seen_buffer: Vec::with_capacity(len as usize),
seen_idx: SeenIdx::START,
iter,
};
this.take_in();
this
}
pub fn peek(&self) -> Option<T> {
self.seen_buffer
.get(self.seen_idx.to_u32() as usize)
.cloned()
}
pub fn idx(&self) -> SeenIdx {
self.seen_idx
}
pub fn move_on(&mut self) -> SeenIdx {
self.take_in();
self.seen_idx.increment()
}
pub fn seek_to(&mut self, idx: SeenIdx) {
self.seen_idx = idx;
}
}
mod indices {
use std::num::NonZero;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SeenIdx {
idx: NonZero<u32>,
}
impl SeenIdx {
pub const START: Self = Self::new_raw(1);
pub const ARBITRARY: Self = {
let value = 1776;
Self::new_raw(value)
};
#[inline]
pub(crate) const fn new_raw(idx: u32) -> Self {
SeenIdx {
idx: NonZero::new(idx).unwrap(),
}
}
#[inline]
pub(crate) fn to_u32(self) -> u32 {
self.idx.get().wrapping_sub(1)
}
#[inline]
pub fn increment(&mut self) -> SeenIdx {
let old = *self;
self.idx = self.idx.checked_add(1).unwrap();
old
}
#[inline]
pub fn seek_back(self, n: usize) -> Option<Self> {
let idx = self.idx.get() as usize;
if n >= idx {
#[cold]
fn none() -> Option<SeenIdx> {
None
}
return none();
}
let idx = u32::try_from(idx - n).unwrap();
Some(Self::new_raw(idx))
}
#[inline]
pub fn from_pos(pos: PosRepr) -> Option<Self> {
NonZero::new(pos.idx).map(|idx| Self { idx })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PosRepr {
idx: u32,
}
impl PosRepr {
pub const EOS: Self = Self { idx: 0 };
#[inline]
pub fn from_seen(seen: SeenIdx) -> Self {
Self {
idx: seen.idx.get(),
}
}
#[cfg(test)]
pub fn into_raw(self) -> u32 {
self.idx
}
}
}
use indices::{PosRepr, SeenIdx};
#[derive(Clone, Debug)]
#[must_use = "saving a `Checkpoint` is useless if it is never restored"]
pub struct Checkpoint {
seen_idx: SeenIdx,
error_count: usize,
}
impl From<Group> for Parser {
fn from(value: Group) -> Self {
Parser::new(value.stream(), value.span())
}
}
pub struct Parser {
stream: Stream<TokenTree, proc_macro::token_stream::IntoIter>,
eos_span: Span,
diag_buf: Vec<Diagnostic>,
}
impl Parser {
pub fn new(stream: TokenStream, parent_span: Span) -> Self {
let len = stream.clone().into_iter().count();
Self {
stream: Stream::with_capacity(stream.into_iter(), len.try_into().unwrap()),
#[cfg(not(feature = "proc-macro2"))]
eos_span: parent_span.end(),
#[cfg(feature = "proc-macro2")]
eos_span: parent_span,
diag_buf: Vec::new(),
}
}
#[inline]
pub fn report(&mut self, err: impl Into<Diagnostic>) {
self.diag_buf.push(err.into());
}
#[inline]
pub fn maybe_report(&mut self, res: Result<(), impl Into<Diagnostic>>) -> bool {
match res {
Ok(()) => true,
Err(err) => {
self.report(err);
false
}
}
}
#[cfg_attr(not(test), expect(dead_code, reason = "test-only function"))]
pub(crate) fn raw_pos(&self) -> ParserPos {
ParserPos {
span: self.eos_span,
repr: PosRepr::from_seen(self.stream.idx()),
}
}
pub fn is_empty(&self) -> bool {
self.here().is_eos()
}
pub fn peek(&self) -> Option<TokenTree> {
self.stream.peek()
}
pub fn nibble(&mut self) -> (Option<TokenTree>, ParserPos) {
let (tt, pos_data) = self.peek().map_or_else(
|| (None, PosRepr::EOS),
|tt| {
let idx = self.stream.move_on();
self.eos_span = tt.span();
(Some(tt), PosRepr::from_seen(idx))
},
);
(
tt,
ParserPos {
span: self.eos_span,
repr: pos_data,
},
)
}
pub fn eat<T: Pattern>(&mut self, meal: T) -> Result<T::Output> {
meal.eat(self)
}
pub fn parse_with<T: Parse>(&mut self, args: T::Args<'_>) -> Result<T> {
T::parse_with(self, args)
}
pub fn parse<T: Parse>(&mut self) -> Result<T>
where
for<'a> T::Args<'a>: Default,
{
T::parse(self)
}
pub fn eat_expectantly<T>(
&mut self,
try_pass: impl FnOnce(TokenTree) -> Option<T>,
expects: impl FnOnce(ParserPos) -> Expected,
) -> Result<T, Expected> {
let pos = match self.nibble() {
(None, pos) => pos,
(Some(tt), pos) => match try_pass(tt) {
None => pos,
Some(res) => return Ok(res),
},
};
Err(expects(pos))
}
pub fn eat_ident(&mut self) -> Result<Ident, Expected> {
self.eat_expectantly(
|tok| match tok {
TokenTree::Ident(ident) => Some(ident),
_ => None,
},
|span| Expected::noun(span, "an identifier"),
)
}
pub fn eat_punct(&mut self, punct: char) -> Result<Punct, Expected> {
self.eat_expectantly(
|tok| match tok {
TokenTree::Punct(pt) if pt.as_char() == punct => Some(pt),
_ => None,
},
|span| Expected::lit(span, punct.to_string()),
)
}
pub fn eat_punct_with_spacing(
&mut self,
punct: char,
spacing: Spacing,
) -> Result<Punct, Expected> {
self.eat_expectantly(
|tok| match tok {
TokenTree::Punct(pt) if pt.as_char() == punct && pt.spacing() == spacing => {
Some(pt)
}
_ => None,
},
|span| Expected::lit(span, punct.to_string()),
)
}
pub fn here(&self) -> ParserPos {
let (span, pos_data) = self.peek().map_or_else(
|| (self.eos_span, PosRepr::EOS),
|tt| (tt.span(), PosRepr::from_seen(self.stream.idx())),
);
ParserPos {
repr: pos_data,
span,
}
}
pub fn save(&self) -> Checkpoint {
Checkpoint {
seen_idx: self.stream.idx(),
error_count: self.diag_buf.len(),
}
}
pub fn restore(&mut self, point: &Checkpoint) -> ParserPos {
let span = self.here();
self.stream.seek_to(point.seen_idx);
span
}
pub fn seek_to(&mut self, pos: &ParserPos) {
if let Some(idx) = SeenIdx::from_pos(pos.repr) {
self.stream.seek_to(idx);
} else {
}
}
pub fn restore_forgiving(&mut self, point: &Checkpoint) -> ParserPos {
self.diag_buf.drain(point.error_count..self.diag_buf.len());
self.restore(point)
}
#[track_caller]
pub fn gag(&mut self, n: usize) {
let idx = self.stream.idx().seek_back(n).unwrap_or_else(|| {
panic!("tried to `Parser::gag` to before the beginning of the parser's stream")
});
self.stream.seek_to(idx);
}
pub fn finish_diagnostics(&mut self) -> impl IntoTokens {
struct Wrap<T>(T);
impl<T: Iterator<Item = Diagnostic>> IntoTokens for Wrap<T> {
fn extend_tokens(self, q: &mut TokenQueue) {
for d in self.0 {
q.extend_from(d.finish());
}
}
}
Wrap(self.diag_buf.drain(..))
}
pub fn collect_until<C: FromIterator<TokenTree>>(
&mut self,
mut stop_condition: impl FnMut(&TokenTree) -> bool,
) -> C {
std::iter::from_fn(move || match self.nibble() {
(Some(tok), _) if stop_condition(&tok) => None,
(Some(tok), _) => Some(tok),
(None, _) => None,
})
.collect()
}
pub fn rest(&mut self) -> TokenStream {
self.stream.iter.by_ref().collect()
}
}
#[derive(Debug, Clone, Copy)]
pub struct ParserPos {
span: Span,
repr: PosRepr,
}
impl ToSpan for ParserPos {
fn span(&self) -> Span {
self.span
}
}
impl ParserPos {
pub fn arbitrary() -> Self {
Self {
repr: PosRepr::from_seen(SeenIdx::ARBITRARY),
span: Span::call_site(),
}
}
pub fn is_eos(self) -> bool {
self.repr == PosRepr::EOS
}
#[cfg(test)]
pub(crate) fn into_raw(self) -> u32 {
self.repr.into_raw()
}
}
pub trait Parse: Sized {
type Args<'a>;
fn parse_with(parser: &mut Parser, args: Self::Args<'_>) -> Result<Self>;
fn parse(parser: &mut Parser) -> Result<Self>
where
for<'a> Self::Args<'a>: Default,
{
Self::parse_with(parser, Default::default())
}
}
impl Parse for TokenStream {
type Args<'a> = ();
fn parse_with(parser: &mut Parser, _args: Self::Args<'_>) -> Result<Self> {
Ok(parser.rest())
}
}