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 {
334 match c {
335 0x5C => 0, 0x25 => 14, 0x41..=0x5A | 0x61..=0x7A => 11, 0x0D => 5, 0x20 => 10, 0x00 | 0xFEFF => 9, 0x7F => 15, _ => 12, }
344}
345
346#[derive(Clone, Debug)]
348pub struct Eqtb {
349 pub cs: Vec<Slot<Meaning>>,
351 pub int_par: Vec<Slot<i32>>,
353 pub count: Vec<Slot<i32>>,
355 pub attr: Vec<Slot<i32>>,
357 pub dimen_par: Vec<Slot<i32>>,
359 pub dimen: Vec<Slot<i32>>,
361 pub glue_par: Vec<Slot<Glue>>,
363 pub skip: Vec<Slot<Glue>>,
365 pub muskip: Vec<Slot<Glue>>,
367 pub toks_par: Vec<Slot<TokenList>>,
369 pub toks: Vec<Slot<TokenList>>,
371 pub boxes: Vec<Slot<NodeRef>>,
373 pub cur_font: Slot<u32>,
375 pub math_font: Vec<Slot<u32>>,
377 pub par_shape: Slot<Option<Rc<[i32]>>>,
379 pub etex_shape: Vec<Slot<Option<Rc<[i32]>>>>,
381 pub catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash>,
383 pub lc: CodeTable,
385 pub uc: CodeTable,
387 pub sf: CodeTable,
389 pub math: CodeTable,
391 pub del: CodeTable,
393 pub math_param: HashMap<u32, Slot<i32>>,
395 pub local_box: [Slot<NodeRef>; 2],
397 pub dir_par: [Slot<i32>; pg::assign_dir::COUNT as usize],
399 pub local_count: [Slot<i32>; 2],
401 pub attr_generation: u64,
403 pub attrs_in_use: bool,
405}
406
407impl Eqtb {
408 pub fn initex() -> Self {
411 use pg::assign_int as ip;
412 let regs_i = || vec![Slot::new(0); NUM_REGISTERS];
413 let mut int_par = vec![Slot::new(0i32); INT_PARS];
414 int_par[ip::TOLERANCE as usize].val = 10000;
415 int_par[ip::MAG as usize].val = 1000;
416 int_par[ip::MAXDEADCYCLES as usize].val = 25;
417 int_par[ip::ESCAPECHAR as usize].val = '\\' as i32;
418 int_par[crate::backend::MAJOR_VERSION as usize].val = 1;
419 int_par[ip::ENDLINECHAR as usize].val = 13;
420 let mut catcode_tables: HashMap<u32, CodeTable, crate::hash::FastHash> = HashMap::default();
421 catcode_tables.insert(0, CodeTable::new(initex_catcode));
422 Eqtb {
423 cs: Vec::new(),
424 int_par,
425 count: regs_i(),
426 attr: vec![Slot::new(UNUSED_ATTRIBUTE); NUM_REGISTERS],
427 dimen_par: vec![Slot::new(0); DIMEN_PARS],
428 dimen: regs_i(),
429 glue_par: vec![
430 Slot::new(Glue::ZERO);
431 (pg::assign_glue::COUNT + pg::assign_mu_glue::COUNT) as usize
432 ],
433 skip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
434 muskip: vec![Slot::new(Glue::ZERO); NUM_REGISTERS],
435 toks_par: vec![Slot::new(empty_list()); TOKS_PARS],
436 toks: vec![Slot::new(empty_list()); NUM_REGISTERS],
437 boxes: vec![Slot::new(NodeRef::NULL); NUM_REGISTERS],
438 cur_font: Slot::new(0),
439 math_font: vec![Slot::new(0); 3 * NUM_FAMILIES],
440 par_shape: Slot::new(None),
441 etex_shape: vec![Slot::new(None); pg::set_etex_shape::COUNT as usize],
442 catcode_tables,
443 lc: CodeTable::new(crate::codes::initex_lccode),
444 uc: CodeTable::new(crate::codes::initex_uccode),
445 sf: CodeTable::new(crate::codes::initex_sfcode),
446 math: CodeTable::new(crate::codes::initex_mathcode),
447 del: CodeTable::new(crate::codes::initex_delcode),
448 math_param: HashMap::new(),
449 local_box: [Slot::new(NodeRef::NULL), Slot::new(NodeRef::NULL)],
450 dir_par: std::array::from_fn(|_| Slot::new(0)),
451 local_count: [Slot::new(0), Slot::new(0)],
452 attr_generation: 0,
453 attrs_in_use: false,
454 }
455 }
456
457 #[inline]
459 pub fn ensure_cs(&mut self, cs: CsId) {
460 let n = cs.0 as usize + 1;
461 if self.cs.len() < n {
462 self.cs.resize(
463 n,
464 Slot {
465 val: Meaning::UNDEFINED,
466 level: LEVEL_ZERO,
467 },
468 );
469 }
470 }
471
472 #[inline]
474 pub fn meaning(&self, cs: CsId) -> &Meaning {
475 match self.cs.get(cs.0 as usize) {
476 Some(slot) => &slot.val,
477 None => &Meaning::UNDEFINED,
478 }
479 }
480
481 #[inline]
483 pub fn int(&self, i: u32) -> i32 {
484 self.int_par[i as usize].val
485 }
486
487 #[inline]
489 pub fn dimen(&self, i: u32) -> i32 {
490 self.dimen_par[i as usize].val
491 }
492
493 #[inline]
495 pub fn glue(&self, i: u32) -> Glue {
496 self.glue_par[i as usize].val
497 }
498
499 #[inline]
501 pub fn toks_param(&self, i: u32) -> &TokenList {
502 &self.toks_par[i as usize].val
503 }
504
505 #[inline]
507 pub fn cur_cat_table(&self) -> u32 {
508 self.int(pg::assign_int::CATCODETABLE) as u32
509 }
510
511 #[inline]
513 pub fn catcode(&self, c: u32) -> u8 {
514 self.catcode_in(self.cur_cat_table(), c)
515 }
516
517 pub fn catcode_in(&self, table: u32, c: u32) -> u8 {
520 match self.catcode_tables.get(&table) {
521 Some(t) => t.get(c) as u8,
522 None => initex_catcode(c) as u8,
523 }
524 }
525
526 pub fn get(&self, loc: Loc) -> Val {
528 match loc {
529 Loc::Cs(cs) => Val::Meaning(self.meaning(cs).clone()),
530 Loc::IntPar(i) => Val::Int(self.int_par[i as usize].val.into()),
531 Loc::Count(i) => Val::Int(self.count[i as usize].val.into()),
532 Loc::Attr(i) => Val::Int(self.attr[i as usize].val.into()),
533 Loc::DimenPar(i) => Val::Int(self.dimen_par[i as usize].val.into()),
534 Loc::Dimen(i) => Val::Int(self.dimen[i as usize].val.into()),
535 Loc::GluePar(i) => Val::Glue(self.glue_par[i as usize].val),
536 Loc::Skip(i) => Val::Glue(self.skip[i as usize].val),
537 Loc::MuSkip(i) => Val::Glue(self.muskip[i as usize].val),
538 Loc::ToksPar(i) => Val::Toks(self.toks_par[i as usize].val.clone()),
539 Loc::Toks(i) => Val::Toks(self.toks[i as usize].val.clone()),
540 Loc::Box(i) => Val::Node(self.boxes[i as usize].val),
541 Loc::CurFont => Val::Int(self.cur_font.val.into()),
542 Loc::MathFont(i) => Val::Int(self.math_font[i as usize].val.into()),
543 Loc::ParShape => Val::Shape(self.par_shape.val.clone()),
544 Loc::EtexShape(i) => Val::Shape(self.etex_shape[i as usize].val.clone()),
545 Loc::Cat { table, ch } => Val::Int(self.catcode_in(table, ch).into()),
546 Loc::Code(kind, ch) => Val::Int(self.code_table(kind).get(ch)),
547 Loc::MathParam(i) => Val::Int(
548 self.math_param
549 .get(&i)
550 .map_or(crate::codes::UNDEFINED_MATH_PARAM.into(), |s| s.val.into()),
551 ),
552 Loc::LocalBox(i) => Val::Node(self.local_box[i as usize].val),
553 Loc::DirPar(i) => Val::Int(self.dir_par[i as usize].val.into()),
554 Loc::LocalCount(i) => Val::Int(self.local_count[i as usize].val.into()),
555 }
556 }
557
558 pub fn holds(&self, loc: Loc, val: &Val) -> bool {
561 match (loc, val) {
562 (Loc::Cs(cs), Val::Meaning(m)) => self.meaning(cs).same_as(m),
563 (Loc::IntPar(i), Val::Int(v)) => i64::from(self.int_par[i as usize].val) == *v,
564 (Loc::Count(i), Val::Int(v)) => i64::from(self.count[i as usize].val) == *v,
565 (Loc::Dimen(i), Val::Int(v)) => i64::from(self.dimen[i as usize].val) == *v,
566 (Loc::DimenPar(i), Val::Int(v)) => i64::from(self.dimen_par[i as usize].val) == *v,
567 (Loc::Code(kind, ch), Val::Int(v)) => self.code_table(kind).get(ch) == *v,
568 (Loc::ToksPar(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks_par[i as usize].val, t),
569 (Loc::Toks(i), Val::Toks(t)) => Rc::ptr_eq(&self.toks[i as usize].val, t),
570 _ => same_val(&self.get(loc), val),
571 }
572 }
573
574 pub fn level(&self, loc: Loc) -> u16 {
576 match loc {
577 Loc::Cs(cs) => self.cs.get(cs.0 as usize).map_or(LEVEL_ZERO, |s| s.level),
578 Loc::IntPar(i) => self.int_par[i as usize].level,
579 Loc::Count(i) => self.count[i as usize].level,
580 Loc::Attr(i) => self.attr[i as usize].level,
581 Loc::DimenPar(i) => self.dimen_par[i as usize].level,
582 Loc::Dimen(i) => self.dimen[i as usize].level,
583 Loc::GluePar(i) => self.glue_par[i as usize].level,
584 Loc::Skip(i) => self.skip[i as usize].level,
585 Loc::MuSkip(i) => self.muskip[i as usize].level,
586 Loc::ToksPar(i) => self.toks_par[i as usize].level,
587 Loc::Toks(i) => self.toks[i as usize].level,
588 Loc::Box(i) => self.boxes[i as usize].level,
589 Loc::CurFont => self.cur_font.level,
590 Loc::MathFont(i) => self.math_font[i as usize].level,
591 Loc::ParShape => self.par_shape.level,
592 Loc::EtexShape(i) => self.etex_shape[i as usize].level,
593 Loc::Cat { table, ch } => self
594 .catcode_tables
595 .get(&table)
596 .map_or(LEVEL_ONE, |t| t.level(ch)),
597 Loc::Code(kind, ch) => self.code_table(kind).level(ch),
598 Loc::MathParam(i) => self.math_param.get(&i).map_or(LEVEL_ONE, |s| s.level),
599 Loc::LocalBox(i) => self.local_box[i as usize].level,
600 Loc::DirPar(i) => self.dir_par[i as usize].level,
601 Loc::LocalCount(i) => self.local_count[i as usize].level,
602 }
603 }
604
605 pub fn set(&mut self, loc: Loc, val: Val, level: u16) {
608 fn int(v: &Val) -> i64 {
609 match v {
610 Val::Int(i) => *i,
611 _ => {
612 debug_assert!(false, "expected an integer value");
613 0
614 }
615 }
616 }
617 match loc {
618 Loc::Cs(cs) => {
619 self.ensure_cs(cs);
620 if let Val::Meaning(m) = val {
621 self.cs[cs.0 as usize] = Slot { val: m, level };
622 }
623 }
624 Loc::IntPar(i) => {
625 self.int_par[i as usize] = Slot {
626 val: int(&val) as i32,
627 level,
628 }
629 }
630 Loc::Count(i) => {
631 self.count[i as usize] = Slot {
632 val: int(&val) as i32,
633 level,
634 }
635 }
636 Loc::Attr(i) => {
637 self.attr[i as usize] = Slot {
638 val: int(&val) as i32,
639 level,
640 };
641 self.attr_generation += 1;
642 self.attrs_in_use = true;
643 }
644 Loc::DimenPar(i) => {
645 self.dimen_par[i as usize] = Slot {
646 val: int(&val) as i32,
647 level,
648 }
649 }
650 Loc::Dimen(i) => {
651 self.dimen[i as usize] = Slot {
652 val: int(&val) as i32,
653 level,
654 }
655 }
656 Loc::GluePar(i) | Loc::Skip(i) | Loc::MuSkip(i) => {
657 if let Val::Glue(g) = val {
658 let slot = Slot { val: g, level };
659 match loc {
660 Loc::GluePar(_) => self.glue_par[i as usize] = slot,
661 Loc::Skip(_) => self.skip[i as usize] = slot,
662 _ => self.muskip[i as usize] = slot,
663 }
664 }
665 }
666 Loc::ToksPar(i) | Loc::Toks(i) => {
667 if let Val::Toks(t) = val {
668 let slot = Slot { val: t, level };
669 if matches!(loc, Loc::ToksPar(_)) {
670 self.toks_par[i as usize] = slot;
671 } else {
672 self.toks[i as usize] = slot;
673 }
674 }
675 }
676 Loc::Box(i) => {
677 if let Val::Node(n) = val {
678 self.boxes[i as usize] = Slot { val: n, level };
679 }
680 }
681 Loc::CurFont => {
682 self.cur_font = Slot {
683 val: int(&val) as u32,
684 level,
685 }
686 }
687 Loc::MathFont(i) => {
688 self.math_font[i as usize] = Slot {
689 val: int(&val) as u32,
690 level,
691 }
692 }
693 Loc::ParShape => {
694 if let Val::Shape(s) = val {
695 self.par_shape = Slot { val: s, level };
696 }
697 }
698 Loc::EtexShape(i) => {
699 if let Val::Shape(s) = val {
700 self.etex_shape[i as usize] = Slot { val: s, level };
701 }
702 }
703 Loc::Cat { table, ch } => {
704 self.catcode_tables
705 .entry(table)
706 .or_insert_with(|| CodeTable::new(initex_catcode))
707 .set(ch, int(&val), level);
708 }
709 Loc::Code(kind, ch) => self.code_table_mut(kind).set(ch, int(&val), level),
710 Loc::MathParam(i) => {
711 self.math_param.insert(
712 i,
713 Slot {
714 val: int(&val) as i32,
715 level,
716 },
717 );
718 }
719 Loc::LocalBox(i) => {
720 if let Val::Node(n) = val {
721 self.local_box[i as usize] = Slot { val: n, level };
722 }
723 }
724 Loc::DirPar(i) => {
725 self.dir_par[i as usize] = Slot {
726 val: int(&val) as i32,
727 level,
728 }
729 }
730 Loc::LocalCount(i) => {
731 self.local_count[i as usize] = Slot {
732 val: int(&val) as i32,
733 level,
734 }
735 }
736 }
737 }
738
739 pub fn code_table(&self, kind: CodeKind) -> &CodeTable {
741 match kind {
742 CodeKind::Lc => &self.lc,
743 CodeKind::Uc => &self.uc,
744 CodeKind::Sf => &self.sf,
745 CodeKind::Math => &self.math,
746 CodeKind::Del => &self.del,
747 }
748 }
749
750 pub(crate) fn code_table_mut(&mut self, kind: CodeKind) -> &mut CodeTable {
751 match kind {
752 CodeKind::Lc => &mut self.lc,
753 CodeKind::Uc => &mut self.uc,
754 CodeKind::Sf => &mut self.sf,
755 CodeKind::Math => &mut self.math,
756 CodeKind::Del => &mut self.del,
757 }
758 }
759
760 pub fn is_word(loc: Loc) -> bool {
764 matches!(
765 loc,
766 Loc::IntPar(_)
767 | Loc::Count(_)
768 | Loc::Attr(_)
769 | Loc::DimenPar(_)
770 | Loc::Dimen(_)
771 | Loc::MathParam(_)
772 )
773 }
774}
775
776pub fn same_val(a: &Val, b: &Val) -> bool {
778 match (a, b) {
779 (Val::Meaning(x), Val::Meaning(y)) => x.same_as(y),
780 (Val::Int(x), Val::Int(y)) => x == y,
781 (Val::Glue(x), Val::Glue(y)) => x == y,
782 (Val::Toks(x), Val::Toks(y)) => Rc::ptr_eq(x, y),
783 (Val::Node(x), Val::Node(y)) => x == y,
784 (Val::Shape(x), Val::Shape(y)) => match (x, y) {
785 (None, None) => true,
786 (Some(p), Some(q)) => Rc::ptr_eq(p, q),
787 _ => false,
788 },
789 _ => false,
790 }
791}