Skip to main content

tex_engine/engine/
state.rs

1/*! The [`State`] of a TeX engine keeps track of scoped (by TeX groups)
2values. */
3pub mod tex_state;
4
5use crate::commands::primitives::{PrimitiveCommands, PrimitiveIdentifier, PRIMITIVES};
6use crate::commands::{PrimitiveCommand, TeXCommand};
7use crate::engine::gullet::methods::CSOrActiveChar;
8use crate::engine::{EngineAux, EngineReferences, EngineTypes};
9use crate::tex::catcodes::{CategoryCode, CategoryCodeScheme};
10use crate::tex::nodes::boxes::TeXBox;
11use crate::tex::numerics::{MuSkip, Skip};
12use crate::tex::tokens::control_sequences::CSName;
13use crate::tex::tokens::token_lists::TokenList;
14use std::fmt::Formatter;
15
16/// The type of a group, e.g. `{...}`, `\begingroup...\endgroup`, `$...$`.
17#[derive(Clone, Copy, Eq, PartialEq, Debug)]
18pub enum GroupType {
19    /// A group delimited by `{` and `}`.
20    Simple,
21    /// `\hbox`
22    HBox,
23    /// `\vadjust`
24    VAdjust,
25    /// `\vbox`
26    VBox,
27    /// `\vtop`
28    VTop,
29    /// `\halign` or `\valign`
30    Align,
31    /// `\noalign`
32    Noalign,
33    /// Output routine
34    Output,
35    /// `{...}` in math mode
36    Math,
37    /// Discretionary
38    Disc,
39    /// Insert
40    Insert,
41    /// `\vcenter`
42    VCenter,
43    /// `\mathchoice`
44    MathChoice,
45    /// A group delimited by `\begingroup` and `\endgroup`.
46    SemiSimple,
47    /// A math group delimited by `$` (inline) or `$$` (display).
48    MathShift { display: bool },
49    /// An inner math group delimited by `\left` and `\right`.
50    LeftRight,
51}
52impl std::fmt::Display for GroupType {
53    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
54        match self {
55            GroupType::Simple => write!(f, "simple"),
56            GroupType::HBox => write!(f, "hbox"),
57            GroupType::VAdjust => write!(f, "adjusted hbox"),
58            GroupType::VBox => write!(f, "vbox"),
59            GroupType::VTop => write!(f, "vtop"),
60            GroupType::Align => write!(f, "align"),
61            GroupType::Noalign => write!(f, "no align"),
62            GroupType::Output => write!(f, "output"),
63            GroupType::Math => write!(f, "math"),
64            GroupType::Disc => write!(f, "disc"),
65            GroupType::Insert => write!(f, "insert"),
66            GroupType::VCenter => write!(f, "vcenter"),
67            GroupType::MathChoice => write!(f, "math choice"),
68            GroupType::SemiSimple => write!(f, "semi simple"),
69            GroupType::MathShift { .. } => write!(f, "math shift"),
70            GroupType::LeftRight { .. } => write!(f, "math left"),
71        }
72    }
73}
74impl GroupType {
75    pub fn to_byte(&self) -> u8 {
76        match self {
77            // 0: bottom level (no group)
78            GroupType::Simple => 1,
79            GroupType::HBox => 2,
80            GroupType::VAdjust => 3,
81            GroupType::VBox => 4,
82            GroupType::VTop => 5,
83            GroupType::Align => 6,
84            GroupType::Noalign => 7,
85            GroupType::Output => 8,
86            GroupType::Math => 9,
87            GroupType::Disc => 10,
88            GroupType::Insert => 11,
89            GroupType::VCenter => 12,
90            GroupType::MathChoice => 13,
91            GroupType::SemiSimple => 14,
92            GroupType::MathShift { .. } => 15,
93            GroupType::LeftRight => 16,
94        }
95    }
96}
97
98/// A TeX state; holds all the different parameters, equivalents, registers etc.
99/// The canoncial implementation is [`TeXState`](tex_state::DefaultState).
100///
101/// Note that we do not require `ET:`[`EngineTypes`]`<`[`State`](EngineTypes::State)`=Self>` - this allows for
102/// implementing your own State by just wrapping an existing implementation in a new wrapper struct and pass on functionality
103/// to the inner State, which would otherwise
104/// fail since `ET::State` would be the outer wrapper struct, not the inner one.
105pub trait State<ET: EngineTypes>: Sized + Clone {
106    /// Instantiate a new [`State`]
107    fn new(nullfont: ET::Font, aux: &mut EngineAux<ET>) -> Self;
108    /// convert the provided integer value into a legal index for a register (or None if out of range)
109    fn register_index(i: ET::Int) -> Option<usize> {
110        let idx: i64 = i.into();
111        if idx < 0 || idx > u16::MAX.into() {
112            None
113        } else {
114            Some(idx as usize)
115        }
116    }
117
118    /// Append a [`Token`](crate::tex::tokens::Token) to be inserted when the current group ends (i.e. `\aftergroup`)
119    fn aftergroup(&mut self, token: ET::Token);
120
121    /// Get the current set of mathfonts (text, script, scriptscript)
122    /// for the given family number, where `fam` is 0..15
123    fn get_mathfonts(&self, fam: u8) -> (ET::Font, ET::Font, ET::Font) {
124        (
125            self.get_textfont(fam).clone(),
126            self.get_scriptfont(fam).clone(),
127            self.get_scriptscriptfont(fam).clone(),
128        )
129    }
130    /// register a new [`PrimitiveCommand`]. Should only be called during engine initialization.
131    fn register_primitive(
132        &mut self,
133        aux: &mut EngineAux<ET>,
134        name: &'static str,
135        cmd: PrimitiveCommand<ET>,
136    );
137    /// return the set of all registered [`PrimitiveCommands`].
138    fn primitives(&self) -> &PrimitiveCommands<ET>;
139    /// push a new group level to the scoping stack; `line_number` is used for `\tracinggroups`
140    fn push(&mut self, aux: &mut EngineAux<ET>, group_type: GroupType, line_number: usize);
141    /// pop a group level from the scoping stack. Needs the mouth to insert the `\aftergroup` [`Token`](crate::tex::tokens::Token)s (if set)
142    fn pop(&mut self, aux: &mut EngineAux<ET>, mouth: &mut ET::Mouth);
143    /// The current [`GroupType] (i.e. `\currentgrouptype`).
144    fn get_group_type(&self) -> Option<GroupType>;
145    /// The current group level/depth (0 being the top level; i.e. `\currentgrouplevel`)
146    fn get_group_level(&self) -> usize;
147    /// Get the current [`Font`](crate::engine::fontsystem::Font)
148    fn get_current_font(&self) -> &ET::Font;
149    /// Set the current [`Font`](crate::engine::fontsystem::Font)
150    fn set_current_font(&mut self, aux: &mut EngineAux<ET>, fnt: ET::Font, globally: bool);
151    /// Get the current `\textfont` for the given family number, where `fam` is 0..15
152    fn get_textfont(&self, fam: u8) -> &ET::Font;
153    /// Set the current `\textfont` for the given family number, where `fam` is 0..15
154    fn set_textfont(&mut self, aux: &mut EngineAux<ET>, fam: u8, fnt: ET::Font, globally: bool);
155    /// Get the current `\scriptfont` for the given family number, where `fam` is 0..15
156    fn get_scriptfont(&self, fam: u8) -> &ET::Font;
157    /// Set the current `\scriptfont` for the given family number, where `fam` is 0..15
158    fn set_scriptfont(&mut self, aux: &mut EngineAux<ET>, fam: u8, fnt: ET::Font, globally: bool);
159    /// Get the current `\scriptscriptfont` for the given family number, where `fam` is 0..15
160    fn get_scriptscriptfont(&self, fam: u8) -> &ET::Font;
161    /// Set the current `\scriptscriptfont` for the given family number, where `fam` is 0..15
162    fn set_scriptscriptfont(
163        &mut self,
164        aux: &mut EngineAux<ET>,
165        fam: u8,
166        fnt: ET::Font,
167        globally: bool,
168    );
169
170    /// Get the current [`CategoryCodeScheme`]
171    fn get_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char>;
172    /// Set the current [`CategoryCode`] for a character
173    fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool);
174    /// Get the current space factor code `\sfcode` for a character
175    fn get_sfcode(&self, c: ET::Char) -> u16;
176    /// Set the current space factor code `\sfcode` for a character
177    fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, sfcode: u16, globally: bool);
178    /// Get the current lower case code `\lccode` for a character
179    fn get_lccode(&self, c: ET::Char) -> ET::Char;
180    /// Set the current lower case code `\lccode` for a character
181    fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, lccode: ET::Char, globally: bool);
182    /// Get the current upper case code `\uccode` for a character
183    fn get_uccode(&self, c: ET::Char) -> ET::Char;
184    /// Set the current upper case code `\uccode` for a character
185    fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, uccode: ET::Char, globally: bool);
186    /// Get the current delimiter code `\delcode` for a character
187    fn get_delcode(&self, c: ET::Char) -> ET::Int;
188    /// Set the current delimiter code `\delcode` for a character
189    fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, delcode: ET::Int, globally: bool);
190    /// Get the current math code `\mathcode` for a character
191    fn get_mathcode(&self, c: ET::Char) -> u32;
192    /// Set the current math code `\mathcode` for a character
193    fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, mathcode: u32, globally: bool);
194    /// Get the current endline character
195    fn get_endline_char(&self) -> Option<ET::Char>;
196    /// Set the current endline character
197    fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
198    /// Get the current escape character
199    fn get_escape_char(&self) -> Option<ET::Char>;
200    /// Set the current escape character
201    fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
202    /// Get the current newline character
203    fn get_newline_char(&self) -> Option<ET::Char>;
204    /// Set the current newline character
205    fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
206    /// Get the current `\parshape`
207    fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)>;
208    /// Get the current `\parshape` and set it to its default value
209    fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)>;
210    /// Set the current `\parshape`
211    fn set_parshape(
212        &mut self,
213        aux: &EngineAux<ET>,
214        parshape: Vec<(ET::Dim, ET::Dim)>,
215        globally: bool,
216    );
217    /// Get an integer register value
218    fn get_int_register(&self, idx: usize) -> ET::Int;
219    /// Set an integer register value
220    fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool);
221    /// Get a primitive integer value
222    fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int;
223    /// Set a primitive integer value
224    fn set_primitive_int(
225        &mut self,
226        aux: &EngineAux<ET>,
227        name: PrimitiveIdentifier,
228        v: ET::Int,
229        globally: bool,
230    );
231    /// Get a dimen register value
232    fn get_dim_register(&self, idx: usize) -> ET::Dim;
233    /// Set a dimen register value
234    fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool);
235    /// Get a skip register value
236    fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim>;
237    /// Set a skip register value
238    fn set_skip_register(
239        &mut self,
240        aux: &EngineAux<ET>,
241        idx: usize,
242        v: Skip<ET::Dim>,
243        globally: bool,
244    );
245    /// Get a muskip register value
246    fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim>;
247    /// Set a muskip register value
248    fn set_muskip_register(
249        &mut self,
250        aux: &EngineAux<ET>,
251        idx: usize,
252        v: MuSkip<ET::MuDim>,
253        globally: bool,
254    );
255    /// Get a token register value
256    fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token>;
257    /// Set a token register value
258    fn set_toks_register(
259        &mut self,
260        aux: &EngineAux<ET>,
261        idx: usize,
262        v: TokenList<ET::Token>,
263        globally: bool,
264    );
265    /// Get a box register value
266    fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>>;
267    /// Get a box register value mutably (to e.g. change `\ht`, `\wd`, `\dp`, etc.)
268    fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>>;
269    /// Take a box register value; replacing it with `None` (i.e. void box)
270    fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>>;
271    /// Set a box register value
272    fn set_box_register(
273        &mut self,
274        aux: &EngineAux<ET>,
275        idx: usize,
276        v: Option<TeXBox<ET>>,
277        globally: bool,
278    );
279    /// Get a primitive dimension value
280    fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim;
281    /// Set a primitive dimension value
282    fn set_primitive_dim(
283        &mut self,
284        aux: &EngineAux<ET>,
285        name: PrimitiveIdentifier,
286        v: ET::Dim,
287        globally: bool,
288    );
289    /// Get a primitive skip value
290    fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim>;
291    /// Set a primitive skip value
292    fn set_primitive_skip(
293        &mut self,
294        aux: &EngineAux<ET>,
295        name: PrimitiveIdentifier,
296        v: Skip<ET::Dim>,
297        globally: bool,
298    );
299    /// Get a primitive muskip value
300    fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim>;
301    /// Set a primitive muskip value
302    fn set_primitive_muskip(
303        &mut self,
304        aux: &EngineAux<ET>,
305        name: PrimitiveIdentifier,
306        v: MuSkip<ET::MuDim>,
307        globally: bool,
308    );
309    /// Get a primitive token list
310    fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token>;
311    /// Set a primitive token list
312    fn set_primitive_tokens(
313        &mut self,
314        aux: &EngineAux<ET>,
315        name: PrimitiveIdentifier,
316        v: TokenList<ET::Token>,
317        globally: bool,
318    );
319    /// Get the current definition for the control sequence name
320    fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>>;
321    /// Set the current definition for the control sequence name
322    fn set_command(
323        &mut self,
324        aux: &EngineAux<ET>,
325        name: ET::CSName,
326        cmd: Option<TeXCommand<ET>>,
327        globally: bool,
328    );
329    /// Get the current definition for the active character
330    fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>>;
331    /// Set the current definition for the active character
332    fn set_ac_command(
333        &mut self,
334        aux: &EngineAux<ET>,
335        c: ET::Char,
336        cmd: Option<TeXCommand<ET>>,
337        globally: bool,
338    );
339}
340
341/// Convenience trait for tracking state changes in a [`StateStack`] and rolling them back
342/// if necessary at the end of a group.
343pub trait StateChangeTracker<ET: EngineTypes>: State<ET> {
344    /// Get the current [`StateStack`]
345    fn stack(&mut self) -> &mut StateStack<ET>;
346    /// Change a field of the state, and add the change to the [`StateStack`]. Also takes care of inspecting
347    /// and considering the current `\globaldefs` value and passes on the *actual* computed `globally` value
348    /// to the continuation function, which should return the [`StateChange`] containg the *old* value.
349    ///
350    /// For example, on `\count5=4`, you could call this function like this:
351    /// `self.change_field(false,|slf,g| `[`StateChange::IntRegister`]`(5,std::mem::replace(&mut slf.int_registers[5],4)))`
352    fn change_field<F: FnOnce(&mut Self, bool) -> StateChange<ET>>(
353        &mut self,
354        globally: bool,
355        f: F,
356    ) {
357        let globaldefs = self.get_primitive_int(PRIMITIVES.globaldefs);
358        let zero = ET::Int::default();
359        let global = if globaldefs == zero {
360            globally
361        } else {
362            globaldefs > zero
363        };
364        let change = f(self, global);
365        if global {
366            self.stack().add_change_globally(change)
367        } else {
368            self.stack().add_change_locally(change)
369        }
370    }
371}
372
373/// A change to a [`State`], to be potentially rolled back when a group ends.
374#[derive(Clone, Debug)]
375pub enum StateChange<ET: EngineTypes> {
376    Catcode {
377        char: ET::Char,
378        old: CategoryCode,
379    },
380    SfCode {
381        char: ET::Char,
382        old: u16,
383    },
384    DelCode {
385        char: ET::Char,
386        old: ET::Int,
387    },
388    LcCode {
389        char: ET::Char,
390        old: ET::Char,
391    },
392    UcCode {
393        char: ET::Char,
394        old: ET::Char,
395    },
396    MathCode {
397        char: ET::Char,
398        old: u32,
399    },
400    ParShape {
401        old: Vec<(ET::Dim, ET::Dim)>,
402    },
403    CurrentFont(ET::Font),
404    TextFont {
405        idx: u8,
406        old: ET::Font,
407    },
408    ScriptFont {
409        idx: u8,
410        old: ET::Font,
411    },
412    ScriptScriptFont {
413        idx: u8,
414        old: ET::Font,
415    },
416    EndlineChar {
417        old: Option<ET::Char>,
418    },
419    EscapeChar {
420        old: Option<ET::Char>,
421    },
422    NewlineChar {
423        old: Option<ET::Char>,
424    },
425    IntRegister {
426        idx: usize,
427        old: ET::Int,
428    },
429    DimRegister {
430        idx: usize,
431        old: ET::Dim,
432    },
433    SkipRegister {
434        idx: usize,
435        old: Skip<ET::Dim>,
436    },
437    MuSkipRegister {
438        idx: usize,
439        old: MuSkip<ET::MuDim>,
440    },
441    BoxRegister {
442        idx: usize,
443        old: Option<TeXBox<ET>>,
444    },
445    ToksRegister {
446        idx: usize,
447        old: TokenList<ET::Token>,
448    },
449    PrimitiveInt {
450        name: PrimitiveIdentifier,
451        old: ET::Int,
452    },
453    PrimitiveDim {
454        name: PrimitiveIdentifier,
455        old: ET::Dim,
456    },
457    PrimitiveToks {
458        name: PrimitiveIdentifier,
459        old: TokenList<ET::Token>,
460    },
461    PrimitiveSkip {
462        name: PrimitiveIdentifier,
463        old: Skip<ET::Dim>,
464    },
465    PrimitiveMuSkip {
466        name: PrimitiveIdentifier,
467        old: MuSkip<ET::MuDim>,
468    },
469    Command {
470        name: ET::CSName,
471        old: Option<TeXCommand<ET>>,
472    },
473    AcCommand {
474        char: ET::Char,
475        old: Option<TeXCommand<ET>>,
476    },
477    // /// A custom state change, to be implemented by the engine, if additional state change types are needed
478    //Custom{ change:Ptr<Mutex<Option<Box<dyn CustomStateChange<ET>>>>>},
479}
480/*
481/// A custom state change, to be implemented by the engine, if additional state change types are needed.
482pub trait CustomStateChange<ET:EngineTypes> {
483    /// Check if this state change is equivalent to another one, i.e. if it needs to be rolled back
484    /// when a group ends, or is superseded by a previous change
485    fn equiv(&self,other:&dyn CustomStateChange<ET>) -> bool;
486    fn restore(&mut self,aux:&EngineAux<ET>,state:&mut ET::State,trace:bool);
487} */
488
489/// A level of the [`StateStack`], to be rolled back when a group ends
490#[derive(Clone)]
491pub struct StackLevel<ET: EngineTypes> {
492    /// The type of the group
493    pub group_type: GroupType,
494    /// The `\aftergroup` [`Token`](crate::tex::tokens::Token)s to be inserted when the group ends
495    pub aftergroup: Vec<ET::Token>,
496    changes: Vec<StateChangeI<ET>>,
497}
498
499/// A stack of [`StateChange`]s, to be rolled back when a group ends
500pub struct StateStack<ET: EngineTypes> {
501    /// The stack of [`StackLevel`]s
502    pub stack: Vec<StackLevel<ET>>,
503    vecs: Vec<Vec<StateChangeI<ET>>>,
504}
505impl<ET: EngineTypes> Clone for StateStack<ET> {
506    fn clone(&self) -> Self {
507        Self {
508            stack: self.stack.clone(),
509            vecs: vec![],
510        }
511    }
512}
513impl<ET: EngineTypes> Default for StateStack<ET> {
514    fn default() -> Self {
515        Self {
516            stack: vec![],
517            vecs: vec![],
518        }
519    }
520}
521impl<ET: EngineTypes> StateStack<ET> {
522    /// Push a new stack level onto the stack with the given [`GroupType`], as a new group begins
523    pub fn push(&mut self, group_type: GroupType) {
524        let lvl = StackLevel {
525            group_type,
526            aftergroup: vec![],
527            changes: self.vecs.pop().unwrap_or_default(),
528        };
529        self.stack.push(lvl);
530    }
531    /// Pop a stack level from the stack, as a group ends. Returns the [`GroupType`], the `\aftergroup` [`Token`](crate::tex::tokens::Token)s,
532    /// and an iterator over the [`StateChange`]s to be rolled back.
533    pub fn pop(&mut self) -> (GroupType, Vec<ET::Token>, ChangeIter<'_, ET>) {
534        let lvl = self.stack.pop().unwrap();
535        (
536            lvl.group_type,
537            lvl.aftergroup,
538            ChangeIter {
539                stack: self,
540                chs: lvl.changes,
541            },
542        )
543    }
544    /// Register a global state change, never to be rolled back;
545    /// if there is a stack level, remove any equivalent previous changes
546    pub fn add_change_globally(&mut self, change: StateChange<ET>) {
547        let change = change.into();
548        for lvl in &mut self.stack {
549            for c in lvl.changes.iter_mut() {
550                if c.equiv(&change) {
551                    c.active = false
552                }
553            }
554        }
555    }
556    /// Register a local state change, to be rolled back when the current group ends
557    pub fn add_change_locally(&mut self, change: StateChange<ET>) {
558        if let Some(lvl) = self.stack.last_mut() {
559            let change = change.into();
560            if lvl.changes.iter().all(|c| !c.equiv(&change)) {
561                lvl.changes.push(change);
562            }
563        }
564    }
565}
566
567/// An iterator over [`StateChange`]s to be rolled back when a group ends. Iterated over using [`ChangeIter::close`].
568pub struct ChangeIter<'a, ET: EngineTypes> {
569    stack: &'a mut StateStack<ET>,
570    chs: Vec<StateChangeI<ET>>,
571}
572impl<'a, ET: EngineTypes> ChangeIter<'a, ET> {
573    /// Close the iterator, rolling back any changes
574    pub fn close<F: FnMut(StateChange<ET>)>(mut self, mut f: F) {
575        for ch in self.chs.drain(..) {
576            if ch.active {
577                f(ch.ch);
578            }
579        }
580        self.stack.vecs.push(self.chs);
581    }
582}
583
584#[derive(Clone)]
585struct StateChangeI<ET: EngineTypes> {
586    active: bool,
587    id: (std::mem::Discriminant<StateChange<ET>>, usize),
588    ch: StateChange<ET>,
589}
590impl<ET: EngineTypes> StateChangeI<ET> {
591    fn equiv(&self, other: &StateChangeI<ET>) -> bool {
592        self.active && self.id == other.id
593    }
594}
595impl<ET: EngineTypes> From<StateChange<ET>> for StateChangeI<ET> {
596    fn from(value: StateChange<ET>) -> Self {
597        let u = match &value {
598            StateChange::Catcode { char, .. } => (*char).into() as usize,
599            StateChange::SfCode { char, .. } => (*char).into() as usize,
600            StateChange::LcCode { char, .. } => (*char).into() as usize,
601            StateChange::UcCode { char, .. } => (*char).into() as usize,
602            StateChange::MathCode { char, .. } => (*char).into() as usize,
603            StateChange::DelCode { char, .. } => (*char).into() as usize,
604            StateChange::AcCommand { char, .. } => (*char).into() as usize,
605            StateChange::CurrentFont(_) => 0,
606            StateChange::EndlineChar { .. } => 0,
607            StateChange::EscapeChar { .. } => 0,
608            StateChange::NewlineChar { .. } => 0,
609            StateChange::ParShape { .. } => 0,
610            StateChange::TextFont { idx, .. } => *idx as usize,
611            StateChange::ScriptFont { idx, .. } => *idx as usize,
612            StateChange::ScriptScriptFont { idx, .. } => *idx as usize,
613            StateChange::IntRegister { idx, .. } => *idx,
614            StateChange::DimRegister { idx, .. } => *idx,
615            StateChange::SkipRegister { idx, .. } => *idx,
616            StateChange::MuSkipRegister { idx, .. } => *idx,
617            StateChange::ToksRegister { idx, .. } => *idx,
618            StateChange::BoxRegister { idx, .. } => *idx,
619            StateChange::PrimitiveInt { name, .. } => name.as_u16() as usize,
620            StateChange::PrimitiveDim { name, .. } => name.as_u16() as usize,
621            StateChange::PrimitiveSkip { name, .. } => name.as_u16() as usize,
622            StateChange::PrimitiveMuSkip { name, .. } => name.as_u16() as usize,
623            StateChange::PrimitiveToks { name, .. } => name.as_u16() as usize,
624            StateChange::Command { name, .. } => name.id(),
625        };
626        StateChangeI {
627            active: true,
628            id: (std::mem::discriminant(&value), u),
629            ch: value,
630        }
631    }
632}
633
634impl<ET: EngineTypes> EngineReferences<'_, ET> {
635    /// Set the current definition of the provided control sequence name or active character
636    pub fn set_command(
637        &mut self,
638        name: &CSOrActiveChar<ET::Token>,
639        cmd: Option<TeXCommand<ET>>,
640        globally: bool,
641    ) {
642        match name {
643            CSOrActiveChar::Active(c) => self.state.set_ac_command(self.aux, *c, cmd, globally),
644            CSOrActiveChar::Name(cs) => self.state.set_command(self.aux, cs.clone(), cmd, globally),
645        }
646    }
647}