1pub 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
26pub trait Gullet<ET: EngineTypes> {
42 fn new(aux: &mut EngineAux<ET>, state: &mut ET::State, mouth: &mut ET::Mouth) -> Self;
44
45 fn push_align(&mut self, ad: AlignData<ET::Token, ET::Dim>);
47 fn pop_align(&mut self) -> Option<AlignData<ET::Token, ET::Dim>>;
49 fn get_align_data(&mut self) -> Option<&mut AlignData<ET::Token, ET::Dim>>;
51 fn get_conditional(&self) -> Option<ActiveConditional<ET::Int>>;
53 fn get_conditionals(&mut self) -> &mut Vec<ActiveConditional<ET::Int>>;
55 fn csnames(&mut self) -> &mut usize;
57
58 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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
288 fn read_keyword(engine: &mut EngineReferences<ET>, kw: &[u8]) -> TeXResult<bool, ET> {
289 methods::read_keyword(engine, kw, None)
290 }
291
292 #[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 #[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 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)), }
321 }
322
323 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 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(); f(engine, &mut exp, token)?;
360 engine.mouth.push_vec(exp);
361 Ok(())
362 }
363 fn do_macro(
365 engine: &mut EngineReferences<ET>,
366 m: Macro<ET::Token>,
367 token: ET::Token,
368 ) -> TeXResult<(), ET>;
369
370 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 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 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 }
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 #[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
470pub 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 }
531 _ => unreachable!(),
532 }
533 }
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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[inline]
698 pub fn read_keyword(&mut self, kw: &[u8]) -> TeXResult<bool, ET> {
699 ET::Gullet::read_keyword(self, kw)
700 }
701 #[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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[inline]
813 pub fn resolve(&self, token: &ET::Token) -> ResolvedToken<'_, ET> {
814 ET::Gullet::resolve(self.state, token)
815 }
816}