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