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 `\par` token
171    fn get_par_token(&self) -> ET::CSName;
172    /// Set the current `\par` token
173    fn set_par_token(&mut self, par: ET::CSName);
174    /// Get the current [`CategoryCodeScheme`]
175    fn get_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char>;
176    /// Set the current [`CategoryCode`] for a character
177    fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool);
178    /// Get the current space factor code `\sfcode` for a character
179    fn get_sfcode(&self, c: ET::Char) -> u16;
180    /// Set the current space factor code `\sfcode` for a character
181    fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, sfcode: u16, globally: bool);
182    /// Get the current lower case code `\lccode` for a character
183    fn get_lccode(&self, c: ET::Char) -> ET::Char;
184    /// Set the current lower case code `\lccode` for a character
185    fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, lccode: ET::Char, globally: bool);
186    /// Get the current upper case code `\uccode` for a character
187    fn get_uccode(&self, c: ET::Char) -> ET::Char;
188    /// Set the current upper case code `\uccode` for a character
189    fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, uccode: ET::Char, globally: bool);
190    /// Get the current delimiter code `\delcode` for a character
191    fn get_delcode(&self, c: ET::Char) -> ET::Int;
192    /// Set the current delimiter code `\delcode` for a character
193    fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, delcode: ET::Int, globally: bool);
194    /// Get the current math code `\mathcode` for a character
195    fn get_mathcode(&self, c: ET::Char) -> u32;
196    /// Set the current math code `\mathcode` for a character
197    fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, mathcode: u32, globally: bool);
198    /// Get the current endline character
199    fn get_endline_char(&self) -> Option<ET::Char>;
200    /// Set the current endline character
201    fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
202    /// Get the current escape character
203    fn get_escape_char(&self) -> Option<ET::Char>;
204    /// Set the current escape character
205    fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
206    /// Get the current newline character
207    fn get_newline_char(&self) -> Option<ET::Char>;
208    /// Set the current newline character
209    fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
210    /// Get the current `\parshape`
211    fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)>;
212    /// Get the current `\parshape` and set it to its default value
213    fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)>;
214    /// Set the current `\parshape`
215    fn set_parshape(
216        &mut self,
217        aux: &EngineAux<ET>,
218        parshape: Vec<(ET::Dim, ET::Dim)>,
219        globally: bool,
220    );
221    /// Get an integer register value
222    fn get_int_register(&self, idx: usize) -> ET::Int;
223    /// Set an integer register value
224    fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool);
225    /// Get a primitive integer value
226    fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int;
227    /// Set a primitive integer value
228    fn set_primitive_int(
229        &mut self,
230        aux: &EngineAux<ET>,
231        name: PrimitiveIdentifier,
232        v: ET::Int,
233        globally: bool,
234    );
235    /// Get a dimen register value
236    fn get_dim_register(&self, idx: usize) -> ET::Dim;
237    /// Set a dimen register value
238    fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool);
239    /// Get a skip register value
240    fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim>;
241    /// Set a skip register value
242    fn set_skip_register(
243        &mut self,
244        aux: &EngineAux<ET>,
245        idx: usize,
246        v: Skip<ET::Dim>,
247        globally: bool,
248    );
249    /// Get a muskip register value
250    fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim>;
251    /// Set a muskip register value
252    fn set_muskip_register(
253        &mut self,
254        aux: &EngineAux<ET>,
255        idx: usize,
256        v: MuSkip<ET::MuDim>,
257        globally: bool,
258    );
259    /// Get a token register value
260    fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token>;
261    /// Set a token register value
262    fn set_toks_register(
263        &mut self,
264        aux: &EngineAux<ET>,
265        idx: usize,
266        v: TokenList<ET::Token>,
267        globally: bool,
268    );
269    /// Get a box register value
270    fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>>;
271    /// Get a box register value mutably (to e.g. change `\ht`, `\wd`, `\dp`, etc.)
272    fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>>;
273    /// Take a box register value; replacing it with `None` (i.e. void box)
274    fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>>;
275    /// Set a box register value
276    fn set_box_register(
277        &mut self,
278        aux: &EngineAux<ET>,
279        idx: usize,
280        v: Option<TeXBox<ET>>,
281        globally: bool,
282    );
283    /// Get a primitive dimension value
284    fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim;
285    /// Set a primitive dimension value
286    fn set_primitive_dim(
287        &mut self,
288        aux: &EngineAux<ET>,
289        name: PrimitiveIdentifier,
290        v: ET::Dim,
291        globally: bool,
292    );
293    /// Get a primitive skip value
294    fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim>;
295    /// Set a primitive skip value
296    fn set_primitive_skip(
297        &mut self,
298        aux: &EngineAux<ET>,
299        name: PrimitiveIdentifier,
300        v: Skip<ET::Dim>,
301        globally: bool,
302    );
303    /// Get a primitive muskip value
304    fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim>;
305    /// Set a primitive muskip value
306    fn set_primitive_muskip(
307        &mut self,
308        aux: &EngineAux<ET>,
309        name: PrimitiveIdentifier,
310        v: MuSkip<ET::MuDim>,
311        globally: bool,
312    );
313    /// Get a primitive token list
314    fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token>;
315    /// Set a primitive token list
316    fn set_primitive_tokens(
317        &mut self,
318        aux: &EngineAux<ET>,
319        name: PrimitiveIdentifier,
320        v: TokenList<ET::Token>,
321        globally: bool,
322    );
323    /// Get the current definition for the control sequence name
324    fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>>;
325    /// Set the current definition for the control sequence name
326    fn set_command(
327        &mut self,
328        aux: &EngineAux<ET>,
329        name: ET::CSName,
330        cmd: Option<TeXCommand<ET>>,
331        globally: bool,
332    );
333    /// Get the current definition for the active character
334    fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>>;
335    /// Set the current definition for the active character
336    fn set_ac_command(
337        &mut self,
338        aux: &EngineAux<ET>,
339        c: ET::Char,
340        cmd: Option<TeXCommand<ET>>,
341        globally: bool,
342    );
343}
344
345/// Convenience trait for tracking state changes in a [`StateStack`] and rolling them back
346/// if necessary at the end of a group.
347pub trait StateChangeTracker<ET: EngineTypes>: State<ET> {
348    /// Get the current [`StateStack`]
349    fn stack(&mut self) -> &mut StateStack<ET>;
350    /// Change a field of the state, and add the change to the [`StateStack`]. Also takes care of inspecting
351    /// and considering the current `\globaldefs` value and passes on the *actual* computed `globally` value
352    /// to the continuation function, which should return the [`StateChange`] containg the *old* value.
353    ///
354    /// For example, on `\count5=4`, you could call this function like this:
355    /// `self.change_field(false,|slf,g| `[`StateChange::IntRegister`]`(5,std::mem::replace(&mut slf.int_registers[5],4)))`
356    fn change_field<F: FnOnce(&mut Self, bool) -> StateChange<ET>>(
357        &mut self,
358        globally: bool,
359        f: F,
360    ) {
361        let globaldefs = self.get_primitive_int(PRIMITIVES.globaldefs);
362        let zero = ET::Int::default();
363        let global = if globaldefs == zero {
364            globally
365        } else {
366            globaldefs > zero
367        };
368        let change = f(self, global);
369        if global {
370            self.stack().add_change_globally(change)
371        } else {
372            self.stack().add_change_locally(change)
373        }
374    }
375}
376
377/// A change to a [`State`], to be potentially rolled back when a group ends.
378#[derive(Clone, Debug)]
379pub enum StateChange<ET: EngineTypes> {
380    Catcode {
381        char: ET::Char,
382        old: CategoryCode,
383    },
384    SfCode {
385        char: ET::Char,
386        old: u16,
387    },
388    DelCode {
389        char: ET::Char,
390        old: ET::Int,
391    },
392    LcCode {
393        char: ET::Char,
394        old: ET::Char,
395    },
396    UcCode {
397        char: ET::Char,
398        old: ET::Char,
399    },
400    MathCode {
401        char: ET::Char,
402        old: u32,
403    },
404    ParShape {
405        old: Vec<(ET::Dim, ET::Dim)>,
406    },
407    CurrentFont(ET::Font),
408    TextFont {
409        idx: u8,
410        old: ET::Font,
411    },
412    ScriptFont {
413        idx: u8,
414        old: ET::Font,
415    },
416    ScriptScriptFont {
417        idx: u8,
418        old: ET::Font,
419    },
420    EndlineChar {
421        old: Option<ET::Char>,
422    },
423    EscapeChar {
424        old: Option<ET::Char>,
425    },
426    NewlineChar {
427        old: Option<ET::Char>,
428    },
429    IntRegister {
430        idx: usize,
431        old: ET::Int,
432    },
433    DimRegister {
434        idx: usize,
435        old: ET::Dim,
436    },
437    SkipRegister {
438        idx: usize,
439        old: Skip<ET::Dim>,
440    },
441    MuSkipRegister {
442        idx: usize,
443        old: MuSkip<ET::MuDim>,
444    },
445    BoxRegister {
446        idx: usize,
447        old: Option<TeXBox<ET>>,
448    },
449    ToksRegister {
450        idx: usize,
451        old: TokenList<ET::Token>,
452    },
453    PrimitiveInt {
454        name: PrimitiveIdentifier,
455        old: ET::Int,
456    },
457    PrimitiveDim {
458        name: PrimitiveIdentifier,
459        old: ET::Dim,
460    },
461    PrimitiveToks {
462        name: PrimitiveIdentifier,
463        old: TokenList<ET::Token>,
464    },
465    PrimitiveSkip {
466        name: PrimitiveIdentifier,
467        old: Skip<ET::Dim>,
468    },
469    PrimitiveMuSkip {
470        name: PrimitiveIdentifier,
471        old: MuSkip<ET::MuDim>,
472    },
473    Command {
474        name: ET::CSName,
475        old: Option<TeXCommand<ET>>,
476    },
477    AcCommand {
478        char: ET::Char,
479        old: Option<TeXCommand<ET>>,
480    },
481    // /// A custom state change, to be implemented by the engine, if additional state change types are needed
482    //Custom{ change:Ptr<Mutex<Option<Box<dyn CustomStateChange<ET>>>>>},
483}
484/*
485/// A custom state change, to be implemented by the engine, if additional state change types are needed.
486pub trait CustomStateChange<ET:EngineTypes> {
487    /// Check if this state change is equivalent to another one, i.e. if it needs to be rolled back
488    /// when a group ends, or is superseded by a previous change
489    fn equiv(&self,other:&dyn CustomStateChange<ET>) -> bool;
490    fn restore(&mut self,aux:&EngineAux<ET>,state:&mut ET::State,trace:bool);
491} */
492
493/// A level of the [`StateStack`], to be rolled back when a group ends
494#[derive(Clone)]
495pub struct StackLevel<ET: EngineTypes> {
496    /// The type of the group
497    pub group_type: GroupType,
498    /// The `\aftergroup` [`Token`](crate::tex::tokens::Token)s to be inserted when the group ends
499    pub aftergroup: Vec<ET::Token>,
500    changes: Vec<StateChangeI<ET>>,
501}
502
503/// A stack of [`StateChange`]s, to be rolled back when a group ends
504pub struct StateStack<ET: EngineTypes> {
505    /// The stack of [`StackLevel`]s
506    pub stack: Vec<StackLevel<ET>>,
507    vecs: Vec<Vec<StateChangeI<ET>>>,
508}
509impl<ET: EngineTypes> Clone for StateStack<ET> {
510    fn clone(&self) -> Self {
511        Self {
512            stack: self.stack.clone(),
513            vecs: vec![],
514        }
515    }
516}
517impl<ET: EngineTypes> Default for StateStack<ET> {
518    fn default() -> Self {
519        Self {
520            stack: vec![],
521            vecs: vec![],
522        }
523    }
524}
525impl<ET: EngineTypes> StateStack<ET> {
526    /// Push a new stack level onto the stack with the given [`GroupType`], as a new group begins
527    pub fn push(&mut self, group_type: GroupType) {
528        let lvl = StackLevel {
529            group_type,
530            aftergroup: vec![],
531            changes: self.vecs.pop().unwrap_or_default(),
532        };
533        self.stack.push(lvl);
534    }
535    /// Pop a stack level from the stack, as a group ends. Returns the [`GroupType`], the `\aftergroup` [`Token`](crate::tex::tokens::Token)s,
536    /// and an iterator over the [`StateChange`]s to be rolled back.
537    pub fn pop(&mut self) -> (GroupType, Vec<ET::Token>, ChangeIter<'_, ET>) {
538        let lvl = self.stack.pop().unwrap();
539        (
540            lvl.group_type,
541            lvl.aftergroup,
542            ChangeIter {
543                stack: self,
544                chs: lvl.changes,
545            },
546        )
547    }
548    /// Register a global state change, never to be rolled back;
549    /// if there is a stack level, remove any equivalent previous changes
550    pub fn add_change_globally(&mut self, change: StateChange<ET>) {
551        let change = change.into();
552        for lvl in &mut self.stack {
553            for c in lvl.changes.iter_mut() {
554                if c.equiv(&change) {
555                    c.active = false
556                }
557            }
558        }
559    }
560    /// Register a local state change, to be rolled back when the current group ends
561    pub fn add_change_locally(&mut self, change: StateChange<ET>) {
562        if let Some(lvl) = self.stack.last_mut() {
563            let change = change.into();
564            if lvl.changes.iter().all(|c| !c.equiv(&change)) {
565                lvl.changes.push(change);
566            }
567        }
568    }
569}
570
571/// An iterator over [`StateChange`]s to be rolled back when a group ends. Iterated over using [`ChangeIter::close`].
572pub struct ChangeIter<'a, ET: EngineTypes> {
573    stack: &'a mut StateStack<ET>,
574    chs: Vec<StateChangeI<ET>>,
575}
576impl<'a, ET: EngineTypes> ChangeIter<'a, ET> {
577    /// Close the iterator, rolling back any changes
578    pub fn close<F: FnMut(StateChange<ET>)>(mut self, mut f: F) {
579        for ch in self.chs.drain(..) {
580            if ch.active {
581                f(ch.ch);
582            }
583        }
584        self.stack.vecs.push(self.chs);
585    }
586}
587
588#[derive(Clone)]
589struct StateChangeI<ET: EngineTypes> {
590    active: bool,
591    id: (std::mem::Discriminant<StateChange<ET>>, usize),
592    ch: StateChange<ET>,
593}
594impl<ET: EngineTypes> StateChangeI<ET> {
595    fn equiv(&self, other: &StateChangeI<ET>) -> bool {
596        self.active && self.id == other.id
597    }
598}
599impl<ET: EngineTypes> From<StateChange<ET>> for StateChangeI<ET> {
600    fn from(value: StateChange<ET>) -> Self {
601        let u = match &value {
602            StateChange::Catcode { char, .. } => (*char).into() as usize,
603            StateChange::SfCode { char, .. } => (*char).into() as usize,
604            StateChange::LcCode { char, .. } => (*char).into() as usize,
605            StateChange::UcCode { char, .. } => (*char).into() as usize,
606            StateChange::MathCode { char, .. } => (*char).into() as usize,
607            StateChange::DelCode { char, .. } => (*char).into() as usize,
608            StateChange::AcCommand { char, .. } => (*char).into() as usize,
609            StateChange::CurrentFont(_) => 0,
610            StateChange::EndlineChar { .. } => 0,
611            StateChange::EscapeChar { .. } => 0,
612            StateChange::NewlineChar { .. } => 0,
613            StateChange::ParShape { .. } => 0,
614            StateChange::TextFont { idx, .. } => *idx as usize,
615            StateChange::ScriptFont { idx, .. } => *idx as usize,
616            StateChange::ScriptScriptFont { idx, .. } => *idx as usize,
617            StateChange::IntRegister { idx, .. } => *idx,
618            StateChange::DimRegister { idx, .. } => *idx,
619            StateChange::SkipRegister { idx, .. } => *idx,
620            StateChange::MuSkipRegister { idx, .. } => *idx,
621            StateChange::ToksRegister { idx, .. } => *idx,
622            StateChange::BoxRegister { idx, .. } => *idx,
623            StateChange::PrimitiveInt { name, .. } => name.as_u16() as usize,
624            StateChange::PrimitiveDim { name, .. } => name.as_u16() as usize,
625            StateChange::PrimitiveSkip { name, .. } => name.as_u16() as usize,
626            StateChange::PrimitiveMuSkip { name, .. } => name.as_u16() as usize,
627            StateChange::PrimitiveToks { name, .. } => name.as_u16() as usize,
628            StateChange::Command { name, .. } => name.id(),
629        };
630        StateChangeI {
631            active: true,
632            id: (std::mem::discriminant(&value), u),
633            ch: value,
634        }
635    }
636}
637
638impl<ET: EngineTypes> EngineReferences<'_, ET> {
639    /// Set the current definition of the provided control sequence name or active character
640    pub fn set_command(
641        &mut self,
642        name: &CSOrActiveChar<ET::Token>,
643        cmd: Option<TeXCommand<ET>>,
644        globally: bool,
645    ) {
646        match name {
647            CSOrActiveChar::Active(c) => self.state.set_ac_command(self.aux, *c, cmd, globally),
648            CSOrActiveChar::Name(cs) => self.state.set_command(self.aux, cs.clone(), cmd, globally),
649        }
650    }
651}