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_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char>;
172 fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool);
174 fn get_sfcode(&self, c: ET::Char) -> u16;
176 fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, sfcode: u16, globally: bool);
178 fn get_lccode(&self, c: ET::Char) -> ET::Char;
180 fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, lccode: ET::Char, globally: bool);
182 fn get_uccode(&self, c: ET::Char) -> ET::Char;
184 fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, uccode: ET::Char, globally: bool);
186 fn get_delcode(&self, c: ET::Char) -> ET::Int;
188 fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, delcode: ET::Int, globally: bool);
190 fn get_mathcode(&self, c: ET::Char) -> u32;
192 fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, mathcode: u32, globally: bool);
194 fn get_endline_char(&self) -> Option<ET::Char>;
196 fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
198 fn get_escape_char(&self) -> Option<ET::Char>;
200 fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
202 fn get_newline_char(&self) -> Option<ET::Char>;
204 fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool);
206 fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)>;
208 fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)>;
210 fn set_parshape(
212 &mut self,
213 aux: &EngineAux<ET>,
214 parshape: Vec<(ET::Dim, ET::Dim)>,
215 globally: bool,
216 );
217 fn get_int_register(&self, idx: usize) -> ET::Int;
219 fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool);
221 fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int;
223 fn set_primitive_int(
225 &mut self,
226 aux: &EngineAux<ET>,
227 name: PrimitiveIdentifier,
228 v: ET::Int,
229 globally: bool,
230 );
231 fn get_dim_register(&self, idx: usize) -> ET::Dim;
233 fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool);
235 fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim>;
237 fn set_skip_register(
239 &mut self,
240 aux: &EngineAux<ET>,
241 idx: usize,
242 v: Skip<ET::Dim>,
243 globally: bool,
244 );
245 fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim>;
247 fn set_muskip_register(
249 &mut self,
250 aux: &EngineAux<ET>,
251 idx: usize,
252 v: MuSkip<ET::MuDim>,
253 globally: bool,
254 );
255 fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token>;
257 fn set_toks_register(
259 &mut self,
260 aux: &EngineAux<ET>,
261 idx: usize,
262 v: TokenList<ET::Token>,
263 globally: bool,
264 );
265 fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>>;
267 fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>>;
269 fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>>;
271 fn set_box_register(
273 &mut self,
274 aux: &EngineAux<ET>,
275 idx: usize,
276 v: Option<TeXBox<ET>>,
277 globally: bool,
278 );
279 fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim;
281 fn set_primitive_dim(
283 &mut self,
284 aux: &EngineAux<ET>,
285 name: PrimitiveIdentifier,
286 v: ET::Dim,
287 globally: bool,
288 );
289 fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim>;
291 fn set_primitive_skip(
293 &mut self,
294 aux: &EngineAux<ET>,
295 name: PrimitiveIdentifier,
296 v: Skip<ET::Dim>,
297 globally: bool,
298 );
299 fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim>;
301 fn set_primitive_muskip(
303 &mut self,
304 aux: &EngineAux<ET>,
305 name: PrimitiveIdentifier,
306 v: MuSkip<ET::MuDim>,
307 globally: bool,
308 );
309 fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token>;
311 fn set_primitive_tokens(
313 &mut self,
314 aux: &EngineAux<ET>,
315 name: PrimitiveIdentifier,
316 v: TokenList<ET::Token>,
317 globally: bool,
318 );
319 fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>>;
321 fn set_command(
323 &mut self,
324 aux: &EngineAux<ET>,
325 name: ET::CSName,
326 cmd: Option<TeXCommand<ET>>,
327 globally: bool,
328 );
329 fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>>;
331 fn set_ac_command(
333 &mut self,
334 aux: &EngineAux<ET>,
335 c: ET::Char,
336 cmd: Option<TeXCommand<ET>>,
337 globally: bool,
338 );
339}
340
341pub trait StateChangeTracker<ET: EngineTypes>: State<ET> {
344 fn stack(&mut self) -> &mut StateStack<ET>;
346 fn change_field<F: FnOnce(&mut Self, bool) -> StateChange<ET>>(
353 &mut self,
354 globally: bool,
355 f: F,
356 ) {
357 let globaldefs = self.get_primitive_int(PRIMITIVES.globaldefs);
358 let zero = ET::Int::default();
359 let global = if globaldefs == zero {
360 globally
361 } else {
362 globaldefs > zero
363 };
364 let change = f(self, global);
365 if global {
366 self.stack().add_change_globally(change)
367 } else {
368 self.stack().add_change_locally(change)
369 }
370 }
371}
372
373#[derive(Clone, Debug)]
375pub enum StateChange<ET: EngineTypes> {
376 Catcode {
377 char: ET::Char,
378 old: CategoryCode,
379 },
380 SfCode {
381 char: ET::Char,
382 old: u16,
383 },
384 DelCode {
385 char: ET::Char,
386 old: ET::Int,
387 },
388 LcCode {
389 char: ET::Char,
390 old: ET::Char,
391 },
392 UcCode {
393 char: ET::Char,
394 old: ET::Char,
395 },
396 MathCode {
397 char: ET::Char,
398 old: u32,
399 },
400 ParShape {
401 old: Vec<(ET::Dim, ET::Dim)>,
402 },
403 CurrentFont(ET::Font),
404 TextFont {
405 idx: u8,
406 old: ET::Font,
407 },
408 ScriptFont {
409 idx: u8,
410 old: ET::Font,
411 },
412 ScriptScriptFont {
413 idx: u8,
414 old: ET::Font,
415 },
416 EndlineChar {
417 old: Option<ET::Char>,
418 },
419 EscapeChar {
420 old: Option<ET::Char>,
421 },
422 NewlineChar {
423 old: Option<ET::Char>,
424 },
425 IntRegister {
426 idx: usize,
427 old: ET::Int,
428 },
429 DimRegister {
430 idx: usize,
431 old: ET::Dim,
432 },
433 SkipRegister {
434 idx: usize,
435 old: Skip<ET::Dim>,
436 },
437 MuSkipRegister {
438 idx: usize,
439 old: MuSkip<ET::MuDim>,
440 },
441 BoxRegister {
442 idx: usize,
443 old: Option<TeXBox<ET>>,
444 },
445 ToksRegister {
446 idx: usize,
447 old: TokenList<ET::Token>,
448 },
449 PrimitiveInt {
450 name: PrimitiveIdentifier,
451 old: ET::Int,
452 },
453 PrimitiveDim {
454 name: PrimitiveIdentifier,
455 old: ET::Dim,
456 },
457 PrimitiveToks {
458 name: PrimitiveIdentifier,
459 old: TokenList<ET::Token>,
460 },
461 PrimitiveSkip {
462 name: PrimitiveIdentifier,
463 old: Skip<ET::Dim>,
464 },
465 PrimitiveMuSkip {
466 name: PrimitiveIdentifier,
467 old: MuSkip<ET::MuDim>,
468 },
469 Command {
470 name: ET::CSName,
471 old: Option<TeXCommand<ET>>,
472 },
473 AcCommand {
474 char: ET::Char,
475 old: Option<TeXCommand<ET>>,
476 },
477 }
480#[derive(Clone)]
491pub struct StackLevel<ET: EngineTypes> {
492 pub group_type: GroupType,
494 pub aftergroup: Vec<ET::Token>,
496 changes: Vec<StateChangeI<ET>>,
497}
498
499pub struct StateStack<ET: EngineTypes> {
501 pub stack: Vec<StackLevel<ET>>,
503 vecs: Vec<Vec<StateChangeI<ET>>>,
504}
505impl<ET: EngineTypes> Clone for StateStack<ET> {
506 fn clone(&self) -> Self {
507 Self {
508 stack: self.stack.clone(),
509 vecs: vec![],
510 }
511 }
512}
513impl<ET: EngineTypes> Default for StateStack<ET> {
514 fn default() -> Self {
515 Self {
516 stack: vec![],
517 vecs: vec![],
518 }
519 }
520}
521impl<ET: EngineTypes> StateStack<ET> {
522 pub fn push(&mut self, group_type: GroupType) {
524 let lvl = StackLevel {
525 group_type,
526 aftergroup: vec![],
527 changes: self.vecs.pop().unwrap_or_default(),
528 };
529 self.stack.push(lvl);
530 }
531 pub fn pop(&mut self) -> (GroupType, Vec<ET::Token>, ChangeIter<'_, ET>) {
534 let lvl = self.stack.pop().unwrap();
535 (
536 lvl.group_type,
537 lvl.aftergroup,
538 ChangeIter {
539 stack: self,
540 chs: lvl.changes,
541 },
542 )
543 }
544 pub fn add_change_globally(&mut self, change: StateChange<ET>) {
547 let change = change.into();
548 for lvl in &mut self.stack {
549 for c in lvl.changes.iter_mut() {
550 if c.equiv(&change) {
551 c.active = false
552 }
553 }
554 }
555 }
556 pub fn add_change_locally(&mut self, change: StateChange<ET>) {
558 if let Some(lvl) = self.stack.last_mut() {
559 let change = change.into();
560 if lvl.changes.iter().all(|c| !c.equiv(&change)) {
561 lvl.changes.push(change);
562 }
563 }
564 }
565}
566
567pub struct ChangeIter<'a, ET: EngineTypes> {
569 stack: &'a mut StateStack<ET>,
570 chs: Vec<StateChangeI<ET>>,
571}
572impl<'a, ET: EngineTypes> ChangeIter<'a, ET> {
573 pub fn close<F: FnMut(StateChange<ET>)>(mut self, mut f: F) {
575 for ch in self.chs.drain(..) {
576 if ch.active {
577 f(ch.ch);
578 }
579 }
580 self.stack.vecs.push(self.chs);
581 }
582}
583
584#[derive(Clone)]
585struct StateChangeI<ET: EngineTypes> {
586 active: bool,
587 id: (std::mem::Discriminant<StateChange<ET>>, usize),
588 ch: StateChange<ET>,
589}
590impl<ET: EngineTypes> StateChangeI<ET> {
591 fn equiv(&self, other: &StateChangeI<ET>) -> bool {
592 self.active && self.id == other.id
593 }
594}
595impl<ET: EngineTypes> From<StateChange<ET>> for StateChangeI<ET> {
596 fn from(value: StateChange<ET>) -> Self {
597 let u = match &value {
598 StateChange::Catcode { char, .. } => (*char).into() as usize,
599 StateChange::SfCode { char, .. } => (*char).into() as usize,
600 StateChange::LcCode { char, .. } => (*char).into() as usize,
601 StateChange::UcCode { char, .. } => (*char).into() as usize,
602 StateChange::MathCode { char, .. } => (*char).into() as usize,
603 StateChange::DelCode { char, .. } => (*char).into() as usize,
604 StateChange::AcCommand { char, .. } => (*char).into() as usize,
605 StateChange::CurrentFont(_) => 0,
606 StateChange::EndlineChar { .. } => 0,
607 StateChange::EscapeChar { .. } => 0,
608 StateChange::NewlineChar { .. } => 0,
609 StateChange::ParShape { .. } => 0,
610 StateChange::TextFont { idx, .. } => *idx as usize,
611 StateChange::ScriptFont { idx, .. } => *idx as usize,
612 StateChange::ScriptScriptFont { idx, .. } => *idx as usize,
613 StateChange::IntRegister { idx, .. } => *idx,
614 StateChange::DimRegister { idx, .. } => *idx,
615 StateChange::SkipRegister { idx, .. } => *idx,
616 StateChange::MuSkipRegister { idx, .. } => *idx,
617 StateChange::ToksRegister { idx, .. } => *idx,
618 StateChange::BoxRegister { idx, .. } => *idx,
619 StateChange::PrimitiveInt { name, .. } => name.as_u16() as usize,
620 StateChange::PrimitiveDim { name, .. } => name.as_u16() as usize,
621 StateChange::PrimitiveSkip { name, .. } => name.as_u16() as usize,
622 StateChange::PrimitiveMuSkip { name, .. } => name.as_u16() as usize,
623 StateChange::PrimitiveToks { name, .. } => name.as_u16() as usize,
624 StateChange::Command { name, .. } => name.id(),
625 };
626 StateChangeI {
627 active: true,
628 id: (std::mem::discriminant(&value), u),
629 ch: value,
630 }
631 }
632}
633
634impl<ET: EngineTypes> EngineReferences<'_, ET> {
635 pub fn set_command(
637 &mut self,
638 name: &CSOrActiveChar<ET::Token>,
639 cmd: Option<TeXCommand<ET>>,
640 globally: bool,
641 ) {
642 match name {
643 CSOrActiveChar::Active(c) => self.state.set_ac_command(self.aux, *c, cmd, globally),
644 CSOrActiveChar::Name(cs) => self.state.set_command(self.aux, cs.clone(), cmd, globally),
645 }
646 }
647}