use std::array;
use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use crate::file::Context;
use crate::file::File;
use crate::file::SpanId;
use crate::report::Report;
use crate::rt;
use crate::rt::Kind;
use crate::rule::Rule;
use crate::spec::Lexeme;
use crate::spec::Spec;
use crate::token;
#[derive(Clone)]
pub struct Stream<'ctx> {
pub(crate) file: File<'ctx>,
pub(crate) spec: &'ctx Spec,
pub(crate) toks: Vec<rt::Token>,
}
impl<'ctx> Stream<'ctx> {
pub fn cursor(&self) -> Cursor {
Cursor {
file: self.file,
spec: self.spec,
toks: &self.toks,
cursor: 0,
}
}
pub fn context(&self) -> &'ctx Context {
self.file.context()
}
pub fn file(&self) -> File<'ctx> {
self.file
}
pub fn spec(&self) -> &'ctx Spec {
self.spec
}
}
impl<'lex> IntoIterator for &'lex Stream<'_> {
type IntoIter = Cursor<'lex>;
type Item = token::Any<'lex>;
fn into_iter(self) -> Self::IntoIter {
self.cursor()
}
}
impl fmt::Debug for Stream<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.cursor(), f)
}
}
#[derive(Copy, Clone)]
pub struct Cursor<'lex> {
file: File<'lex>,
spec: &'lex Spec,
toks: &'lex [rt::Token],
cursor: usize,
}
impl<'lex> Cursor<'lex> {
fn end(&self) -> SpanId {
self.toks.last().unwrap().span
}
pub fn context(&self) -> &'lex Context {
self.file.context()
}
pub fn file(&self) -> File<'lex> {
self.file
}
pub fn spec(&self) -> &'lex Spec {
self.spec
}
pub fn is_empty(&self) -> bool {
self.cursor >= self.toks.len()
}
pub fn peek_any(&self) -> Option<token::Any<'lex>> {
let mut copy = *self;
copy.next()
}
pub fn back_up(&mut self, count: usize) {
for _ in 0..count {
assert!(self.cursor > 0, "cursor underflowed");
self.cursor -= 1;
if let Kind::Close { offset_to_open, .. } = &self.toks[self.cursor].kind {
self.cursor -= *offset_to_open as usize;
}
}
}
#[track_caller]
pub fn expect_finished(&self, report: &Report) {
if let Some(next) = self.peek_any() {
report
.builtins(self.spec)
.expected([Lexeme::eof()], next, self.end());
}
}
#[track_caller]
pub fn take<R: Rule>(
&mut self,
lexeme: Lexeme<R>,
report: &Report,
) -> Option<R::Token<'lex>> {
switch::switch().case(lexeme, |t, _| t).take(self, report)
}
pub fn try_take<R: Rule>(
&mut self,
lexeme: Lexeme<R>,
) -> Option<R::Token<'lex>> {
switch::switch().case(lexeme, |t, _| t).try_take(self)
}
pub fn peek<R: Rule>(&self, lexeme: Lexeme<R>) -> Option<R::Token<'lex>> {
switch::switch().case(lexeme, |t, _| t).peek(self)
}
pub fn delimited<'a, T, R: Rule>(
&'a mut self,
delim: Lexeme<R>,
mut cb: impl FnMut(&mut Self) -> Option<T> + 'a,
) -> impl Iterator<Item = (T, Option<R::Token<'lex>>)> + '_ {
let mut sep = switch::switch().case(delim, |x, _| x);
let mut done = false;
let mut prev = self.cursor;
iter::from_fn(move || {
if done {
return None;
}
match cb(self) {
None => {
done = true;
None
}
Some(next) => {
assert!(
mem::replace(&mut prev, self.cursor) != self.cursor,
"Cursor::delimited() callback failed to make progress"
);
let sep = sep.try_take(self);
if sep.is_none() {
done = true;
}
Some((next, sep))
}
}
})
}
pub(crate) fn fake_token(
file: File<'lex>,
spec: &'lex Spec,
tok: &'lex rt::Token,
) -> token::Any<'lex> {
Self {
file,
spec,
toks: array::from_ref(tok),
cursor: 0,
}
.next()
.unwrap()
}
}
impl fmt::Debug for Cursor<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut copy = *self;
copy.cursor = 0;
let mut list = f.debug_list();
for (i, tok) in copy.enumerate() {
struct Selectable<'a>(token::Any<'a>, bool);
impl fmt::Debug for Selectable<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.1 {
f.write_str(">")?;
} else {
f.write_str("-")?;
}
fmt::Debug::fmt(&self.0, f)
}
}
list.entry(&Selectable(tok, i == self.cursor));
}
list.finish()
}
}
impl<'lex> Iterator for Cursor<'lex> {
type Item = token::Any<'lex>;
fn next(&mut self) -> Option<Self::Item> {
let tok = self.toks.get(self.cursor)?;
let next = match &tok.kind {
Kind::Eof => {
self.cursor += 1;
token::Any::Eof(token::Eof {
span: tok.span,
ctx: self.context(),
spec: self.spec,
})
}
Kind::Keyword => {
self.cursor += 1;
token::Any::Keyword(token::Keyword {
lexeme: tok.lexeme.cast(),
ctx: self.context(),
spec: self.spec,
span: tok.span,
_ph: PhantomData,
})
}
Kind::Open { offset_to_close } => {
if *offset_to_close == !0 {
return Some(token::Any::Bracket(token::Bracket {
span: tok.span,
open: tok.span,
close: tok.span,
lexeme: tok.lexeme.cast(),
ctx: self.context(),
spec: self.spec,
contents: *self,
}));
}
let open_idx = self.cursor;
let close_idx = open_idx + (*offset_to_close as usize);
self.cursor = close_idx + 1;
let close = &self.toks[close_idx];
let &Kind::Close { full_span, .. } = &close.kind else {
bug!("Kind::Open did not point to an Kind::Close");
};
token::Any::Bracket(token::Bracket {
span: full_span,
open: tok.span,
close: close.span,
lexeme: tok.lexeme.cast(),
ctx: self.context(),
spec: self.spec,
contents: Cursor {
file: self.file,
spec: self.spec,
toks: &self.toks[open_idx + 1..close_idx],
cursor: 0,
},
})
}
Kind::Close { .. } => {
bug!("stray closing delimiter {:?} in token stream", tok.span)
}
Kind::Ident { .. } => {
self.cursor += 1;
token::Any::Ident(token::Ident {
tok,
ctx: self.context(),
spec: self.spec,
})
}
Kind::Quoted { .. } => {
self.cursor += 1;
token::Any::Quoted(token::Quoted {
tok,
ctx: self.context(),
spec: self.spec,
})
}
Kind::Digital { .. } => {
self.cursor += 1;
token::Any::Digital(token::Digital {
tok,
ctx: self.context(),
idx: 0,
spec: self.spec,
})
}
};
Some(next)
}
}
pub mod switch {
use crate::report::Report;
use crate::rule;
use crate::rule::Rule;
use crate::spec::Lexeme;
use crate::token::Any;
use crate::token::Cursor;
use crate::token::Token;
pub struct Switch<X>(pub(crate) X);
pub fn switch<'lex, T>() -> Switch<impl Impl<'lex, T>> {
Switch(First)
}
impl<X> Switch<X> {
#[inline(always)]
pub fn case<'lex, T, R: Rule>(
self,
lexeme: Lexeme<R>,
then: impl FnMut(R::Token<'lex>, &mut Cursor<'lex>) -> T,
) -> Switch<impl Impl<'lex, T>>
where
X: Impl<'lex, T>,
{
Switch(Case { prev: self.0, lexemes: [lexeme], then })
}
#[inline(always)]
pub fn cases<'lex, T, R: Rule, const LEXEMES: usize>(
self,
lexemes: [Lexeme<R>; LEXEMES],
then: impl FnMut(R::Token<'lex>, &mut Cursor<'lex>) -> T,
) -> Switch<impl Impl<'lex, T>>
where
X: Impl<'lex, T>,
{
Switch(Case { prev: self.0, lexemes, then })
}
#[track_caller]
pub fn take<'lex, T>(
&mut self,
cursor: &mut Cursor<'lex>,
report: &Report,
) -> Option<T>
where
X: Impl<'lex, T>,
{
let Some(next) = cursor.next() else {
report.builtins(cursor.spec).expected(
self.0.lexemes(0),
Lexeme::eof(),
cursor.end(),
);
return None;
};
if let Some(found) = self.0.apply(next, cursor) {
return Some(found);
}
report
.builtins(cursor.spec)
.expected(self.0.lexemes(0), next, next);
None
}
pub fn try_take<'lex, T>(&mut self, cursor: &mut Cursor<'lex>) -> Option<T>
where
X: Impl<'lex, T>,
{
if let Some(found) = self.0.apply(cursor.next()?, cursor) {
return Some(found);
}
cursor.back_up(1);
None
}
pub fn peek<'lex, T>(&mut self, cursor: &Cursor<'lex>) -> Option<T>
where
X: Impl<'lex, T>,
{
self.0.apply(cursor.peek_any()?, &mut { *cursor })
}
}
#[derive(Default)]
pub struct First;
pub struct Case<Prev, Rule, const LEXEMES: usize, Then> {
prev: Prev,
lexemes: [Lexeme<Rule>; LEXEMES],
then: Then,
}
pub trait Impl<'lex, T> {
fn lexemes(&self, total: usize) -> Vec<Lexeme<rule::Any>>;
fn apply(&mut self, any: Any<'lex>, cursor: &mut Cursor<'lex>)
-> Option<T>;
}
impl<'lex, T> Impl<'lex, T> for First {
fn lexemes(&self, total: usize) -> Vec<Lexeme<rule::Any>> {
Vec::with_capacity(total)
}
#[inline(always)]
fn apply(&mut self, _: Any<'lex>, _: &mut Cursor<'lex>) -> Option<T> {
None
}
}
impl<'lex, T, Prev, Rule, const LEXEMES: usize, Cb> Impl<'lex, T>
for Case<Prev, Rule, LEXEMES, Cb>
where
Prev: Impl<'lex, T>,
Rule: rule::Rule,
Cb: FnMut(Rule::Token<'lex>, &mut Cursor<'lex>) -> T,
{
fn lexemes(&self, total: usize) -> Vec<Lexeme<rule::Any>> {
let mut prev = self.prev.lexemes(total + LEXEMES);
prev.extend(self.lexemes.iter().copied().map(Lexeme::any));
prev
}
#[inline(always)]
fn apply(
&mut self,
any: Any<'lex>,
cursor: &mut Cursor<'lex>,
) -> Option<T> {
if let Some(prev) = self.prev.apply(any, cursor) {
Some(prev)
} else if !self.lexemes.iter().any(|&l| any.lexeme() == l.any()) {
None
} else {
Some((self.then)(Token::from_any(any), cursor))
}
}
}
}