1#![allow(clippy::result_unit_err)]
8
9use crate::commands::primitives::PrimitiveIdentifier;
10use crate::engine::state::State;
11use crate::engine::utils::memory::MemoryManager;
12use crate::engine::{EngineAux, EngineReferences, EngineTypes};
13use crate::prelude::{Mouth, TeXMode};
14use crate::tex::characters::{Character, StringLineSource};
15use crate::tex::tokens::control_sequences::CSHandler;
16use crate::tex::tokens::StandardToken;
17use crate::tex::tokens::Token;
18use std::fmt::Debug;
19use std::marker::PhantomData;
20use thiserror::Error;
21
22#[derive(Debug, Error)]
23pub enum TeXError<ET: EngineTypes> {
24 #[error(transparent)]
25 InvalidCharacter(#[from] InvalidCharacter<ET::Char>),
26 #[error("Use of {0} doesn't match its definition")]
27 WrongDefinition(String),
28 #[error("! File ended while scanning use of `{0}`")]
29 FileEndedWhileScanningUseOf(String),
30 #[error("Undefined {0}")]
31 Undefined(String),
32 #[error("! Too many }}'s")]
33 TooManyCloseBraces,
34 #[error("Emergency stop.")]
35 EmergencyStop,
36 #[error("! Missing {{ inserted")]
37 MissingBegingroup,
38 #[error("! Missing }} inserted")]
39 MissingEndgroup,
40 #[error("! Missing $ inserted")]
41 MissingDollar,
42 #[error("Missing number, treated as zero.")]
43 MissingNumber,
44 #[error("Illegal unit of measure (pt inserted)")]
45 MissingUnit,
46 #[error("Runaway argument? Paragraph ended before {0} was complete.")]
47 ParagraphEnded(String),
48 #[error("! Missing {} inserted",.0[0])]
49 MissingKeyword(&'static [&'static str]),
50 #[error("Incomplete {}; all text was ignored after line {line_no}",.name.display::<u8>(Some(b'\\')))]
51 IncompleteConditional {
52 name: PrimitiveIdentifier,
53 line_no: usize,
54 },
55 #[error("You can't use {} in {mode} mode",.name.display::<u8>(Some(b'\\')))]
56 NotAllowedInMode {
57 name: PrimitiveIdentifier,
58 mode: TeXMode,
59 },
60 #[error("Errror: {0}")]
61 General(String),
62 #[error(transparent)]
63 Fmt(#[from] std::fmt::Error),
64 }
73impl<ET: EngineTypes> From<String> for TeXError<ET> {
74 #[inline]
75 fn from(value: String) -> Self {
76 Self::General(value)
77 }
78}
79
80macro_rules! throw {
81 ($aux:expr,$state:expr,$mouth:expr,$f:ident($($arg:expr),*) => $err:expr) => {{
82 let eh = &$aux.error_handler;
83 let ret = eh.$f(&$aux.outputs,&$aux.memory,$state$(,$arg)*);
84 match ret {
85 Ok(Some(src)) => {
86 $mouth.push_string(src);
87 Ok(())
88 },
89 Ok(None) => Ok(()),
90 _ => Err($err)
91 }
92 }};
93}
94impl<ET: EngineTypes> TeXError<ET> {
95 pub fn incomplete_conditional<M: Mouth<ET>>(
98 aux: &EngineAux<ET>,
99 state: &ET::State,
100 mouth: &mut M,
101 name: PrimitiveIdentifier,
102 ) -> TeXResult<(), ET> {
103 let line_no = mouth.line_number();
104 throw!(aux,state,mouth,incomplete_conditional(name,line_no) => Self::IncompleteConditional{name,line_no})
105 }
106 pub fn wrong_definition<M: Mouth<ET>>(
109 aux: &EngineAux<ET>,
110 state: &ET::State,
111 mouth: &mut M,
112 found: &ET::Token,
113 expected: &ET::Token,
114 in_macro: &ET::Token,
115 ) -> TeXResult<(), ET> {
116 throw!(aux,state,mouth,wrong_definition(found,expected,in_macro) =>
117 Self::WrongDefinition(Token::display(in_macro,aux.memory.cs_interner(),state.get_catcode_scheme(),state.get_escape_char()).to_string())
118 )
119 }
120 pub fn file_end_while_use<M: Mouth<ET>>(
123 aux: &EngineAux<ET>,
124 state: &ET::State,
125 mouth: &mut M,
126 in_macro: &ET::Token,
127 ) -> TeXResult<(), ET> {
128 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()))
129 }
130 pub fn undefined<M: Mouth<ET>>(
133 aux: &EngineAux<ET>,
134 state: &ET::State,
135 mouth: &mut M,
136 token: &ET::Token,
137 ) -> TeXResult<(), ET> {
138 throw!(aux,state,mouth,undefined(token) => Self::Undefined(
139 match token.to_enum() {
140 StandardToken::ControlSequence(cs) =>
141 format!("control sequence {}{}",ET::Char::display_opt(state.get_escape_char()),aux.memory.cs_interner().resolve(&cs)),
142 StandardToken::Character(c,_) =>
143 format!("active character {}",c.display()),
144 StandardToken::Primitive(_) => unreachable!()
145 }
146 ))
147 }
148 pub fn not_allowed_in_mode<M: Mouth<ET>>(
151 aux: &EngineAux<ET>,
152 state: &ET::State,
153 mouth: &mut M,
154 name: PrimitiveIdentifier,
155 mode: TeXMode,
156 ) -> TeXResult<(), ET> {
157 throw!(aux,state,mouth,not_allowed_in_mode(name,mode) => Self::NotAllowedInMode{name,mode})
158 }
159 pub fn missing_begingroup<M: Mouth<ET>>(
162 aux: &EngineAux<ET>,
163 state: &ET::State,
164 mouth: &mut M,
165 ) -> TeXResult<(), ET> {
166 throw!(aux,state,mouth,missing_begingroup() => Self::MissingBegingroup)
167 }
168 pub fn missing_endgroup<M: Mouth<ET>>(
171 aux: &EngineAux<ET>,
172 state: &ET::State,
173 mouth: &mut M,
174 ) -> TeXResult<(), ET> {
175 throw!(aux,state,mouth,missing_endgroup() => Self::MissingEndgroup)
176 }
177 pub fn missing_number<M: Mouth<ET>>(
180 aux: &EngineAux<ET>,
181 state: &ET::State,
182 mouth: &mut M,
183 ) -> TeXResult<(), ET> {
184 throw!(aux,state,mouth,missing_number() => Self::MissingNumber)
185 }
186 pub fn missing_unit<M: Mouth<ET>>(
189 aux: &EngineAux<ET>,
190 state: &ET::State,
191 mouth: &mut M,
192 ) -> TeXResult<(), ET> {
193 throw!(aux,state,mouth,missing_number() => Self::MissingUnit)
194 }
195 pub fn missing_keyword<M: Mouth<ET>>(
198 aux: &EngineAux<ET>,
199 state: &ET::State,
200 mouth: &mut M,
201 kws: &'static [&'static str],
202 ) -> TeXResult<(), ET> {
203 throw!(aux,state,mouth,missing_keyword(kws) => Self::MissingKeyword(kws))
204 }
205 pub fn missing_dollar_inserted<M: Mouth<ET>>(
208 aux: &EngineAux<ET>,
209 state: &ET::State,
210 mouth: &mut M,
211 ) -> TeXResult<(), ET> {
212 throw!(aux,state,mouth,missing_dollar() => Self::MissingDollar)
213 }
214 pub fn paragraph_ended<M: Mouth<ET>>(
217 aux: &EngineAux<ET>,
218 state: &ET::State,
219 mouth: &mut M,
220 t: &ET::Token,
221 ) -> TeXResult<(), ET> {
222 throw!(aux,state,mouth,paragraph_ended(t) => Self::ParagraphEnded(
223 match t.to_enum() {
224 StandardToken::ControlSequence(cs) =>
225 format!("control sequence {}{}",ET::Char::display_opt(state.get_escape_char()),aux.memory.cs_interner().resolve(&cs)),
226 StandardToken::Character(c,_) =>
227 format!("active character {}",c.display()),
228 StandardToken::Primitive(_) => unreachable!()
229 }
230 ))
231 }
232}
233pub type TeXResult<A, ET> = Result<A, TeXError<ET>>;
234
235pub trait IntoErr<ET: EngineTypes, Err> {
236 fn into_err(self, aux: &EngineAux<ET>, state: &ET::State) -> Err;
237}
238impl<ET: EngineTypes, Err, A> IntoErr<ET, Err> for A
239where
240 Err: From<A>,
241{
242 fn into_err(self, _aux: &EngineAux<ET>, _state: &ET::State) -> Err {
243 self.into()
244 }
245}
246
247pub trait RecoverableError<ET: EngineTypes>: Sized {
248 fn recover<M: Mouth<ET>, Err>(
251 self,
252 aux: &EngineAux<ET>,
253 state: &ET::State,
254 mouth: &mut M,
255 ) -> Result<(), Err>
256 where
257 Self: IntoErr<ET, Err>;
258
259 #[inline]
262 fn throw<M: Mouth<ET>>(
263 self,
264 aux: &EngineAux<ET>,
265 state: &ET::State,
266 mouth: &mut M,
267 ) -> Result<(), TeXError<ET>>
268 where
269 Self: IntoErr<ET, TeXError<ET>>,
270 {
271 self.recover(aux, state, mouth)
272 .map_err(|e| e.into_err(aux, state))
273 }
274}
275macro_rules! split {
276 ($self:ident,$aux:expr,$state:ident,$mouth:ident,$f:ident($($arg:expr),*)) => {{
277 throw!($aux,$state,$mouth,$f($($arg),*) => $self.into_err($aux,$state))
278 }};
279}
280
281#[derive(Debug, Error)]
282#[error("! Too many }}'s")]
283pub enum GulletError<C: Character> {
284 #[error("! Text line contains an invalid character.\n{}",.0.display())]
285 InvalidChar(C),
286 TooManyCloseBraces,
287}
288impl<C: Character> From<InvalidCharacter<C>> for GulletError<C> {
289 fn from(InvalidCharacter(c): InvalidCharacter<C>) -> Self {
290 Self::InvalidChar(c)
291 }
292}
293impl<C: Character> From<TooManyCloseBraces> for GulletError<C> {
294 fn from(_: TooManyCloseBraces) -> Self {
295 Self::TooManyCloseBraces
296 }
297}
298impl<ET: EngineTypes> From<GulletError<ET::Char>> for TeXError<ET> {
299 fn from(e: GulletError<ET::Char>) -> Self {
300 match e {
301 GulletError::InvalidChar(c) => Self::InvalidCharacter(InvalidCharacter(c)),
302 GulletError::TooManyCloseBraces => Self::TooManyCloseBraces,
303 }
304 }
305}
306
307#[derive(Debug, Error)]
309#[error("! Text line contains an invalid character.\n{}",.0.display())]
310pub struct InvalidCharacter<C: Character>(pub C);
311impl<ET: EngineTypes> RecoverableError<ET> for InvalidCharacter<ET::Char> {
312 fn recover<M: Mouth<ET>, Err>(
313 self,
314 aux: &EngineAux<ET>,
315 state: &ET::State,
316 mouth: &mut M,
317 ) -> Result<(), Err>
318 where
319 Self: IntoErr<ET, Err>,
320 {
321 split!(self, aux, state, mouth, invalid_character(self.0))
322 }
323}
324
325#[derive(Debug, Error)]
326#[error("! Too many }}'s")]
327pub struct TooManyCloseBraces;
328impl<ET: EngineTypes> RecoverableError<ET> for TooManyCloseBraces {
329 fn recover<M: Mouth<ET>, Err>(
330 self,
331 aux: &EngineAux<ET>,
332 state: &ET::State,
333 mouth: &mut M,
334 ) -> Result<(), Err>
335 where
336 Self: IntoErr<ET, Err>,
337 {
338 split!(self, aux, state, mouth, too_many_closebraces())
339 }
340}
341impl<ET: EngineTypes> From<TooManyCloseBraces> for TeXError<ET> {
342 fn from(_: TooManyCloseBraces) -> Self {
343 Self::TooManyCloseBraces
344 }
345}
346
347pub trait ErrorHandler<ET: EngineTypes> {
349 #[inline]
353 fn invalid_character(
354 &self,
355 _out: &ET::Outputs,
356 _memory: &MemoryManager<ET::Token>,
357 _state: &ET::State,
358 _c: ET::Char,
359 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
360 Err(())
361 }
362 #[inline]
366 fn wrong_definition(
367 &self,
368 _out: &ET::Outputs,
369 _memory: &MemoryManager<ET::Token>,
370 _state: &ET::State,
371 _found: &ET::Token,
372 _expected: &ET::Token,
373 _in_macro: &ET::Token,
374 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
375 Err(())
376 }
377 #[inline]
381 fn file_end_while_use(
382 &self,
383 _out: &ET::Outputs,
384 _memory: &MemoryManager<ET::Token>,
385 _state: &ET::State,
386 _in_macro: &ET::Token,
387 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
388 Err(())
389 }
390 #[inline]
394 fn too_many_closebraces(
395 &self,
396 _out: &ET::Outputs,
397 _memory: &MemoryManager<ET::Token>,
398 _state: &ET::State,
399 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
400 Err(())
401 }
402 #[inline]
406 fn missing_begingroup(
407 &self,
408 _out: &ET::Outputs,
409 _memory: &MemoryManager<ET::Token>,
410 _state: &ET::State,
411 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
412 Err(())
413 }
414 #[inline]
418 fn missing_dollar(
419 &self,
420 _out: &ET::Outputs,
421 _memory: &MemoryManager<ET::Token>,
422 _state: &ET::State,
423 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
424 Err(())
425 }
426 #[inline]
430 fn missing_endgroup(
431 &self,
432 _out: &ET::Outputs,
433 _memory: &MemoryManager<ET::Token>,
434 _state: &ET::State,
435 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
436 Err(())
437 }
438
439 #[inline]
443 fn incomplete_conditional(
444 &self,
445 _out: &ET::Outputs,
446 _memory: &MemoryManager<ET::Token>,
447 _state: &ET::State,
448 _name: PrimitiveIdentifier,
449 _line_no: usize,
450 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
451 Err(())
452 }
453
454 #[inline]
458 fn undefined(
459 &self,
460 _out: &ET::Outputs,
461 _memory: &MemoryManager<ET::Token>,
462 _state: &ET::State,
463 _token: &ET::Token,
464 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
465 Err(())
466 }
467
468 #[inline]
472 fn paragraph_ended(
473 &self,
474 _out: &ET::Outputs,
475 _memory: &MemoryManager<ET::Token>,
476 _state: &ET::State,
477 _token: &ET::Token,
478 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
479 Err(())
480 }
481
482 #[inline]
486 fn not_allowed_in_mode(
487 &self,
488 _out: &ET::Outputs,
489 _memory: &MemoryManager<ET::Token>,
490 _state: &ET::State,
491 _name: PrimitiveIdentifier,
492 _mode: TeXMode,
493 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
494 Err(())
495 }
496 #[inline]
500 fn missing_keyword(
501 &self,
502 _out: &ET::Outputs,
503 _memory: &MemoryManager<ET::Token>,
504 _state: &ET::State,
505 _kws: &'static [&'static str],
506 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
507 Err(())
508 }
509
510 #[inline]
514 fn missing_number(
515 &self,
516 _out: &ET::Outputs,
517 _memory: &MemoryManager<ET::Token>,
518 _state: &ET::State,
519 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
520 Err(())
521 }
522
523 #[inline]
527 fn other(
528 &self,
529 _out: &ET::Outputs,
530 _memory: &MemoryManager<ET::Token>,
531 _state: &ET::State,
532 _msg: &str,
533 ) -> Result<Option<StringLineSource<ET::Char>>, ()> {
534 Err(())
535 }
536
537 fn new() -> Self;
551}
552
553pub struct ErrorThrower<ET: EngineTypes>(PhantomData<ET>);
555impl<ET: EngineTypes> ErrorHandler<ET> for ErrorThrower<ET> {
556 fn new() -> Self {
557 Self(PhantomData)
558 }
559}
560
561impl<ET: EngineTypes> EngineReferences<'_, ET> {
562 #[inline]
565 pub fn general_error(&mut self, msg: String) -> TeXResult<(), ET> {
566 throw!(self.aux,self.state,self.mouth,other(&msg) => TeXError::General(msg))
567 }
568}