Skip to main content

tex_engine/engine/
gullet.rs

1/*! A [`Gullet`] is the part of the engine that reads tokens from the input stream and expands them;
2   including conditionals etc.
3*/
4pub mod hvalign;
5pub mod methods;
6
7use crate::commands::primitives::{PrimitiveIdentifier, PRIMITIVES};
8use crate::commands::{ActiveConditional, CharOrPrimitive, Macro, ResolvedToken, TeXCommand};
9use crate::engine::gullet::hvalign::AlignData;
10use crate::engine::mouth::Mouth;
11use crate::engine::state::State;
12use crate::engine::utils::outputs::Outputs;
13use crate::engine::{EngineAux, EngineReferences, EngineTypes};
14use crate::tex::catcodes::CommandCode;
15use crate::tex::characters::Character;
16use crate::tex::numerics::{MuSkip, NumSet, Skip};
17use crate::tex::tokens::control_sequences::CSHandler;
18use crate::tex::tokens::token_lists::MacroExpansion;
19use crate::tex::tokens::{StandardToken, Token};
20use crate::utils::errors::{
21    GulletError, RecoverableError, TeXError, TeXResult, TooManyCloseBraces,
22};
23use either::Either;
24use std::marker::PhantomData;
25
26/// A [`Gullet`] is the part of the engine that reads tokens from the input stream and expands them;
27/// including conditionals etc.
28/// Additionally, it takes care of reading keywords, strings (e.g. filenames in `\input`),
29/// integers, dimensions, skips...
30/// Basically, all processing of [`Token`]s that does not result in scoped [`State`] changes or
31/// [nodes](crate::tex::nodes::NodeTrait) in the [`Stomach`](crate::engine::stomach::Stomach).
32/// The canonical implementation of a [`Gullet`] is [`DefaultGullet`].
33///
34/// As part of that, it has to do some bookkeeping already when reading [`Token`]s from the
35/// [`Mouth`] and therefore implements wrapper methods around its methods as well.
36///
37/// Note that we do not require `ET:`[`EngineTypes`]`<`[`Gullet`](EngineTypes::Gullet)`=Self>` - this allows for
38/// implementing your own Gullet by just wrapping an existing implementation in a new wrapper struct and pass on functionality
39/// to the inner Gullet, which would otherwise
40/// fail since `ET::Gullet` would be the outer wrapper struct, not the inner one.
41pub trait Gullet<ET: EngineTypes> {
42    /// Instantiate a new Gullet
43    fn new(aux: &mut EngineAux<ET>, state: &mut ET::State, mouth: &mut ET::Mouth) -> Self;
44
45    /// Push a new [`AlignData`] onto the stack, i.e. on `\halign` or `\valign`
46    fn push_align(&mut self, ad: AlignData<ET::Token, ET::Dim>);
47    /// Pop the last [`AlignData`] from the stack, i.e. at the end of `\halign` or `\valign`
48    fn pop_align(&mut self) -> Option<AlignData<ET::Token, ET::Dim>>;
49    /// Inspect the current [`AlignData`], if any (i.e. if we are in an `\halign` or `\valign`)
50    fn get_align_data(&mut self) -> Option<&mut AlignData<ET::Token, ET::Dim>>;
51    /// Inspect the current [`ActiveConditional`], if any (i.e. if we are in an `\if` or similar)
52    fn get_conditional(&self) -> Option<ActiveConditional<ET::Int>>;
53    /// Get a mutable reference to the stack of [`ActiveConditional`]s
54    fn get_conditionals(&mut self) -> &mut Vec<ActiveConditional<ET::Int>>;
55    /// Get a mutable reference to the counter for the number of `\csname`s we are currently in
56    fn csnames(&mut self) -> &mut usize;
57
58    /// Wrapper around [`Mouth::iterate`] that, in case we are in an `\halign` or `\valign`,
59    /// make sure to replace `&`, `\cr` etc. with the appropriate tokens.
60    /// See also [`EngineReferences::iterate`].
61    fn iterate<
62        R,
63        E,
64        F: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<Option<R>, ET>,
65    >(
66        &mut self,
67        mouth: &mut ET::Mouth,
68        aux: &mut EngineAux<ET>,
69        state: &ET::State,
70        mut cont: F,
71        eof: E,
72    ) -> TeXResult<R, ET>
73    where
74        E: Fn(&EngineAux<ET>, &ET::State, &mut ET::Mouth) -> TeXResult<(), ET>,
75    {
76        match self.get_align_data() {
77            None => mouth.iterate(aux, state, |a, t| cont(a, state, t), eof),
78            Some(data) => mouth.iterate(
79                aux,
80                state,
81                |aux, t| {
82                    if let Some(false) = data.check_token(&t) {
83                        return Err(TeXError::TooManyCloseBraces);
84                    }
85                    cont(aux, state, t)
86                },
87                eof,
88            ),
89        }
90    }
91
92    /// Wrapper around [`Mouth::get_next`] that, in case we are in an `\halign` or `\valign`,
93    /// make sure to replace `&`, `\cr` etc. with the appropriate tokens. If `noexpand` is `true`, `\cr`, `\crcr` and `&` are not expanded.
94    /// See also [`EngineReferences::get_next`].
95    fn get_next(
96        &mut self,
97        mouth: &mut ET::Mouth,
98        aux: &mut EngineAux<ET>,
99        state: &ET::State,
100        noexpand: bool,
101    ) -> Result<Option<ET::Token>, GulletError<ET::Char>> {
102        match self.get_align_data() {
103            None => Ok(mouth.get_next(aux, state)?),
104            Some(a) => {
105                if let Some(t) = mouth.get_next(aux, state)? {
106                    match a.check_token(&t) {
107                        Some(false) => {
108                            TooManyCloseBraces.recover::<_, GulletError<_>>(aux, state, mouth)?;
109                            return Ok(Some(t));
110                        }
111                        Some(true) => return Ok(Some(t)),
112                        _ => (),
113                    }
114                    if noexpand || a.ingroups != a.groupval() {
115                        return Ok(Some(t));
116                    }
117                    match t.command_code() {
118                        CommandCode::AlignmentTab => {
119                            let t = a.on_alignment_tab(mouth, aux);
120                            if a.check_token(&t) == Some(false) {
121                                TooManyCloseBraces
122                                    .recover::<_, GulletError<_>>(aux, state, mouth)?
123                            }
124                            Ok(Some(t))
125                        }
126                        CommandCode::Escape | CommandCode::Active | CommandCode::Primitive => {
127                            match Self::char_or_primitive(state, &t) {
128                                Some(CharOrPrimitive::Primitive(name))
129                                    if name == PRIMITIVES.cr || name == PRIMITIVES.crcr =>
130                                {
131                                    let t = a.on_cr(mouth, aux, state);
132                                    if a.check_token(&t) == Some(false) {
133                                        TooManyCloseBraces
134                                            .recover::<_, GulletError<_>>(aux, state, mouth)?
135                                    }
136                                    Ok(Some(t))
137                                }
138                                Some(CharOrPrimitive::Primitive(name))
139                                    if name == PRIMITIVES.span =>
140                                {
141                                    a.span = true;
142                                    let t = a.on_alignment_tab(mouth, aux);
143                                    if a.check_token(&t) == Some(false) {
144                                        TooManyCloseBraces
145                                            .recover::<_, GulletError<_>>(aux, state, mouth)?
146                                    }
147                                    Ok(Some(t))
148                                }
149                                _ => Ok(Some(t)),
150                            }
151                        }
152                        _ => Ok(Some(t)),
153                    }
154                } else {
155                    Ok(None)
156                }
157            }
158        }
159    }
160
161    /// Wrapper around [`Mouth::read_until_endgroup`] that, in case we are in an `\halign` or `\valign`,
162    /// make sure to replace `&`, `\cr` etc. with the appropriate tokens.
163    /// See also [`EngineReferences::read_until_endgroup`].
164    fn read_until_endgroup<
165        E,
166        F: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<(), ET>,
167    >(
168        &mut self,
169        mouth: &mut ET::Mouth,
170        aux: &mut EngineAux<ET>,
171        state: &ET::State,
172        mut cont: F,
173        eof: E,
174    ) -> TeXResult<ET::Token, ET>
175    where
176        E: Fn(&EngineAux<ET>, &ET::State, &mut ET::Mouth) -> TeXResult<(), ET>,
177    {
178        match self.get_align_data() {
179            None => (),
180            Some(d) => {
181                if d.ingroups == 0 {
182                    TooManyCloseBraces.throw(aux, state, mouth)?
183                } else {
184                    d.ingroups -= 1
185                }
186            }
187        }
188        mouth.read_until_endgroup(aux, state, |a, t| cont(a, state, t), eof)
189    }
190
191    /// Wrapper around [`Mouth::requeue`] that makes sure to update the [`AlignData`] if we are in an
192    /// `\halign` or `\valign`.
193    /// See also [`EngineReferences::requeue`].
194    fn requeue(
195        &mut self,
196        aux: &EngineAux<ET>,
197        state: &ET::State,
198        mouth: &mut ET::Mouth,
199        t: ET::Token,
200    ) -> TeXResult<(), ET> {
201        if let Some(data) = self.get_align_data() {
202            let cc = t.command_code();
203            if cc == CommandCode::BeginGroup {
204                if data.ingroups == 0 {
205                    TooManyCloseBraces.throw(aux, state, mouth)?
206                }
207                data.ingroups -= 1;
208            } else if cc == CommandCode::EndGroup {
209                data.ingroups += 1;
210            }
211        }
212        mouth.requeue(t);
213        Ok(())
214    }
215
216    /// Read an integer from the input stream. See also [`EngineReferences::read_int`].
217    #[inline]
218    fn read_int(
219        engine: &mut EngineReferences<ET>,
220        skip_eq: bool,
221        in_token: &ET::Token,
222    ) -> TeXResult<ET::Int, ET> {
223        methods::read_int(engine, skip_eq, in_token)
224    }
225
226    /// Read a dimension value from the input stream. See also [`EngineReferences::read_dim`].
227    #[inline]
228    fn read_dim(
229        engine: &mut EngineReferences<ET>,
230        skip_eq: bool,
231        in_token: &ET::Token,
232    ) -> TeXResult<ET::Dim, ET> {
233        methods::read_dim(engine, skip_eq, in_token)
234    }
235
236    /// Read a skip value from the input stream. See also [`EngineReferences::read_skip`].
237    #[inline]
238    fn read_skip(
239        engine: &mut EngineReferences<ET>,
240        skip_eq: bool,
241        in_token: &ET::Token,
242    ) -> TeXResult<Skip<ET::Dim>, ET> {
243        methods::read_skip(engine, skip_eq, in_token)
244    }
245
246    /// Read a muskip value from the input stream. See also [`EngineReferences::read_muskip`].
247    #[inline]
248    fn read_muskip(
249        engine: &mut EngineReferences<ET>,
250        skip_eq: bool,
251        in_token: &ET::Token,
252    ) -> TeXResult<MuSkip<ET::MuDim>, ET> {
253        methods::read_muskip(engine, skip_eq, in_token)
254    }
255
256    /// Read a mudim value from the input stream (for `\mkern`). See also [`EngineReferences::read_mudim`].
257    #[inline]
258    fn read_mudim(
259        engine: &mut EngineReferences<ET>,
260        skip_eq: bool,
261        in_token: &ET::Token,
262    ) -> TeXResult<ET::MuDim, ET> {
263        methods::read_mudim(engine, skip_eq, in_token)
264    }
265
266    /// Read a string from the input stream. See also [`EngineReferences::read_string`].
267    #[inline]
268    fn read_string(
269        engine: &mut EngineReferences<ET>,
270        skip_eq: bool,
271        target: &mut String,
272        in_token: &ET::Token,
273    ) -> TeXResult<(), ET> {
274        methods::read_string(engine, skip_eq, target, in_token)
275    }
276    #[inline]
277    fn read_maybe_braced_string(
278        engine: &mut EngineReferences<ET>,
279        skip_eq: bool,
280        target: &mut String,
281        in_token: &ET::Token,
282    ) -> TeXResult<(), ET> {
283        methods::read_maybe_braced_string(engine, skip_eq, target, in_token)
284    }
285
286    /// Check whether the input stream starts with the given keyword. See also [`EngineReferences::read_keyword`].
287    #[inline]
288    fn read_keyword(engine: &mut EngineReferences<ET>, kw: &[u8]) -> TeXResult<bool, ET> {
289        methods::read_keyword(engine, kw, None)
290    }
291
292    /// Check whether the input stream starts with one of the given keywords. See also [`EngineReferences::read_keywords`].
293    #[inline]
294    fn read_keywords<'a>(
295        engine: &mut EngineReferences<ET>,
296        kw: &'a [&'a [u8]],
297    ) -> TeXResult<Option<&'a [u8]>, ET> {
298        methods::read_keywords(engine, kw, None)
299    }
300
301    /// Check whether the next character is one of the provided ones. See also [`EngineReferences::read_chars`].
302    #[inline]
303    fn read_chars(
304        engine: &mut EngineReferences<ET>,
305        kws: &[u8],
306    ) -> TeXResult<Either<u8, Option<ET::Token>>, ET> {
307        methods::read_chars(engine, kws)
308    }
309
310    /// Inspect the given [`Token`] and return its current definition, if any.
311    /// See also [`EngineReferences::resolve`].
312    fn resolve<'a>(state: &'a ET::State, token: &ET::Token) -> ResolvedToken<'a, ET> {
313        match token.to_enum() {
314            StandardToken::Character(c, CommandCode::Active) => {
315                ResolvedToken::Cmd(state.get_ac_command(c))
316            }
317            StandardToken::Character(c, o) => ResolvedToken::Tk { char: c, code: o },
318            StandardToken::ControlSequence(cs) => ResolvedToken::Cmd(state.get_command(&cs)),
319            StandardToken::Primitive(id) => ResolvedToken::Cmd(state.primitives().get_id(id)), //ResolvedToken::Cmd{token,cmd:state.primitives().get_id(id).map(|p| Command::Primitive {name:id,cmd:p.clone()}) }
320        }
321    }
322
323    /// Inspects the [`Token`] and returns either the [`PrimitiveIdentifier`] or the [`Character`] and [`CommandCode`]
324    /// or None if the token is neither a primitive nor a character. Useful for e.g. quick checks in an `\halign` whether the
325    /// token is a specific primitive (`\cr`,`\crcr`,`\span`) or a character (`&`, space).
326    fn char_or_primitive(state: &ET::State, token: &ET::Token) -> Option<CharOrPrimitive<ET>> {
327        match token.to_enum() {
328            StandardToken::Primitive(id) => Some(CharOrPrimitive::Primitive(id)),
329            StandardToken::Character(c, CommandCode::Active) => match state.get_ac_command(c) {
330                Some(TeXCommand::Primitive { name, .. }) => Some(CharOrPrimitive::Primitive(*name)),
331                Some(TeXCommand::Char { char, code }) => {
332                    Some(CharOrPrimitive::Char(*char, Some(*code)))
333                }
334                Some(TeXCommand::CharDef(c)) => Some(CharOrPrimitive::Char(*c, None)),
335                _ => None,
336            },
337            StandardToken::ControlSequence(cs) => match state.get_command(&cs) {
338                Some(TeXCommand::Primitive { name, .. }) => Some(CharOrPrimitive::Primitive(*name)),
339                Some(TeXCommand::Char { char, code }) => {
340                    Some(CharOrPrimitive::Char(*char, Some(*code)))
341                }
342                Some(TeXCommand::CharDef(c)) => Some(CharOrPrimitive::Char(*c, None)),
343                _ => None,
344            },
345            StandardToken::Character(c, code) => Some(CharOrPrimitive::Char(c, Some(code))),
346        }
347    }
348
349    /// Expand the given expandable [`Token`] with its given expansion function. See also
350    /// [`Expandable`](crate::commands::PrimitiveCommand::Expandable).
351    fn do_expandable(
352        engine: &mut EngineReferences<ET>,
353        name: PrimitiveIdentifier,
354        token: ET::Token,
355        f: fn(&mut EngineReferences<ET>, &mut Vec<ET::Token>, ET::Token) -> TeXResult<(), ET>,
356    ) -> TeXResult<(), ET> {
357        engine.trace_command(|engine| format!("{}", name.display(engine.state.get_escape_char())));
358        let mut exp = Vec::new(); // ExpansionContainer::new(engine.aux.memory.get_token_vec());
359        f(engine, &mut exp, token)?;
360        engine.mouth.push_vec(exp);
361        Ok(())
362    }
363    /// Expand the given [`Macro`] with its given expansion function.
364    fn do_macro(
365        engine: &mut EngineReferences<ET>,
366        m: Macro<ET::Token>,
367        token: ET::Token,
368    ) -> TeXResult<(), ET>;
369
370    /// Expand the given expandable [`Token`] with its given simple expansion function. See also
371    /// [`SimpleExpandable`](crate::commands::PrimitiveCommand::SimpleExpandable).
372    fn do_simple_expandable(
373        engine: &mut EngineReferences<ET>,
374        name: PrimitiveIdentifier,
375        token: ET::Token,
376        f: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>,
377    ) -> TeXResult<(), ET> {
378        engine.trace_command(|engine| format!("{}", name.display(engine.state.get_escape_char())));
379        f(engine, token)
380    }
381
382    /// Expand the given conditional. See also [`Conditional`](crate::commands::PrimitiveCommand::Conditional)
383    fn do_conditional(
384        engine: &mut EngineReferences<ET>,
385        name: PrimitiveIdentifier,
386        token: ET::Token,
387        f: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<bool, ET>,
388        unless: bool,
389    ) -> TeXResult<(), ET> {
390        let trace = engine.state.get_primitive_int(PRIMITIVES.tracingifs) > ET::Int::default();
391        let index = engine.gullet.get_conditionals().len();
392        engine
393            .gullet
394            .get_conditionals()
395            .push(ActiveConditional::Unfinished(name));
396        if trace {
397            //crate::debug_log!(error => "Here: {}",engine.preview());
398            engine.aux.outputs.write_neg1(format_args!(
399                "{{{}: (level {}) entered on line {}}}",
400                name.display(engine.state.get_escape_char()),
401                index + 1,
402                engine.mouth.line_number()
403            ));
404            //engine.aux.outputs.write_neg1(format_args!("Here: {}",engine.preview()));
405        }
406        let mut ret = f(engine, token.clone())?;
407        if unless {
408            ret = !ret
409        }
410        if ret {
411            if trace {
412                engine.aux.outputs.write_neg1("{true}");
413            }
414            if name != PRIMITIVES.ifcase {
415                match engine.gullet.get_conditionals().get_mut(index) {
416                    Some(u @ ActiveConditional::Unfinished(_)) => {
417                        *u = ActiveConditional::True(name)
418                    }
419                    _ => unreachable!(),
420                }
421            }
422        } else {
423            if trace {
424                engine.aux.outputs.write_neg1("{false}");
425            }
426            if name == PRIMITIVES.ifcase {
427                methods::case_loop(engine, index + 1)?;
428            } else {
429                methods::false_loop(engine, index + 1, true, false)?;
430            }
431            if trace {
432                if engine.gullet.get_conditionals().len() > index {
433                    engine.aux.outputs.write_neg1(format_args!(
434                        "{{{}else: {} (level {}) entered on line {}}}",
435                        ET::Char::display_opt(engine.state.get_escape_char()),
436                        name.display(engine.state.get_escape_char()),
437                        index + 1,
438                        engine.mouth.line_number()
439                    ));
440                } else {
441                    engine.aux.outputs.write_neg1(format_args!(
442                        "{{{}fi: {} (level {}) entered on line {}}}",
443                        ET::Char::display_opt(engine.state.get_escape_char()),
444                        name.display(engine.state.get_escape_char()),
445                        index + 1,
446                        engine.mouth.line_number()
447                    ));
448                }
449            }
450        }
451        Ok(())
452    }
453
454    /// Expand [`Token`]s until encountering an [`EndGroup`](CommandCode::EndGroup)-token.
455    /// See also [`EngineReferences::expand_until_endgroup`].
456    #[inline]
457    fn expand_until_endgroup<
458        Fn: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<(), ET>,
459    >(
460        engine: &mut EngineReferences<ET>,
461        expand_protected: bool,
462        edef_like: bool,
463        token: &ET::Token,
464        cont: Fn,
465    ) -> TeXResult<(), ET> {
466        methods::expand_until_endgroup(engine, expand_protected, edef_like, token, cont)
467    }
468}
469
470/// Default implementation of a [`Gullet`].
471pub struct DefaultGullet<ET: EngineTypes> {
472    align_data: Vec<AlignData<ET::Token, ET::Dim>>,
473    conditionals: Vec<ActiveConditional<ET::Int>>,
474    csnames: usize,
475    phantom: PhantomData<ET>,
476}
477impl<ET: EngineTypes> Gullet<ET> for DefaultGullet<ET> {
478    fn new(_aux: &mut EngineAux<ET>, _state: &mut ET::State, _mouth: &mut ET::Mouth) -> Self {
479        DefaultGullet {
480            align_data: Vec::new(),
481            phantom: PhantomData,
482            conditionals: Vec::new(),
483            csnames: 0,
484        }
485    }
486    fn csnames(&mut self) -> &mut usize {
487        &mut self.csnames
488    }
489    fn push_align(&mut self, ad: AlignData<ET::Token, ET::Dim>) {
490        self.align_data.push(ad)
491    }
492    fn pop_align(&mut self) -> Option<AlignData<ET::Token, ET::Dim>> {
493        self.align_data.pop()
494    }
495    fn do_macro(
496        engine: &mut EngineReferences<ET>,
497        m: Macro<ET::Token>,
498        token: ET::Token,
499    ) -> TeXResult<(), ET> {
500        let trace = engine.state.get_primitive_int(PRIMITIVES.tracingcommands) > ET::Int::default();
501        if trace {
502            match token.to_enum() {
503                StandardToken::ControlSequence(cs) => {
504                    engine.aux.outputs.write_neg1(format_args!(
505                        "~.{}{} {}",
506                        ET::Char::display_opt(engine.state.get_escape_char()),
507                        engine.aux.memory.cs_interner().resolve(&cs),
508                        m.meaning(
509                            engine.aux.memory.cs_interner(),
510                            engine.state.get_catcode_scheme(),
511                            engine.state.get_escape_char()
512                        )
513                    ));
514                    engine
515                        .aux
516                        .outputs
517                        .write_neg1(format_args!("Here: {}", engine.preview()));
518                }
519                StandardToken::Character(c, _) => {
520                    engine.aux.outputs.write_neg1(format_args!(
521                        "~.{} {}",
522                        c.display(),
523                        m.meaning(
524                            engine.aux.memory.cs_interner(),
525                            engine.state.get_catcode_scheme(),
526                            engine.state.get_escape_char()
527                        )
528                    ));
529                    //engine.aux.outputs.write_neg1(format_args!("Here: {}",engine.preview()));
530                }
531                _ => unreachable!(),
532            }
533            //crate::debug_log!(error => "Here: {}",engine.preview());
534
535            /*
536                        match engine.gullet.get_align_data() {
537                            Some(d) => {
538                                let ig = d.ingroups;
539                                let val = d.groupval();
540                                println!("Here: {} == {}",ig,val);
541                            }
542                            _ => ()
543                        }
544            */
545        }
546        if m.signature.params.is_empty() {
547            engine.mouth.push_exp(&m.expansion);
548            return Ok(());
549        }
550        let mut args = engine.mouth.get_args();
551        methods::read_arguments(engine, &mut args, m.signature.params, m.long, &token)?;
552        if trace {
553            for i in 0..m.signature.arity {
554                engine.aux.outputs.write_neg1(format_args!(
555                    "#{}<-{}",
556                    i + 1,
557                    crate::tex::tokens::token_lists::TokenListDisplay::from_vec(
558                        &args[i as usize],
559                        engine.aux.memory.cs_interner(),
560                        engine.state.get_catcode_scheme(),
561                        engine.state.get_escape_char(),
562                        false
563                    )
564                ));
565            }
566        }
567        if m.signature.arity == 0 {
568            engine.mouth.return_args(args);
569            engine.mouth.push_exp(&m.expansion);
570        } else {
571            engine
572                .mouth
573                .push_macro_exp(MacroExpansion::new(m.expansion, args))
574        }
575        Ok(())
576    }
577    fn get_align_data(&mut self) -> Option<&mut AlignData<ET::Token, ET::Dim>> {
578        self.align_data.last_mut()
579    }
580    fn get_conditional(&self) -> Option<ActiveConditional<ET::Int>> {
581        self.conditionals.last().cloned()
582    }
583    fn get_conditionals(&mut self) -> &mut Vec<ActiveConditional<ET::Int>> {
584        &mut self.conditionals
585    }
586}
587
588impl<ET: EngineTypes> EngineReferences<'_, ET> {
589    /// Yields [`Token`]s from the input stream until and passes them on to `cont` until `cont` returns `false`.
590    #[inline]
591    pub fn iterate<
592        R,
593        E,
594        F: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<Option<R>, ET>,
595    >(
596        &mut self,
597        mut cont: F,
598        eof: E,
599    ) -> TeXResult<R, ET>
600    where
601        E: Fn(&EngineAux<ET>, &ET::State, &mut ET::Mouth) -> TeXResult<(), ET>,
602    {
603        self.gullet.iterate(
604            self.mouth,
605            self.aux,
606            self.state,
607            |a, s, t| cont(a, s, t),
608            eof,
609        )
610    }
611
612    /// Push the provided [`Token`] back onto the input stream.
613    #[inline]
614    pub fn requeue(&mut self, t: ET::Token) -> TeXResult<(), ET> {
615        self.gullet.requeue(self.aux, self.state, self.mouth, t)
616    }
617
618    /// Yields [`Token`]s from the input stream until an [`EndGroup`](CommandCode::EndGroup)-token is encountered.
619    /// If the input stream is empty, returns a "File ended while scanning use of `in_token`" error.
620    #[inline]
621    pub fn read_until_endgroup<
622        F: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<(), ET>,
623    >(
624        &mut self,
625        in_token: &ET::Token,
626        cont: F,
627    ) -> TeXResult<ET::Token, ET> {
628        self.gullet
629            .read_until_endgroup(self.mouth, self.aux, self.state, cont, |a, s, m| {
630                TeXError::file_end_while_use(a, s, m, in_token)
631            })
632    }
633    /// Yields [`Token`]s from the input stream until an [`EndGroup`](CommandCode::EndGroup)-token is encountered, expanding
634    /// all expandable tokens along the way. If `expand_protected` is `true`, protected tokens are expanded as well.
635    /// If `edef_like` is `true`, the expansion is done in `\edef`-mode, i.e. tokens expanded from `\the` are not expanded
636    /// further and [`Parameter`](CommandCode::Parameter)-tokens expanded from `\the` are doubled.
637    #[inline]
638    pub fn expand_until_endgroup<
639        Fn: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<(), ET>,
640    >(
641        &mut self,
642        expand_protected: bool,
643        edef_like: bool,
644        token: &ET::Token,
645        mut cont: Fn,
646    ) -> TeXResult<(), ET> {
647        ET::Gullet::expand_until_endgroup(self, expand_protected, edef_like, token, |a, s, t| {
648            cont(a, s, t)
649        })
650    }
651    /// Yields the next [`Token`] from the input stream. If `noexpand` is `true`, `\cr`, `\crcr` and `&` are not expanded.
652    #[inline]
653    pub fn get_next(&mut self, noexpand: bool) -> Result<Option<ET::Token>, GulletError<ET::Char>> {
654        self.gullet
655            .get_next(self.mouth, self.aux, self.state, noexpand)
656    }
657
658    /// Yields the next [`Token`] from the input stream. If `noexpand` is `true`, `\cr`, `\crcr` and `&` are not expanded.
659    /// If the input stream is empty, returns a "File ended while scanning use of `in_token`" error.
660    pub fn need_next(&mut self, noexpand: bool, in_token: &ET::Token) -> TeXResult<ET::Token, ET> {
661        loop {
662            match self.get_next(noexpand)? {
663                Some(t) => return Ok(t),
664                None => TeXError::file_end_while_use(self.aux, self.state, self.mouth, in_token)?,
665            }
666        }
667    }
668    /// Read an integer value from the input stream.
669    #[inline]
670    pub fn read_int(
671        &mut self,
672        skip_eq: bool,
673        in_token: &ET::Token,
674    ) -> TeXResult<<ET::Num as NumSet>::Int, ET> {
675        ET::Gullet::read_int(self, skip_eq, in_token)
676    }
677    /// Read a string from the input stream.
678    #[inline]
679    pub fn read_string(
680        &mut self,
681        skip_eq: bool,
682        target: &mut String,
683        in_token: &ET::Token,
684    ) -> TeXResult<(), ET> {
685        ET::Gullet::read_string(self, skip_eq, target, in_token)
686    }
687    #[inline]
688    pub fn read_maybe_braced_string(
689        &mut self,
690        skip_eq: bool,
691        target: &mut String,
692        in_token: &ET::Token,
693    ) -> TeXResult<(), ET> {
694        ET::Gullet::read_maybe_braced_string(self, skip_eq, target, in_token)
695    }
696    /// Check whether the input stream starts with the given keyword.
697    #[inline]
698    pub fn read_keyword(&mut self, kw: &[u8]) -> TeXResult<bool, ET> {
699        ET::Gullet::read_keyword(self, kw)
700    }
701    /// Check whether the input stream starts with one of the given keywords.
702    #[inline]
703    pub fn read_keywords<'a>(&mut self, kw: &'a [&'a [u8]]) -> TeXResult<Option<&'a [u8]>, ET> {
704        ET::Gullet::read_keywords(self, kw)
705    }
706    /// Read a character code from the input stream.
707    pub fn read_charcode(
708        &mut self,
709        skip_eq: bool,
710        in_token: &ET::Token,
711    ) -> TeXResult<ET::Char, ET> {
712        let i: i64 = ET::Gullet::read_int(self, skip_eq, in_token)?.into();
713        if i < 0 {
714            self.general_error(format!("Bad character code ({})", i))?;
715            return Ok(ET::Char::from(0));
716        }
717        match ET::Char::try_from(i as u64) {
718            Ok(c) => Ok(c),
719            _ => {
720                self.general_error(format!("Bad character code ({})", i))?;
721                Ok(ET::Char::from(0))
722            }
723        }
724    }
725    /// Read a braced string from the input stream (unlike [`read_string`](EngineReferences::read_string)
726    /// which does not require braces). Compare e.g. `\message{Hello World}` (which would use `read_braced_string`)
727    /// and `\input myfile.tex` (which would use [`read_string`](EngineReferences::read_string)).
728    pub fn read_braced_string(
729        &mut self,
730        skip_ws: bool,
731        expand_protected: bool,
732        token: &ET::Token,
733        mut str: &mut String,
734    ) -> TeXResult<(), ET> {
735        loop {
736            match self.get_next(false)? {
737                Some(t) => match t.command_code() {
738                    CommandCode::BeginGroup => break,
739                    CommandCode::Space if skip_ws => (),
740                    _ => {
741                        TeXError::missing_begingroup(self.aux, self.state, self.mouth)?;
742                        break;
743                    }
744                },
745                None => TeXError::file_end_while_use(self.aux, self.state, self.mouth, token)?,
746            }
747        }
748        ET::Gullet::expand_until_endgroup(self, expand_protected, false, token, |a, s, t| {
749            Ok(t.display_fmt(
750                a.memory.cs_interner(),
751                s.get_catcode_scheme(),
752                s.get_escape_char(),
753                &mut str,
754            )?)
755        })
756    }
757
758    /// Expand expandable tokens until a [`BeginGroup`](CommandCode::BeginGroup) is found.
759    /// Throws a [`TeXError`] if any unexpandable other token is encountered.
760    /// If `allow_let` is true, other [`Token`]s which have been `\let` to a [`BeginGroup`](CommandCode::BeginGroup)
761    /// are also accepted (e.g. `\bgroup`).
762    pub fn expand_until_bgroup(&mut self, allow_let: bool, t: &ET::Token) -> TeXResult<(), ET> {
763        loop {
764            let tk = self.need_next(false, t)?;
765            if tk.command_code() == CommandCode::BeginGroup {
766                return Ok(());
767            }
768            crate::expand!(self,tk;
769                ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::BeginGroup,..})) if allow_let =>
770                    return Ok(()),
771                _ => break
772            );
773        }
774        TeXError::missing_begingroup(self.aux, self.state, self.mouth)
775    }
776
777    /// Read a dimension value from the input stream.
778    #[inline]
779    pub fn read_dim(&mut self, skip_eq: bool, in_token: &ET::Token) -> TeXResult<ET::Dim, ET> {
780        ET::Gullet::read_dim(self, skip_eq, in_token)
781    }
782    /// Read a skip value from the input stream.
783    #[inline]
784    pub fn read_skip(
785        &mut self,
786        skip_eq: bool,
787        in_token: &ET::Token,
788    ) -> TeXResult<Skip<ET::Dim>, ET> {
789        ET::Gullet::read_skip(self, skip_eq, in_token)
790    }
791    /// Read a muskip value from the input stream.
792    #[inline]
793    pub fn read_muskip(
794        &mut self,
795        skip_eq: bool,
796        in_token: &ET::Token,
797    ) -> TeXResult<MuSkip<ET::MuDim>, ET> {
798        ET::Gullet::read_muskip(self, skip_eq, in_token)
799    }
800    /// Read a mudim value from the input stream (for `\mkern`).
801    #[inline]
802    pub fn read_mudim(&mut self, skip_eq: bool, in_token: &ET::Token) -> TeXResult<ET::MuDim, ET> {
803        ET::Gullet::read_mudim(self, skip_eq, in_token)
804    }
805    /// Check whether the next character is one of the provided ones. Returns the character if so,
806    /// otherwise returns the inspected [`Token`] to be further processed or [Self::requeue]d.
807    #[inline]
808    pub fn read_chars(&mut self, kws: &[u8]) -> TeXResult<Either<u8, Option<ET::Token>>, ET> {
809        ET::Gullet::read_chars(self, kws)
810    }
811    /// Inspect the given [`Token`] and return its current definition, if any.
812    #[inline]
813    pub fn resolve(&self, token: &ET::Token) -> ResolvedToken<'_, ET> {
814        ET::Gullet::resolve(self.state, token)
815    }
816}