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 pub fn pages(&self) -> &[Option<Rc<Page>>] {
227 &self.pages
228 }
229
230 fn page_mut(&mut self, c: u32) -> &mut Page {
231 let k = c as usize / PAGE;
232 if self.pages.len() <= k {
233 self.pages.resize_with(k + 1, || None);
234 }
235 let default = self.default;
236 let page = self.pages[k].get_or_insert_with(|| {
237 let base = (k * PAGE) as u32;
238 Rc::new(std::array::from_fn(|i| Slot::new(default(base + i as u32))))
239 });
240 Rc::make_mut(page)
241 }
242
243 #[inline]
244 fn slot(&self, c: u32) -> Option<&Slot<i64>> {
245 match self.pages.get(c as usize / PAGE) {
246 Some(Some(p)) => Some(&p[c as usize % PAGE]),
247 _ => None,
248 }
249 }
250
251 #[inline]
253 pub fn get(&self, c: u32) -> i64 {
254 self.slot(c).map_or_else(|| (self.default)(c), |s| s.val)
255 }
256
257 pub fn level(&self, c: u32) -> u16 {
259 self.slot(c).map_or(LEVEL_ONE, |s| s.level)
260 }
261
262 #[inline]
266 pub fn define_in_place(&mut self, c: u32, val: i64, cur: u16) -> bool {
267 let (old, level) = match self.slot(c) {
268 Some(s) => (s.val, s.level),
269 None => ((self.default)(c), LEVEL_ONE),
270 };
271 if old == val {
272 return true;
273 }
274 if level != cur && cur > LEVEL_ONE {
275 return false;
276 }
277 let s = &mut self.page_mut(c)[c as usize % PAGE];
278 s.val = val;
279 s.level = cur;
280 true
281 }
282
283 pub fn define_identity(&mut self, upto: u32, cur: u16) -> CodeTable {
289 let old = self.clone();
290 for c in 0..upto {
291 if self.get(c) != i64::from(c) {
292 self.page_mut(c)[c as usize % PAGE] = Slot {
293 val: i64::from(c),
294 level: cur,
295 };
296 }
297 }
298 old
299 }
300
301 pub fn restore_identity(&mut self, old: &CodeTable, upto: u32) {
306 for c in 0..upto {
307 let val = old.get(c);
308 if val != i64::from(c) && self.level(c) != LEVEL_ONE {
309 self.set(c, val, old.level(c));
310 }
311 }
312 }
313
314 pub fn set(&mut self, c: u32, val: i64, level: u16) {
316 self.page_mut(c)[c as usize % PAGE] = Slot { val, level };
317 }
318
319 pub fn default_fn(&self) -> fn(u32) -> i64 {
321 self.default
322 }
323
324 pub fn dump_pages(&self) -> Vec<(usize, [i64; PAGE])> {
327 let mut out = Vec::new();
328 for (k, p) in self.pages.iter().enumerate() {
329 let Some(p) = p else { continue };
330 let base = (k * PAGE) as u32;
331 let vals: [i64; PAGE] = std::array::from_fn(|i| p[i].val);
332 if vals
333 .iter()
334 .enumerate()
335 .any(|(i, v)| *v != (self.default)(base + i as u32))
336 {
337 out.push((k, vals));
338 }
339 }
340 out
341 }
342
343 pub fn load_page(&mut self, k: usize, page: Rc<Page>) {
345 if self.pages.len() <= k {
346 self.pages.resize_with(k + 1, || None);
347 }
348 self.pages[k] = Some(page);
349 }
350
351 pub fn explicit(&self) -> impl Iterator<Item = (u32, i64, u16)> + '_ {
355 self.pages.iter().enumerate().flat_map(|(k, p)| {
356 p.iter().flat_map(move |p| {
357 p.iter()
358 .enumerate()
359 .map(move |(i, s)| ((k * PAGE + i) as u32, s.val, s.level))
360 })
361 })
362 }
363}
364
365pub fn initex_catcode(c: u32) -> i64 {
370 match c {
371 0x5C => 0, 0x25 => 14, 0x41..=0x5A | 0x61..=0x7A => 11, 0x0D => 5, 0x20 => 10, 0x00 | 0xFEFF => 9, 0x7F => 15, _ => 12, }
380}
381
382#[derive(Clone, Debug)]
384pub struct Eqtb {
385 pub cs: Vec<Slot<Meaning>>,
387 pub int_par: Vec<Slot<i32>>,
389 pub count: Vec<Slot<i32>>,
391 pub attr: Vec<Slot<i32>>,
393 pub dimen_par: Vec<Slot<i32>>,
395 pub dimen: Vec<Slot<i32>>,
397 pub glue_par: Vec<Slot<Glue>>,
399 pub skip: Vec<Slot<Glue>>,
401 pub muskip: Vec<Slot<Glue>>,
403 pub toks_par: Vec<Slot<TokenList>>,
405 pub toks: Vec<Slot<TokenList>>,
407 pub boxes: Vec<Slot<NodeRef>>,
409 pub cur_font: Slot<u32>,
411 pub math_font: Vec<Slot<u32>>,
413 pub par_shape: Slot<Option<Rc<[i32]>>>,
415 pub etex_shape: Vec<Slot<Option<Rc<[i32]>>>>,
417 pub catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash>,
419 pub lc: CodeTable,
421 pub uc: CodeTable,
423 pub sf: CodeTable,
425 pub math: CodeTable,
427 pub del: CodeTable,
429 pub math_param: HashMap<u32, Slot<i32>>,
431 pub local_box: [Slot<NodeRef>; 2],
433 pub dir_par: [Slot<i32>; pg::assign_dir::COUNT as usize],
435 pub local_count: [Slot<i32>; 2],
437 pub attr_generation: u64,
439 pub attrs_in_use: bool,
441}
442
443impl Eqtb {
444 pub fn initex() -> Self {
447 use pg::assign_int as ip;
448 let regs_i = || vec![Slot::new(0); NUM_REGISTERS];
449 let mut int_par = vec![Slot::new(0i32); INT_PARS];
450 int_par[ip::TOLERANCE as usize].val = 10000;
451 int_par[ip::MAG as usize].val = 1000;
452 int_par[ip::MAXDEADCYCLES as usize].val = 25;
453 int_par[ip::ESCAPECHAR as usize].val = '\\' as i32;
454 int_par[crate::backend::MAJOR_VERSION as usize].val = 1;
455 int_par[ip::ENDLINECHAR as usize].val = 13;
456 let mut catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash> = HashMap::default();
457 catcode_tables.insert(0, CodeTable::new(initex_catcode));
458 Eqtb {
459 cs: Vec::new(),
460 int_par,
461 count: regs_i(),
462 attr: vec![Slot::new(UNUSED_ATTRIBUTE); NUM_REGISTERS],
463 dimen_par: vec![Slot::new(0); DIMEN_PARS],
464 dimen: regs_i(),
465 glue_par: vec![
466 Slot::new(Glue::ZERO);
467 (pg::assign_glue::COUNT + pg::assign_mu_glue::COUNT) as usize
468 ],
469 skip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
470 muskip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
471 toks_par: vec![Slot::new(empty_list()); TOKS_PARS],
472 toks: vec![Slot::new(empty_list()); NUM_REGISTERS],
473 boxes: vec![Slot::new(NodeRef::NULL); NUM_REGISTERS],
474 cur_font: Slot::new(0),
475 math_font: vec![Slot::new(0); 3 * NUM_FAMILIES],
476 par_shape: Slot::new(None),
477 etex_shape: vec![Slot::new(None); pg::set_etex_shape::COUNT as usize],
478 catcode_tables,
479 lc: CodeTable::new(crate::codes::initex_lccode),
480 uc: CodeTable::new(crate::codes::initex_uccode),
481 sf: CodeTable::new(crate::codes::initex_sfcode),
482 math: CodeTable::new(crate::codes::initex_mathcode),
483 del: CodeTable::new(crate::codes::initex_delcode),
484 math_param: HashMap::new(),
485 local_box: [Slot::new(NodeRef::NULL), Slot::new(NodeRef::NULL)],
486 dir_par: std::array::from_fn(|_| Slot::new(0)),
487 local_count: [Slot::new(0), Slot::new(0)],
488 attr_generation: 0,
489 attrs_in_use: false,
490 }
491 }
492
493 #[inline]
495 pub fn ensure_cs(&mut self, cs: CsId) {
496 let n = cs.0 as usize + 1;
497 if self.cs.len() < n {
498 self.cs.resize(
499 n,
500 Slot {
501 val: Meaning::UNDEFINED,
502 level: LEVEL_ZERO,
503 },
504 );
505 }
506 }
507
508 #[inline]
510 pub fn meaning(&self, cs: CsId) -> &Meaning {
511 match self.cs.get(cs.0 as usize) {
512 Some(slot) => &slot.val,
513 None => &Meaning::UNDEFINED,
514 }
515 }
516
517 #[inline]
519 pub fn int(&self, i: u32) -> i32 {
520 self.int_par[i as usize].val
521 }
522
523 #[inline]
525 pub fn dimen(&self, i: u32) -> i32 {
526 self.dimen_par[i as usize].val
527 }
528
529 #[inline]
531 pub fn glue(&self, i: u32) -> Glue {
532 self.glue_par[i as usize].val
533 }
534
535 #[inline]
537 pub fn toks_param(&self, i: u32) -> &TokenList {
538 &self.toks_par[i as usize].val
539 }
540
541 #[inline]
543 pub fn cur_cat_table(&self) -> u32 {
544 self.int(pg::assign_int::CATCODETABLE) as u32
545 }
546
547 #[inline]
549 pub fn catcode(&self, c: u32) -> u8 {
550 self.catcode_in(self.cur_cat_table(), c)
551 }
552
553 pub fn catcode_in(&self, table: u32, c: u32) -> u8 {
556 match self.catcode_tables.get(&table) {
557 Some(t) => t.get(c) as u8,
558 None => initex_catcode(c) as u8,
559 }
560 }
561
562 pub fn get(&self, loc: Loc) -> Val {
564 match loc {
565 Loc::Cs(cs) => Val::Meaning(self.meaning(cs).clone()),
566 Loc::IntPar(i) => Val::Int(self.int_par[i as usize].val.into()),
567 Loc::Count(i) => Val::Int(self.count[i as usize].val.into()),
568 Loc::Attr(i) => Val::Int(self.attr[i as usize].val.into()),
569 Loc::DimenPar(i) => Val::Int(self.dimen_par[i as usize].val.into()),
570 Loc::Dimen(i) => Val::Int(self.dimen[i as usize].val.into()),
571 Loc::GluePar(i) => Val::Glue(self.glue_par[i as usize].val),
572 Loc::Skip(i) => Val::Glue(self.skip[i as usize].val),
573 Loc::MuSkip(i) => Val::Glue(self.muskip[i as usize].val),
574 Loc::ToksPar(i) => Val::Toks(self.toks_par[i as usize].val.clone()),
575 Loc::Toks(i) => Val::Toks(self.toks[i as usize].val.clone()),
576 Loc::Box(i) => Val::Node(self.boxes[i as usize].val),
577 Loc::CurFont => Val::Int(self.cur_font.val.into()),
578 Loc::MathFont(i) => Val::Int(self.math_font[i as usize].val.into()),
579 Loc::ParShape => Val::Shape(self.par_shape.val.clone()),
580 Loc::EtexShape(i) => Val::Shape(self.etex_shape[i as usize].val.clone()),
581 Loc::Cat { table, ch } => Val::Int(self.catcode_in(table, ch).into()),
582 Loc::Code(kind, ch) => Val::Int(self.code_table(kind).get(ch)),
583 Loc::MathParam(i) => Val::Int(
584 self.math_param
585 .get(&i)
586 .map_or(crate::codes::UNDEFINED_MATH_PARAM.into(), |s| s.val.into()),
587 ),
588 Loc::LocalBox(i) => Val::Node(self.local_box[i as usize].val),
589 Loc::DirPar(i) => Val::Int(self.dir_par[i as usize].val.into()),
590 Loc::LocalCount(i) => Val::Int(self.local_count[i as usize].val.into()),
591 }
592 }
593
594 pub fn holds(&self, loc: Loc, val: &Val) -> bool {
597 match (loc, val) {
598 (Loc::Cs(cs), Val::Meaning(m)) => self.meaning(cs).same_as(m),
599 (Loc::IntPar(i), Val::Int(v)) => i64::from(self.int_par[i as usize].val) == *v,
600 (Loc::Count(i), Val::Int(v)) => i64::from(self.count[i as usize].val) == *v,
601 (Loc::Dimen(i), Val::Int(v)) => i64::from(self.dimen[i as usize].val) == *v,
602 (Loc::DimenPar(i), Val::Int(v)) => i64::from(self.dimen_par[i as usize].val) == *v,
603 (Loc::Code(kind, ch), Val::Int(v)) => self.code_table(kind).get(ch) == *v,
604 (Loc::ToksPar(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks_par[i as usize].val, t),
605 (Loc::Toks(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks[i as usize].val, t),
606 _ => same_val(&self.get(loc), val),
607 }
608 }
609
610 pub fn level(&self, loc: Loc) -> u16 {
612 match loc {
613 Loc::Cs(cs) => self.cs.get(cs.0 as usize).map_or(LEVEL_ZERO, |s| s.level),
614 Loc::IntPar(i) => self.int_par[i as usize].level,
615 Loc::Count(i) => self.count[i as usize].level,
616 Loc::Attr(i) => self.attr[i as usize].level,
617 Loc::DimenPar(i) => self.dimen_par[i as usize].level,
618 Loc::Dimen(i) => self.dimen[i as usize].level,
619 Loc::GluePar(i) => self.glue_par[i as usize].level,
620 Loc::Skip(i) => self.skip[i as usize].level,
621 Loc::MuSkip(i) => self.muskip[i as usize].level,
622 Loc::ToksPar(i) => self.toks_par[i as usize].level,
623 Loc::Toks(i) => self.toks[i as usize].level,
624 Loc::Box(i) => self.boxes[i as usize].level,
625 Loc::CurFont => self.cur_font.level,
626 Loc::MathFont(i) => self.math_font[i as usize].level,
627 Loc::ParShape => self.par_shape.level,
628 Loc::EtexShape(i) => self.etex_shape[i as usize].level,
629 Loc::Cat { table, ch } => self
630 .catcode_tables
631 .get(&table)
632 .map_or(LEVEL_ONE, |t| t.level(ch)),
633 Loc::Code(kind, ch) => self.code_table(kind).level(ch),
634 Loc::MathParam(i) => self.math_param.get(&i).map_or(LEVEL_ONE, |s| s.level),
635 Loc::LocalBox(i) => self.local_box[i as usize].level,
636 Loc::DirPar(i) => self.dir_par[i as usize].level,
637 Loc::LocalCount(i) => self.local_count[i as usize].level,
638 }
639 }
640
641 pub fn set(&mut self, loc: Loc, val: Val, level: u16) {
644 fn int(v: &Val) -> i64 {
645 match v {
646 Val::Int(i) => *i,
647 _ => {
648 debug_assert!(false, "expected an integer value");
649 0
650 }
651 }
652 }
653 match loc {
654 Loc::Cs(cs) => {
655 self.ensure_cs(cs);
656 if let Val::Meaning(m) = val {
657 self.cs[cs.0 as usize] = Slot { val: m, level };
658 }
659 }
660 Loc::IntPar(i) => {
661 self.int_par[i as usize] = Slot {
662 val: int(&val) as i32,
663 level,
664 }
665 }
666 Loc::Count(i) => {
667 self.count[i as usize] = Slot {
668 val: int(&val) as i32,
669 level,
670 }
671 }
672 Loc::Attr(i) => {
673 self.attr[i as usize] = Slot {
674 val: int(&val) as i32,
675 level,
676 };
677 self.attr_generation += 1;
678 self.attrs_in_use = true;
679 }
680 Loc::DimenPar(i) => {
681 self.dimen_par[i as usize] = Slot {
682 val: int(&val) as i32,
683 level,
684 }
685 }
686 Loc::Dimen(i) => {
687 self.dimen[i as usize] = Slot {
688 val: int(&val) as i32,
689 level,
690 }
691 }
692 Loc::GluePar(i) | Loc::Skip(i) | Loc::MuSkip(i) => {
693 if let Val::Glue(g) = val {
694 let slot = Slot { val: g, level };
695 match loc {
696 Loc::GluePar(_) => self.glue_par[i as usize] = slot,
697 Loc::Skip(_) => self.skip[i as usize] = slot,
698 _ => self.muskip[i as usize] = slot,
699 }
700 }
701 }
702 Loc::ToksPar(i) | Loc::Toks(i) => {
703 if let Val::Toks(t) = val {
704 let slot = Slot { val: t, level };
705 if matches!(loc, Loc::ToksPar(_)) {
706 self.toks_par[i as usize] = slot;
707 } else {
708 self.toks[i as usize] = slot;
709 }
710 }
711 }
712 Loc::Box(i) => {
713 if let Val::Node(n) = val {
714 self.boxes[i as usize] = Slot { val: n, level };
715 }
716 }
717 Loc::CurFont => {
718 self.cur_font = Slot {
719 val: int(&val) as u32,
720 level,
721 }
722 }
723 Loc::MathFont(i) => {
724 self.math_font[i as usize] = Slot {
725 val: int(&val) as u32,
726 level,
727 }
728 }
729 Loc::ParShape => {
730 if let Val::Shape(s) = val {
731 self.par_shape = Slot { val: s, level };
732 }
733 }
734 Loc::EtexShape(i) => {
735 if let Val::Shape(s) = val {
736 self.etex_shape[i as usize] = Slot { val: s, level };
737 }
738 }
739 Loc::Cat { table, ch } => {
740 self.catcode_tables
741 .entry(table)
742 .or_insert_with(|| CodeTable::new(initex_catcode))
743 .set(ch, int(&val), level);
744 }
745 Loc::Code(kind, ch) => self.code_table_mut(kind).set(ch, int(&val), level),
746 Loc::MathParam(i) => {
747 self.math_param.insert(
748 i,
749 Slot {
750 val: int(&val) as i32,
751 level,
752 },
753 );
754 }
755 Loc::LocalBox(i) => {
756 if let Val::Node(n) = val {
757 self.local_box[i as usize] = Slot { val: n, level };
758 }
759 }
760 Loc::DirPar(i) => {
761 self.dir_par[i as usize] = Slot {
762 val: int(&val) as i32,
763 level,
764 }
765 }
766 Loc::LocalCount(i) => {
767 self.local_count[i as usize] = Slot {
768 val: int(&val) as i32,
769 level,
770 }
771 }
772 }
773 }
774
775 pub fn code_table(&self, kind: CodeKind) -> &CodeTable {
777 match kind {
778 CodeKind::Lc => &self.lc,
779 CodeKind::Uc => &self.uc,
780 CodeKind::Sf => &self.sf,
781 CodeKind::Math => &self.math,
782 CodeKind::Del => &self.del,
783 }
784 }
785
786 pub(crate) fn code_table_mut(&mut self, kind: CodeKind) -> &mut CodeTable {
787 match kind {
788 CodeKind::Lc => &mut self.lc,
789 CodeKind::Uc => &mut self.uc,
790 CodeKind::Sf => &mut self.sf,
791 CodeKind::Math => &mut self.math,
792 CodeKind::Del => &mut self.del,
793 }
794 }
795
796 pub fn is_word(loc: Loc) -> bool {
800 matches!(
801 loc,
802 Loc::IntPar(_)
803 | Loc::Count(_)
804 | Loc::Attr(_)
805 | Loc::DimenPar(_)
806 | Loc::Dimen(_)
807 | Loc::MathParam(_)
808 )
809 }
810}
811
812pub fn same_val(a: &Val, b: &Val) -> bool {
814 match (a, b) {
815 (Val::Meaning(x), Val::Meaning(y)) => x.same_as(y),
816 (Val::Int(x), Val::Int(y)) => x == y,
817 (Val::Glue(x), Val::Glue(y)) => x == y,
818 (Val::Toks(x), Val::Toks(y)) => Rc::ptr_eq(x, y),
819 (Val::Node(x), Val::Node(y)) => x == y,
820 (Val::Shape(x), Val::Shape(y)) => match (x, y) {
821 (None, None) => true,
822 (Some(p), Some(q)) => Rc::ptr_eq(p, q),
823 _ => false,
824 },
825 _ => false,
826 }
827}