Skip to main content

tex_engine/
commands.rs

1/*! [Commands](TeXCommand) - [primitives](PrimitiveCommand), [macros](Macro), etc. The B-book largely calls these
2"equivalents", but we use the more standard term "command" instead.
3*/
4
5use crate::commands::methods::MacroParser;
6use crate::commands::primitives::{PrimitiveIdentifier, PRIMITIVES};
7use crate::engine::fontsystem::Font;
8use crate::engine::fontsystem::FontSystem;
9use crate::engine::mouth::strings::InputTokenizer;
10use crate::engine::state::State;
11use crate::engine::{EngineAux, EngineReferences, EngineTypes};
12use crate::prelude::CSHandler;
13use crate::tex::catcodes::{CategoryCodeScheme, CommandCode};
14use crate::tex::characters::StringLineSource;
15use crate::tex::nodes::boxes::{BoxInfo, TeXBox};
16use crate::tex::nodes::WhatsitFunction;
17use crate::tex::numerics::{MuSkip, Skip, TeXInt};
18use crate::tex::tokens::control_sequences::CSName;
19use crate::tex::tokens::token_lists::{CharWrite, StringCharWrite, TokenList};
20use crate::tex::tokens::Token;
21use crate::utils::errors::TeXResult;
22use either::Either;
23use std::fmt::Display;
24
25pub mod etex;
26pub mod methods;
27pub mod primitives;
28pub mod tex;
29
30/// A [`Token`] that has been resolved to a [`TeXCommand`] or a character (if not a control sequence / active character).
31#[derive(Debug)]
32pub enum ResolvedToken<'a, ET: EngineTypes> {
33    /// The token is a simple character with the given [`CommandCode`].
34    Tk { char: ET::Char, code: CommandCode },
35    /// The token is a control sequence or active character, which
36    ///is currently defined as the give [`TeXCommand`] (or undefined).
37    Cmd(Option<&'a TeXCommand<ET>>),
38}
39
40/// See [`Gullet::char_or_primitive`](crate::engine::gullet::Gullet::char_or_primitive).
41#[derive(Debug)]
42pub enum CharOrPrimitive<ET: EngineTypes> {
43    Char(ET::Char, Option<CommandCode>),
44    Primitive(PrimitiveIdentifier),
45}
46
47/// A currently active conditional, e.g. `\ifnum`, `\ifx`, etc.
48#[derive(Copy, Clone, Eq, PartialEq, Debug)]
49pub enum ActiveConditional<I: TeXInt> {
50    /// An unfinished conditional, e.g. `\ifnum` before both numbers have been read.
51    Unfinished(PrimitiveIdentifier),
52    /// `\ifcase` of the provided number
53    Case(I),
54    /// A conditional that has evaluated to true
55    True(PrimitiveIdentifier),
56    /// A conditional that has evaluated to false after the matching `\else` branch
57    Else(PrimitiveIdentifier),
58}
59impl<I: TeXInt> ActiveConditional<I> {
60    /// The (original, primitive) name of the conditional.
61    pub fn name(&self) -> PrimitiveIdentifier {
62        match self {
63            Self::Case(_) => PRIMITIVES.ifcase,
64            Self::True(n) | Self::Unfinished(n) | Self::Else(n) => *n,
65        }
66    }
67}
68
69/// A command.
70#[derive(Clone, Debug)]
71pub enum TeXCommand<ET: EngineTypes> {
72    /// A user defined [`Macro`], to be expanded (unless [protected](Macro::protected))
73    Macro(Macro<ET::Token>),
74    /// A character with the given [`CommandCode`]; e.g. the result of `\let\foo={`.
75    Char { char: ET::Char, code: CommandCode },
76    /// A character defined via `\chardef\foo...`.
77    CharDef(ET::Char),
78    /// A math character defined via `\mathchardef\foo...`.
79    MathChar(u32),
80    /// A font defined via `\font\foo...`.
81    Font(<ET::FontSystem as FontSystem>::Font),
82    /// An integer register defined via `\countdef\foo...`.
83    IntRegister(usize),
84    /// A dimension register defined via `\dimendef\foo...`.
85    DimRegister(usize),
86    /// A skip register defined via `\skipdef\foo...`.
87    SkipRegister(usize),
88    /// A muskip register defined via `\muskipdef\foo...`.
89    MuSkipRegister(usize),
90    /// A token register defined via `\toksdef\foo...`.
91    ToksRegister(usize),
92    /// A [primitive command](PrimitiveCommand), e.g. `\relax`, `\endgroup`, `\count` etc.
93    Primitive {
94        name: PrimitiveIdentifier,
95        cmd: PrimitiveCommand<ET>,
96    },
97}
98impl<ET: EngineTypes> TeXCommand<ET> {
99    /// returns a helper struct for displaying the `\meaning` of this command; implements [`Display`].
100    pub const fn meaning<'a>(
101        &'a self,
102        int: &'a <<ET::Token as Token>::CS as CSName<ET::Char>>::Handler,
103        cc: &'a CategoryCodeScheme<ET::Char>,
104        escapechar: Option<ET::Char>,
105    ) -> Meaning<'a, ET> {
106        Meaning {
107            cmd: self,
108            int,
109            cc,
110            escapechar,
111        }
112    }
113
114    /// implements `\the` for this command, e.g. `\the\count0` or `\the\font`.
115    /// #### Errors
116    /// If self is not allowed after `\the`
117    pub fn the<F: FnMut(&mut EngineAux<ET>, &ET::State, &mut ET::Gullet, ET::Token)>(
118        &self,
119        engine: &mut EngineReferences<ET>,
120        token: ET::Token,
121        mut cont: F,
122    ) -> TeXResult<(), ET> {
123        use crate::tex::tokens::token_lists::Otherize;
124        use std::fmt::Write;
125        match self {
126            Self::IntRegister(u) => {
127                let val = engine.state.get_int_register(*u);
128                write!(
129                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
130                    "{val}"
131                )?;
132            }
133            Self::DimRegister(u) => {
134                let val = engine.state.get_dim_register(*u);
135                write!(
136                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
137                    "{val}"
138                )?;
139            }
140            Self::SkipRegister(u) => {
141                let val = engine.state.get_skip_register(*u);
142                write!(
143                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
144                    "{val}"
145                )?;
146            }
147            Self::MuSkipRegister(u) => {
148                let val = engine.state.get_muskip_register(*u);
149                write!(
150                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
151                    "{val}"
152                )?;
153            }
154            Self::CharDef(c) => {
155                let val: u64 = (*c).into();
156                write!(
157                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
158                    "{val}"
159                )?;
160            }
161            Self::MathChar(u) => {
162                write!(
163                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
164                    "{u}"
165                )?;
166            }
167            Self::ToksRegister(u) => {
168                for t in &engine.state.get_toks_register(*u).0 {
169                    cont(engine.aux, engine.state, engine.gullet, t.clone());
170                }
171            }
172            Self::Font(fnt) => {
173                let t = fnt.name();
174                cont(
175                    engine.aux,
176                    engine.state,
177                    engine.gullet,
178                    ET::Token::from_cs(t.clone()),
179                );
180            }
181            Self::Primitive { name, cmd } => return cmd.the(engine, token, *name, cont),
182            o => engine.general_error(format!(
183                "You can't use {} after \\the",
184                o.meaning(
185                    engine.aux.memory.cs_interner(),
186                    engine.state.get_catcode_scheme(),
187                    engine.state.get_escape_char()
188                )
189            ))?,
190        }
191        Ok(())
192    }
193}
194
195/// A *primitive* command defined from the outset. All of the `fn` methods
196/// are called with (at least) the current [`EngineReferences`] and the [`Token`] that
197/// triggered the command.
198#[derive(Copy, Clone, Debug)]
199pub enum PrimitiveCommand<ET: EngineTypes> {
200    /// A conditional, e.g. `\ifnum`, `\ifx`, etc.
201    Conditional(fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<bool, ET>),
202    /// An expandable primitive, e.g. `\the`, `\number`, etc. - should push its expansion to the `Vec` argument.
203    Expandable(fn(&mut EngineReferences<ET>, &mut Vec<ET::Token>, ET::Token) -> TeXResult<(), ET>),
204    /// An expandable primitive that does not actually produce any tokens, or does via more complicated means
205    /// than simply returning a `Vec<ET::Token>` - e.g. `\csname`, `\input`, `\else`, etc.
206    SimpleExpandable(fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>),
207    /// A primitive that cannot be expanded, e.g. `\relax`, `\end`, etc. See [`CommandScope`].
208    Unexpandable {
209        scope: CommandScope,
210        apply: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>,
211    },
212    /// An assignment primitive, e.g. `\def`, `\advance` - basically, an unexpandable primitive that
213    /// causes `\afterassignment` to be inserted.
214    Assignment(fn(&mut EngineReferences<ET>, ET::Token, bool) -> TeXResult<(), ET>),
215    /// A primitive that yields an integer value if one is expected, or optionally can assign one if not;
216    /// e.g. `\count`.
217    Int {
218        read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<ET::Int, ET>,
219        assign: Option<
220            for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
221        >,
222    },
223    /// A primitive that yields a dimension value if one is expected, or optionally can assign one if not;
224    /// e.g. `\dimen`.
225    Dim {
226        read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<ET::Dim, ET>,
227        assign: Option<
228            for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
229        >,
230    },
231    /// A primitive that yields a skip value if one is expected, or optionally can assign one if not;
232    /// e.g. `\skip`.
233    Skip {
234        read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<Skip<ET::Dim>, ET>,
235        assign: Option<
236            for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
237        >,
238    },
239    /// A primitive that yields a muskip value if one is expected, or optionally can assign one if not;
240    /// e.g. `\muskip`.
241    MuSkip {
242        read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<MuSkip<ET::MuDim>, ET>,
243        assign: Option<
244            for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
245        >,
246    },
247    /// A primitive that yields a [`Font`] if one is expected, or optionally can assign one if not;
248    /// e.g. `\font`.
249    FontCmd {
250        read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<ET::Font, ET>,
251        assign: Option<
252            for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
253        >,
254    },
255    /// A primitive that yields either a finished [`TeXBox`], or opens a new one, depending on
256    /// the case of the return value. Used for e.g. `\setbox` or `\raise`, which may be followed by
257    /// a finished box (e.g. `\box0`) or a new box (e.g. `\hbox{...}`).
258    Box(
259        fn(
260            &mut EngineReferences<ET>,
261            ET::Token,
262        ) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET>,
263    ),
264    /// A primitive assignable integer value, e.g. `\hangindent` or `\tolerance`.
265    PrimitiveInt,
266    /// A primitive assignable dimension value, e.g. `\parindent` or `\hsize`.
267    PrimitiveDim,
268    /// A primitive assignable skip value, e.g. `\parskip` or `\lineskip`.
269    PrimitiveSkip,
270    /// A primitive assignable muskip value, e.g. `\thinmuskip` or `\medmuskip`.
271    PrimitiveMuSkip,
272    /// A primitive assignable token list, e.g. `\everypar` or `\output`.
273    PrimitiveToks,
274    /// A Whatsit, e.g. `\write`, `\special`, etc. - if following an `\immediate`, the `immediate` function
275    /// is called, otherwise, `get` may return a (boxed) continuation to be called at shipout.
276    Whatsit {
277        get: fn(
278            &mut EngineReferences<ET>,
279            ET::Token,
280        ) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET>,
281        immediate: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>,
282        the: Option<fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<Vec<ET::Token>, ET>>,
283    },
284    /// `\relax` - does nothing.
285    Relax,
286}
287impl<ET: EngineTypes> PrimitiveCommand<ET> {
288    /// implements `\the` for this command, e.g. `\the\count0` or `\the\font`.
289    /// #### Errors
290    /// If self is not allowed after `\the`
291    pub fn the<F: FnMut(&mut EngineAux<ET>, &ET::State, &mut ET::Gullet, ET::Token)>(
292        &self,
293        engine: &mut EngineReferences<ET>,
294        token: ET::Token,
295        name: PrimitiveIdentifier,
296        mut cont: F,
297    ) -> TeXResult<(), ET> {
298        use crate::tex::tokens::token_lists::Otherize;
299        use std::fmt::Write;
300        match self {
301            Self::Int { read, .. } => {
302                let val = read(engine, token)?;
303                write!(
304                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
305                    "{val}"
306                )?;
307            }
308            Self::Dim { read, .. } => {
309                let val = read(engine, token)?;
310                write!(
311                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
312                    "{val}"
313                )?;
314            }
315            Self::Skip { read, .. } => {
316                let val = read(engine, token)?;
317                write!(
318                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
319                    "{val}"
320                )?;
321            }
322            Self::MuSkip { read, .. } => {
323                let val = read(engine, token)?;
324                write!(
325                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
326                    "{val}"
327                )?;
328            }
329            Self::PrimitiveInt => {
330                let val = engine.state.get_primitive_int(name);
331                write!(
332                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
333                    "{val}"
334                )?;
335            }
336            Self::PrimitiveDim => {
337                let val = engine.state.get_primitive_dim(name);
338                write!(
339                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
340                    "{val}"
341                )?;
342            }
343            Self::PrimitiveSkip => {
344                let val = engine.state.get_primitive_skip(name);
345                write!(
346                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
347                    "{val}"
348                )?;
349            }
350            Self::PrimitiveMuSkip => {
351                let val = engine.state.get_primitive_muskip(name);
352                write!(
353                    Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
354                    "{val}"
355                )?;
356            }
357            Self::PrimitiveToks => {
358                for t in &engine.state.get_primitive_tokens(name).0 {
359                    cont(engine.aux, engine.state, engine.gullet, t.clone());
360                }
361            }
362            Self::FontCmd { read, .. } => {
363                let fnt = read(engine, token)?;
364                let t = fnt.name();
365                cont(
366                    engine.aux,
367                    engine.state,
368                    engine.gullet,
369                    ET::Token::from_cs(t.clone()),
370                );
371            }
372            Self::Whatsit { the: Some(the), .. } => {
373                for t in the(engine, token)? {
374                    cont(engine.aux, engine.state, engine.gullet, t);
375                }
376            }
377            _ if name == PRIMITIVES.toks => {
378                let u = engine.read_register_index(false, &token)?;
379                for t in &engine.state.get_toks_register(u).0 {
380                    cont(engine.aux, engine.state, engine.gullet, t.clone());
381                }
382            }
383            _ => engine.general_error(format!(
384                "You can't use {} after \\the",
385                name.display(engine.state.get_escape_char())
386            ))?,
387        }
388        Ok(())
389    }
390}
391
392/// A helper struct for displaying the `\meaning` of a [`TeXCommand`]; implements [`Display`].
393pub struct Meaning<'a, ET: EngineTypes> {
394    cmd: &'a TeXCommand<ET>,
395    int: &'a <<ET::Token as Token>::CS as CSName<ET::Char>>::Handler,
396    cc: &'a CategoryCodeScheme<ET::Char>,
397    escapechar: Option<ET::Char>,
398}
399impl<ET: EngineTypes> Meaning<'_, ET> {
400    /// Write the meaning directly to a [`CharWrite`].
401    /// #### Errors
402    /// Formatting error (should never happen)
403    pub fn write_chars<W: CharWrite<ET::Char, ET::CSName>>(&self, f: &mut W) -> std::fmt::Result {
404        match self.cmd {
405            TeXCommand::Macro(m) => m.meaning_char(self.int, self.cc, self.escapechar, f),
406            TeXCommand::Char { char, code } => code.meaning(*char, f),
407            TeXCommand::CharDef(c) => {
408                if let Some(e) = self.escapechar {
409                    f.push_char(e);
410                }
411                write!(f, "char\"{:X}", Into::<u64>::into(*c))
412            }
413            TeXCommand::MathChar(u) => {
414                if let Some(e) = self.escapechar {
415                    f.push_char(e);
416                }
417                write!(f, "mathchar\"{u:X}")
418            }
419            TeXCommand::Font(i) => {
420                write!(f, "select font ")?;
421                i.display(self.int, f)
422            }
423            TeXCommand::IntRegister(i) => {
424                if let Some(e) = self.escapechar {
425                    f.push_char(e);
426                }
427                write!(f, "count{i}")
428            }
429            TeXCommand::DimRegister(i) => {
430                if let Some(e) = self.escapechar {
431                    f.push_char(e);
432                }
433                write!(f, "dimen{i}")
434            }
435            TeXCommand::SkipRegister(i) => {
436                if let Some(e) = self.escapechar {
437                    f.push_char(e);
438                }
439                write!(f, "skip{i}")
440            }
441            TeXCommand::MuSkipRegister(i) => {
442                if let Some(e) = self.escapechar {
443                    f.push_char(e);
444                }
445                write!(f, "muskip{i}")
446            }
447            TeXCommand::ToksRegister(i) => {
448                if let Some(e) = self.escapechar {
449                    f.push_char(e);
450                }
451                write!(f, "toks{i}")
452            }
453            TeXCommand::Primitive { name, .. } => {
454                write!(f, "{}", name.display(self.escapechar))
455            }
456        }
457    }
458}
459impl<ET: EngineTypes> Display for Meaning<'_, ET> {
460    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461        self.write_chars(&mut StringCharWrite::new(f))
462    }
463}
464
465/// A macro signature, e.g. `#1#2#3` or `#1t#2\foo{`.
466#[derive(Clone, Debug)]
467pub struct MacroSignature<T: Token> {
468    /// The number of parameters, e.g. `3` in `#1#2#3`.
469    pub arity: u8,
470    /// The token list specifying the parameters, e.g. `[#1,#2,#3]` in `#1#2#3`.
471    pub params: TokenList<T>,
472}
473
474/// A macro, e.g. the result of `\def\foo#1#2{...}`.
475#[derive(Clone, Debug)]
476pub struct Macro<T: Token> {
477    /// Whether the macro is protected, e.g. the result of `\protected\def`.
478    pub protected: bool,
479    /// Whether the macro is long, e.g. the result of `\long\def`.
480    pub long: bool,
481    /// Whether the macro is outer, e.g. the result of `\outer\def`.
482    pub outer: bool,
483    /// The expansion of the macro, e.g. `...` in `\def\foo#1#2{...}`.
484    pub expansion: TokenList<T>,
485    /// The signatureof the macro, e.g. `#1#2` in `\def\foo#1#2{...}`.
486    pub signature: MacroSignature<T>,
487}
488impl<T: Token> Macro<T> {
489    /// Convenience method for creating a new macro from a signature and expansion as strings; given the provided [`CategoryCodeScheme`].
490    /// Allows for e.g. `as_point = Macro::new(int,`[`&DEFAULT_SCHEME_U8`](crate::tex::catcodes::DEFAULT_SCHEME_U8)`,"#1#2","(#1,#2)")`.
491    /// #### Errors
492    /// on invalid tex strings
493    pub fn new<Sig: AsRef<str>, Exp: AsRef<str>, ET: EngineTypes<Token = T, Char = T::Char>>(
494        int: &mut <T::CS as CSName<T::Char>>::Handler,
495        cc: &CategoryCodeScheme<T::Char>,
496        sig: Sig,
497        exp: Exp,
498    ) -> TeXResult<Self, ET> {
499        let mut parser = MacroParser::new();
500        let sig = sig.as_ref();
501        if !sig.is_empty() {
502            let sigsrc: StringLineSource<T::Char> = sig.into();
503            let mut sigsrc = InputTokenizer::new(sigsrc);
504            let par = int.par();
505            while let Some(t) = sigsrc.get_next(int, cc, None, &par)? {
506                parser.do_signature_token::<ET>(t)?;
507            }
508        }
509        let exp = exp.as_ref();
510        let expsrc: StringLineSource<T::Char> = exp.into();
511        let mut expsrc = InputTokenizer::new(expsrc);
512        let par = int.par();
513        while let Some(t) = expsrc.get_next(int, cc, None, &par)? {
514            parser.do_expansion_token::<ET>(t)?;
515        }
516        Ok(parser.close(false, false, false))
517    }
518
519    /// returns a helper struct for displaying the `\meaning` of this command; implements [`Display`].
520    pub fn meaning<'a>(
521        &'a self,
522        int: &'a <T::CS as CSName<T::Char>>::Handler,
523        cc: &'a CategoryCodeScheme<T::Char>,
524        escapechar: Option<T::Char>,
525    ) -> impl Display + 'a {
526        MacroMeaning {
527            cmd: self,
528            int,
529            cc,
530            escapechar,
531        }
532    }
533    /// Write the meaning directly to a [`CharWrite`].
534    /// #### Errors
535    /// Formatting error (should never happen)
536    pub fn meaning_char<F: CharWrite<T::Char, T::CS>>(
537        &self,
538        int: &<T::CS as CSName<T::Char>>::Handler,
539        cc: &CategoryCodeScheme<T::Char>,
540        escapechar: Option<T::Char>,
541        f: &mut F,
542    ) -> std::fmt::Result {
543        if self.protected {
544            if let Some(e) = escapechar {
545                f.push_char(e);
546            }
547            write!(f, "protected ")?;
548        }
549        if self.long {
550            if let Some(e) = escapechar {
551                f.push_char(e);
552            }
553            write!(f, "long ")?;
554        }
555        if self.outer {
556            if let Some(e) = escapechar {
557                f.push_char(e);
558            }
559            write!(f, "outer ")?;
560        }
561        write!(f, "macro:")?;
562        self.signature
563            .params
564            .display(int, cc, escapechar, false)
565            .fmt_cw(f)?;
566        write!(f, "->")?;
567        self.expansion.display(int, cc, escapechar, true).fmt_cw(f)
568    }
569}
570
571struct MacroMeaning<'a, T: Token> {
572    cmd: &'a Macro<T>,
573    int: &'a <T::CS as CSName<T::Char>>::Handler,
574    cc: &'a CategoryCodeScheme<T::Char>,
575    escapechar: Option<T::Char>,
576}
577impl<T: Token> Display for MacroMeaning<'_, T> {
578    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579        self.cmd.meaning_char(
580            self.int,
581            self.cc,
582            self.escapechar,
583            &mut StringCharWrite::new(f),
584        )
585    }
586}
587
588/// The scope of a [`PrimitiveCommand::Unexpandable`].
589#[derive(Clone, Debug, Copy)]
590pub enum CommandScope {
591    /// The command is only valid in vertical mode. If occuring in horizontal mode, it will
592    /// close the current paragraph, i.e. switch to vertical mode.
593    /// In restricted horizontal or math mode, it will throw an error.
594    SwitchesToVertical,
595    /// The command is only valid in horizontal mode. If occuring in (internal) vertical mode, it will
596    /// open a new paragraph, i.e. switch to horizontal mode. In math mode, it will throw an error.
597    SwitchesToHorizontal,
598    /// The command is only valid in math mode. If occuring in non-math mode, it will
599    /// throw an error
600    MathOnly,
601    /// The command is only valid in horizontal or math mode. If occuring in vertical mode, it will
602    /// open a new paragraph, i.e. switch to horizontal mode.
603    SwitchesToHorizontalOrMath,
604    /// The command is valid anywhere and will not switch modes.
605    Any,
606}