Skip to main content

tex_engine/engine/state/
tex_state.rs

1/*! Implementation of a plain TeX [`State`]. */
2use crate::commands::primitives::{PrimitiveCommands, PrimitiveIdentifier, PRIMITIVES};
3use crate::commands::{PrimitiveCommand, TeXCommand};
4use crate::engine::fontsystem::Font;
5use crate::engine::mouth::Mouth;
6use crate::engine::state::{GroupType, State, StateChange, StateChangeTracker, StateStack};
7use crate::engine::utils::outputs::Outputs;
8use crate::engine::{EngineAux, EngineTypes};
9use crate::tex::catcodes::{CategoryCode, CategoryCodeScheme};
10use crate::tex::characters::Character;
11use crate::tex::characters::CharacterMap;
12use crate::tex::nodes::boxes::TeXBox;
13use crate::tex::numerics::{MuSkip, Skip};
14use crate::tex::tokens::control_sequences::CSNameMap;
15use crate::tex::tokens::control_sequences::{CSHandler, CSName};
16use crate::tex::tokens::token_lists::TokenList;
17use crate::utils::HMap;
18
19/// Default implementation of a plain TeX [`State`].
20#[derive(Clone)]
21pub struct DefaultState<ET: EngineTypes> {
22    stack: StateStack<ET>,
23    primitives: PrimitiveCommands<ET>,
24    catcodes: CategoryCodeScheme<ET::Char>,
25    sfcodes: <ET::Char as Character>::CharMap<u16>,
26    lccodes: <ET::Char as Character>::CharMap<ET::Char>,
27    uccodes: <ET::Char as Character>::CharMap<ET::Char>,
28    mathcodes: <ET::Char as Character>::CharMap<u32>,
29    delcodes: <ET::Char as Character>::CharMap<ET::Int>,
30    primitive_ints: HMap<PrimitiveIdentifier, ET::Int>,
31    primitive_dims: HMap<PrimitiveIdentifier, ET::Dim>,
32    primitive_skips: HMap<PrimitiveIdentifier, Skip<ET::Dim>>,
33    primitive_muskips: HMap<PrimitiveIdentifier, MuSkip<ET::MuDim>>,
34    primitive_toks: HMap<PrimitiveIdentifier, TokenList<ET::Token>>,
35    int_register: Vec<ET::Int>,
36    dim_register: Vec<ET::Dim>,
37    skip_register: Vec<Skip<ET::Dim>>,
38    muskip_register: Vec<MuSkip<ET::MuDim>>,
39    toks_register: Vec<TokenList<ET::Token>>,
40    box_register: Vec<Option<TeXBox<ET>>>,
41    pub commands: <ET::CSName as CSName<ET::Char>>::Map<TeXCommand<ET>>, //Vec<Option<Command<ET>>>,//HMap<<ET::Token as Token>::CS,Command<ET>>,
42    ac_commands: <ET::Char as Character>::CharMap<Option<TeXCommand<ET>>>,
43    endline_char: Option<ET::Char>,
44    escape_char: Option<ET::Char>,
45    newline_char: Option<ET::Char>,
46    current_font: ET::Font,
47    textfonts: [ET::Font; 16],
48    scriptfonts: [ET::Font; 16],
49    scriptscriptfonts: [ET::Font; 16],
50    empty_list: TokenList<ET::Token>,
51    parshape: Vec<(ET::Dim, ET::Dim)>,
52    par_token: ET::CSName,
53}
54impl<ET: EngineTypes> DefaultState<ET> {
55    fn tracing_assigns(&self) -> bool {
56        matches!(self.primitive_ints.get(&PRIMITIVES.tracingassigns), Some(v) if *v > ET::Int::default())
57    }
58    fn tracing_restores(&self) -> bool {
59        matches!(self.primitive_ints.get(&PRIMITIVES.tracingrestores), Some(v) if *v > ET::Int::default())
60    }
61    pub fn set_command_direct(&mut self, name: ET::CSName, cmd: Option<TeXCommand<ET>>) {
62        match cmd {
63            None => self.commands.remove(&name),
64            Some(c) => self.commands.insert(name, c),
65        };
66    }
67}
68
69impl<ET: EngineTypes> StateChangeTracker<ET> for DefaultState<ET> {
70    fn stack(&mut self) -> &mut StateStack<ET> {
71        &mut self.stack
72    }
73}
74
75impl<ET: EngineTypes> State<ET> for DefaultState<ET> {
76    fn new(nullfont: ET::Font, aux: &mut EngineAux<ET>) -> Self {
77        let mem = &aux.memory;
78        let mut lccodes: <ET::Char as Character>::CharMap<ET::Char> = CharacterMap::default();
79        let mut uccodes: <ET::Char as Character>::CharMap<ET::Char> = CharacterMap::default();
80        let mut mathcodes: <ET::Char as Character>::CharMap<u32> = CharacterMap::default();
81        for i in 97..123 {
82            *uccodes.get_mut(i.into()) = (i - 32).into();
83            *lccodes.get_mut((i - 32).into()) = i.into();
84            *mathcodes.get_mut(ET::Char::from(i - 32)) =
85                (i as u32 - 32) + (16 * 16) + (7 * 16 * 16 * 16);
86            *mathcodes.get_mut(ET::Char::from(i)) = (i as u32) + (16 * 16) + (7 * 16 * 16 * 16);
87        }
88        for i in 48..58 {
89            *mathcodes.get_mut(ET::Char::from(i)) = (i as u32) + (7 * 16 * 16 * 16);
90        }
91        let mathfonts = array_init::array_init(|_| nullfont.clone());
92        let par = aux.memory.cs_interner().par();
93        Self {
94            stack: StateStack::default(),
95            primitives: PrimitiveCommands::default(),
96            catcodes: ET::Char::starting_catcode_scheme(),
97            sfcodes: CharacterMap::default(),
98            delcodes: CharacterMap::default(),
99            lccodes,
100            uccodes,
101            mathcodes,
102            current_font: nullfont,
103            primitive_ints: HMap::default(),
104            primitive_dims: HMap::default(),
105            primitive_skips: HMap::default(),
106            primitive_muskips: HMap::default(),
107            primitive_toks: HMap::default(),
108            int_register: Vec::new(),
109            dim_register: Vec::new(),
110            skip_register: Vec::new(),
111            muskip_register: Vec::new(),
112            toks_register: Vec::new(),
113            box_register: Vec::new(),
114            commands: <ET::CSName as CSName<ET::Char>>::Map::default(),
115            ac_commands: <ET::Char as Character>::CharMap::default(),
116            endline_char: Some(ET::Char::from(b'\r')),
117            escape_char: Some(ET::Char::from(b'\\')),
118            newline_char: None,
119            empty_list: mem.empty_list(),
120            parshape: Vec::new(),
121            textfonts: mathfonts.clone(),
122            scriptfonts: mathfonts.clone(),
123            scriptscriptfonts: mathfonts,
124            par_token: par,
125        }
126    }
127
128    fn get_par_token(&self) -> <ET as EngineTypes>::CSName {
129        self.par_token.clone()
130    }
131    fn set_par_token(&mut self, par: <ET as EngineTypes>::CSName) {
132        self.par_token = par;
133    }
134
135    fn register_primitive(
136        &mut self,
137        aux: &mut EngineAux<ET>,
138        name: &'static str,
139        cmd: PrimitiveCommand<ET>,
140    ) {
141        let id = self.primitives.register(name, cmd.clone());
142        let name = aux.memory.cs_interner_mut().cs_from_str(name);
143        self.commands
144            .insert(name, TeXCommand::Primitive { name: id, cmd });
145    }
146    fn primitives(&self) -> &PrimitiveCommands<ET> {
147        &self.primitives
148    }
149
150    fn get_group_type(&self) -> Option<GroupType> {
151        self.stack.stack.last().map(|lvl| lvl.group_type)
152    }
153
154    fn get_group_level(&self) -> usize {
155        self.stack.stack.len()
156    }
157
158    fn aftergroup(&mut self, token: ET::Token) {
159        match self.stack.stack.last_mut() {
160            None => (),
161            Some(lvl) => lvl.aftergroup.push(token),
162        }
163    }
164
165    fn push(&mut self, aux: &mut EngineAux<ET>, group_type: GroupType, line_number: usize) {
166        self.stack.push(group_type);
167        let tracing = matches!(self.primitive_ints.get(&PRIMITIVES.tracinggroups), Some(v) if *v > ET::Int::default());
168        if tracing {
169            aux.outputs.write_neg1(format_args!(
170                "{{entering {} group (level {}) at line {}}}",
171                group_type,
172                self.stack.stack.len(),
173                line_number
174            ))
175        }
176    }
177
178    fn pop(&mut self, aux: &mut EngineAux<ET>, mouth: &mut ET::Mouth) {
179        let len = self.stack.stack.len();
180        assert!(len > 0);
181        let traceg = matches!(self.primitive_ints.get(&PRIMITIVES.tracinggroups), Some(v) if *v > ET::Int::default());
182        let trace = self.tracing_restores();
183
184        let (gt, ag, ch) = self.stack.pop();
185
186        if traceg {
187            aux.outputs.write_neg1(format_args!(
188                "{{leaving {} group (level {}) at line {}}}",
189                gt,
190                len,
191                mouth.line_number()
192            ))
193        }
194        ch.close(|c| {
195            match c {
196                //StateChange::Custom { change } => change.restore(aux,self,trace),
197                StateChange::Catcode { char, old } => {
198                    if trace {
199                        aux.outputs.write_neg1(format_args!(
200                            "{{restoring {}catcode{}={}}}",
201                            <ET::Char as Character>::display_opt(self.escape_char),
202                            char.into(),
203                            old
204                        ));
205                    }
206                    *self.catcodes.get_mut(char) = old;
207                }
208                StateChange::CurrentFont(font) => {
209                    if trace {
210                        aux.outputs.write_neg1(format_args!(
211                            "{{restoring current font ={}{}}}",
212                            <ET::Char as Character>::display_opt(self.escape_char),
213                            aux.memory.cs_interner().resolve(font.name())
214                        ));
215                    }
216                    self.current_font = font;
217                }
218                StateChange::TextFont { idx, old: font } => {
219                    if trace {
220                        aux.outputs.write_neg1(format_args!(
221                            "{{restoring textfont{} ={}{}}}",
222                            idx,
223                            <ET::Char as Character>::display_opt(self.escape_char),
224                            aux.memory.cs_interner().resolve(font.name())
225                        ));
226                    }
227                    self.textfonts[idx as usize] = font;
228                }
229                StateChange::ScriptFont { idx, old: font } => {
230                    if trace {
231                        aux.outputs.write_neg1(format_args!(
232                            "{{restoring scriptfont{} ={}{}}}",
233                            idx,
234                            <ET::Char as Character>::display_opt(self.escape_char),
235                            aux.memory.cs_interner().resolve(font.name())
236                        ));
237                    }
238                    self.scriptfonts[idx as usize] = font;
239                }
240                StateChange::ScriptScriptFont { idx, old: font } => {
241                    if trace {
242                        aux.outputs.write_neg1(format_args!(
243                            "{{restoring scriptscriptfont{} ={}{}}}",
244                            idx,
245                            <ET::Char as Character>::display_opt(self.escape_char),
246                            aux.memory.cs_interner().resolve(font.name())
247                        ));
248                    }
249                    self.scriptscriptfonts[idx as usize] = font;
250                }
251                StateChange::ParShape { old } => {
252                    if trace {
253                        aux.outputs.write_neg1(format_args!("{{TODO parshape}}"));
254                    }
255                    self.parshape = old;
256                }
257                StateChange::SfCode { char, old } => {
258                    if trace {
259                        aux.outputs.write_neg1(format_args!(
260                            "{{restoring {}sfcode{}={}}}",
261                            <ET::Char as Character>::display_opt(self.escape_char),
262                            char.into(),
263                            old
264                        ));
265                    }
266                    *self.sfcodes.get_mut(char) = old;
267                }
268                StateChange::DelCode { char, old } => {
269                    if trace {
270                        aux.outputs.write_neg1(format_args!(
271                            "{{restoring {}delcode{}={}}}",
272                            <ET::Char as Character>::display_opt(self.escape_char),
273                            char.into(),
274                            old
275                        ));
276                    }
277                    *self.delcodes.get_mut(char) = old;
278                }
279                StateChange::LcCode { char, old } => {
280                    if trace {
281                        aux.outputs.write_neg1(format_args!(
282                            "{{restoring {}lccode{}={}}}",
283                            <ET::Char as Character>::display_opt(self.escape_char),
284                            char.into(),
285                            old
286                        ));
287                    }
288                    *self.lccodes.get_mut(char) = old;
289                }
290                StateChange::UcCode { char, old } => {
291                    if trace {
292                        aux.outputs.write_neg1(format_args!(
293                            "{{restoring {}uccode{}={}}}",
294                            <ET::Char as Character>::display_opt(self.escape_char),
295                            char.into(),
296                            old
297                        ));
298                    }
299                    *self.uccodes.get_mut(char) = old;
300                }
301                StateChange::MathCode { char, old } => {
302                    if trace {
303                        aux.outputs.write_neg1(format_args!(
304                            "{{restoring {}mathcode{}=\"{:X}}}",
305                            <ET::Char as Character>::display_opt(self.escape_char),
306                            char.into(),
307                            old
308                        ));
309                    }
310                    *self.mathcodes.get_mut(char) = old;
311                }
312                StateChange::EndlineChar { old } => {
313                    if trace {
314                        aux.outputs.write_neg1(format_args!(
315                            "{{restoring {}endlinechar={}}}",
316                            <ET::Char as Character>::display_opt(self.escape_char),
317                            match old {
318                                None => -1,
319                                Some(c) => c.into() as i64,
320                            }
321                        ));
322                    }
323                    self.endline_char = old;
324                }
325                StateChange::EscapeChar { old } => {
326                    if trace {
327                        aux.outputs.write_neg1(format_args!(
328                            "{{restoring {}escapechar={}}}",
329                            <ET::Char as Character>::display_opt(self.escape_char),
330                            match old {
331                                None => -1,
332                                Some(c) => c.into() as i64,
333                            }
334                        ));
335                    }
336                    self.escape_char = old;
337                }
338                StateChange::NewlineChar { old } => {
339                    if trace {
340                        aux.outputs.write_neg1(format_args!(
341                            "{{restoring {}newlinechar={}}}",
342                            <ET::Char as Character>::display_opt(self.escape_char),
343                            match old {
344                                None => -1,
345                                Some(c) => c.into() as i64,
346                            }
347                        ));
348                    }
349                    self.newline_char = old;
350                }
351                StateChange::IntRegister { idx, old } => {
352                    if trace {
353                        aux.outputs.write_neg1(format_args!(
354                            "{{restoring {}count{}={}}}",
355                            <ET::Char as Character>::display_opt(self.escape_char),
356                            idx,
357                            old
358                        ));
359                    }
360                    self.int_register[idx] = old;
361                }
362                StateChange::DimRegister { idx, old } => {
363                    if trace {
364                        aux.outputs.write_neg1(format_args!(
365                            "{{restoring {}dimen{}={}}}",
366                            <ET::Char as Character>::display_opt(self.escape_char),
367                            idx,
368                            old
369                        ));
370                    }
371                    self.dim_register[idx] = old;
372                }
373                StateChange::SkipRegister { idx, old } => {
374                    if trace {
375                        aux.outputs.write_neg1(format_args!(
376                            "{{restoring {}skip{}={}}}",
377                            <ET::Char as Character>::display_opt(self.escape_char),
378                            idx,
379                            old
380                        ));
381                    }
382                    self.skip_register[idx] = old;
383                }
384                StateChange::MuSkipRegister { idx, old } => {
385                    if trace {
386                        aux.outputs.write_neg1(format_args!(
387                            "{{restoring {}muskip{}={}}}",
388                            <ET::Char as Character>::display_opt(self.escape_char),
389                            idx,
390                            old
391                        ));
392                    }
393                    self.muskip_register[idx] = old;
394                }
395                StateChange::ToksRegister { idx, old } => {
396                    if trace {
397                        aux.outputs.write_neg1(format_args!(
398                            "{{restoring {}toks{}={}}}",
399                            <ET::Char as Character>::display_opt(self.escape_char),
400                            idx,
401                            old.display(
402                                aux.memory.cs_interner(),
403                                &self.catcodes,
404                                self.escape_char,
405                                false
406                            )
407                        ));
408                    }
409                    self.toks_register[idx] = old;
410                }
411                StateChange::BoxRegister { idx, old } => {
412                    if trace {
413                        aux.outputs.write_neg1("TODO trace box restore")
414                    }
415                    self.box_register[idx] = old;
416                }
417                StateChange::PrimitiveInt { name, old } => {
418                    if trace {
419                        aux.outputs.write_neg1(format_args!(
420                            "{{restoring {}{}={}}}",
421                            <ET::Char as Character>::display_opt(self.escape_char),
422                            name.display(self.escape_char),
423                            old
424                        ));
425                    }
426                    self.primitive_ints.insert(name, old);
427                }
428                StateChange::PrimitiveDim { name, old } => {
429                    if trace {
430                        aux.outputs.write_neg1(format_args!(
431                            "{{restoring {}{}={}}}",
432                            <ET::Char as Character>::display_opt(self.escape_char),
433                            name.display(self.escape_char),
434                            old
435                        ));
436                    }
437                    self.primitive_dims.insert(name, old);
438                }
439                StateChange::PrimitiveSkip { name, old } => {
440                    if trace {
441                        aux.outputs.write_neg1(format_args!(
442                            "{{restoring {}{}={}}}",
443                            <ET::Char as Character>::display_opt(self.escape_char),
444                            name.display(self.escape_char),
445                            old
446                        ));
447                    }
448                    self.primitive_skips.insert(name, old);
449                }
450                StateChange::PrimitiveMuSkip { name, old } => {
451                    if trace {
452                        aux.outputs.write_neg1(format_args!(
453                            "{{restoring {}{}={}}}",
454                            <ET::Char as Character>::display_opt(self.escape_char),
455                            name.display(self.escape_char),
456                            old
457                        ));
458                    }
459                    self.primitive_muskips.insert(name, old);
460                }
461                StateChange::PrimitiveToks { name, old } => {
462                    if trace {
463                        aux.outputs.write_neg1(format_args!(
464                            "{{restoring {}{}={}}}",
465                            <ET::Char as Character>::display_opt(self.escape_char),
466                            name.display(self.escape_char),
467                            old.display(
468                                aux.memory.cs_interner(),
469                                &self.catcodes,
470                                self.escape_char,
471                                false
472                            )
473                        ));
474                    }
475                    self.primitive_toks.insert(name, old);
476                }
477                StateChange::Command { name, old } => {
478                    /*
479                                        {
480                                            let dpname = aux.memory.cs_interner().resolve(&name);
481                                            let dpname = dpname.to_string();
482                                            if dpname.starts_with("c_stex_module_") && dpname.ends_with("_notations_prop") {
483                                                println!("\n\n Resetting {dpname} to {}",old.as_ref().map(|c| c.meaning(aux.memory.cs_interner(),&self.catcodes,self.escape_char).to_string()).unwrap_or_else(|| "(NONE)".to_string()));
484                                            }
485                                        }
486                    */
487                    if trace {
488                        match old {
489                            None => aux.outputs.write_neg1(format_args!(
490                                "{{restoring {}{}={}undefined}}",
491                                <ET::Char as Character>::display_opt(self.escape_char),
492                                aux.memory.cs_interner().resolve(&name),
493                                <ET::Char as Character>::display_opt(self.escape_char)
494                            )),
495                            Some(ref c) => aux.outputs.write_neg1(format_args!(
496                                "{{restoring {}{}={}}}",
497                                <ET::Char as Character>::display_opt(self.escape_char),
498                                aux.memory.cs_interner().resolve(&name),
499                                c.meaning(
500                                    aux.memory.cs_interner(),
501                                    &self.catcodes,
502                                    self.escape_char
503                                )
504                            )),
505                        }
506                    }
507                    match old {
508                        Some(o) => self.commands.insert(name, o),
509                        None => self.commands.remove(&name),
510                    };
511                }
512                StateChange::AcCommand { char, old } => {
513                    if trace {
514                        match old {
515                            None => aux.outputs.write_neg1(format_args!(
516                                "{{restoring {}{}={}undefined}}",
517                                <ET::Char as Character>::display_opt(self.escape_char),
518                                char.display(),
519                                <ET::Char as Character>::display_opt(self.escape_char)
520                            )),
521                            Some(ref c) => aux.outputs.write_neg1(format_args!(
522                                "{{restoring {}{}={}}}",
523                                <ET::Char as Character>::display_opt(self.escape_char),
524                                char.display(),
525                                c.meaning(
526                                    aux.memory.cs_interner(),
527                                    &self.catcodes,
528                                    self.escape_char
529                                )
530                            )),
531                        }
532                    }
533                    *self.ac_commands.get_mut(char) = old;
534                } /*
535                  StateChange::Custom {change:c} => {
536                      let mut m = c.lock().unwrap().take();
537                      if let Some(ref mut c) = m {
538                          c.restore(aux,self,trace);
539                      }
540                  }*/
541            }
542        });
543        if !ag.is_empty() {
544            mouth.push_vec(ag)
545        }
546    }
547
548    fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)> {
549        &self.parshape
550    }
551    fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)> {
552        let sh = std::mem::take(&mut self.parshape);
553        self.stack
554            .add_change_locally(StateChange::ParShape { old: sh.clone() });
555        sh
556    }
557    fn set_parshape(
558        &mut self,
559        aux: &EngineAux<ET>,
560        parshape: Vec<(ET::Dim, ET::Dim)>,
561        globally: bool,
562    ) {
563        self.change_field(globally, |s, g| {
564            if s.tracing_assigns() {
565                aux.outputs.write_neg1(format_args!(
566                    "{{{}changing parshape}}",
567                    if g { "globally " } else { "" }
568                ));
569            }
570            let old = std::mem::replace(&mut s.parshape, parshape);
571            StateChange::ParShape { old }
572        })
573    }
574
575    fn get_current_font(&self) -> &ET::Font {
576        &self.current_font
577    }
578    fn set_current_font(&mut self, aux: &mut EngineAux<ET>, fnt: ET::Font, globally: bool) {
579        self.change_field(globally, |s, g| {
580            if s.tracing_assigns() {
581                aux.outputs.write_neg1(format_args!(
582                    "{{{}changing current font={}{}}}",
583                    if g { "globally " } else { "" },
584                    <ET::Char as Character>::display_opt(s.escape_char),
585                    aux.memory.cs_interner().resolve(s.current_font.name())
586                ));
587                aux.outputs.write_neg1(format_args!(
588                    "{{into current font={}{}}}",
589                    <ET::Char as Character>::display_opt(s.escape_char),
590                    aux.memory.cs_interner().resolve(fnt.name())
591                ));
592            }
593            let old = std::mem::replace(&mut s.current_font, fnt);
594            StateChange::CurrentFont(old)
595        })
596    }
597
598    fn get_textfont(&self, i: u8) -> &<ET as EngineTypes>::Font {
599        &self.textfonts[i as usize]
600    }
601    fn set_textfont(
602        &mut self,
603        aux: &mut EngineAux<ET>,
604        idx: u8,
605        fnt: <ET as EngineTypes>::Font,
606        globally: bool,
607    ) {
608        self.change_field(globally, |s, g| {
609            if s.tracing_assigns() {
610                aux.outputs.write_neg1(format_args!(
611                    "{{{}changing textfont{}={}{}}}",
612                    if g { "globally " } else { "" },
613                    idx,
614                    <ET::Char as Character>::display_opt(s.escape_char),
615                    aux.memory
616                        .cs_interner()
617                        .resolve(s.textfonts[idx as usize].name())
618                ));
619                aux.outputs.write_neg1(format_args!(
620                    "{{into textfont{}={}{}}}",
621                    idx,
622                    <ET::Char as Character>::display_opt(s.escape_char),
623                    aux.memory.cs_interner().resolve(fnt.name())
624                ));
625            }
626            let old = std::mem::replace(&mut s.textfonts[idx as usize], fnt);
627            StateChange::TextFont { idx, old }
628        })
629    }
630
631    fn get_scriptfont(&self, i: u8) -> &<ET as EngineTypes>::Font {
632        &self.scriptfonts[i as usize]
633    }
634    fn set_scriptfont(
635        &mut self,
636        aux: &mut EngineAux<ET>,
637        idx: u8,
638        fnt: <ET as EngineTypes>::Font,
639        globally: bool,
640    ) {
641        self.change_field(globally, |s, g| {
642            if s.tracing_assigns() {
643                aux.outputs.write_neg1(format_args!(
644                    "{{{}changing scriptfont{}={}{}}}",
645                    if g { "globally " } else { "" },
646                    idx,
647                    <ET::Char as Character>::display_opt(s.escape_char),
648                    aux.memory
649                        .cs_interner()
650                        .resolve(s.scriptfonts[idx as usize].name())
651                ));
652                aux.outputs.write_neg1(format_args!(
653                    "{{into scriptfont{}={}{}}}",
654                    idx,
655                    <ET::Char as Character>::display_opt(s.escape_char),
656                    aux.memory.cs_interner().resolve(fnt.name())
657                ));
658            }
659            let old = std::mem::replace(&mut s.scriptfonts[idx as usize], fnt);
660            StateChange::ScriptFont { idx, old }
661        })
662    }
663
664    fn get_scriptscriptfont(&self, i: u8) -> &<ET as EngineTypes>::Font {
665        &self.scriptscriptfonts[i as usize]
666    }
667    fn set_scriptscriptfont(
668        &mut self,
669        aux: &mut EngineAux<ET>,
670        idx: u8,
671        fnt: <ET as EngineTypes>::Font,
672        globally: bool,
673    ) {
674        self.change_field(globally, |s, g| {
675            if s.tracing_assigns() {
676                aux.outputs.write_neg1(format_args!(
677                    "{{{}changing scriptscriptfont{}={}{}}}",
678                    if g { "globally " } else { "" },
679                    idx,
680                    <ET::Char as Character>::display_opt(s.escape_char),
681                    aux.memory
682                        .cs_interner()
683                        .resolve(s.scriptscriptfonts[idx as usize].name())
684                ));
685                aux.outputs.write_neg1(format_args!(
686                    "{{into scriptscriptfont{}={}{}}}",
687                    idx,
688                    <ET::Char as Character>::display_opt(s.escape_char),
689                    aux.memory.cs_interner().resolve(fnt.name())
690                ));
691            }
692            let old = std::mem::replace(&mut s.scriptscriptfonts[idx as usize], fnt);
693            StateChange::ScriptScriptFont { idx, old }
694        })
695    }
696
697    fn get_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char> {
698        &self.catcodes
699    }
700    fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool) {
701        self.change_field(globally, |s, g| {
702            if s.tracing_assigns() {
703                let num = c.into();
704                let cc: u8 = cc.into();
705                aux.outputs.write_neg1(format_args!(
706                    "{{{} {}catcode{}={}}}",
707                    if g {
708                        "globally changing"
709                    } else {
710                        "reassigning"
711                    },
712                    <ET::Char as Character>::display_opt(s.escape_char),
713                    num,
714                    cc
715                ));
716            }
717            let old = std::mem::replace(s.catcodes.get_mut(c), cc);
718            StateChange::Catcode { char: c, old }
719        })
720    }
721
722    fn get_sfcode(&self, c: ET::Char) -> u16 {
723        *self.sfcodes.get(c)
724    }
725    fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: u16, globally: bool) {
726        self.change_field(globally, |s, g| {
727            let old = std::mem::replace(s.sfcodes.get_mut(c), val);
728            if s.tracing_assigns() {
729                let num = c.into();
730                aux.outputs.write_neg1(format_args!(
731                    "{{{}changing {}sfcode{}={}}}",
732                    if g { "globally " } else { "" },
733                    <ET::Char as Character>::display_opt(s.escape_char),
734                    num,
735                    old
736                ));
737                aux.outputs.write_neg1(format_args!(
738                    "{{into {}sfcode{}={}}}",
739                    <ET::Char as Character>::display_opt(s.escape_char),
740                    num,
741                    val
742                ));
743            }
744            StateChange::SfCode { char: c, old }
745        })
746    }
747
748    fn get_delcode(&self, c: ET::Char) -> ET::Int {
749        *self.delcodes.get(c)
750    }
751    fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: ET::Int, globally: bool) {
752        self.change_field(globally, |s, g| {
753            let old = std::mem::replace(s.delcodes.get_mut(c), val);
754            if s.tracing_assigns() {
755                let num = c.into();
756                aux.outputs.write_neg1(format_args!(
757                    "{{{}changing {}delcode{}={}}}",
758                    if g { "globally " } else { "" },
759                    <ET::Char as Character>::display_opt(s.escape_char),
760                    num,
761                    old
762                ));
763                aux.outputs.write_neg1(format_args!(
764                    "{{into {}delcode{}={}}}",
765                    <ET::Char as Character>::display_opt(s.escape_char),
766                    num,
767                    val
768                ));
769            }
770            StateChange::DelCode { char: c, old }
771        })
772    }
773
774    fn get_lccode(&self, c: ET::Char) -> ET::Char {
775        *self.lccodes.get(c)
776    }
777    fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: ET::Char, globally: bool) {
778        self.change_field(globally, |s, g| {
779            let old = std::mem::replace(s.lccodes.get_mut(c), val);
780            if s.tracing_assigns() {
781                let num = c.into();
782                aux.outputs.write_neg1(format_args!(
783                    "{{{}changing {}lccode{}={}}}",
784                    if g { "globally " } else { "" },
785                    <ET::Char as Character>::display_opt(s.escape_char),
786                    num,
787                    old.into()
788                ));
789                aux.outputs.write_neg1(format_args!(
790                    "{{into {}lccode{}={}}}",
791                    <ET::Char as Character>::display_opt(s.escape_char),
792                    num,
793                    val.into()
794                ));
795            }
796            StateChange::LcCode { char: c, old }
797        })
798    }
799
800    fn get_uccode(&self, c: ET::Char) -> ET::Char {
801        *self.uccodes.get(c)
802    }
803    fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: ET::Char, globally: bool) {
804        self.change_field(globally, |s, g| {
805            let old = std::mem::replace(s.uccodes.get_mut(c), val);
806            if s.tracing_assigns() {
807                let num = c.into();
808                aux.outputs.write_neg1(format_args!(
809                    "{{{}changing {}uccode{}={}}}",
810                    if g { "globally " } else { "" },
811                    <ET::Char as Character>::display_opt(s.escape_char),
812                    num,
813                    old.into()
814                ));
815                aux.outputs.write_neg1(format_args!(
816                    "{{into {}uccode{}={}}}",
817                    <ET::Char as Character>::display_opt(s.escape_char),
818                    num,
819                    val.into()
820                ));
821            }
822            StateChange::UcCode { char: c, old }
823        })
824    }
825
826    fn get_mathcode(&self, c: ET::Char) -> u32 {
827        *self.mathcodes.get(c)
828    }
829    fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: u32, globally: bool) {
830        self.change_field(globally, |s, g| {
831            let old = std::mem::replace(s.mathcodes.get_mut(c), val);
832            if s.tracing_assigns() {
833                let num = c.into();
834                aux.outputs.write_neg1(format_args!(
835                    "{{{}changing {}mathcode{}=\"{:X}}}",
836                    if g { "globally " } else { "" },
837                    <ET::Char as Character>::display_opt(s.escape_char),
838                    num,
839                    old
840                ));
841                aux.outputs.write_neg1(format_args!(
842                    "{{into {}mathcode{}=\"{:X}}}",
843                    <ET::Char as Character>::display_opt(s.escape_char),
844                    num,
845                    val
846                ));
847            }
848            StateChange::MathCode { char: c, old }
849        })
850    }
851
852    fn get_endline_char(&self) -> Option<ET::Char> {
853        self.endline_char
854    }
855    fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool) {
856        self.change_field(globally, |s, g| {
857            if s.tracing_assigns() {
858                aux.outputs.write_neg1(format_args!(
859                    "{{{}changing {}endlinechar={}}}",
860                    if g { "globally " } else { "" },
861                    <ET::Char as Character>::display_opt(s.escape_char),
862                    match s.endline_char {
863                        None => -1,
864                        Some(c) => c.into() as i64,
865                    }
866                ));
867                aux.outputs.write_neg1(format_args!(
868                    "{{into {}endlinechar={}}}",
869                    <ET::Char as Character>::display_opt(s.escape_char),
870                    match c {
871                        None => -1,
872                        Some(c) => c.into() as i64,
873                    }
874                ));
875            }
876            let old = std::mem::replace(&mut s.endline_char, c);
877            StateChange::EndlineChar { old }
878        })
879    }
880
881    fn get_escape_char(&self) -> Option<ET::Char> {
882        self.escape_char
883    }
884    fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool) {
885        self.change_field(globally, |s, g| {
886            if s.tracing_assigns() {
887                aux.outputs.write_neg1(format_args!(
888                    "{{{}changing {}escapechar={}}}",
889                    if g { "globally " } else { "" },
890                    <ET::Char as Character>::display_opt(s.escape_char),
891                    match s.escape_char {
892                        None => -1,
893                        Some(c) => c.into() as i64,
894                    }
895                ));
896                aux.outputs.write_neg1(format_args!(
897                    "{{into {}escapechar={}}}",
898                    <ET::Char as Character>::display_opt(s.escape_char),
899                    match c {
900                        None => -1,
901                        Some(c) => c.into() as i64,
902                    }
903                ));
904            }
905            let old = std::mem::replace(&mut s.escape_char, c);
906            StateChange::EscapeChar { old }
907        })
908    }
909
910    fn get_newline_char(&self) -> Option<ET::Char> {
911        self.newline_char
912    }
913    fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool) {
914        self.change_field(globally, |s, g| {
915            if s.tracing_assigns() {
916                aux.outputs.write_neg1(format_args!(
917                    "{{{}changing {}newlinechar={}}}",
918                    if g { "globally " } else { "" },
919                    <ET::Char as Character>::display_opt(s.escape_char),
920                    match s.newline_char {
921                        None => -1,
922                        Some(c) => c.into() as i64,
923                    }
924                ));
925                aux.outputs.write_neg1(format_args!(
926                    "{{into {}newlinechar={}}}",
927                    <ET::Char as Character>::display_opt(s.escape_char),
928                    match c {
929                        None => -1,
930                        Some(c) => c.into() as i64,
931                    }
932                ));
933            }
934            let old = std::mem::replace(&mut s.newline_char, c);
935            StateChange::NewlineChar { old }
936        })
937    }
938
939    fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int {
940        match self.primitive_ints.get(&name) {
941            Some(i) => *i,
942            _ => ET::Int::default(),
943        }
944    }
945    fn set_primitive_int(
946        &mut self,
947        aux: &EngineAux<ET>,
948        name: PrimitiveIdentifier,
949        v: ET::Int,
950        globally: bool,
951    ) {
952        self.change_field(globally, |s, g| {
953            let old = s.primitive_ints.insert(name, v).unwrap_or_default();
954            if s.tracing_assigns() {
955                aux.outputs.write_neg1(format_args!(
956                    "{{{}changing {}={}}}",
957                    if g { "globally " } else { "" },
958                    name.display(s.escape_char),
959                    old
960                ));
961                aux.outputs.write_neg1(format_args!(
962                    "{{into {}={}}}",
963                    name.display(s.escape_char),
964                    v
965                ))
966            }
967            StateChange::PrimitiveInt { name, old }
968        });
969    }
970
971    fn get_int_register(&self, idx: usize) -> ET::Int {
972        match self.int_register.get(idx) {
973            Some(i) => *i,
974            _ => ET::Int::default(),
975        }
976    }
977    fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool) {
978        self.change_field(globally, |s, g| {
979            if s.int_register.len() <= idx {
980                s.int_register.resize(idx + 1, ET::Int::default());
981            }
982            let old = std::mem::replace(&mut s.int_register[idx], v);
983            if s.tracing_assigns() {
984                aux.outputs.write_neg1(format_args!(
985                    "{{{}changing {}count{}={}}}",
986                    if g { "globally " } else { "" },
987                    ET::Char::display_opt(s.escape_char),
988                    idx,
989                    old
990                ));
991                aux.outputs.write_neg1(format_args!(
992                    "{{into {}count{}={}}}",
993                    ET::Char::display_opt(s.escape_char),
994                    idx,
995                    v
996                ))
997            }
998            StateChange::IntRegister { idx, old }
999        });
1000    }
1001
1002    fn get_dim_register(&self, idx: usize) -> ET::Dim {
1003        match self.dim_register.get(idx) {
1004            Some(i) => *i,
1005            _ => ET::Dim::default(),
1006        }
1007    }
1008    fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool) {
1009        self.change_field(globally, |s, g| {
1010            if s.dim_register.len() <= idx {
1011                s.dim_register.resize(idx + 1, ET::Dim::default());
1012            }
1013            let old = std::mem::replace(&mut s.dim_register[idx], v);
1014            if s.tracing_assigns() {
1015                aux.outputs.write_neg1(format_args!(
1016                    "{{{}changing {}dimen{}={}}}",
1017                    if g { "globally " } else { "" },
1018                    ET::Char::display_opt(s.escape_char),
1019                    idx,
1020                    old
1021                ));
1022                aux.outputs.write_neg1(format_args!(
1023                    "{{into {}dimen{}={}}}",
1024                    ET::Char::display_opt(s.escape_char),
1025                    idx,
1026                    v
1027                ));
1028            }
1029            StateChange::DimRegister { idx, old }
1030        });
1031    }
1032
1033    fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim> {
1034        match self.skip_register.get(idx) {
1035            Some(i) => *i,
1036            _ => Skip::default(),
1037        }
1038    }
1039    fn set_skip_register(
1040        &mut self,
1041        aux: &EngineAux<ET>,
1042        idx: usize,
1043        v: Skip<ET::Dim>,
1044        globally: bool,
1045    ) {
1046        self.change_field(globally, |s, g| {
1047            if s.skip_register.len() <= idx {
1048                s.skip_register.resize(idx + 1, Skip::default());
1049            }
1050            let old = std::mem::replace(&mut s.skip_register[idx], v);
1051            if s.tracing_assigns() {
1052                aux.outputs.write_neg1(format_args!(
1053                    "{{{}changing {}skip{}={}}}",
1054                    if g { "globally " } else { "" },
1055                    ET::Char::display_opt(s.escape_char),
1056                    idx,
1057                    old
1058                ));
1059                aux.outputs.write_neg1(format_args!(
1060                    "{{into {}skip{}={}}}",
1061                    ET::Char::display_opt(s.escape_char),
1062                    idx,
1063                    v
1064                ))
1065            }
1066            StateChange::SkipRegister { idx, old }
1067        });
1068    }
1069
1070    fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim> {
1071        match self.muskip_register.get(idx) {
1072            Some(i) => *i,
1073            _ => MuSkip::default(),
1074        }
1075    }
1076    fn set_muskip_register(
1077        &mut self,
1078        aux: &EngineAux<ET>,
1079        idx: usize,
1080        v: MuSkip<ET::MuDim>,
1081        globally: bool,
1082    ) {
1083        self.change_field(globally, |s, g| {
1084            if s.muskip_register.len() <= idx {
1085                s.muskip_register.resize(idx + 1, MuSkip::default());
1086            }
1087            let old = std::mem::replace(&mut s.muskip_register[idx], v);
1088            if s.tracing_assigns() {
1089                aux.outputs.write_neg1(format_args!(
1090                    "{{{}changing {}muskip{}={}}}",
1091                    if g { "globally " } else { "" },
1092                    ET::Char::display_opt(s.escape_char),
1093                    idx,
1094                    old
1095                ));
1096                aux.outputs.write_neg1(format_args!(
1097                    "{{into {}muskip{}={}}}",
1098                    ET::Char::display_opt(s.escape_char),
1099                    idx,
1100                    v
1101                ))
1102            }
1103            StateChange::MuSkipRegister { idx, old }
1104        });
1105    }
1106
1107    fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>> {
1108        match self.box_register.get(idx) {
1109            None => None,
1110            Some(i) => i.as_ref(),
1111        }
1112    }
1113    fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>> {
1114        match self.box_register.get_mut(idx) {
1115            None => None,
1116            Some(i) => i.as_mut(),
1117        }
1118    }
1119    fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>> {
1120        match self.box_register.get_mut(idx) {
1121            None => None,
1122            Some(i) => std::mem::take(i),
1123        }
1124    }
1125    fn set_box_register(
1126        &mut self,
1127        aux: &EngineAux<ET>,
1128        idx: usize,
1129        v: Option<TeXBox<ET>>,
1130        globally: bool,
1131    ) {
1132        self.change_field(globally, |s, _| {
1133            if s.box_register.len() <= idx {
1134                s.box_register.resize(idx + 1, None);
1135            }
1136            let old = std::mem::replace(&mut s.box_register[idx], v);
1137            if s.tracing_assigns() {
1138                aux.outputs
1139                    .write_neg1(format_args!("{{TODO: trace box register change {}}}", idx));
1140            }
1141            StateChange::BoxRegister { idx, old }
1142        });
1143    }
1144
1145    fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token> {
1146        match self.toks_register.get(idx) {
1147            Some(i) => i,
1148            _ => &self.empty_list,
1149        }
1150    }
1151    fn set_toks_register(
1152        &mut self,
1153        aux: &EngineAux<ET>,
1154        idx: usize,
1155        v: TokenList<ET::Token>,
1156        globally: bool,
1157    ) {
1158        self.change_field(globally, |s, g| {
1159            if s.toks_register.len() <= idx {
1160                s.toks_register.resize(idx + 1, s.empty_list.clone());
1161            }
1162            let trace = s.tracing_assigns();
1163            if trace {
1164                aux.outputs.write_neg1(format_args!(
1165                    "{{{}changing {}toks{}={}}}",
1166                    if g { "globally " } else { "" },
1167                    ET::Char::display_opt(s.escape_char),
1168                    idx,
1169                    s.toks_register[idx].display(
1170                        aux.memory.cs_interner(),
1171                        &s.catcodes,
1172                        s.escape_char,
1173                        false
1174                    )
1175                ));
1176            }
1177            let old = std::mem::replace(&mut s.toks_register[idx], v);
1178            if s.tracing_assigns() {
1179                aux.outputs.write_neg1(format_args!(
1180                    "{{into {}toks{}={}}}",
1181                    ET::Char::display_opt(s.escape_char),
1182                    idx,
1183                    s.toks_register[idx].display(
1184                        aux.memory.cs_interner(),
1185                        &s.catcodes,
1186                        s.escape_char,
1187                        false
1188                    )
1189                ))
1190            }
1191            StateChange::ToksRegister { idx, old }
1192        });
1193    }
1194
1195    fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token> {
1196        match self.primitive_toks.get(&name) {
1197            Some(i) => i,
1198            _ => &self.empty_list,
1199        }
1200    }
1201    fn set_primitive_tokens(
1202        &mut self,
1203        aux: &EngineAux<ET>,
1204        name: PrimitiveIdentifier,
1205        v: TokenList<ET::Token>,
1206        globally: bool,
1207    ) {
1208        self.change_field(globally, |s, g| {
1209            let old = s
1210                .primitive_toks
1211                .insert(name, v)
1212                .unwrap_or(s.empty_list.clone());
1213            if s.tracing_assigns() {
1214                aux.outputs.write_neg1(format_args!(
1215                    "{{{}changing {}={}}}",
1216                    if g { "globally " } else { "" },
1217                    name.display(s.escape_char),
1218                    old.display(aux.memory.cs_interner(), &s.catcodes, s.escape_char, false)
1219                ));
1220                aux.outputs.write_neg1(format_args!(
1221                    "{{into {}={}}}",
1222                    name.display(s.escape_char),
1223                    s.primitive_toks.get(&name).unwrap().display(
1224                        aux.memory.cs_interner(),
1225                        &s.catcodes,
1226                        s.escape_char,
1227                        false
1228                    )
1229                ))
1230            }
1231            StateChange::PrimitiveToks { name, old }
1232        });
1233    }
1234
1235    fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim {
1236        match self.primitive_dims.get(&name) {
1237            Some(i) => *i,
1238            _ => ET::Dim::default(),
1239        }
1240    }
1241    fn set_primitive_dim(
1242        &mut self,
1243        aux: &EngineAux<ET>,
1244        name: PrimitiveIdentifier,
1245        v: ET::Dim,
1246        globally: bool,
1247    ) {
1248        self.change_field(globally, |s, g| {
1249            let old = s.primitive_dims.insert(name, v).unwrap_or_default();
1250            if s.tracing_assigns() {
1251                aux.outputs.write_neg1(format_args!(
1252                    "{{{}changing {}={}}}",
1253                    if g { "globally " } else { "" },
1254                    name.display(s.escape_char),
1255                    old
1256                ));
1257                aux.outputs.write_neg1(format_args!(
1258                    "{{into {}={}}}",
1259                    name.display(s.escape_char),
1260                    v
1261                ))
1262            }
1263            StateChange::PrimitiveDim { name, old }
1264        });
1265    }
1266
1267    fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim> {
1268        match self.primitive_skips.get(&name) {
1269            Some(i) => *i,
1270            _ => Skip::default(),
1271        }
1272    }
1273    fn set_primitive_skip(
1274        &mut self,
1275        aux: &EngineAux<ET>,
1276        name: PrimitiveIdentifier,
1277        v: Skip<ET::Dim>,
1278        globally: bool,
1279    ) {
1280        self.change_field(globally, |s, g| {
1281            let old = s.primitive_skips.insert(name, v).unwrap_or_default();
1282            if s.tracing_assigns() {
1283                aux.outputs.write_neg1(format_args!(
1284                    "{{{}changing {}={}}}",
1285                    if g { "globally " } else { "" },
1286                    name.display(s.escape_char),
1287                    old
1288                ));
1289                aux.outputs.write_neg1(format_args!(
1290                    "{{into {}={}}}",
1291                    name.display(s.escape_char),
1292                    v
1293                ))
1294            }
1295            StateChange::PrimitiveSkip { name, old }
1296        });
1297    }
1298
1299    fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim> {
1300        match self.primitive_muskips.get(&name) {
1301            Some(i) => *i,
1302            _ => MuSkip::default(),
1303        }
1304    }
1305    fn set_primitive_muskip(
1306        &mut self,
1307        aux: &EngineAux<ET>,
1308        name: PrimitiveIdentifier,
1309        v: MuSkip<ET::MuDim>,
1310        globally: bool,
1311    ) {
1312        self.change_field(globally, |s, g| {
1313            let old = s.primitive_muskips.insert(name, v).unwrap_or_default();
1314            if s.tracing_assigns() {
1315                aux.outputs.write_neg1(format_args!(
1316                    "{{{}changing {}={}}}",
1317                    if g { "globally " } else { "" },
1318                    name.display(s.escape_char),
1319                    old
1320                ));
1321                aux.outputs.write_neg1(format_args!(
1322                    "{{into {}={}}}",
1323                    name.display(s.escape_char),
1324                    v
1325                ))
1326            }
1327            StateChange::PrimitiveMuSkip { name, old }
1328        });
1329    }
1330
1331    fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>> {
1332        self.commands.get(name)
1333    }
1334    fn set_command(
1335        &mut self,
1336        aux: &EngineAux<ET>,
1337        name: ET::CSName,
1338        cmd: Option<TeXCommand<ET>>,
1339        globally: bool,
1340    ) {
1341        self.change_field(globally, |s, g| {
1342            /*
1343                       {
1344                           let dpname = aux.memory.cs_interner().resolve(&name);
1345                           let dpname = dpname.to_string();
1346                           if dpname.starts_with("c_stex_module_") && dpname.ends_with("_notations_prop") {
1347                               println!("\n\n Setting {dpname} to {} \n(globally: {g})\n",cmd.as_ref().map(|c| c.meaning(aux.memory.cs_interner(),&s.catcodes,s.escape_char).to_string()).unwrap_or_else(|| "(NONE)".to_string()));
1348                           }
1349                       }
1350            */
1351            let old = match cmd {
1352                None => {
1353                    let o = s.commands.remove(&name);
1354                    if s.tracing_assigns() {
1355                        match o {
1356                            None => aux.outputs.write_neg1(format_args!(
1357                                "{{{}changing {}{}={}undefined}}",
1358                                if g { "globally " } else { "" },
1359                                ET::Char::display_opt(s.escape_char),
1360                                aux.memory.cs_interner().resolve(&name),
1361                                ET::Char::display_opt(s.escape_char)
1362                            )),
1363                            Some(ref c) => aux.outputs.write_neg1(format_args!(
1364                                "{{{}changing {}{}={}}}",
1365                                if g { "globally " } else { "" },
1366                                ET::Char::display_opt(s.escape_char),
1367                                aux.memory.cs_interner().resolve(&name),
1368                                c.meaning(aux.memory.cs_interner(), &s.catcodes, s.escape_char)
1369                            )),
1370                        }
1371                        aux.outputs.write_neg1(format_args!(
1372                            "{{into {}{}={}undefined}}",
1373                            ET::Char::display_opt(s.escape_char),
1374                            aux.memory.cs_interner().resolve(&name),
1375                            ET::Char::display_opt(s.escape_char)
1376                        ));
1377                    }
1378                    o
1379                }
1380                Some(cmd) => {
1381                    if s.tracing_assigns() {
1382                        match s.commands.get(&name) {
1383                            None => aux.outputs.write_neg1(format_args!(
1384                                "{{{}changing {}{}={}undefined}}",
1385                                if g { "globally " } else { "" },
1386                                ET::Char::display_opt(s.escape_char),
1387                                aux.memory.cs_interner().resolve(&name),
1388                                ET::Char::display_opt(s.escape_char)
1389                            )),
1390                            Some(c) => aux.outputs.write_neg1(format_args!(
1391                                "{{{}changing {}{}={}}}",
1392                                if g { "globally " } else { "" },
1393                                ET::Char::display_opt(s.escape_char),
1394                                aux.memory.cs_interner().resolve(&name),
1395                                c.meaning(aux.memory.cs_interner(), &s.catcodes, s.escape_char)
1396                            )),
1397                        }
1398                        aux.outputs.write_neg1(format_args!(
1399                            "{{into {}{}={}}}",
1400                            ET::Char::display_opt(s.escape_char),
1401                            aux.memory.cs_interner().resolve(&name),
1402                            cmd.meaning(aux.memory.cs_interner(), &s.catcodes, s.escape_char)
1403                        ));
1404                    }
1405                    s.commands.insert(name.clone(), cmd)
1406                }
1407            };
1408            StateChange::Command { name, old }
1409        });
1410    }
1411
1412    fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>> {
1413        self.ac_commands.get(c).as_ref()
1414    }
1415    fn set_ac_command(
1416        &mut self,
1417        _aux: &EngineAux<ET>,
1418        c: ET::Char,
1419        cmd: Option<TeXCommand<ET>>,
1420        globally: bool,
1421    ) {
1422        self.change_field(globally, |s, _| {
1423            let old = std::mem::replace(s.ac_commands.get_mut(c), cmd);
1424            StateChange::AcCommand { char: c, old }
1425        });
1426    }
1427}