#![allow(clippy::result_unit_err)]
use crate::commands::primitives::PrimitiveIdentifier;
use crate::engine::state::State;
use crate::engine::utils::memory::MemoryManager;
use crate::engine::{EngineAux, EngineReferences, EngineTypes};
use crate::prelude::{Mouth, TeXMode};
use crate::tex::characters::{Character, StringLineSource};
use crate::tex::tokens::control_sequences::CSHandler;
use crate::tex::tokens::StandardToken;
use crate::tex::tokens::Token;
use std::fmt::Debug;
use std::marker::PhantomData;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TeXError<ET: EngineTypes> {
#[error(transparent)]
InvalidCharacter(#[from] InvalidCharacter<ET::Char>),
#[error("Use of {0} doesn't match its definition")]
WrongDefinition(String),
#[error("! File ended while scanning use of `{0}`")]
FileEndedWhileScanningUseOf(String),
#[error("Undefined {0}")]
Undefined(String),
#[error("! Too many }}'s")]
TooManyCloseBraces,
#[error("Emergency stop.")]
EmergencyStop,
#[error("! Missing {{ inserted")]
MissingBegingroup,
#[error("! Missing }} inserted")]
MissingEndgroup,
#[error("! Missing $ inserted")]
MissingDollar,
#[error("Missing number, treated as zero.")]
MissingNumber,
#[error("Illegal unit of measure (pt inserted)")]
MissingUnit,
#[error("Runaway argument? Paragraph ended before {0} was complete.")]
ParagraphEnded(String),
#[error("! Missing {} inserted",.0[0])]
MissingKeyword(&'static [&'static str]),
#[error("Incomplete {}; all text was ignored after line {line_no}",.name.display::<u8>(Some(b'\\')))]
IncompleteConditional {
name: PrimitiveIdentifier,
line_no: usize,
},
#[error("You can't use {} in {mode} mode",.name.display::<u8>(Some(b'\\')))]
NotAllowedInMode {
name: PrimitiveIdentifier,
mode: TeXMode,
},
#[error("Errror: {0}")]
General(String),
#[error(transparent)]
Fmt(#[from] std::fmt::Error),
}
impl<ET: EngineTypes> From<String> for TeXError<ET> {
#[inline]
fn from(value: String) -> Self {
Self::General(value)
}
}
macro_rules! throw {
($aux:expr,$state:expr,$mouth:expr,$f:ident($($arg:expr),*) => $err:expr) => {{
let eh = &$aux.error_handler;
let ret = eh.$f(&$aux.outputs,&$aux.memory,$state$(,$arg)*);
match ret {
Ok(Some(src)) => {
$mouth.push_string(src);
Ok(())
},
Ok(None) => Ok(()),
_ => Err($err)
}
}};
}
impl<ET: EngineTypes> TeXError<ET> {
pub fn incomplete_conditional<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
name: PrimitiveIdentifier,
) -> TeXResult<(), ET> {
let line_no = mouth.line_number();
throw!(aux,state,mouth,incomplete_conditional(name,line_no) => Self::IncompleteConditional{name,line_no})
}
pub fn wrong_definition<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
found: &ET::Token,
expected: &ET::Token,
in_macro: &ET::Token,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,wrong_definition(found,expected,in_macro) =>
Self::WrongDefinition(Token::display(in_macro,aux.memory.cs_interner(),state.get_catcode_scheme(),state.get_escape_char()).to_string())
)
}
pub fn file_end_while_use<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
in_macro: &ET::Token,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,file_end_while_use(in_macro) => Self::FileEndedWhileScanningUseOf(Token::display(in_macro,aux.memory.cs_interner(),state.get_catcode_scheme(),state.get_escape_char()).to_string()))
}
pub fn undefined<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
token: &ET::Token,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,undefined(token) => Self::Undefined(
match token.to_enum() {
StandardToken::ControlSequence(cs) =>
format!("control sequence {}{}",ET::Char::display_opt(state.get_escape_char()),aux.memory.cs_interner().resolve(&cs)),
StandardToken::Character(c,_) =>
format!("active character {}",c.display()),
StandardToken::Primitive(_) => unreachable!()
}
))
}
pub fn not_allowed_in_mode<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
name: PrimitiveIdentifier,
mode: TeXMode,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,not_allowed_in_mode(name,mode) => Self::NotAllowedInMode{name,mode})
}
pub fn missing_begingroup<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,missing_begingroup() => Self::MissingBegingroup)
}
pub fn missing_endgroup<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,missing_endgroup() => Self::MissingEndgroup)
}
pub fn missing_number<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,missing_number() => Self::MissingNumber)
}
pub fn missing_unit<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,missing_number() => Self::MissingUnit)
}
pub fn missing_keyword<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
kws: &'static [&'static str],
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,missing_keyword(kws) => Self::MissingKeyword(kws))
}
pub fn missing_dollar_inserted<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,missing_dollar() => Self::MissingDollar)
}
pub fn paragraph_ended<M: Mouth<ET>>(
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
t: &ET::Token,
) -> TeXResult<(), ET> {
throw!(aux,state,mouth,paragraph_ended(t) => Self::ParagraphEnded(
match t.to_enum() {
StandardToken::ControlSequence(cs) =>
format!("control sequence {}{}",ET::Char::display_opt(state.get_escape_char()),aux.memory.cs_interner().resolve(&cs)),
StandardToken::Character(c,_) =>
format!("active character {}",c.display()),
StandardToken::Primitive(_) => unreachable!()
}
))
}
}
pub type TeXResult<A, ET> = Result<A, TeXError<ET>>;
pub trait IntoErr<ET: EngineTypes, Err> {
fn into_err(self, aux: &EngineAux<ET>, state: &ET::State) -> Err;
}
impl<ET: EngineTypes, Err, A> IntoErr<ET, Err> for A
where
Err: From<A>,
{
fn into_err(self, _aux: &EngineAux<ET>, _state: &ET::State) -> Err {
self.into()
}
}
pub trait RecoverableError<ET: EngineTypes>: Sized {
fn recover<M: Mouth<ET>, Err>(
self,
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> Result<(), Err>
where
Self: IntoErr<ET, Err>;
#[inline]
fn throw<M: Mouth<ET>>(
self,
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> Result<(), TeXError<ET>>
where
Self: IntoErr<ET, TeXError<ET>>,
{
self.recover(aux, state, mouth)
.map_err(|e| e.into_err(aux, state))
}
}
macro_rules! split {
($self:ident,$aux:expr,$state:ident,$mouth:ident,$f:ident($($arg:expr),*)) => {{
throw!($aux,$state,$mouth,$f($($arg),*) => $self.into_err($aux,$state))
}};
}
#[derive(Debug, Error)]
#[error("! Too many }}'s")]
pub enum GulletError<C: Character> {
#[error("! Text line contains an invalid character.\n{}",.0.display())]
InvalidChar(C),
TooManyCloseBraces,
}
impl<C: Character> From<InvalidCharacter<C>> for GulletError<C> {
fn from(InvalidCharacter(c): InvalidCharacter<C>) -> Self {
Self::InvalidChar(c)
}
}
impl<C: Character> From<TooManyCloseBraces> for GulletError<C> {
fn from(_: TooManyCloseBraces) -> Self {
Self::TooManyCloseBraces
}
}
impl<ET: EngineTypes> From<GulletError<ET::Char>> for TeXError<ET> {
fn from(e: GulletError<ET::Char>) -> Self {
match e {
GulletError::InvalidChar(c) => Self::InvalidCharacter(InvalidCharacter(c)),
GulletError::TooManyCloseBraces => Self::TooManyCloseBraces,
}
}
}
#[derive(Debug, Error)]
#[error("! Text line contains an invalid character.\n{}",.0.display())]
pub struct InvalidCharacter<C: Character>(pub C);
impl<ET: EngineTypes> RecoverableError<ET> for InvalidCharacter<ET::Char> {
fn recover<M: Mouth<ET>, Err>(
self,
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> Result<(), Err>
where
Self: IntoErr<ET, Err>,
{
split!(self, aux, state, mouth, invalid_character(self.0))
}
}
#[derive(Debug, Error)]
#[error("! Too many }}'s")]
pub struct TooManyCloseBraces;
impl<ET: EngineTypes> RecoverableError<ET> for TooManyCloseBraces {
fn recover<M: Mouth<ET>, Err>(
self,
aux: &EngineAux<ET>,
state: &ET::State,
mouth: &mut M,
) -> Result<(), Err>
where
Self: IntoErr<ET, Err>,
{
split!(self, aux, state, mouth, too_many_closebraces())
}
}
impl<ET: EngineTypes> From<TooManyCloseBraces> for TeXError<ET> {
fn from(_: TooManyCloseBraces) -> Self {
Self::TooManyCloseBraces
}
}
pub trait ErrorHandler<ET: EngineTypes> {
#[inline]
fn invalid_character(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_c: ET::Char,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn wrong_definition(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_found: &ET::Token,
_expected: &ET::Token,
_in_macro: &ET::Token,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn file_end_while_use(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_in_macro: &ET::Token,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn too_many_closebraces(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn missing_begingroup(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn missing_dollar(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn missing_endgroup(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn incomplete_conditional(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_name: PrimitiveIdentifier,
_line_no: usize,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn undefined(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_token: &ET::Token,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn paragraph_ended(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_token: &ET::Token,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn not_allowed_in_mode(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_name: PrimitiveIdentifier,
_mode: TeXMode,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn missing_keyword(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_kws: &'static [&'static str],
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn missing_number(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
#[inline]
fn other(
&self,
_out: &ET::Outputs,
_memory: &MemoryManager<ET::Token>,
_state: &ET::State,
_msg: &str,
) -> Result<Option<StringLineSource<ET::Char>>, ()> {
Err(())
}
fn new() -> Self;
}
pub struct ErrorThrower<ET: EngineTypes>(PhantomData<ET>);
impl<ET: EngineTypes> ErrorHandler<ET> for ErrorThrower<ET> {
fn new() -> Self {
Self(PhantomData)
}
}
impl<ET: EngineTypes> EngineReferences<'_, ET> {
#[inline]
pub fn general_error(&mut self, msg: String) -> TeXResult<(), ET> {
throw!(self.aux,self.state,self.mouth,other(&msg) => TeXError::General(msg))
}
}