use logos::Logos;
use crate::lexer::LexIt;
use crate::error::ParseError;
use crate::error::ParseError::{ReachedEOF, UnreachedEOF};
use crate::step::Step;
use crate::step::Step::{Error, Fail, Success};
pub struct ParseIt<'a, T> where T: Logos<'a, Source=str>, {
lexer: LexIt<'a, T>,
}
impl<'a, Token> ParseIt<'a, Token>
where Token: Logos<'a, Source=str> + PartialEq,
{
pub fn new(src: &'a str) -> Result<Self, ParseError<'a>>
where Token::Extras: Default
{
Ok(ParseIt {
lexer: LexIt::new(src)?,
})
}
pub fn token(&self, pos: usize) -> Result<(&Token, usize), ParseError<'a>> {
self.lexer.token(pos)
}
pub fn one_or_more<T, Then>(&self, pos: usize, then: Then) -> Step<'a, Vec<T>>
where
Then: FnOnce(usize) -> Step<'a, T> + Copy,
{
match self.zero_or_more(pos, then) {
Success(vals, _) if vals.is_empty() => Fail(pos),
other => other,
}
}
pub fn zero_or_more<T, Then>(&self, pos: usize, then: Then) -> Step<'a, Vec<T>>
where
Then: FnOnce(usize) -> Step<'a, T> + Copy,
{
match then(pos).then_multi_zip(then).merge() {
Fail(_) => Success(vec![], pos),
Error(ReachedEOF(_)) => Success(vec![], pos),
success => success,
}
}
pub fn validate_eof<T>(&self, res: Step<'a, T>) -> Step<'a, T> {
match res {
Success(_, pos) if self.lexer.len() != pos => Error(UnreachedEOF(pos)),
other => other,
}
}
pub fn env<T>(&self, step: Step<'a, T>) -> String {
match step {
Success(_, p) => self.lexer.env(p),
Fail(p) => self.lexer.env(p),
Error(e) => format!("{:?}", e)
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct EmptyToken {}
#[macro_export]
macro_rules! token {
($obj:expr => $($matcher:pat $(if $pred:expr)* => $result:expr),*) => {
match $obj {
Ok((t,p)) => match t {
$($matcher $(if $pred)* => Step::Success($result, p + 1)),*,
_ => Step::Fail(p)
}
Err(e) => Step::Error(e)
}
};
($obj:expr => $($matcher:pat $(if $pred:expr)*),*) => {
match $obj {
Ok((t,p)) => match t {
$($matcher $(if $pred)* => Step::Success(EmptyToken{}, p + 1)),*,
_ => Step::Fail(p)
}
Err(e) => Step::Error(e)
}
}
}
#[macro_export]
macro_rules! wrap {
($pos:literal => $left:ident; $internal:ident; $right:ident ) => {
$left($pos).then($internal).then_zip($right).take_left()
} ;
($pos:literal => $left:ident; $internal:ident ?; $right:ident ) => {
$left($pos).then_or_none($internal).then_zip($right).take_left()
};
($pos:literal => $left:ident; $internal:ident or $default:ident; $right:ident ) => {
$left($pos).then_or_val($internal,$default).then_zip($right).take_left()
};
($pos:literal => $left:ident; $internal:ident or $default:literal; $right:ident ) => {
$left($pos).then_or_val($internal,$default).then_zip($right).take_left()
};
($pos:ident => $left:ident; $internal:ident; $right:ident ) => {
$left($pos).then($internal).then_zip($right).take_left()
} ;
($pos:ident => $left:ident; $internal:ident ?; $right:ident ) => {
$left($pos).then_or_none($internal).then_zip($right).take_left()
};
($pos:ident => $left:ident; $internal:ident or $default:ident; $right:ident ) => {
$left($pos).then_or_val($internal,$default).then_zip($right).take_left()
};
($pos:ident => $left:ident; $internal:ident or $default:literal; $right:ident ) => {
$left($pos).then_or_val($internal,$default).then_zip($right).take_left()
}
}
#[macro_export]
macro_rules! seq {
($pos:ident => $elem:ident, $sep:ident ) => {
$elem($pos)
.then_multi_zip(|p| $sep(p).then($elem))
.merge()
};
($pos:ident => $elem:ident,$sep:ident, ) => {
$elem($pos)
.then_multi_zip(|p| {$sep(p).then($elem)})
.then_or_none_zip(|p| $sep(p).or_none())
.take_left()
.merge()
};
}