use super::{ast, TokenKind, Keyword};
#[derive(Debug)]
pub enum Strategy {
Keep,
SkipOne {
skipped: bool,
},
Token {
token: TokenKind,
found: bool,
},
Keyword {
keyword: Keyword,
found: bool,
},
BlockTerminator {
skipped: bool,
},
BasicCommandTerminator {
skipped: bool,
},
}
impl Strategy {
pub fn eof() -> Self {
Self::Keep
}
pub fn keep() -> Self {
Self::Keep
}
pub fn skip_one() -> Self {
Self::SkipOne { skipped: false }
}
pub fn token(token: TokenKind) -> Self {
Self::Token { token, found: false }
}
pub fn keyword(keyword: Keyword) -> Self {
Self::Keyword { keyword, found: false }
}
pub fn block_terminator() -> Self {
Self::BlockTerminator { skipped: false }
}
pub fn basic_command_terminator() -> Self {
Self::BasicCommandTerminator { skipped: false }
}
pub fn synchronized(&mut self, token: &TokenKind) -> bool {
match self {
Self::Keep => true,
Self::SkipOne { skipped: true } => true,
Self::SkipOne { skipped } => {
*skipped = true;
false
},
Self::Keyword { found: true, .. } => true,
Self::Keyword { keyword, found } => {
*found = matches!(token, TokenKind::Keyword(kwd) if kwd == keyword);
false
},
Self::Token { found: true, .. } => true,
Self::Token { token: expected, found } => {
*found = token == expected;
false
},
Self::BlockTerminator { skipped: true } => true,
Self::BlockTerminator { skipped } => {
*skipped = token.is_block_terminator();
false
},
Self::BasicCommandTerminator { skipped: true } => true,
Self::BasicCommandTerminator { skipped } => {
*skipped = token.is_basic_command_terminator();
false
},
}
}
}
pub trait Synchronizable<E> {
fn synchronize(&mut self, error: E, sync: Strategy);
}
pub type Result<T, E> = std::result::Result<T, (E, Strategy)>;
pub trait WithSync<T, E> {
fn with_sync(self, strategy: Strategy) -> Result<T, E>;
}
impl<T, E> WithSync<T, E> for std::result::Result<T, E> {
fn with_sync(self, strategy: Strategy) -> Result<T, E> {
self.map_err(|error| (error, strategy))
}
}
impl<T, E> WithSync<T, E> for Result<T, E> {
fn with_sync(self, strategy: Strategy) -> Result<T, E> {
self.map_err(|(error, _)| (error, strategy))
}
}
pub trait ResultExt<T, E> {
fn synchronize<P: Synchronizable<E>>(self, parser: &mut P) -> T;
fn force_sync_skip(self) -> Self;
}
impl<T, E> ResultExt<T, E> for Result<T, E>
where
T: ast::IllFormed,
{
fn synchronize<P: Synchronizable<E>>(self, parser: &mut P) -> T {
match self {
Ok(value) => value,
Err((error, sync)) => {
parser.synchronize(error, sync);
T::ill_formed()
}
}
}
fn force_sync_skip(self) -> Self {
self.map_err(
|(error, strategy)| match strategy {
Strategy::Keep => (error, Strategy::skip_one()),
_ => (error, strategy)
}
)
}
}