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 set(&mut self, c: u32, val: i64, level: u16) {
280 self.page_mut(c)[c as usize % PAGE] = Slot { val, level };
281 }
282
283 pub fn default_fn(&self) -> fn(u32) -> i64 {
285 self.default
286 }
287
288 pub fn dump_pages(&self) -> Vec<(usize, [i64; PAGE])> {
291 let mut out = Vec::new();
292 for (k, p) in self.pages.iter().enumerate() {
293 let Some(p) = p else { continue };
294 let base = (k * PAGE) as u32;
295 let vals: [i64; PAGE] = std::array::from_fn(|i| p[i].val);
296 if vals
297 .iter()
298 .enumerate()
299 .any(|(i, v)| *v != (self.default)(base + i as u32))
300 {
301 out.push((k, vals));
302 }
303 }
304 out
305 }
306
307 pub fn load_page(&mut self, k: usize, page: Rc<Page>) {
309 if self.pages.len() <= k {
310 self.pages.resize_with(k + 1, || None);
311 }
312 self.pages[k] = Some(page);
313 }
314
315 pub fn explicit(&self) -> impl Iterator<Item = (u32, i64, u16)> + '_ {
319 self.pages.iter().enumerate().flat_map(|(k, p)| {
320 p.iter().flat_map(move |p| {
321 p.iter()
322 .enumerate()
323 .map(move |(i, s)| ((k * PAGE + i) as u32, s.val, s.level))
324 })
325 })
326 }
327}
328
329pub fn initex_catcode(c: u32) -> i64 {
333 match c {
334 0x5C => 0, 0x25 => 14, 0x41..=0x5A | 0x61..=0x7A => 11, 0x0D => 5, 0x20 => 10, 0x00 => 9, 0x7F => 15, _ => 12, }
343}
344
345#[derive(Clone, Debug)]
347pub struct Eqtb {
348 pub cs: Vec<Slot<Meaning>>,
350 pub int_par: Vec<Slot<i32>>,
352 pub count: Vec<Slot<i32>>,
354 pub attr: Vec<Slot<i32>>,
356 pub dimen_par: Vec<Slot<i32>>,
358 pub dimen: Vec<Slot<i32>>,
360 pub glue_par: Vec<Slot<Glue>>,
362 pub skip: Vec<Slot<Glue>>,
364 pub muskip: Vec<Slot<Glue>>,
366 pub toks_par: Vec<Slot<TokenList>>,
368 pub toks: Vec<Slot<TokenList>>,
370 pub boxes: Vec<Slot<NodeRef>>,
372 pub cur_font: Slot<u32>,
374 pub math_font: Vec<Slot<u32>>,
376 pub par_shape: Slot<Option<Rc<[i32]>>>,
378 pub etex_shape: Vec<Slot<Option<Rc<[i32]>>>>,
380 pub catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash>,
382 pub lc: CodeTable,
384 pub uc: CodeTable,
386 pub sf: CodeTable,
388 pub math: CodeTable,
390 pub del: CodeTable,
392 pub math_param: HashMap<u32, Slot<i32>>,
394 pub local_box: [Slot<NodeRef>; 2],
396 pub dir_par: [Slot<i32>; pg::assign_dir::COUNT as usize],
398 pub local_count: [Slot<i32>; 2],
400 pub attr_generation: u64,
402 pub attrs_in_use: bool,
404}
405
406impl Eqtb {
407 pub fn initex() -> Self {
410 use pg::assign_int as ip;
411 let regs_i = || vec![Slot::new(0); NUM_REGISTERS];
412 let mut int_par = vec![Slot::new(0i32); INT_PARS];
413 int_par[ip::TOLERANCE as usize].val = 10000;
414 int_par[ip::MAG as usize].val = 1000;
415 int_par[ip::MAXDEADCYCLES as usize].val = 25;
416 int_par[ip::ESCAPECHAR as usize].val = '\\' as i32;
417 int_par[crate::backend::MAJOR_VERSION as usize].val = 1;
418 int_par[ip::ENDLINECHAR as usize].val = 13;
419 let mut catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash> = HashMap::default();
420 catcode_tables.insert(0, CodeTable::new(initex_catcode));
421 Eqtb {
422 cs: Vec::new(),
423 int_par,
424 count: regs_i(),
425 attr: vec![Slot::new(UNUSED_ATTRIBUTE); NUM_REGISTERS],
426 dimen_par: vec![Slot::new(0); DIMEN_PARS],
427 dimen: regs_i(),
428 glue_par: vec![
429 Slot::new(Glue::ZERO);
430 (pg::assign_glue::COUNT + pg::assign_mu_glue::COUNT) as usize
431 ],
432 skip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
433 muskip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
434 toks_par: vec![Slot::new(empty_list()); TOKS_PARS],
435 toks: vec![Slot::new(empty_list()); NUM_REGISTERS],
436 boxes: vec![Slot::new(NodeRef::NULL); NUM_REGISTERS],
437 cur_font: Slot::new(0),
438 math_font: vec![Slot::new(0); 3 * NUM_FAMILIES],
439 par_shape: Slot::new(None),
440 etex_shape: vec![Slot::new(None); pg::set_etex_shape::COUNT as usize],
441 catcode_tables,
442 lc: CodeTable::new(crate::codes::initex_lccode),
443 uc: CodeTable::new(crate::codes::initex_uccode),
444 sf: CodeTable::new(crate::codes::initex_sfcode),
445 math: CodeTable::new(crate::codes::initex_mathcode),
446 del: CodeTable::new(crate::codes::initex_delcode),
447 math_param: HashMap::new(),
448 local_box: [Slot::new(NodeRef::NULL), Slot::new(NodeRef::NULL)],
449 dir_par: std::array::from_fn(|_| Slot::new(0)),
450 local_count: [Slot::new(0), Slot::new(0)],
451 attr_generation: 0,
452 attrs_in_use: false,
453 }
454 }
455
456 #[inline]
458 pub fn ensure_cs(&mut self, cs: CsId) {
459 let n = cs.0 as usize + 1;
460 if self.cs.len() < n {
461 self.cs.resize(
462 n,
463 Slot {
464 val: Meaning::UNDEFINED,
465 level: LEVEL_ZERO,
466 },
467 );
468 }
469 }
470
471 #[inline]
473 pub fn meaning(&self, cs: CsId) -> &Meaning {
474 match self.cs.get(cs.0 as usize) {
475 Some(slot) => &slot.val,
476 None => &Meaning::UNDEFINED,
477 }
478 }
479
480 #[inline]
482 pub fn int(&self, i: u32) -> i32 {
483 self.int_par[i as usize].val
484 }
485
486 #[inline]
488 pub fn dimen(&self, i: u32) -> i32 {
489 self.dimen_par[i as usize].val
490 }
491
492 #[inline]
494 pub fn glue(&self, i: u32) -> Glue {
495 self.glue_par[i as usize].val
496 }
497
498 #[inline]
500 pub fn toks_param(&self, i: u32) -> &TokenList {
501 &self.toks_par[i as usize].val
502 }
503
504 #[inline]
506 pub fn cur_cat_table(&self) -> u32 {
507 self.int(pg::assign_int::CATCODETABLE) as u32
508 }
509
510 #[inline]
512 pub fn catcode(&self, c: u32) -> u8 {
513 self.catcode_in(self.cur_cat_table(), c)
514 }
515
516 pub fn catcode_in(&self, table: u32, c: u32) -> u8 {
519 match self.catcode_tables.get(&table) {
520 Some(t) => t.get(c) as u8,
521 None => initex_catcode(c) as u8,
522 }
523 }
524
525 pub fn get(&self, loc: Loc) -> Val {
527 match loc {
528 Loc::Cs(cs) => Val::Meaning(self.meaning(cs).clone()),
529 Loc::IntPar(i) => Val::Int(self.int_par[i as usize].val.into()),
530 Loc::Count(i) => Val::Int(self.count[i as usize].val.into()),
531 Loc::Attr(i) => Val::Int(self.attr[i as usize].val.into()),
532 Loc::DimenPar(i) => Val::Int(self.dimen_par[i as usize].val.into()),
533 Loc::Dimen(i) => Val::Int(self.dimen[i as usize].val.into()),
534 Loc::GluePar(i) => Val::Glue(self.glue_par[i as usize].val),
535 Loc::Skip(i) => Val::Glue(self.skip[i as usize].val),
536 Loc::MuSkip(i) => Val::Glue(self.muskip[i as usize].val),
537 Loc::ToksPar(i) => Val::Toks(self.toks_par[i as usize].val.clone()),
538 Loc::Toks(i) => Val::Toks(self.toks[i as usize].val.clone()),
539 Loc::Box(i) => Val::Node(self.boxes[i as usize].val),
540 Loc::CurFont => Val::Int(self.cur_font.val.into()),
541 Loc::MathFont(i) => Val::Int(self.math_font[i as usize].val.into()),
542 Loc::ParShape => Val::Shape(self.par_shape.val.clone()),
543 Loc::EtexShape(i) => Val::Shape(self.etex_shape[i as usize].val.clone()),
544 Loc::Cat { table, ch } => Val::Int(self.catcode_in(table, ch).into()),
545 Loc::Code(kind, ch) => Val::Int(self.code_table(kind).get(ch)),
546 Loc::MathParam(i) => Val::Int(
547 self.math_param
548 .get(&i)
549 .map_or(crate::codes::UNDEFINED_MATH_PARAM.into(), |s| s.val.into()),
550 ),
551 Loc::LocalBox(i) => Val::Node(self.local_box[i as usize].val),
552 Loc::DirPar(i) => Val::Int(self.dir_par[i as usize].val.into()),
553 Loc::LocalCount(i) => Val::Int(self.local_count[i as usize].val.into()),
554 }
555 }
556
557 pub fn holds(&self, loc: Loc, val: &Val) -> bool {
560 match (loc, val) {
561 (Loc::Cs(cs), Val::Meaning(m)) => self.meaning(cs).same_as(m),
562 (Loc::IntPar(i), Val::Int(v)) => i64::from(self.int_par[i as usize].val) == *v,
563 (Loc::Count(i), Val::Int(v)) => i64::from(self.count[i as usize].val) == *v,
564 (Loc::Dimen(i), Val::Int(v)) => i64::from(self.dimen[i as usize].val) == *v,
565 (Loc::DimenPar(i), Val::Int(v)) => i64::from(self.dimen_par[i as usize].val) == *v,
566 (Loc::Code(kind, ch), Val::Int(v)) => self.code_table(kind).get(ch) == *v,
567 (Loc::ToksPar(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks_par[i as usize].val, t),
568 (Loc::Toks(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks[i as usize].val, t),
569 _ => same_val(&self.get(loc), val),
570 }
571 }
572
573 pub fn level(&self, loc: Loc) -> u16 {
575 match loc {
576 Loc::Cs(cs) => self.cs.get(cs.0 as usize).map_or(LEVEL_ZERO, |s| s.level),
577 Loc::IntPar(i) => self.int_par[i as usize].level,
578 Loc::Count(i) => self.count[i as usize].level,
579 Loc::Attr(i) => self.attr[i as usize].level,
580 Loc::DimenPar(i) => self.dimen_par[i as usize].level,
581 Loc::Dimen(i) => self.dimen[i as usize].level,
582 Loc::GluePar(i) => self.glue_par[i as usize].level,
583 Loc::Skip(i) => self.skip[i as usize].level,
584 Loc::MuSkip(i) => self.muskip[i as usize].level,
585 Loc::ToksPar(i) => self.toks_par[i as usize].level,
586 Loc::Toks(i) => self.toks[i as usize].level,
587 Loc::Box(i) => self.boxes[i as usize].level,
588 Loc::CurFont => self.cur_font.level,
589 Loc::MathFont(i) => self.math_font[i as usize].level,
590 Loc::ParShape => self.par_shape.level,
591 Loc::EtexShape(i) => self.etex_shape[i as usize].level,
592 Loc::Cat { table, ch } => self
593 .catcode_tables
594 .get(&table)
595 .map_or(LEVEL_ONE, |t| t.level(ch)),
596 Loc::Code(kind, ch) => self.code_table(kind).level(ch),
597 Loc::MathParam(i) => self.math_param.get(&i).map_or(LEVEL_ONE, |s| s.level),
598 Loc::LocalBox(i) => self.local_box[i as usize].level,
599 Loc::DirPar(i) => self.dir_par[i as usize].level,
600 Loc::LocalCount(i) => self.local_count[i as usize].level,
601 }
602 }
603
604 pub fn set(&mut self, loc: Loc, val: Val, level: u16) {
607 fn int(v: &Val) -> i64 {
608 match v {
609 Val::Int(i) => *i,
610 _ => {
611 debug_assert!(false, "expected an integer value");
612 0
613 }
614 }
615 }
616 match loc {
617 Loc::Cs(cs) => {
618 self.ensure_cs(cs);
619 if let Val::Meaning(m) = val {
620 self.cs[cs.0 as usize] = Slot { val: m, level };
621 }
622 }
623 Loc::IntPar(i) => {
624 self.int_par[i as usize] = Slot {
625 val: int(&val) as i32,
626 level,
627 }
628 }
629 Loc::Count(i) => {
630 self.count[i as usize] = Slot {
631 val: int(&val) as i32,
632 level,
633 }
634 }
635 Loc::Attr(i) => {
636 self.attr[i as usize] = Slot {
637 val: int(&val) as i32,
638 level,
639 };
640 self.attr_generation += 1;
641 self.attrs_in_use = true;
642 }
643 Loc::DimenPar(i) => {
644 self.dimen_par[i as usize] = Slot {
645 val: int(&val) as i32,
646 level,
647 }
648 }
649 Loc::Dimen(i) => {
650 self.dimen[i as usize] = Slot {
651 val: int(&val) as i32,
652 level,
653 }
654 }
655 Loc::GluePar(i) | Loc::Skip(i) | Loc::MuSkip(i) => {
656 if let Val::Glue(g) = val {
657 let slot = Slot { val: g, level };
658 match loc {
659 Loc::GluePar(_) => self.glue_par[i as usize] = slot,
660 Loc::Skip(_) => self.skip[i as usize] = slot,
661 _ => self.muskip[i as usize] = slot,
662 }
663 }
664 }
665 Loc::ToksPar(i) | Loc::Toks(i) => {
666 if let Val::Toks(t) = val {
667 let slot = Slot { val: t, level };
668 if matches!(loc, Loc::ToksPar(_)) {
669 self.toks_par[i as usize] = slot;
670 } else {
671 self.toks[i as usize] = slot;
672 }
673 }
674 }
675 Loc::Box(i) => {
676 if let Val::Node(n) = val {
677 self.boxes[i as usize] = Slot { val: n, level };
678 }
679 }
680 Loc::CurFont => {
681 self.cur_font = Slot {
682 val: int(&val) as u32,
683 level,
684 }
685 }
686 Loc::MathFont(i) => {
687 self.math_font[i as usize] = Slot {
688 val: int(&val) as u32,
689 level,
690 }
691 }
692 Loc::ParShape => {
693 if let Val::Shape(s) = val {
694 self.par_shape = Slot { val: s, level };
695 }
696 }
697 Loc::EtexShape(i) => {
698 if let Val::Shape(s) = val {
699 self.etex_shape[i as usize] = Slot { val: s, level };
700 }
701 }
702 Loc::Cat { table, ch } => {
703 self.catcode_tables
704 .entry(table)
705 .or_insert_with(|| CodeTable::new(initex_catcode))
706 .set(ch, int(&val), level);
707 }
708 Loc::Code(kind, ch) => self.code_table_mut(kind).set(ch, int(&val), level),
709 Loc::MathParam(i) => {
710 self.math_param.insert(
711 i,
712 Slot {
713 val: int(&val) as i32,
714 level,
715 },
716 );
717 }
718 Loc::LocalBox(i) => {
719 if let Val::Node(n) = val {
720 self.local_box[i as usize] = Slot { val: n, level };
721 }
722 }
723 Loc::DirPar(i) => {
724 self.dir_par[i as usize] = Slot {
725 val: int(&val) as i32,
726 level,
727 }
728 }
729 Loc::LocalCount(i) => {
730 self.local_count[i as usize] = Slot {
731 val: int(&val) as i32,
732 level,
733 }
734 }
735 }
736 }
737
738 pub fn code_table(&self, kind: CodeKind) -> &CodeTable {
740 match kind {
741 CodeKind::Lc => &self.lc,
742 CodeKind::Uc => &self.uc,
743 CodeKind::Sf => &self.sf,
744 CodeKind::Math => &self.math,
745 CodeKind::Del => &self.del,
746 }
747 }
748
749 pub(crate) fn code_table_mut(&mut self, kind: CodeKind) -> &mut CodeTable {
750 match kind {
751 CodeKind::Lc => &mut self.lc,
752 CodeKind::Uc => &mut self.uc,
753 CodeKind::Sf => &mut self.sf,
754 CodeKind::Math => &mut self.math,
755 CodeKind::Del => &mut self.del,
756 }
757 }
758
759 pub fn is_word(loc: Loc) -> bool {
763 matches!(
764 loc,
765 Loc::IntPar(_)
766 | Loc::Count(_)
767 | Loc::Attr(_)
768 | Loc::DimenPar(_)
769 | Loc::Dimen(_)
770 | Loc::MathParam(_)
771 )
772 }
773}
774
775pub fn same_val(a: &Val, b: &Val) -> bool {
777 match (a, b) {
778 (Val::Meaning(x), Val::Meaning(y)) => x.same_as(y),
779 (Val::Int(x), Val::Int(y)) => x == y,
780 (Val::Glue(x), Val::Glue(y)) => x == y,
781 (Val::Toks(x), Val::Toks(y)) => Rc::ptr_eq(x, y),
782 (Val::Node(x), Val::Node(y)) => x == y,
783 (Val::Shape(x), Val::Shape(y)) => match (x, y) {
784 (None, None) => true,
785 (Some(p), Some(q)) => Rc::ptr_eq(p, q),
786 _ => false,
787 },
788 _ => false,
789 }
790}