1pub 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#[derive(Clone, Copy, Eq, PartialEq, Debug)]
18pub enum GroupType {
19 Simple,
21 HBox,
23 VAdjust,
25 VBox,
27 VTop,
29 Align,
31 Noalign,
33 Output,
35 Math,
37 Disc,
39 Insert,
41 VCenter,
43 MathChoice,
45 SemiSimple,
47 MathShift { display: bool },
49 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 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
98pub trait State<ET: EngineTypes>: Sized + Clone {
106 fn new(nullfont: ET::Font, aux: &mut EngineAux<ET>) -> Self;
108 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 fn aftergroup(&mut self, token: ET::Token);
120
121 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 fn register_primitive(
132 &mut self,
133 aux: &mut EngineAux<ET>,
134 name: &'static str,
135 cmd: PrimitiveCommand<ET>,
136 );
137 fn primitives(&self) -> &PrimitiveCommands<ET>;
139 fn push(&mut self, aux: &mut EngineAux<ET>, group_type: GroupType, line_number: usize);
141 fn pop(&mut self, aux: &mut EngineAux<ET>, mouth: &mut ET::Mouth);
143 fn get_group_type(&self) -> Option<GroupType>;
145 fn get_group_level(&self) -> usize;
147 fn get_current_font(&self) -> &ET::Font;
149 fn set_current_font(&mut self, aux: &mut EngineAux<ET>, fnt: ET::Font, globally: bool);
151 fn get_textfont(&self, fam: u8) -> &ET::Font;
153 fn set_textfont(&mut self, aux: &mut EngineAux<ET>, fam: u8, fnt: ET::Font, globally: bool);
155 fn get_scriptfont(&self, fam: u8) -> &ET::Font;
157 fn set_scriptfont(&mut self, aux: &mut EngineAux<ET>, fam: u8, fnt: ET::Font, globally: bool);
159 fn get_scriptscriptfont(&self, fam: u8) -> &ET::Font;
161 fn set_scriptscriptfont(
163 &mut self,
164 aux: &mut EngineAux<ET>,
165 fam: u8,
166 fnt: ET::Font,
167 globally: bool,
168 );
169
170 fn get_par_token(&self) -> ET::CSName;
172 fn set_par_token(&mut self, par: ET::CSName);
174 fn get_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char>;
176 fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool);
178 fn get_sfcode(&self, c: ET::Char) -> u16;
180 fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, sfcode: u16, globally: bool);
182 fn get_lccode(&self, c: ET::Char) -> ET::Char;
184 fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, lccode: ET::Char, globally: bool);
186 fn get_uccode(&self, c: ET::Char) -> ET::Char;
188 fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, uccode: ET::Char, globally: bool);
190 fn get_delcode(&self, c: ET::Char) -> ET::Int;
192 fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, delcode: ET::Int, globally: bool);
194 fn get_mathcode(&self, c: ET::Char) -> u32;
196 fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, mathcode: u32, globally: bool);
198 fn get_endline_char(&self) -> Option<ET::Char>;
200 fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
202 fn get_escape_char(&self) -> Option<ET::Char>;
204 fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
206 fn get_newline_char(&self) -> Option<ET::Char>;
208 fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
210 fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)>;
212 fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)>;
214 fn set_parshape(
216 &mut self,
217 aux: &EngineAux<ET>,
218 parshape: Vec<(ET::Dim, ET::Dim)>,
219 globally: bool,
220 );
221 fn get_int_register(&self, idx: usize) -> ET::Int;
223 fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool);
225 fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int;
227 fn set_primitive_int(
229 &mut self,
230 aux: &EngineAux<ET>,
231 name: PrimitiveIdentifier,
232 v: ET::Int,
233 globally: bool,
234 );
235 fn get_dim_register(&self, idx: usize) -> ET::Dim;
237 fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool);
239 fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim>;
241 fn set_skip_register(
243 &mut self,
244 aux: &EngineAux<ET>,
245 idx: usize,
246 v: Skip<ET::Dim>,
247 globally: bool,
248 );
249 fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim>;
251 fn set_muskip_register(
253 &mut self,
254 aux: &EngineAux<ET>,
255 idx: usize,
256 v: MuSkip<ET::MuDim>,
257 globally: bool,
258 );
259 fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token>;
261 fn set_toks_register(
263 &mut self,
264 aux: &EngineAux<ET>,
265 idx: usize,
266 v: TokenList<ET::Token>,
267 globally: bool,
268 );
269 fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>>;
271 fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>>;
273 fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>>;
275 fn set_box_register(
277 &mut self,
278 aux: &EngineAux<ET>,
279 idx: usize,
280 v: Option<TeXBox<ET>>,
281 globally: bool,
282 );
283 fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim;
285 fn set_primitive_dim(
287 &mut self,
288 aux: &EngineAux<ET>,
289 name: PrimitiveIdentifier,
290 v: ET::Dim,
291 globally: bool,
292 );
293 fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim>;
295 fn set_primitive_skip(
297 &mut self,
298 aux: &EngineAux<ET>,
299 name: PrimitiveIdentifier,
300 v: Skip<ET::Dim>,
301 globally: bool,
302 );
303 fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim>;
305 fn set_primitive_muskip(
307 &mut self,
308 aux: &EngineAux<ET>,
309 name: PrimitiveIdentifier,
310 v: MuSkip<ET::MuDim>,
311 globally: bool,
312 );
313 fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token>;
315 fn set_primitive_tokens(
317 &mut self,
318 aux: &EngineAux<ET>,
319 name: PrimitiveIdentifier,
320 v: TokenList<ET::Token>,
321 globally: bool,
322 );
323 fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>>;
325 fn set_command(
327 &mut self,
328 aux: &EngineAux<ET>,
329 name: ET::CSName,
330 cmd: Option<TeXCommand<ET>>,
331 globally: bool,
332 );
333 fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>>;
335 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
345pub trait StateChangeTracker<ET: EngineTypes>: State<ET> {
348 fn stack(&mut self) -> &mut StateStack<ET>;
350 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#[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 }
484#[derive(Clone)]
495pub struct StackLevel<ET: EngineTypes> {
496 pub group_type: GroupType,
498 pub aftergroup: Vec<ET::Token>,
500 changes: Vec<StateChangeI<ET>>,
501}
502
503pub struct StateStack<ET: EngineTypes> {
505 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 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 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 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 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
571pub 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 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 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}