1use std::collections::HashMap;
11use std::rc::Rc;
12
13use crate::cmd::Cmd;
14use crate::glue::Glue;
15use crate::hash::CsId;
16use crate::nodes::NodeRef;
17use crate::primitives_gen as pg;
18use crate::token::{Macro, TokenList, empty_list};
19
20pub const LEVEL_ONE: u16 = 1;
22pub const LEVEL_ZERO: u16 = 0;
24
25pub const NUM_REGISTERS: usize = 65536;
27pub const NUM_FAMILIES: usize = 256;
29pub const UNUSED_ATTRIBUTE: i32 = -0x7FFF_FFFF;
31
32#[derive(Clone, Debug)]
35pub struct Meaning {
36 pub cmd: Cmd,
38 pub chr: u32,
40 pub mac: Option<Rc<Macro>>,
42}
43
44impl Meaning {
45 pub const UNDEFINED: Meaning = Meaning {
47 cmd: Cmd::UndefinedCs,
48 chr: 0,
49 mac: None,
50 };
51
52 pub const fn prim(cmd: Cmd, chr: u32) -> Meaning {
54 Meaning {
55 cmd,
56 chr,
57 mac: None,
58 }
59 }
60
61 pub fn same_as(&self, other: &Meaning) -> bool {
64 self.cmd == other.cmd
65 && self.chr == other.chr
66 && match (&self.mac, &other.mac) {
67 (None, None) => true,
68 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
69 _ => false,
70 }
71 }
72}
73
74#[derive(Clone, Debug)]
76pub struct Slot<T> {
77 pub val: T,
79 pub level: u16,
81}
82
83impl<T> Slot<T> {
84 fn new(val: T) -> Self {
85 Slot {
86 val,
87 level: LEVEL_ONE,
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
94pub enum CodeKind {
95 Lc,
97 Uc,
99 Sf,
101 Math,
103 Del,
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
109pub enum Loc {
110 Cs(CsId),
112 IntPar(u32),
114 Count(u32),
116 Attr(u32),
118 DimenPar(u32),
120 Dimen(u32),
122 GluePar(u32),
125 Skip(u32),
127 MuSkip(u32),
129 ToksPar(u32),
131 Toks(u32),
133 Box(u32),
135 CurFont,
137 MathFont(u32),
140 ParShape,
142 EtexShape(u32),
144 Cat {
146 table: u32,
148 ch: u32,
150 },
151 Code(CodeKind, u32),
153 MathParam(u32),
155 LocalBox(u32),
157 DirPar(u32),
160 LocalCount(u32),
163}
164
165pub const INT_PARS: usize = 165;
168pub const DIMEN_PARS: usize = 34;
170pub const TOKS_PARS: usize = 18;
172
173pub const MU_GLUE_OFFSET: u32 = pg::assign_glue::COUNT;
176
177#[derive(Clone, Debug)]
179pub enum Val {
180 Meaning(Meaning),
182 Int(i64),
184 Glue(Glue),
186 Toks(TokenList),
188 Node(NodeRef),
190 Shape(Option<Rc<[i32]>>),
192}
193
194#[derive(Clone, Debug)]
201pub struct CodeTable {
202 pages: Vec<Option<Rc<Page>>>,
205 default: fn(u32) -> i64,
206}
207
208const PAGE: usize = 256;
210pub type Page = [Slot<i64>; PAGE];
212
213impl CodeTable {
214 pub fn new(default: fn(u32) -> i64) -> Self {
216 let mut t = CodeTable {
217 pages: Vec::new(),
218 default,
219 };
220 t.page_mut(0);
222 t
223 }
224
225 fn page_mut(&mut self, c: u32) -> &mut Page {
226 let k = c as usize / PAGE;
227 if self.pages.len() <= k {
228 self.pages.resize_with(k + 1, || None);
229 }
230 let default = self.default;
231 let page = self.pages[k].get_or_insert_with(|| {
232 let base = (k * PAGE) as u32;
233 Rc::new(std::array::from_fn(|i| Slot::new(default(base + i as u32))))
234 });
235 Rc::make_mut(page)
236 }
237
238 #[inline]
239 fn slot(&self, c: u32) -> Option<&Slot<i64>> {
240 match self.pages.get(c as usize / PAGE) {
241 Some(Some(p)) => Some(&p[c as usize % PAGE]),
242 _ => None,
243 }
244 }
245
246 #[inline]
248 pub fn get(&self, c: u32) -> i64 {
249 self.slot(c).map_or_else(|| (self.default)(c), |s| s.val)
250 }
251
252 pub fn level(&self, c: u32) -> u16 {
254 self.slot(c).map_or(LEVEL_ONE, |s| s.level)
255 }
256
257 #[inline]
261 pub fn define_in_place(&mut self, c: u32, val: i64, cur: u16) -> bool {
262 let (old, level) = match self.slot(c) {
263 Some(s) => (s.val, s.level),
264 None => ((self.default)(c), LEVEL_ONE),
265 };
266 if old == val {
267 return true;
268 }
269 if level != cur && cur > LEVEL_ONE {
270 return false;
271 }
272 let s = &mut self.page_mut(c)[c as usize % PAGE];
273 s.val = val;
274 s.level = cur;
275 true
276 }
277
278 pub fn define_identity(&mut self, upto: u32, cur: u16) -> CodeTable {
284 let old = self.clone();
285 for c in 0..upto {
286 if self.get(c) != i64::from(c) {
287 self.page_mut(c)[c as usize % PAGE] = Slot {
288 val: i64::from(c),
289 level: cur,
290 };
291 }
292 }
293 old
294 }
295
296 pub fn restore_identity(&mut self, old: &CodeTable, upto: u32) {
301 for c in 0..upto {
302 let val = old.get(c);
303 if val != i64::from(c) && self.level(c) != LEVEL_ONE {
304 self.set(c, val, old.level(c));
305 }
306 }
307 }
308
309 pub fn set(&mut self, c: u32, val: i64, level: u16) {
311 self.page_mut(c)[c as usize % PAGE] = Slot { val, level };
312 }
313
314 pub fn default_fn(&self) -> fn(u32) -> i64 {
316 self.default
317 }
318
319 pub fn dump_pages(&self) -> Vec<(usize, [i64; PAGE])> {
322 let mut out = Vec::new();
323 for (k, p) in self.pages.iter().enumerate() {
324 let Some(p) = p else { continue };
325 let base = (k * PAGE) as u32;
326 let vals: [i64; PAGE] = std::array::from_fn(|i| p[i].val);
327 if vals
328 .iter()
329 .enumerate()
330 .any(|(i, v)| *v != (self.default)(base + i as u32))
331 {
332 out.push((k, vals));
333 }
334 }
335 out
336 }
337
338 pub fn load_page(&mut self, k: usize, page: Rc<Page>) {
340 if self.pages.len() <= k {
341 self.pages.resize_with(k + 1, || None);
342 }
343 self.pages[k] = Some(page);
344 }
345
346 pub fn explicit(&self) -> impl Iterator<Item = (u32, i64, u16)> + '_ {
350 self.pages.iter().enumerate().flat_map(|(k, p)| {
351 p.iter().flat_map(move |p| {
352 p.iter()
353 .enumerate()
354 .map(move |(i, s)| ((k * PAGE + i) as u32, s.val, s.level))
355 })
356 })
357 }
358}
359
360pub fn initex_catcode(c: u32) -> i64 {
365 match c {
366 0x5C => 0, 0x25 => 14, 0x41..=0x5A | 0x61..=0x7A => 11, 0x0D => 5, 0x20 => 10, 0x00 | 0xFEFF => 9, 0x7F => 15, _ => 12, }
375}
376
377#[derive(Clone, Debug)]
379pub struct Eqtb {
380 pub cs: Vec<Slot<Meaning>>,
382 pub int_par: Vec<Slot<i32>>,
384 pub count: Vec<Slot<i32>>,
386 pub attr: Vec<Slot<i32>>,
388 pub dimen_par: Vec<Slot<i32>>,
390 pub dimen: Vec<Slot<i32>>,
392 pub glue_par: Vec<Slot<Glue>>,
394 pub skip: Vec<Slot<Glue>>,
396 pub muskip: Vec<Slot<Glue>>,
398 pub toks_par: Vec<Slot<TokenList>>,
400 pub toks: Vec<Slot<TokenList>>,
402 pub boxes: Vec<Slot<NodeRef>>,
404 pub cur_font: Slot<u32>,
406 pub math_font: Vec<Slot<u32>>,
408 pub par_shape: Slot<Option<Rc<[i32]>>>,
410 pub etex_shape: Vec<Slot<Option<Rc<[i32]>>>>,
412 pub catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash>,
414 pub lc: CodeTable,
416 pub uc: CodeTable,
418 pub sf: CodeTable,
420 pub math: CodeTable,
422 pub del: CodeTable,
424 pub math_param: HashMap<u32, Slot<i32>>,
426 pub local_box: [Slot<NodeRef>; 2],
428 pub dir_par: [Slot<i32>; pg::assign_dir::COUNT as usize],
430 pub local_count: [Slot<i32>; 2],
432 pub attr_generation: u64,
434 pub attrs_in_use: bool,
436}
437
438impl Eqtb {
439 pub fn initex() -> Self {
442 use pg::assign_int as ip;
443 let regs_i = || vec![Slot::new(0); NUM_REGISTERS];
444 let mut int_par = vec![Slot::new(0i32); INT_PARS];
445 int_par[ip::TOLERANCE as usize].val = 10000;
446 int_par[ip::MAG as usize].val = 1000;
447 int_par[ip::MAXDEADCYCLES as usize].val = 25;
448 int_par[ip::ESCAPECHAR as usize].val = '\\' as i32;
449 int_par[crate::backend::MAJOR_VERSION as usize].val = 1;
450 int_par[ip::ENDLINECHAR as usize].val = 13;
451 let mut catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash> = HashMap::default();
452 catcode_tables.insert(0, CodeTable::new(initex_catcode));
453 Eqtb {
454 cs: Vec::new(),
455 int_par,
456 count: regs_i(),
457 attr: vec![Slot::new(UNUSED_ATTRIBUTE); NUM_REGISTERS],
458 dimen_par: vec![Slot::new(0); DIMEN_PARS],
459 dimen: regs_i(),
460 glue_par: vec![
461 Slot::new(Glue::ZERO);
462 (pg::assign_glue::COUNT + pg::assign_mu_glue::COUNT) as usize
463 ],
464 skip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
465 muskip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
466 toks_par: vec![Slot::new(empty_list()); TOKS_PARS],
467 toks: vec![Slot::new(empty_list()); NUM_REGISTERS],
468 boxes: vec![Slot::new(NodeRef::NULL); NUM_REGISTERS],
469 cur_font: Slot::new(0),
470 math_font: vec![Slot::new(0); 3 * NUM_FAMILIES],
471 par_shape: Slot::new(None),
472 etex_shape: vec![Slot::new(None); pg::set_etex_shape::COUNT as usize],
473 catcode_tables,
474 lc: CodeTable::new(crate::codes::initex_lccode),
475 uc: CodeTable::new(crate::codes::initex_uccode),
476 sf: CodeTable::new(crate::codes::initex_sfcode),
477 math: CodeTable::new(crate::codes::initex_mathcode),
478 del: CodeTable::new(crate::codes::initex_delcode),
479 math_param: HashMap::new(),
480 local_box: [Slot::new(NodeRef::NULL), Slot::new(NodeRef::NULL)],
481 dir_par: std::array::from_fn(|_| Slot::new(0)),
482 local_count: [Slot::new(0), Slot::new(0)],
483 attr_generation: 0,
484 attrs_in_use: false,
485 }
486 }
487
488 #[inline]
490 pub fn ensure_cs(&mut self, cs: CsId) {
491 let n = cs.0 as usize + 1;
492 if self.cs.len() < n {
493 self.cs.resize(
494 n,
495 Slot {
496 val: Meaning::UNDEFINED,
497 level: LEVEL_ZERO,
498 },
499 );
500 }
501 }
502
503 #[inline]
505 pub fn meaning(&self, cs: CsId) -> &Meaning {
506 match self.cs.get(cs.0 as usize) {
507 Some(slot) => &slot.val,
508 None => &Meaning::UNDEFINED,
509 }
510 }
511
512 #[inline]
514 pub fn int(&self, i: u32) -> i32 {
515 self.int_par[i as usize].val
516 }
517
518 #[inline]
520 pub fn dimen(&self, i: u32) -> i32 {
521 self.dimen_par[i as usize].val
522 }
523
524 #[inline]
526 pub fn glue(&self, i: u32) -> Glue {
527 self.glue_par[i as usize].val
528 }
529
530 #[inline]
532 pub fn toks_param(&self, i: u32) -> &TokenList {
533 &self.toks_par[i as usize].val
534 }
535
536 #[inline]
538 pub fn cur_cat_table(&self) -> u32 {
539 self.int(pg::assign_int::CATCODETABLE) as u32
540 }
541
542 #[inline]
544 pub fn catcode(&self, c: u32) -> u8 {
545 self.catcode_in(self.cur_cat_table(), c)
546 }
547
548 pub fn catcode_in(&self, table: u32, c: u32) -> u8 {
551 match self.catcode_tables.get(&table) {
552 Some(t) => t.get(c) as u8,
553 None => initex_catcode(c) as u8,
554 }
555 }
556
557 pub fn get(&self, loc: Loc) -> Val {
559 match loc {
560 Loc::Cs(cs) => Val::Meaning(self.meaning(cs).clone()),
561 Loc::IntPar(i) => Val::Int(self.int_par[i as usize].val.into()),
562 Loc::Count(i) => Val::Int(self.count[i as usize].val.into()),
563 Loc::Attr(i) => Val::Int(self.attr[i as usize].val.into()),
564 Loc::DimenPar(i) => Val::Int(self.dimen_par[i as usize].val.into()),
565 Loc::Dimen(i) => Val::Int(self.dimen[i as usize].val.into()),
566 Loc::GluePar(i) => Val::Glue(self.glue_par[i as usize].val),
567 Loc::Skip(i) => Val::Glue(self.skip[i as usize].val),
568 Loc::MuSkip(i) => Val::Glue(self.muskip[i as usize].val),
569 Loc::ToksPar(i) => Val::Toks(self.toks_par[i as usize].val.clone()),
570 Loc::Toks(i) => Val::Toks(self.toks[i as usize].val.clone()),
571 Loc::Box(i) => Val::Node(self.boxes[i as usize].val),
572 Loc::CurFont => Val::Int(self.cur_font.val.into()),
573 Loc::MathFont(i) => Val::Int(self.math_font[i as usize].val.into()),
574 Loc::ParShape => Val::Shape(self.par_shape.val.clone()),
575 Loc::EtexShape(i) => Val::Shape(self.etex_shape[i as usize].val.clone()),
576 Loc::Cat { table, ch } => Val::Int(self.catcode_in(table, ch).into()),
577 Loc::Code(kind, ch) => Val::Int(self.code_table(kind).get(ch)),
578 Loc::MathParam(i) => Val::Int(
579 self.math_param
580 .get(&i)
581 .map_or(crate::codes::UNDEFINED_MATH_PARAM.into(), |s| s.val.into()),
582 ),
583 Loc::LocalBox(i) => Val::Node(self.local_box[i as usize].val),
584 Loc::DirPar(i) => Val::Int(self.dir_par[i as usize].val.into()),
585 Loc::LocalCount(i) => Val::Int(self.local_count[i as usize].val.into()),
586 }
587 }
588
589 pub fn holds(&self, loc: Loc, val: &Val) -> bool {
592 match (loc, val) {
593 (Loc::Cs(cs), Val::Meaning(m)) => self.meaning(cs).same_as(m),
594 (Loc::IntPar(i), Val::Int(v)) => i64::from(self.int_par[i as usize].val) == *v,
595 (Loc::Count(i), Val::Int(v)) => i64::from(self.count[i as usize].val) == *v,
596 (Loc::Dimen(i), Val::Int(v)) => i64::from(self.dimen[i as usize].val) == *v,
597 (Loc::DimenPar(i), Val::Int(v)) => i64::from(self.dimen_par[i as usize].val) == *v,
598 (Loc::Code(kind, ch), Val::Int(v)) => self.code_table(kind).get(ch) == *v,
599 (Loc::ToksPar(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks_par[i as usize].val, t),
600 (Loc::Toks(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks[i as usize].val, t),
601 _ => same_val(&self.get(loc), val),
602 }
603 }
604
605 pub fn level(&self, loc: Loc) -> u16 {
607 match loc {
608 Loc::Cs(cs) => self.cs.get(cs.0 as usize).map_or(LEVEL_ZERO, |s| s.level),
609 Loc::IntPar(i) => self.int_par[i as usize].level,
610 Loc::Count(i) => self.count[i as usize].level,
611 Loc::Attr(i) => self.attr[i as usize].level,
612 Loc::DimenPar(i) => self.dimen_par[i as usize].level,
613 Loc::Dimen(i) => self.dimen[i as usize].level,
614 Loc::GluePar(i) => self.glue_par[i as usize].level,
615 Loc::Skip(i) => self.skip[i as usize].level,
616 Loc::MuSkip(i) => self.muskip[i as usize].level,
617 Loc::ToksPar(i) => self.toks_par[i as usize].level,
618 Loc::Toks(i) => self.toks[i as usize].level,
619 Loc::Box(i) => self.boxes[i as usize].level,
620 Loc::CurFont => self.cur_font.level,
621 Loc::MathFont(i) => self.math_font[i as usize].level,
622 Loc::ParShape => self.par_shape.level,
623 Loc::EtexShape(i) => self.etex_shape[i as usize].level,
624 Loc::Cat { table, ch } => self
625 .catcode_tables
626 .get(&table)
627 .map_or(LEVEL_ONE, |t| t.level(ch)),
628 Loc::Code(kind, ch) => self.code_table(kind).level(ch),
629 Loc::MathParam(i) => self.math_param.get(&i).map_or(LEVEL_ONE, |s| s.level),
630 Loc::LocalBox(i) => self.local_box[i as usize].level,
631 Loc::DirPar(i) => self.dir_par[i as usize].level,
632 Loc::LocalCount(i) => self.local_count[i as usize].level,
633 }
634 }
635
636 pub fn set(&mut self, loc: Loc, val: Val, level: u16) {
639 fn int(v: &Val) -> i64 {
640 match v {
641 Val::Int(i) => *i,
642 _ => {
643 debug_assert!(false, "expected an integer value");
644 0
645 }
646 }
647 }
648 match loc {
649 Loc::Cs(cs) => {
650 self.ensure_cs(cs);
651 if let Val::Meaning(m) = val {
652 self.cs[cs.0 as usize] = Slot { val: m, level };
653 }
654 }
655 Loc::IntPar(i) => {
656 self.int_par[i as usize] = Slot {
657 val: int(&val) as i32,
658 level,
659 }
660 }
661 Loc::Count(i) => {
662 self.count[i as usize] = Slot {
663 val: int(&val) as i32,
664 level,
665 }
666 }
667 Loc::Attr(i) => {
668 self.attr[i as usize] = Slot {
669 val: int(&val) as i32,
670 level,
671 };
672 self.attr_generation += 1;
673 self.attrs_in_use = true;
674 }
675 Loc::DimenPar(i) => {
676 self.dimen_par[i as usize] = Slot {
677 val: int(&val) as i32,
678 level,
679 }
680 }
681 Loc::Dimen(i) => {
682 self.dimen[i as usize] = Slot {
683 val: int(&val) as i32,
684 level,
685 }
686 }
687 Loc::GluePar(i) | Loc::Skip(i) | Loc::MuSkip(i) => {
688 if let Val::Glue(g) = val {
689 let slot = Slot { val: g, level };
690 match loc {
691 Loc::GluePar(_) => self.glue_par[i as usize] = slot,
692 Loc::Skip(_) => self.skip[i as usize] = slot,
693 _ => self.muskip[i as usize] = slot,
694 }
695 }
696 }
697 Loc::ToksPar(i) | Loc::Toks(i) => {
698 if let Val::Toks(t) = val {
699 let slot = Slot { val: t, level };
700 if matches!(loc, Loc::ToksPar(_)) {
701 self.toks_par[i as usize] = slot;
702 } else {
703 self.toks[i as usize] = slot;
704 }
705 }
706 }
707 Loc::Box(i) => {
708 if let Val::Node(n) = val {
709 self.boxes[i as usize] = Slot { val: n, level };
710 }
711 }
712 Loc::CurFont => {
713 self.cur_font = Slot {
714 val: int(&val) as u32,
715 level,
716 }
717 }
718 Loc::MathFont(i) => {
719 self.math_font[i as usize] = Slot {
720 val: int(&val) as u32,
721 level,
722 }
723 }
724 Loc::ParShape => {
725 if let Val::Shape(s) = val {
726 self.par_shape = Slot { val: s, level };
727 }
728 }
729 Loc::EtexShape(i) => {
730 if let Val::Shape(s) = val {
731 self.etex_shape[i as usize] = Slot { val: s, level };
732 }
733 }
734 Loc::Cat { table, ch } => {
735 self.catcode_tables
736 .entry(table)
737 .or_insert_with(|| CodeTable::new(initex_catcode))
738 .set(ch, int(&val), level);
739 }
740 Loc::Code(kind, ch) => self.code_table_mut(kind).set(ch, int(&val), level),
741 Loc::MathParam(i) => {
742 self.math_param.insert(
743 i,
744 Slot {
745 val: int(&val) as i32,
746 level,
747 },
748 );
749 }
750 Loc::LocalBox(i) => {
751 if let Val::Node(n) = val {
752 self.local_box[i as usize] = Slot { val: n, level };
753 }
754 }
755 Loc::DirPar(i) => {
756 self.dir_par[i as usize] = Slot {
757 val: int(&val) as i32,
758 level,
759 }
760 }
761 Loc::LocalCount(i) => {
762 self.local_count[i as usize] = Slot {
763 val: int(&val) as i32,
764 level,
765 }
766 }
767 }
768 }
769
770 pub fn code_table(&self, kind: CodeKind) -> &CodeTable {
772 match kind {
773 CodeKind::Lc => &self.lc,
774 CodeKind::Uc => &self.uc,
775 CodeKind::Sf => &self.sf,
776 CodeKind::Math => &self.math,
777 CodeKind::Del => &self.del,
778 }
779 }
780
781 pub(crate) fn code_table_mut(&mut self, kind: CodeKind) -> &mut CodeTable {
782 match kind {
783 CodeKind::Lc => &mut self.lc,
784 CodeKind::Uc => &mut self.uc,
785 CodeKind::Sf => &mut self.sf,
786 CodeKind::Math => &mut self.math,
787 CodeKind::Del => &mut self.del,
788 }
789 }
790
791 pub fn is_word(loc: Loc) -> bool {
795 matches!(
796 loc,
797 Loc::IntPar(_)
798 | Loc::Count(_)
799 | Loc::Attr(_)
800 | Loc::DimenPar(_)
801 | Loc::Dimen(_)
802 | Loc::MathParam(_)
803 )
804 }
805}
806
807pub fn same_val(a: &Val, b: &Val) -> bool {
809 match (a, b) {
810 (Val::Meaning(x), Val::Meaning(y)) => x.same_as(y),
811 (Val::Int(x), Val::Int(y)) => x == y,
812 (Val::Glue(x), Val::Glue(y)) => x == y,
813 (Val::Toks(x), Val::Toks(y)) => Rc::ptr_eq(x, y),
814 (Val::Node(x), Val::Node(y)) => x == y,
815 (Val::Shape(x), Val::Shape(y)) => match (x, y) {
816 (None, None) => true,
817 (Some(p), Some(q)) => Rc::ptr_eq(p, q),
818 _ => false,
819 },
820 _ => false,
821 }
822}