Skip to main content

latexml_core/common/
store.rs

1use std::{borrow::Cow, cell::RefCell, collections::VecDeque, fmt, rc::Rc};
2
3use libxml::tree::Node;
4use rustc_hash::FxHashMap as HashMap;
5
6use crate::definition::math_primitive::MathPrimitive; //MathPrimitiveOptions
7use crate::{
8  alignment::Alignment,
9  common::{
10    arena::{self, SymStr, data::SymHashMap},
11    dimension::Dimension,
12    error::*,
13    float::Float,
14    font::Font,
15    glue::Glue,
16    locator::Locator,
17    mudimension::MuDimension,
18    muglue::MuGlue,
19    number::Number,
20    numeric_ops::NumericOps,
21  },
22  definition::{
23    Definition, FontDirective, Reversion,
24    argument::ArgWrap,
25    conditional::{Conditional, IfFrame},
26    constructor::Constructor,
27    expandable::Expandable,
28    primitive::Primitive,
29    register::{Register, RegisterValue},
30  },
31  document::tag::{RawFrontmatter, TagData},
32  keyval::KeyVal,
33  keyvals::KeyVals,
34  ligature::Ligature,
35  list::List,
36  mouth,
37  mouth::Mouth,
38  parameter::Parameter,
39  rewrite::Rewrite,
40  state::StashTable,
41  token::{Catcode, Token},
42  tokens::{TeXString, Tokens},
43};
44
45const STORED_TRUE: Stored = Stored::Bool(true);
46const STORED_FALSE: Stored = Stored::Bool(false);
47
48// Basic principles:
49// 1. If the type is `Copy`, store directly
50// 2. If the type is intended as state-exclusive, store in a Box (or directly if any already Boxed
51//    datatype such as Vec, VecDeque, HashMap)
52// 3. If the struct is intended for reuse in digestion components, store it in an Rc, e.g. Rc<Font>
53// 4. In the very unfortunate cases where we have to mutate items while they are stored, we may
54//    consider a RefCell<> wrapper for interior mutability. BUT it is often possible to take the
55//    value out to mutate, and re-insert. state::checkout_value(key) + mutate +
56//    state::checkin_value(key,val) The only cases where this isn't straightforward is for deep
57//    recursive callchains, as in the ones where we rely on Stomach or a Mouth being in state.
58/// The original global state (in Perl) allowed arbitrary values. To stay consistent, we create an
59/// extremely permissive struct that affords all essential kinds of values that appear essential.
60#[derive(Default, Clone)]
61pub enum Stored {
62  /// if we want to keep a key but make it 'undef', set it to None
63  #[default]
64  None,
65  // Primitives (Copy types, or cheap Clone)
66  /// atomic data (Copy)
67  Bool(bool),
68  /// atomic data (Clone)
69  String(SymStr),
70  /// atomic data (Copy)
71  Charcode(u16),
72  /// atomic data (Copy)
73  /// note that we currently work with 64-bit integers
74  Int(i64),
75  /// atomic data (Clone)
76  Node(Node),
77  // Collections (boxed)
78  /// boxed [char]
79  Chars(Box<[char]>),
80  /// boxed `[Option<char>]`
81  Fontmap(Rc<[Option<char>]>),
82  /// boxed collection
83  Strings(Rc<[SymStr]>),
84  /// boxed collection (latexml)
85  VecDigested(Vec<crate::Digested>),
86  /// the heart of state - a stored Stash table
87  Stash(StashTable),
88  /// boxed map
89  HashString(HashMap<String, String>),
90  /// boxed collection - Stored
91  VecDequeStored(VecDeque<Stored>),
92  /// boxed map - Stored
93  HashStored(SymHashMap<Stored>),
94  /// boxed map (latexml)
95  HashTagData(HashMap<String, Vec<TagData>>),
96  /// queued-but-undigested frontmatter commands (Perl: `frontmatter_raw`)
97  FrontmatterRaw(Vec<RawFrontmatter>),
98  // LaTeXML primitives (Copy types)
99  /// latexml object
100  Catcode(Catcode),
101  /// latexml object
102  Token(Token),
103  /// latexml object
104  Tokens(Tokens),
105  /// latexml object
106  Number(Number),
107  /// latexml object
108  Float(Float),
109  /// latexml object
110  Glue(Glue),
111  /// latexml object
112  MuGlue(MuGlue),
113  /// latexml object
114  Dimension(Dimension),
115  /// latexml object
116  MuDimension(MuDimension),
117  /// metadata object
118  Locator(Box<Locator>),
119  /// latexml object
120  Rewrite(Box<Rewrite>),
121  /// latexml object
122  Ligature(Box<Ligature>),
123  /// latexml object
124  Reversion(Reversion),
125  // LaTeXML objects (Rc-wrapped)
126  /// latexml object (Rc-wrapped)
127  Register(Rc<Register>),
128  /// latexml object (Rc-wrapped)
129  Expandable(Rc<Expandable>),
130  /// latexml object (Rc-wrapped)
131  Conditional(Rc<Conditional>),
132  /// latexml object (Rc-wrapped)
133  Primitive(Rc<Primitive>),
134  /// latexml object (Rc-wrapped)
135  MathPrimitive(Rc<MathPrimitive>),
136  //  MathPrimitiveOptions(MathPrimitiveOptions), // Maybe later
137  /// latexml object (Rc-wrapped)
138  Constructor(Rc<Constructor>),
139  /// latexml object (Rc-wrapped from within)
140  Digested(crate::Digested),
141  /// latexml object (Rc-wrapped)
142  Parameter(Rc<Parameter>),
143  /// latexml object (Rc-wrapped)
144  Font(Rc<Font>),
145  /// a stored FontDirective (Font or closure building a Font)
146  FontDirective(FontDirective),
147  /// WALL OF SHAME (interior mutability) -- can we dispense with these?
148  Mouth(Rc<RefCell<Mouth>>),
149  /// WALL OF SHAME (interior mutability) -- can we dispense with these?
150  IfFrame(Rc<RefCell<IfFrame>>),
151  /// latexml keyval definition
152  KeyVal(KeyVal),
153  /// latexml keyvals collection
154  KeyVals(Rc<KeyVals>),
155  /// alignment template (for storing between macros, e.g. deluxetable)
156  Template(Rc<crate::alignment::template::Template>),
157}
158
159impl fmt::Debug for Stored {
160  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161    use crate::Stored::*;
162    match *self {
163      None => write!(f, "None"),
164      String(ref s) => arena::with(*s, |str| write!(f, "{str}")),
165      Int(ref num) => write!(f, "Stored::Int[{num:?}]"),
166      Node(ref n) => write!(f, "Stored::Node[{n:?}]"),
167      Chars(ref vs) => write!(f, "Stored::Chars[{vs:?}]"),
168      Fontmap(ref vs) => write!(f, "Stored::Fontmap[{vs:?}]"),
169      Stash(ref vs) => write!(f, "Stored::Stash[{vs:?}]"),
170      Strings(ref vs) => write!(f, "Stored::Strings[{vs:?}]"),
171      Bool(ref b) => write!(f, "Stored::Bool[{b:?}]"),
172      Token(ref t) => write!(f, "Stored::Token[{t:?}]"),
173      Tokens(ref t) => write!(f, "Stored::Tokens[{t:?}]"),
174      Locator(ref t) => write!(f, "Stored::Locator[{t:?}]"),
175      Reversion(ref _t) => write!(f, "Stored::Reversion[TODO]"),
176      Catcode(ref cc) => write!(f, "Stored::Catcode[{cc:?}]"),
177      Charcode(ref cc) => write!(f, "Stored::Charcode[{cc:?}]"),
178      IfFrame(ref fr) => write!(f, "Stored::IfFrame[{fr:?}]"),
179      Expandable(ref expandable) => write!(f, "Stored::Expandable[{expandable:?}]"),
180      Conditional(ref _conditional) => write!(f, "Stored::Conditional[TODO]"),
181      Primitive(ref _primitive) => write!(f, "Stored::Primitive[TODO]"),
182      MathPrimitive(ref _primitive) => write!(f, "Stored::MathPrimitive[TODO]"),
183      // MathPrimitiveOptions(ref _primitive) => write!(f, "<math primitive options>"),
184      Constructor(ref _constructor) => write!(f, "Stored::Constructor[TODO]"),
185      Digested(ref digested) => write!(f, "Stored::Digested[{digested:?}]"),
186      Parameter(ref parameter) => write!(f, "Stored::Parameter[{parameter:?}]"),
187      Register(ref register) => write!(f, "Stored::Register[{:?}]", register.cs),
188      Rewrite(ref rewrite) => write!(f, "Stored::Rewrite[{rewrite:?}]"),
189      Mouth(ref mouth) => write!(f, "Stored::Mouth[{:?}]", mouth.borrow().get_source()),
190      Font(ref font) => write!(f, "Stored::Font[{font:?}]"),
191      FontDirective(ref font) => write!(f, "Stored::FontDirective[{font:?}]"),
192      Number(ref number) => write!(f, "Stored::Number[{number:?}]"),
193      Float(ref float) => write!(f, "Stored::Float[{float:?}]"),
194      Glue(ref glue) => write!(f, "Stored::Glue[{glue:?}]"),
195      MuGlue(ref glue) => write!(f, "Stored::MuGlue[{glue:?}]"),
196      Dimension(ref dimension) => write!(f, "Stored::Dimension[{dimension:?}]"),
197      MuDimension(ref dimension) => write!(f, "Stored::MuDimension[{dimension:?}]"),
198      VecDigested(ref digested_vec) => write!(f, "VecDigested{digested_vec:?}"),
199      VecDequeStored(ref vec) => write!(f, "VecDequeStored{vec:?}"),
200      HashStored(ref hos) => write!(f, "HashStored{hos:?}"),
201      HashTagData(ref htd) => write!(f, "HashTagData[{htd:?}]"),
202      FrontmatterRaw(ref raw) => write!(f, "FrontmatterRaw[{raw:?}]"),
203      HashString(ref hstr) => write!(f, "HashStr[{hstr:?}]"),
204      Ligature(ref lig) => write!(f, "Ligature[{lig:?}]"),
205      KeyVal(ref kv) => write!(f, "KeyVal[{kv:?}]"),
206      KeyVals(ref kvs) => write!(f, "KeyVals[{kvs:?}]"),
207      Template(ref t) => write!(f, "Template[{t}]"),
208    }
209  }
210}
211impl fmt::Display for Stored {
212  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213    use crate::Stored::*;
214    match *self {
215      Digested(ref digested) => write!(f, "{digested}"),
216      Dimension(ref v) => write!(f, "{v}"),
217      Number(ref v) => write!(f, "{v}"),
218      Float(ref v) => write!(f, "{v}"),
219      Glue(ref v) => write!(f, "{v}"),
220      MuGlue(ref v) => write!(f, "{v}"),
221      MuDimension(ref v) => write!(f, "{v}"),
222      Font(ref font) => write!(f, "{font}"),
223      FontDirective(ref font) => write!(f, "{font:?}"),
224      String(ref s) => arena::with(*s, |str| write!(f, "{str}")),
225      Int(ref s) => write!(f, "{s}"),
226      Bool(ref s) => write!(f, "{s}"),
227      Tokens(ref s) => write!(f, "{s}"),
228      Token(ref s) => write!(f, "{s}"),
229      Conditional(ref s) => write!(f, "{s}"),
230      Constructor(ref c) => write!(f, "Constructor[{}]", c.get_cs_name()),
231      Strings(ref vs) => write!(f, "{}", arena::join(vs, ",")),
232      KeyVals(ref kvs) => write!(f, "{kvs}"),
233      Template(ref t) => write!(f, "{t}"),
234      None => write!(f, "Stored[None]"),
235      _ => write!(f, "Stored[??]"),
236    }
237  }
238}
239
240/// Note: PartialEq on Stored is *structural*. See WISDOM.md for RegisterType trap.
241impl PartialEq for Stored {
242  fn eq(&self, other: &Stored) -> bool {
243    use crate::Stored::*;
244    match *self {
245      None => matches!(other, None),
246      String(ref s) => {
247        if let String(s2) = other {
248          *s == *s2
249        } else {
250          false
251        }
252      },
253      Int(ref num) => {
254        if let Int(num2) = other {
255          *num == *num2
256        } else {
257          false
258        }
259      },
260      Node(ref n) => {
261        if let Node(n2) = other {
262          *n == *n2
263        } else {
264          false
265        }
266      },
267      Chars(ref vs) => {
268        if let Chars(vs2) = other {
269          **vs == **vs2
270        } else {
271          false
272        }
273      },
274      Fontmap(ref vs) => {
275        if let Fontmap(vs2) = other {
276          *vs == *vs2
277        } else {
278          false
279        }
280      },
281      Strings(ref vs) => {
282        if let Strings(vs2) = other {
283          *vs == *vs2
284        } else {
285          false
286        }
287      },
288      Bool(ref b) => {
289        if let Bool(b2) = other {
290          *b == *b2
291        } else {
292          false
293        }
294      },
295      Token(ref t) => {
296        if let Token(t2) = other {
297          *t == *t2
298        } else {
299          false
300        }
301      },
302      Tokens(ref t) => {
303        if let Tokens(t2) = other {
304          *t == *t2
305        } else {
306          false
307        }
308      },
309      Locator(ref t) => {
310        if let Locator(t2) = other {
311          *t == *t2
312        } else {
313          false
314        }
315      },
316      Reversion(ref t) => {
317        if let Reversion(t2) = other {
318          *t == *t2
319        } else {
320          false
321        }
322      },
323      Catcode(ref cc) => {
324        if let Catcode(cc2) = other {
325          *cc == *cc2
326        } else {
327          false
328        }
329      },
330      Charcode(ref cc) => {
331        if let Charcode(cc2) = other {
332          *cc == *cc2
333        } else {
334          false
335        }
336      },
337      IfFrame(ref fr) => {
338        if let IfFrame(fr2) = other {
339          *fr.borrow() == *fr2.borrow()
340        } else {
341          false
342        }
343      },
344      Expandable(ref e) => {
345        if let Expandable(e2) = other {
346          **e == **e2
347        } else {
348          false
349        }
350      },
351      Conditional(ref c) => {
352        if let Conditional(c2) = other {
353          **c == **c2
354        } else {
355          false
356        }
357      },
358      Primitive(ref p) => {
359        if let Primitive(p2) = other {
360          **p == **p2
361        } else {
362          false
363        }
364      },
365      MathPrimitive(ref p) => {
366        if let MathPrimitive(p2) = other {
367          **p == **p2
368        } else {
369          false
370        }
371      },
372      // MathPrimitiveOptions(ref _primitive) =>
373      Constructor(ref c) => {
374        if let Constructor(c2) = other {
375          **c == **c2
376        } else {
377          false
378        }
379      },
380      Digested(ref d) => {
381        if let Digested(d2) = other {
382          *d == *d2
383        } else {
384          false
385        }
386      },
387      Parameter(ref p) => {
388        if let Parameter(p2) = other {
389          **p == **p2
390        } else {
391          false
392        }
393      },
394      Register(ref r) => {
395        if let Register(r2) = other {
396          **r == **r2
397        } else {
398          false
399        }
400      },
401      Rewrite(ref r) => {
402        if let Rewrite(r2) = other {
403          *r == *r2
404        } else {
405          false
406        }
407      },
408      Mouth(ref m) => {
409        if let Mouth(m2) = other {
410          *m.borrow() == *m2.borrow()
411        } else {
412          false
413        }
414      },
415      Font(ref f) => {
416        if let Font(f2) = other {
417          **f == **f2
418        } else {
419          false
420        }
421      },
422      FontDirective(ref fd) => {
423        if let FontDirective(fd2) = other {
424          fd == fd2
425        } else {
426          false
427        }
428      },
429      Number(ref n) => {
430        if let Number(n2) = other {
431          *n == *n2
432        } else {
433          false
434        }
435      },
436      Float(ref n) => {
437        if let Float(n2) = other {
438          *n == *n2
439        } else {
440          false
441        }
442      },
443      Glue(ref g) => {
444        if let Glue(g2) = other {
445          *g == *g2
446        } else {
447          false
448        }
449      },
450      MuGlue(ref mg) => {
451        if let MuGlue(mg2) = other {
452          *mg == *mg2
453        } else {
454          false
455        }
456      },
457      Dimension(ref d) => {
458        if let Dimension(d2) = other {
459          *d == *d2
460        } else {
461          false
462        }
463      },
464      MuDimension(ref md) => {
465        if let MuDimension(md2) = other {
466          *md == *md2
467        } else {
468          false
469        }
470      },
471      VecDigested(ref vd) => {
472        if let VecDigested(vd2) = other {
473          *vd == *vd2
474        } else {
475          false
476        }
477      },
478      VecDequeStored(ref v) => {
479        if let VecDequeStored(v2) = other {
480          v.len() == v2.len() && v.iter().zip(v2.iter()).all(|(item1, item2)| item1 == item2)
481        } else {
482          false
483        }
484      },
485      Stash(ref v) => {
486        if let Stash(v2) = other {
487          v.len() == v2.len() // TODO: Do we need accuracy on stash comparisons?
488        } else {
489          false
490        }
491      },
492      HashStored(ref hs) => {
493        if let HashStored(hs2) = other {
494          hs.len() == hs2.len()
495            && hs.iter().all(|(key, value)| {
496              if let Some(item2) = hs2.get_sym(*key) {
497                value == item2
498              } else {
499                false
500              }
501            })
502        } else {
503          false
504        }
505      },
506      HashTagData(ref htd) => {
507        if let HashTagData(htd2) = other {
508          *htd == *htd2
509        } else {
510          false
511        }
512      },
513      FrontmatterRaw(ref raw) => {
514        if let FrontmatterRaw(raw2) = other {
515          *raw == *raw2
516        } else {
517          false
518        }
519      },
520      HashString(ref hstr) => {
521        if let HashString(hstr2) = other {
522          *hstr == *hstr2
523        } else {
524          false
525        }
526      },
527      Ligature(ref lig) => {
528        if let Ligature(lig2) = other {
529          *lig == *lig2
530        } else {
531          false
532        }
533      },
534      KeyVal(ref kv) => {
535        if let KeyVal(kv2) = other {
536          *kv == *kv2
537        } else {
538          false
539        }
540      },
541      KeyVals(ref kvs) => {
542        if let KeyVals(kvs2) = other {
543          Rc::ptr_eq(kvs, kvs2)
544        } else {
545          false
546        }
547      },
548      Template(ref t) => {
549        if let Template(t2) = other {
550          Rc::ptr_eq(t, t2)
551        } else {
552          false
553        }
554      },
555    }
556  }
557}
558
559// SAFETY: `Stored` contains `Rc`/`RefCell` (which are !Send/!Sync by default)
560// because it embeds libxml::tree::Node and other reference-counted values.
561// This crate's convention is that `State` (and therefore all `Stored`
562// values held inside it) is thread-local — each conversion job is pinned
563// to exactly one OS thread via `use_{main,std,sty}_state()` in state.rs.
564// No `Stored` instance is ever moved across threads at runtime.
565// These impls exist to satisfy trait bounds on error paths (e.g. Box<dyn
566// Error + Send + Sync>) that transitively require Send/Sync on all inner
567// types. The invariant is maintained by construction, not the type system.
568unsafe impl Send for Stored {}
569unsafe impl Sync for Stored {}
570impl Stored {
571  /// Zero-alloc equivalent of `self.to_string() == target` for the two
572  /// string-carrying variants (`String`, `Tokens`). Falls back to
573  /// `to_string()` for everything else, where the Display impl allocates
574  /// anyway.
575  pub fn eq_text(&self, target: &str) -> bool {
576    match self {
577      Stored::String(s) => arena::with(*s, |v| v == target),
578      Stored::Tokens(t) => t.eq_text(target),
579      Stored::Token(t) => t.with_str(|v| v == target),
580      other => other.to_string() == target,
581    }
582  }
583
584  /// A list-shaped value: exposed to the Rhai `LookupValue` binding as an array
585  /// (mirroring Perl's `LookupValue` returning an arrayref), and having no
586  /// scalar string form — so `lookup_string` returns "" for it rather than
587  /// leaking the internal Debug repr (#315). The two variants a `push_value` /
588  /// list `AssignValue` can produce: `VecDequeStored` (a pushed queue, e.g.
589  /// `class_options`) and `Strings` (an immutable string array).
590  pub fn is_list(&self) -> bool { matches!(self, Stored::VecDequeStored(_) | Stored::Strings(_)) }
591
592  /// The items of a list value as strings, for structural access. `None` for a
593  /// non-list value.
594  pub fn list_items(&self) -> Option<Vec<String>> {
595    match self {
596      Stored::Strings(ss) => Some(ss.iter().map(|s| arena::to_string(*s)).collect()),
597      Stored::VecDequeStored(v) => Some(v.iter().map(String::from).collect()),
598      _ => None,
599    }
600  }
601
602  /// Zero-alloc `self.to_string().starts_with(prefix)` for the
603  /// string-carrying variants; falls back to `to_string()` for others.
604  pub fn starts_with_text(&self, prefix: &str) -> bool {
605    match self {
606      Stored::String(s) => arena::with(*s, |v| v.starts_with(prefix)),
607      Stored::Tokens(t) => t.starts_with_text(prefix),
608      Stored::Token(t) => t.with_str(|v| v.starts_with(prefix)),
609      other => other.to_string().starts_with(prefix),
610    }
611  }
612
613  /// Zero-alloc `self.to_string().ends_with(suffix)` for the String /
614  /// Token variants (where the entire Display output equals the
615  /// interned text). For Tokens, we still walk into a small owned
616  /// String — ends_with requires anchoring at the tail and rolling
617  /// backward, which needs random access. Others fall back to
618  /// `to_string()` (cost paid anyway via the Display impl).
619  pub fn ends_with_text(&self, suffix: &str) -> bool {
620    match self {
621      Stored::String(s) => arena::with(*s, |v| v.ends_with(suffix)),
622      Stored::Token(t) => t.with_str(|v| v.ends_with(suffix)),
623      other => other.to_string().ends_with(suffix),
624    }
625  }
626
627  /// helper method that uses `ToString::to_string` to flatten a map with Stored values
628  // TODO: Obviously a performance issue, find a way to unify the interfaces where string allocation
629  // is completely avoided until serialization in the XML.
630  // libxml can accept &str, so as long as we can stay within the interner arena paradigm,
631  // we should be allocation free. [end TODO]
632  pub fn cast_to_string_hash(in_map: &SymHashMap<Stored>) -> HashMap<String, String> {
633    let mut out_map: HashMap<String, String> = HashMap::default();
634    for (key, val) in in_map {
635      // Use to_attribute() so MuGlue/MuDimension widths are converted to pt
636      // (e.g. `3.0mu` → `1.66663pt`) before becoming XML attribute strings.
637      // Mirror Perl `attributeformat` which uses `ptValue` for mu-typed
638      // lengths in attribute context.
639      out_map.insert(arena::to_string(*key), val.to_attribute());
640    }
641    out_map
642  }
643  /// Dynamic dispatch for Definition's `read_arguments`,
644  /// to circumvent the limitations of using trait objects with `Rc<Definition>`
645  pub fn read_arguments(&self) -> Result<Vec<ArgWrap>> {
646    match self {
647      Stored::Conditional(entry) => entry.read_arguments(),
648      Stored::Constructor(entry) => entry.read_arguments(),
649      Stored::Expandable(entry) => entry.read_arguments(),
650      Stored::MathPrimitive(entry) => entry.read_arguments(),
651      Stored::Primitive(entry) => entry.read_arguments(),
652      Stored::Register(entry) => entry.read_arguments(),
653      e => Err(s!(".read_arguments not defined for stored variant {:?}", e).into()),
654    }
655  }
656  /// Uses `NumericOps::to_attribute` for Stored values supporting it, otherwise
657  /// `ToString::to_string`
658  pub fn to_attribute(&self) -> String {
659    match self {
660      Stored::Dimension(v) => v.to_attribute(),
661      Stored::Number(v) => v.to_attribute(),
662      Stored::MuDimension(v) => v.to_attribute(),
663      Stored::Glue(v) => v.to_attribute(),
664      Stored::MuGlue(v) => v.to_attribute(),
665      other => other.to_string(),
666    }
667  }
668  pub fn to_definition(&self) -> Option<Rc<dyn Definition>> {
669    match self {
670      Stored::Primitive(defn) => Some(defn.clone()),
671      Stored::MathPrimitive(defn) => Some(defn.clone()),
672      Stored::Conditional(defn) => Some(defn.clone()),
673      Stored::Register(defn) => Some(defn.clone()),
674      Stored::Expandable(defn) => Some(defn.clone()),
675      Stored::Constructor(defn) => Some(defn.clone()),
676      _ => None,
677    }
678  }
679}
680
681impl From<bool> for Stored {
682  fn from(value: bool) -> Self { Stored::Bool(value) }
683}
684
685impl From<bool> for &Stored {
686  fn from(value: bool) -> Self { if value { &STORED_TRUE } else { &STORED_FALSE } }
687}
688
689impl From<Cow<'_, str>> for Stored {
690  fn from(value: Cow<'_, str>) -> Self { Stored::String(arena::pin(value)) }
691}
692impl From<String> for Stored {
693  fn from(value: String) -> Self { Stored::String(arena::pin(value)) }
694}
695impl From<SymStr> for Stored {
696  fn from(value: SymStr) -> Self { Stored::String(value) }
697}
698
699impl From<char> for Stored {
700  fn from(value: char) -> Self { Stored::String(arena::pin_char(value)) }
701}
702
703impl<'a> From<&'a String> for Stored {
704  fn from(value: &'a String) -> Self { Stored::String(arena::pin(value)) }
705}
706
707impl From<&'static str> for Stored {
708  fn from(value: &'static str) -> Self { Stored::String(arena::pin_static(value)) }
709}
710
711impl From<usize> for Stored {
712  fn from(value: usize) -> Self { Stored::Int(value as i64) }
713}
714
715// TODO: Should we add a lot more numeric nuance to the Store ?
716impl From<u8> for Stored {
717  fn from(value: u8) -> Self { Stored::Int(value as i64) }
718}
719
720impl From<i32> for Stored {
721  fn from(value: i32) -> Self { Stored::Int(value as i64) }
722}
723impl From<i64> for Stored {
724  fn from(value: i64) -> Self { Stored::Int(value) }
725}
726
727impl From<f64> for Stored {
728  fn from(value: f64) -> Self { Stored::Number(Number::new(value.floor() as i64)) }
729}
730
731impl From<Catcode> for Stored {
732  fn from(value: Catcode) -> Self { Stored::Catcode(value) }
733}
734
735impl From<Token> for Stored {
736  fn from(value: Token) -> Self { Stored::Token(value) }
737}
738
739// Storing all definitions is expected - Rc<Expandable> case
740
741impl From<Tokens> for Stored {
742  fn from(value: Tokens) -> Self { Stored::Tokens(value) }
743}
744
745impl From<Locator> for Stored {
746  fn from(value: Locator) -> Self { Stored::Locator(Box::new(value)) }
747}
748
749impl From<Mouth> for Stored {
750  fn from(value: Mouth) -> Self { Stored::Mouth(Rc::new(RefCell::new(value))) }
751}
752
753impl From<Rc<Expandable>> for Stored {
754  fn from(definition: Rc<Expandable>) -> Self { Stored::Expandable(definition) }
755}
756/// Storing all definitions is expected - Expandable case
757impl From<Expandable> for Stored {
758  fn from(definition: Expandable) -> Self { Rc::new(definition).into() }
759}
760
761impl From<Rc<Conditional>> for Stored {
762  fn from(definition: Rc<Conditional>) -> Self { Stored::Conditional(definition) }
763}
764impl From<Conditional> for Stored {
765  fn from(value: Conditional) -> Self { Rc::new(value).into() }
766}
767
768impl From<Rc<Primitive>> for Stored {
769  fn from(definition: Rc<Primitive>) -> Self { Stored::Primitive(definition) }
770}
771impl From<Primitive> for Stored {
772  fn from(value: Primitive) -> Self { Rc::new(value).into() }
773}
774
775impl From<Rc<MathPrimitive>> for Stored {
776  fn from(definition: Rc<MathPrimitive>) -> Self { Stored::MathPrimitive(definition) }
777}
778impl From<MathPrimitive> for Stored {
779  fn from(value: MathPrimitive) -> Self { Rc::new(value).into() }
780}
781
782impl From<Rc<Constructor>> for Stored {
783  fn from(definition: Rc<Constructor>) -> Self { Stored::Constructor(definition) }
784}
785impl From<Constructor> for Stored {
786  fn from(value: Constructor) -> Self { Rc::new(value).into() }
787}
788
789impl From<List> for Stored {
790  fn from(value: List) -> Self { crate::Digested::from(value).into() }
791}
792
793impl From<Node> for Stored {
794  fn from(value: Node) -> Self { Stored::Node(value) }
795}
796
797impl From<crate::Digested> for Stored {
798  fn from(value: crate::Digested) -> Self { Stored::Digested(value) }
799}
800
801impl From<&crate::Digested> for Stored {
802  fn from(value: &crate::Digested) -> Self { Stored::Digested(value.clone()) }
803}
804
805impl<T> From<Option<T>> for Stored
806where T: Into<Stored> + Sized
807{
808  fn from(value_opt: Option<T>) -> Self {
809    match value_opt {
810      None => Stored::None,
811      Some(v) => v.into(),
812    }
813  }
814}
815
816impl<'a> From<Cow<'a, crate::Digested>> for Stored {
817  fn from(value: Cow<'a, crate::Digested>) -> Self { Stored::Digested(value.into_owned()) }
818}
819
820impl From<Box<crate::Digested>> for Stored {
821  fn from(value: Box<crate::Digested>) -> Self { Stored::Digested(*value) }
822}
823
824impl From<Parameter> for Stored {
825  fn from(value: Parameter) -> Self { Stored::Parameter(Rc::new(value)) }
826}
827
828impl From<Rc<Font>> for Stored {
829  fn from(font: Rc<Font>) -> Self { Stored::Font(font) }
830}
831
832impl From<Rc<Register>> for Stored {
833  fn from(register: Rc<Register>) -> Self { Stored::Register(register) }
834}
835impl From<Register> for Stored {
836  fn from(register: Register) -> Self { Rc::new(register).into() }
837}
838
839impl From<Rewrite> for Stored {
840  fn from(value: Rewrite) -> Self { Stored::Rewrite(Box::new(value)) }
841}
842
843impl From<Font> for Stored {
844  fn from(value: Font) -> Self { Rc::new(value).into() }
845}
846
847impl From<Cow<'_, Font>> for Stored {
848  fn from(value: Cow<Font>) -> Self { Rc::new((*value).clone()).into() }
849}
850
851impl From<Number> for Stored {
852  fn from(value: Number) -> Self { Stored::Number(value) }
853}
854// A distinct slot from `From<f64>` above, which FLOORS to an integer `Number`
855// (TeX registers are integral). A `Float` value keeps its fraction — the only
856// path that stores `Stored::Float`, so an `AssignFloat` binding must build a
857// `Float`, not lean on `f64 -> Stored`.
858impl From<Float> for Stored {
859  fn from(value: Float) -> Self { Stored::Float(value) }
860}
861impl From<Dimension> for Stored {
862  fn from(value: Dimension) -> Self { Stored::Dimension(value) }
863}
864impl From<MuDimension> for Stored {
865  fn from(value: MuDimension) -> Self { Stored::MuDimension(value) }
866}
867impl From<Glue> for Stored {
868  fn from(value: Glue) -> Self { Stored::Glue(value) }
869}
870impl From<MuGlue> for Stored {
871  fn from(value: MuGlue) -> Self { Stored::MuGlue(value) }
872}
873
874impl<'a> From<&'a Token> for Stored {
875  fn from(value: &'a Token) -> Self { Stored::Token(*value) }
876}
877
878impl From<Alignment> for Stored {
879  fn from(a: Alignment) -> Self { Stored::Digested(crate::Digested::from(a)) }
880}
881
882impl From<Box<[char]>> for Stored {
883  fn from(value: Box<[char]>) -> Self { Stored::Chars(value) }
884}
885impl From<Rc<[Option<char>]>> for Stored {
886  fn from(value: Rc<[Option<char>]>) -> Self { Stored::Fontmap(value) }
887}
888
889impl From<Rc<[SymStr]>> for Stored {
890  fn from(value: Rc<[SymStr]>) -> Self { Stored::Strings(value) }
891}
892
893impl From<Vec<String>> for Stored {
894  fn from(value: Vec<String>) -> Self { Stored::Strings(value.iter().map(arena::pin).collect()) }
895}
896
897impl<'a> From<Vec<&'a str>> for Stored {
898  fn from(value: Vec<&'a str>) -> Self { Stored::Strings(value.iter().map(arena::pin).collect()) }
899}
900
901impl From<Vec<Token>> for Stored {
902  fn from(value: Vec<Token>) -> Self { Stored::Tokens(Tokens::new(value)) }
903}
904
905impl From<Vec<crate::Digested>> for Stored {
906  fn from(value: Vec<crate::Digested>) -> Self { Stored::VecDigested(value) }
907}
908
909impl From<HashMap<String, String>> for Stored {
910  fn from(value: HashMap<String, String>) -> Self { Stored::HashString(value) }
911}
912
913impl From<VecDeque<Stored>> for Stored {
914  fn from(value: VecDeque<Stored>) -> Self { Stored::VecDequeStored(value) }
915}
916
917impl From<HashMap<SymStr, Stored>> for Stored {
918  fn from(value: HashMap<SymStr, Stored>) -> Self { Stored::HashStored(SymHashMap(value)) }
919}
920impl From<SymHashMap<Stored>> for Stored {
921  fn from(value: SymHashMap<Stored>) -> Self { Stored::HashStored(value) }
922}
923
924// TODO: What is the right interface here? Should we really commit to SymStr?
925// Or is it too distracting from a developer perspective and String should be allowed more widely?
926impl From<HashMap<String, Stored>> for Stored {
927  fn from(str_hash: HashMap<String, Stored>) -> Self {
928    let mut arena_value = HashMap::default();
929    for (key, value) in str_hash {
930      arena_value.insert(arena::pin(key), value);
931    }
932    Stored::HashStored(SymHashMap(arena_value))
933  }
934}
935
936impl From<HashMap<String, Vec<TagData>>> for Stored {
937  fn from(value: HashMap<String, Vec<TagData>>) -> Self { Stored::HashTagData(value) }
938}
939
940impl From<RegisterValue> for Stored {
941  fn from(rv: RegisterValue) -> Self {
942    match rv {
943      RegisterValue::Number(v) => Stored::Number(v),
944      RegisterValue::Dimension(v) => Stored::Dimension(v),
945      RegisterValue::MuDimension(v) => Stored::MuDimension(v),
946      RegisterValue::Glue(v) => Stored::Glue(v),
947      RegisterValue::MuGlue(v) => Stored::MuGlue(v),
948      RegisterValue::Token(v) => Stored::Token(v),
949      RegisterValue::Tokens(v) => Stored::Tokens(v),
950      RegisterValue::Pair(_) => Stored::None, // TODO: add Stored::Pair
951    }
952  }
953}
954
955impl From<Rc<RefCell<IfFrame>>> for Stored {
956  fn from(frame: Rc<RefCell<IfFrame>>) -> Stored { Stored::IfFrame(frame) }
957}
958
959impl From<Ligature> for Stored {
960  fn from(lig: Ligature) -> Stored { Stored::Ligature(Box::new(lig)) }
961}
962
963impl From<Reversion> for Stored {
964  fn from(rev: Reversion) -> Stored { Stored::Reversion(rev) }
965}
966
967impl From<KeyVal> for Stored {
968  fn from(kv: KeyVal) -> Stored { Stored::KeyVal(kv) }
969}
970
971impl From<KeyVals> for Stored {
972  fn from(kvs: KeyVals) -> Stored { Stored::KeyVals(Rc::new(kvs)) }
973}
974
975impl From<crate::alignment::template::Template> for Stored {
976  fn from(t: crate::alignment::template::Template) -> Stored { Stored::Template(Rc::new(t)) }
977}
978
979impl From<Option<&Stored>> for Stored {
980  fn from(stored_opt: Option<&Stored>) -> Stored {
981    match stored_opt {
982      Some(val) => val.clone(),
983      None => Stored::Bool(false),
984    }
985  }
986}
987
988// Reverse direction -- cast Stored back into concrete types, with meaningfull fallbacks where
989// impossible
990
991impl From<&Stored> for bool {
992  fn from(value: &Stored) -> bool {
993    // Mirror Perl's `if ($val)` truthiness: defined-and-nonzero is true,
994    // numeric-zero is false. Without the numeric-zero check, registers
995    // initialized to 0 (e.g. `\globaldefs` default, `\count255` unset)
996    // would read as "set" via `lookup_bool`, breaking flag-style probes.
997    match value {
998      Stored::Bool(b) => *b,
999      Stored::Int(0) => false,
1000      Stored::Number(n) if n.0 == 0 => false,
1001      _ => true,
1002    }
1003  }
1004}
1005
1006impl From<&Stored> for String {
1007  fn from(value: &Stored) -> String {
1008    match value {
1009      Stored::String(v) => arena::to_string(*v),
1010      v => s!("{v:?}"),
1011    }
1012  }
1013}
1014
1015impl<'a> From<&'a Stored> for Option<&'a VecDeque<Stored>> {
1016  fn from(value: &'a Stored) -> Option<&'a VecDeque<Stored>> {
1017    match value {
1018      Stored::VecDequeStored(v) => Some(v),
1019      _ => None,
1020    }
1021  }
1022}
1023
1024impl<'a> From<&'a Stored> for Option<Rc<Font>> {
1025  fn from(value: &'a Stored) -> Option<Rc<Font>> {
1026    match value {
1027      Stored::Font(f) => Some(Rc::clone(f)),
1028      _ => None,
1029    }
1030  }
1031}
1032
1033impl<'a> From<&'a Stored> for Option<Number> {
1034  fn from(value: &'a Stored) -> Option<Number> {
1035    match value {
1036      Stored::Number(n) => Some(*n),
1037      // A `Float` narrows to an integer `Number` through `Float::value_of`
1038      // (truncation toward zero, the crate's canonical Float->i64), the same
1039      // `value_of` narrowing the dimension family uses just below. NB this is
1040      // *not* the `floor` that `From<f64> for Stored` applies — they agree for
1041      // non-negative values (the only ones stored here in practice).
1042      Stored::Float(f) => Some(Number::new(f.value_of())),
1043      Stored::Dimension(n) => Some(Number::new(n.value_of())),
1044      Stored::Glue(n) => Some(Number::new(n.value_of())),
1045      Stored::MuDimension(n) => Some(Number::new(n.value_of())),
1046      Stored::MuGlue(n) => Some(Number::new(n.value_of())),
1047      other => {
1048        eprintln!("TODO: auto-cast of Stored to Number attempted on {other:?}");
1049        None
1050      },
1051    }
1052  }
1053}
1054
1055// The counterpart of the `Option<Number>` narrowing above, kept lenient in the
1056// same spirit: a stored `Float` reads as itself, and the integral numerics
1057// widen to `Float` (`Number`/`Int`), so `lookup_float` sees a value assigned
1058// either way. Non-numeric variants are `None` (no eprintln — an optional typed
1059// read, not a coercion error).
1060impl<'a> From<&'a Stored> for Option<Float> {
1061  fn from(value: &'a Stored) -> Option<Float> {
1062    match value {
1063      Stored::Float(f) => Some(*f),
1064      Stored::Number(n) => Some(Float::new(n.value_of())),
1065      Stored::Int(i) => Some(Float(*i as f64)),
1066      _ => None,
1067    }
1068  }
1069}
1070
1071// MuGlue/MuDimension store raw mu in fixpoint units (1mu = 1/18 em).
1072// Convert to scaled-pt by mirroring Perl `MuGlue::spValue` →
1073// `fixpoint(mu/UNITY, MUWidth)` where `MUWidth = int(size * emwidth /
1074// 18)`. The two-step integer truncation in Perl is load-bearing: a
1075// single-step `(mu * size / 18)` gives a slightly larger value (109226
1076// vs 109219 for 3mu at 10pt), and Knuth's `print_scaled` then formats
1077// "1.66666pt" instead of the expected "1.66663pt". See
1078// LaTeXML/lib/LaTeXML/Common/Font.pm:580 (getMUWidth) and
1079// Core/MuGlue.pm spValue.
1080fn mu_to_pt_value(mu_val: i64) -> i64 {
1081  let fs = crate::state::lookup_font()
1082    .and_then(|f| f.get_size())
1083    .unwrap_or(10.0);
1084  let unity = crate::common::numeric_ops::UNITY_F64;
1085  // MUWidth = int(font_size * emwidth(=1.0*UNITY) / 18)
1086  let muwidth = (fs * unity / 18.0) as i64;
1087  // fixpoint(mu/UNITY, MUWidth) ≈ (mu_val * muwidth / UNITY).trunc()
1088  ((mu_val as f64 * muwidth as f64 / unity).trunc()) as i64
1089}
1090
1091impl<'a> From<&'a Stored> for Option<Dimension> {
1092  fn from(value: &'a Stored) -> Option<Dimension> {
1093    match value {
1094      Stored::Dimension(n) => Some(*n),
1095      Stored::Number(n) => Some(Dimension::new(n.value_of())),
1096      Stored::Glue(n) => Some(Dimension::new(n.value_of())),
1097      Stored::MuDimension(n) => Some(Dimension::new(mu_to_pt_value(n.value_of()))),
1098      Stored::MuGlue(n) => Some(Dimension::new(mu_to_pt_value(n.value_of()))),
1099      _ => None,
1100    }
1101  }
1102}
1103
1104impl<'a> From<&'a Stored> for Option<Glue> {
1105  fn from(value: &'a Stored) -> Option<Glue> {
1106    match value {
1107      Stored::Dimension(n) => Some(Glue::new(n.value_of())),
1108      Stored::Number(n) => Some(Glue::new(n.value_of())),
1109      Stored::MuDimension(n) => Some(Glue::new(mu_to_pt_value(n.value_of()))),
1110      Stored::MuGlue(n) => Some(Glue::new(mu_to_pt_value(n.value_of()))),
1111      Stored::Glue(n) => Some(*n),
1112      _ => None,
1113    }
1114  }
1115}
1116
1117impl From<Stored> for Option<Tokens> {
1118  fn from(value: Stored) -> Option<Tokens> {
1119    match value {
1120      Stored::String(sym) => Some(mouth::tokenize_internal(TeXString::assembled(
1121        arena::to_string(sym),
1122      ))),
1123      Stored::Token(ts) => Some(Tokens::new(vec![ts])),
1124      Stored::Tokens(ts) => Some(ts),
1125      // Digested: revert to tokens (needed for \AtBeginDocument hooks that
1126      // store Digested arguments via push_value)
1127      Stored::Digested(d) => {
1128        use crate::common::object::Object;
1129        d.revert().ok()
1130      },
1131      Stored::VecDequeStored(vdq) => {
1132        // Each item in the queue can be unlisted into a Vec<Token>
1133        // and then the result can be re-cast as a single Tokens
1134        let mut collected: Vec<Token> = Vec::new();
1135        for item in vdq {
1136          let item_tokens_opt: Option<Tokens> = item.into();
1137          if let Some(item_tokens) = item_tokens_opt {
1138            collected.extend(item_tokens.unlist());
1139          }
1140        }
1141        if collected.is_empty() {
1142          None
1143        } else {
1144          Some(Tokens::new(collected))
1145        }
1146      },
1147      _ => None,
1148    }
1149  }
1150}
1151
1152impl<'a> From<&'a Stored> for Option<Rc<Register>> {
1153  fn from(value: &'a Stored) -> Option<Rc<Register>> {
1154    match value {
1155      Stored::Register(reg) => Some(Rc::clone(reg)),
1156      _ => None,
1157    }
1158  }
1159}
1160
1161impl<'a> From<&'a Stored> for Option<Catcode> {
1162  fn from(value: &'a Stored) -> Option<Catcode> {
1163    match value {
1164      Stored::Catcode(cc) => Some(*cc),
1165      _ => None,
1166    }
1167  }
1168}
1169
1170impl<'a> From<&'a Stored> for Option<&'a [char]> {
1171  fn from(value: &'a Stored) -> Option<&'a [char]> {
1172    match value {
1173      Stored::Chars(cc) => Some(cc),
1174      _ => None,
1175    }
1176  }
1177}
1178
1179impl From<&Stored> for Option<Rc<[Option<char>]>> {
1180  fn from(value: &Stored) -> Option<Rc<[Option<char>]>> {
1181    match value {
1182      Stored::Fontmap(cc) => Some(Rc::clone(cc)),
1183      _ => None,
1184    }
1185  }
1186}
1187impl From<Stored> for Option<Rc<[Option<char>]>> {
1188  fn from(value: Stored) -> Option<Rc<[Option<char>]>> { (&value).into() }
1189}
1190
1191impl<'a> From<&'a Stored> for Option<RegisterValue> {
1192  fn from(value: &'a Stored) -> Option<RegisterValue> {
1193    match value {
1194      Stored::Number(v) => Some(RegisterValue::Number(*v)),
1195      Stored::Dimension(v) => Some(RegisterValue::Dimension(*v)),
1196      Stored::Glue(v) => Some(RegisterValue::Glue(*v)),
1197      Stored::MuGlue(v) => Some(RegisterValue::MuGlue(*v)),
1198      Stored::Token(v) => Some(RegisterValue::Token(*v)),
1199      Stored::Tokens(v) => Some(RegisterValue::Tokens(v.clone())),
1200      _ => None,
1201    }
1202  }
1203}
1204
1205impl From<Stored> for Option<crate::Digested> {
1206  fn from(value: Stored) -> Option<crate::Digested> {
1207    match value {
1208      Stored::Digested(digested) => Some(digested),
1209      Stored::String(text) => Some(text.into()),
1210      Stored::Int(text) => Some(text.to_string().into()),
1211      _ => None,
1212    }
1213  }
1214}
1215
1216impl<'a> From<&'a Stored> for Option<crate::Digested> {
1217  fn from(value: &'a Stored) -> Option<crate::Digested> {
1218    match value {
1219      Stored::Digested(digested) => Some((*digested).clone()),
1220      Stored::String(text) => Some((*text).into()),
1221      Stored::Int(text) => Some(text.to_string().into()),
1222      _ => None,
1223    }
1224  }
1225}
1226
1227impl<'a, 'b> From<&'a &'b Stored> for Option<crate::Digested> {
1228  fn from(value: &'a &'b Stored) -> Option<crate::Digested> { (*value).into() }
1229}
1230
1231impl<'a> From<&'a Stored> for Token {
1232  fn from(value: &'a Stored) -> Token {
1233    match value {
1234      Stored::Tokens(ts) => ts.into(),
1235      Stored::Token(t) => *t,
1236      Stored::String(text) => Token {
1237        text: *text,
1238        code: Catcode::CS,
1239        #[cfg(feature = "token-locators")]
1240        loc: 0,
1241      },
1242      t => {
1243        let message = s!("dangerous cast to CS for {:?}", t);
1244        Warn!("stored", "cast", message);
1245        T_CS!(t.to_string())
1246      }, /* TODO, is this the right place to default to CS? Do we need a
1247          * custom method instead? */
1248    }
1249  }
1250}