Skip to main content

tex_engine/commands/
primitives.rs

1/*! Methods for [`PrimitiveCommand`]s and for registering new ones */
2use crate::commands::{CommandScope, PrimitiveCommand, TeXCommand};
3use crate::engine::state::State;
4use crate::engine::{EngineReferences, EngineTypes, TeXEngine};
5use crate::prelude::Character;
6use crate::tex::nodes::boxes::{BoxInfo, TeXBox};
7use crate::tex::numerics::{MuSkip, Skip};
8use crate::utils::HMap;
9use either::Either;
10use lazy_static::lazy_static;
11use std::sync::RwLock;
12
13macro_rules! cmtodos {
14    ($engine:ident,$($name:ident),*) => {
15        $(cmtodo!($engine,$name);)*
16    };
17}
18
19macro_rules! cmstodos {
20    ($engine:ident,$($name:ident),*) => {
21        $(cmstodo!($engine,$name);)*
22    };
23}
24
25macro_rules! cmtodo {
26    ($engine:ident,$name:ident) => {{
27        let command = $crate::commands::PrimitiveCommand::SimpleExpandable(|e, _| {
28            e.general_error(format!(
29                "Not yet implemented: \\{} at {}",
30                stringify!($name),
31                crate::engine::mouth::Mouth::current_sourceref(e.mouth).display(e.filesystem)
32            ))
33        });
34        let refs = $engine.get_engine_refs();
35        refs.state
36            .register_primitive(refs.aux, stringify!($name), command);
37    }};
38}
39
40macro_rules! cmstodo {
41    ($engine:ident,$name:ident) => {{
42        let command = $crate::commands::PrimitiveCommand::Unexpandable {
43            scope: $crate::commands::CommandScope::Any,
44            apply: |e, _| {
45                e.general_error(format!(
46                    "Not yet implemented: \\{} at {}",
47                    stringify!($name),
48                    $crate::engine::mouth::Mouth::current_sourceref(e.mouth).display(e.filesystem)
49                ))
50            },
51        };
52        let refs = $engine.get_engine_refs();
53        refs.state
54            .register_primitive(refs.aux, stringify!($name), command);
55    }};
56}
57
58use crate::tex::nodes::WhatsitFunction;
59use crate::utils::errors::TeXResult;
60pub(crate) use cmstodo;
61pub(crate) use cmstodos;
62pub(crate) use cmtodo;
63pub(crate) use cmtodos;
64
65/// Creates a new expandable primitive and registers it with the engine.
66pub fn register_expandable<E: TeXEngine>(
67    engine: &mut E,
68    name: &'static str,
69    f: fn(
70        &mut EngineReferences<E::Types>,
71        &mut Vec<<E::Types as EngineTypes>::Token>,
72        <E::Types as EngineTypes>::Token,
73    ) -> TeXResult<(), E::Types>,
74) {
75    let command = PrimitiveCommand::Expandable(f);
76    let refs = engine.get_engine_refs();
77    refs.state.register_primitive(refs.aux, name, command);
78}
79
80/// Creates a new simple expandable primitive (which does not produce new tokens) and registers it with the engine.
81pub fn register_simple_expandable<E: TeXEngine>(
82    engine: &mut E,
83    name: &'static str,
84    f: fn(
85        &mut EngineReferences<E::Types>,
86        <E::Types as EngineTypes>::Token,
87    ) -> TeXResult<(), E::Types>,
88) {
89    let command = PrimitiveCommand::SimpleExpandable(f);
90    let refs = engine.get_engine_refs();
91    refs.state.register_primitive(refs.aux, name, command);
92}
93
94/// Creates a new conditional primitive and registers it with the engine.
95pub fn register_conditional<E: TeXEngine>(
96    engine: &mut E,
97    name: &'static str,
98    f: fn(
99        &mut EngineReferences<E::Types>,
100        <E::Types as EngineTypes>::Token,
101    ) -> TeXResult<bool, E::Types>,
102) {
103    let command = PrimitiveCommand::Conditional(f);
104    let refs = engine.get_engine_refs();
105    refs.state.register_primitive(refs.aux, name, command);
106}
107
108/// Creates a new unexpandable primitive and registers it with the engine.
109pub fn register_unexpandable<E: TeXEngine>(
110    engine: &mut E,
111    name: &'static str,
112    scope: CommandScope,
113    f: fn(
114        &mut EngineReferences<E::Types>,
115        <E::Types as EngineTypes>::Token,
116    ) -> TeXResult<(), E::Types>,
117) {
118    let command = PrimitiveCommand::Unexpandable { scope, apply: f };
119    let refs = engine.get_engine_refs();
120    refs.state.register_primitive(refs.aux, name, command)
121}
122
123/// Creates a new primitive named integer value and registers it with the engine.
124pub fn register_primitive_int<E: TeXEngine>(engine: &mut E, names: &[&'static str]) {
125    let refs = engine.get_engine_refs();
126    for name in names {
127        refs.state
128            .register_primitive(refs.aux, name, PrimitiveCommand::PrimitiveInt);
129    }
130}
131
132/// Creates a new primitive command that yields (and optionally assigns) an
133///integer value and registers it with the engine.
134pub fn register_int<E: TeXEngine>(
135    engine: &mut E,
136    name: &'static str,
137    read: fn(
138        &mut EngineReferences<E::Types>,
139        <E::Types as EngineTypes>::Token,
140    ) -> TeXResult<<E::Types as EngineTypes>::Int, E::Types>,
141    assign: Option<
142        for<'a, 'b> fn(
143            &'a mut EngineReferences<'b, E::Types>,
144            <E::Types as EngineTypes>::Token,
145            bool,
146        ) -> TeXResult<(), E::Types>,
147    >,
148) {
149    let refs = engine.get_engine_refs();
150    let command = PrimitiveCommand::Int { read, assign };
151    refs.state.register_primitive(refs.aux, name, command);
152}
153
154/// Creates a new primitive command that yields (and optionally assigns) a
155/// dimension value and registers it with the engine.
156pub fn register_dim<E: TeXEngine>(
157    engine: &mut E,
158    name: &'static str,
159    read: fn(
160        &mut EngineReferences<E::Types>,
161        <E::Types as EngineTypes>::Token,
162    ) -> TeXResult<<E::Types as EngineTypes>::Dim, E::Types>,
163    assign: Option<
164        for<'a, 'b> fn(
165            &'a mut EngineReferences<'b, E::Types>,
166            <E::Types as EngineTypes>::Token,
167            bool,
168        ) -> TeXResult<(), E::Types>,
169    >,
170) {
171    let refs = engine.get_engine_refs();
172    let command = PrimitiveCommand::Dim { read, assign };
173    refs.state.register_primitive(refs.aux, name, command);
174}
175
176/// Creates a new primitive command that yields (and optionally assigns) a
177/// skip value and registers it with the engine.
178pub fn register_skip<E: TeXEngine>(
179    engine: &mut E,
180    name: &'static str,
181    read: fn(
182        &mut EngineReferences<E::Types>,
183        <E::Types as EngineTypes>::Token,
184    ) -> TeXResult<Skip<<E::Types as EngineTypes>::Dim>, E::Types>,
185    assign: Option<
186        for<'a, 'b> fn(
187            &'a mut EngineReferences<'b, E::Types>,
188            <E::Types as EngineTypes>::Token,
189            bool,
190        ) -> TeXResult<(), E::Types>,
191    >,
192) {
193    let refs = engine.get_engine_refs();
194    let command = PrimitiveCommand::Skip { read, assign };
195    refs.state.register_primitive(refs.aux, name, command);
196}
197
198/// Creates a new primitive command that yields (and optionally assigns) a
199/// muskip value and registers it with the engine.
200pub fn register_muskip<E: TeXEngine>(
201    engine: &mut E,
202    name: &'static str,
203    read: fn(
204        &mut EngineReferences<E::Types>,
205        <E::Types as EngineTypes>::Token,
206    ) -> TeXResult<MuSkip<<E::Types as EngineTypes>::MuDim>, E::Types>,
207    assign: Option<
208        for<'a, 'b> fn(
209            &'a mut EngineReferences<'b, E::Types>,
210            <E::Types as EngineTypes>::Token,
211            bool,
212        ) -> TeXResult<(), E::Types>,
213    >,
214) {
215    let refs = engine.get_engine_refs();
216    let command = PrimitiveCommand::MuSkip { read, assign };
217    refs.state.register_primitive(refs.aux, name, command);
218}
219
220/// Creates a new primitive command that yields (and optionally assigns) a
221/// font value and registers it with the engine.
222pub fn register_font<E: TeXEngine>(
223    engine: &mut E,
224    name: &'static str,
225    read: fn(
226        &mut EngineReferences<E::Types>,
227        <E::Types as EngineTypes>::Token,
228    ) -> TeXResult<<E::Types as EngineTypes>::Font, E::Types>,
229    assign: Option<
230        for<'a, 'b> fn(
231            &'a mut EngineReferences<'b, E::Types>,
232            <E::Types as EngineTypes>::Token,
233            bool,
234        ) -> TeXResult<(), E::Types>,
235    >,
236) {
237    let refs = engine.get_engine_refs();
238    let command = PrimitiveCommand::FontCmd { read, assign };
239    refs.state.register_primitive(refs.aux, name, command);
240}
241
242/// Creates a new primitive command that yields a
243/// box and registers it with the engine.
244pub fn register_box<E: TeXEngine>(
245    engine: &mut E,
246    name: &'static str,
247    read: fn(
248        &mut EngineReferences<E::Types>,
249        <E::Types as EngineTypes>::Token,
250    ) -> TeXResult<Either<Option<TeXBox<E::Types>>, BoxInfo<E::Types>>, E::Types>,
251) {
252    let refs = engine.get_engine_refs();
253    let command = PrimitiveCommand::Box(read);
254    refs.state.register_primitive(refs.aux, name, command);
255}
256
257/// Creates a new primitive assignment command.
258pub fn register_assignment<E: TeXEngine>(
259    engine: &mut E,
260    name: &'static str,
261    assign: for<'a, 'b> fn(
262        &'a mut EngineReferences<'b, E::Types>,
263        <E::Types as EngineTypes>::Token,
264        bool,
265    ) -> TeXResult<(), E::Types>,
266) {
267    let refs = engine.get_engine_refs();
268    let command = PrimitiveCommand::Assignment(assign);
269    refs.state.register_primitive(refs.aux, name, command);
270}
271
272/// Creates a new primitive named dimension and registers it with the engine.
273pub fn register_primitive_dim<E: TeXEngine>(engine: &mut E, names: &[&'static str]) {
274    let refs = engine.get_engine_refs();
275    for name in names {
276        refs.state
277            .register_primitive(refs.aux, name, PrimitiveCommand::PrimitiveDim);
278    }
279}
280
281/// Creates a new primitive named skip and registers it with the engine.
282pub fn register_primitive_skip<E: TeXEngine>(engine: &mut E, names: &[&'static str]) {
283    let refs = engine.get_engine_refs();
284    for name in names {
285        refs.state
286            .register_primitive(refs.aux, name, PrimitiveCommand::PrimitiveSkip);
287    }
288}
289
290/// Creates a new primitive named skip and registers it with the engine.
291pub fn register_primitive_muskip<E: TeXEngine>(engine: &mut E, names: &[&'static str]) {
292    let refs = engine.get_engine_refs();
293    for name in names {
294        refs.state
295            .register_primitive(refs.aux, name, PrimitiveCommand::PrimitiveMuSkip);
296    }
297}
298
299/// Creates a new primitive named token register and registers it with the engine.
300pub fn register_primitive_toks<E: TeXEngine>(engine: &mut E, names: &[&'static str]) {
301    let refs = engine.get_engine_refs();
302    for name in names {
303        refs.state
304            .register_primitive(refs.aux, name, PrimitiveCommand::PrimitiveToks);
305    }
306}
307
308/// Creates a new "Whatsit" primitive and registers it with the engine.
309pub fn register_whatsit<E: TeXEngine>(
310    engine: &mut E,
311    name: &'static str,
312    get: fn(
313        &mut EngineReferences<E::Types>,
314        <E::Types as EngineTypes>::Token,
315    ) -> TeXResult<Option<Box<WhatsitFunction<E::Types>>>, E::Types>,
316    immediate: fn(
317        &mut EngineReferences<E::Types>,
318        <E::Types as EngineTypes>::Token,
319    ) -> TeXResult<(), E::Types>,
320    the: Option<
321        fn(
322            &mut EngineReferences<E::Types>,
323            <E::Types as EngineTypes>::Token,
324        ) -> TeXResult<Vec<<E::Types as EngineTypes>::Token>, E::Types>,
325    >,
326) {
327    let command = PrimitiveCommand::Whatsit {
328        get,
329        immediate,
330        the,
331    };
332    let refs = engine.get_engine_refs();
333    refs.state.register_primitive(refs.aux, name, command);
334}
335
336/// A store for all primitive commands.
337#[derive(Clone)]
338pub struct PrimitiveCommands<ET: EngineTypes> {
339    commands: Vec<TeXCommand<ET>>,
340    names: HMap<&'static str, u16>,
341}
342impl<ET: EngineTypes> Default for PrimitiveCommands<ET> {
343    fn default() -> Self {
344        Self {
345            commands: Vec::new(),
346            names: HMap::default(),
347        }
348    }
349}
350impl<ET: EngineTypes> PrimitiveCommands<ET> {
351    /// Registers a new primitive command.
352    pub fn register(
353        &mut self,
354        name: &'static str,
355        cmd: PrimitiveCommand<ET>,
356    ) -> PrimitiveIdentifier {
357        let id = PRIMITIVES.new_id(name);
358        let idx = id.as_u16() as usize;
359        if idx >= self.commands.len() {
360            self.commands.resize(
361                idx + 1,
362                TeXCommand::Primitive {
363                    name: id,
364                    cmd: PrimitiveCommand::Relax,
365                },
366            );
367        }
368        self.commands[idx] = TeXCommand::Primitive { name: id, cmd };
369        self.names.insert(name, idx as u16);
370        id
371    }
372    /// Return the primitive command with the given identifier.
373    pub fn get_id(&self, id: PrimitiveIdentifier) -> Option<&TeXCommand<ET>> {
374        let idx = id.as_u16() as usize;
375        self.commands.get(idx)
376    }
377    /// Return the [`PrimitiveIdentifier`] of the primitive command with the given name.
378    pub fn get_name(&self, s: &str) -> Option<PrimitiveIdentifier> {
379        self.names
380            .get(s)
381            .and_then(|&u| PrimitiveIdentifier::try_from_u16(u))
382    }
383}
384
385/// We always intern the names for primitive commands/macros, for efficiency; in particular for equality checks.
386/// Uses `u16` internally, i.e. allowing for up to 65536 primitives.
387///
388/// It is never necessary to instantiate a new [`PrimitiveInterner`]; instead, use the global [`PRIMITIVES`](static@PRIMITIVES) instance.
389pub struct PrimitiveInterner {
390    interner: RwLock<
391        string_interner::StringInterner<
392            string_interner::backend::StringBackend<string_interner::symbol::SymbolU16>,
393            rustc_hash::FxBuildHasher,
394        >,
395    >,
396    pub globaldefs: PrimitiveIdentifier,
397    pub relax: PrimitiveIdentifier,
398    pub mag: PrimitiveIdentifier,
399    pub fam: PrimitiveIdentifier,
400    pub ifcase: PrimitiveIdentifier,
401    pub tracingifs: PrimitiveIdentifier,
402    pub tracingassigns: PrimitiveIdentifier,
403    pub tracingcommands: PrimitiveIdentifier,
404    pub tracinggroups: PrimitiveIdentifier,
405    pub tracingrestores: PrimitiveIdentifier,
406    pub r#else: PrimitiveIdentifier,
407    pub fi: PrimitiveIdentifier,
408    pub or: PrimitiveIdentifier,
409    pub global: PrimitiveIdentifier,
410    pub long: PrimitiveIdentifier,
411    pub outer: PrimitiveIdentifier,
412    pub protected: PrimitiveIdentifier,
413    pub def: PrimitiveIdentifier,
414    pub edef: PrimitiveIdentifier,
415    pub xdef: PrimitiveIdentifier,
416    pub gdef: PrimitiveIdentifier,
417    pub everyeof: PrimitiveIdentifier,
418    pub everyhbox: PrimitiveIdentifier,
419    pub everyvbox: PrimitiveIdentifier,
420    pub everyjob: PrimitiveIdentifier,
421    pub count: PrimitiveIdentifier,
422    pub noexpand: PrimitiveIdentifier,
423    pub unexpanded: PrimitiveIdentifier,
424    pub endcsname: PrimitiveIdentifier,
425    pub the: PrimitiveIdentifier,
426    pub toks: PrimitiveIdentifier,
427    pub vsize: PrimitiveIdentifier,
428    pub output: PrimitiveIdentifier,
429    pub badness: PrimitiveIdentifier,
430    pub outputpenalty: PrimitiveIdentifier,
431    pub dimen: PrimitiveIdentifier,
432    pub skip: PrimitiveIdentifier,
433    pub everypar: PrimitiveIdentifier,
434    pub indent: PrimitiveIdentifier,
435    pub noindent: PrimitiveIdentifier,
436    pub hangindent: PrimitiveIdentifier,
437    pub hangafter: PrimitiveIdentifier,
438    pub leftskip: PrimitiveIdentifier,
439    pub rightskip: PrimitiveIdentifier,
440    pub hsize: PrimitiveIdentifier,
441    pub pdfpagewidth: PrimitiveIdentifier,
442    pub everymath: PrimitiveIdentifier,
443    pub everydisplay: PrimitiveIdentifier,
444    pub char: PrimitiveIdentifier,
445    pub tabskip: PrimitiveIdentifier,
446    pub cr: PrimitiveIdentifier,
447    pub crcr: PrimitiveIdentifier,
448    pub everycr: PrimitiveIdentifier,
449    pub span: PrimitiveIdentifier,
450    pub noalign: PrimitiveIdentifier,
451    pub omit: PrimitiveIdentifier,
452    pub baselineskip: PrimitiveIdentifier,
453    pub lineskip: PrimitiveIdentifier,
454    pub lineskiplimit: PrimitiveIdentifier,
455    pub parindent: PrimitiveIdentifier,
456    pub hrule: PrimitiveIdentifier,
457    pub vrule: PrimitiveIdentifier,
458    pub vskip: PrimitiveIdentifier,
459    pub hskip: PrimitiveIdentifier,
460    pub vfil: PrimitiveIdentifier,
461    pub hfil: PrimitiveIdentifier,
462    pub vfill: PrimitiveIdentifier,
463    pub hfill: PrimitiveIdentifier,
464    pub parskip: PrimitiveIdentifier,
465    pub delimiter: PrimitiveIdentifier,
466    pub abovedisplayskip: PrimitiveIdentifier,
467    pub belowdisplayskip: PrimitiveIdentifier,
468    pub iffalse: PrimitiveIdentifier,
469    pub iftrue: PrimitiveIdentifier,
470    pub year: PrimitiveIdentifier,
471    pub month: PrimitiveIdentifier,
472    pub day: PrimitiveIdentifier,
473    pub time: PrimitiveIdentifier,
474    pub mathchar: PrimitiveIdentifier,
475}
476impl PrimitiveInterner {
477    fn new() -> Self {
478        let mut interner = string_interner::StringInterner::<
479            string_interner::backend::StringBackend<string_interner::symbol::SymbolU16>,
480            rustc_hash::FxBuildHasher,
481        >::new();
482        let globaldefs = PrimitiveIdentifier(interner.get_or_intern_static("globaldefs"));
483        let relax = PrimitiveIdentifier(interner.get_or_intern_static("relax"));
484        let mag = PrimitiveIdentifier(interner.get_or_intern_static("mag"));
485        let fam = PrimitiveIdentifier(interner.get_or_intern_static("fam"));
486        let ifcase = PrimitiveIdentifier(interner.get_or_intern_static("ifcase"));
487        let tracingifs = PrimitiveIdentifier(interner.get_or_intern_static("tracingifs"));
488        let tracingassigns = PrimitiveIdentifier(interner.get_or_intern_static("tracingassigns"));
489        let tracingcommands = PrimitiveIdentifier(interner.get_or_intern_static("tracingcommands"));
490        let tracinggroups = PrimitiveIdentifier(interner.get_or_intern_static("tracinggroups"));
491        let tracingrestores = PrimitiveIdentifier(interner.get_or_intern_static("tracingrestores"));
492        let r#else = PrimitiveIdentifier(interner.get_or_intern_static("else"));
493        let fi = PrimitiveIdentifier(interner.get_or_intern_static("fi"));
494        let or = PrimitiveIdentifier(interner.get_or_intern_static("or"));
495        let global = PrimitiveIdentifier(interner.get_or_intern_static("global"));
496        let long = PrimitiveIdentifier(interner.get_or_intern_static("long"));
497        let outer = PrimitiveIdentifier(interner.get_or_intern_static("outer"));
498        let protected = PrimitiveIdentifier(interner.get_or_intern_static("protected"));
499        let def = PrimitiveIdentifier(interner.get_or_intern_static("def"));
500        let edef = PrimitiveIdentifier(interner.get_or_intern_static("edef"));
501        let xdef = PrimitiveIdentifier(interner.get_or_intern_static("xdef"));
502        let gdef = PrimitiveIdentifier(interner.get_or_intern_static("gdef"));
503        let everyeof = PrimitiveIdentifier(interner.get_or_intern_static("everyeof"));
504        let everyhbox = PrimitiveIdentifier(interner.get_or_intern_static("everyhbox"));
505        let everyvbox = PrimitiveIdentifier(interner.get_or_intern_static("everyvbox"));
506        let everyjob = PrimitiveIdentifier(interner.get_or_intern_static("everyjob"));
507        let count = PrimitiveIdentifier(interner.get_or_intern_static("count"));
508        let noexpand = PrimitiveIdentifier(interner.get_or_intern_static("noexpand"));
509        let endcsname = PrimitiveIdentifier(interner.get_or_intern_static("endcsname"));
510        let unexpanded = PrimitiveIdentifier(interner.get_or_intern_static("unexpanded"));
511        let the = PrimitiveIdentifier(interner.get_or_intern_static("the"));
512        let toks = PrimitiveIdentifier(interner.get_or_intern_static("toks"));
513        let vsize = PrimitiveIdentifier(interner.get_or_intern_static("vsize"));
514        let output = PrimitiveIdentifier(interner.get_or_intern_static("output"));
515        let badness = PrimitiveIdentifier(interner.get_or_intern_static("badness"));
516        let outputpenalty = PrimitiveIdentifier(interner.get_or_intern_static("outputpenalty"));
517        let dimen = PrimitiveIdentifier(interner.get_or_intern_static("dimen"));
518        let skip = PrimitiveIdentifier(interner.get_or_intern_static("skip"));
519        let everypar = PrimitiveIdentifier(interner.get_or_intern_static("everypar"));
520        let indent = PrimitiveIdentifier(interner.get_or_intern_static("indent"));
521        let noindent = PrimitiveIdentifier(interner.get_or_intern_static("noindent"));
522        let hangindent = PrimitiveIdentifier(interner.get_or_intern_static("hangindent"));
523        let hangafter = PrimitiveIdentifier(interner.get_or_intern_static("hangafter"));
524        let leftskip = PrimitiveIdentifier(interner.get_or_intern_static("leftskip"));
525        let rightskip = PrimitiveIdentifier(interner.get_or_intern_static("rightskip"));
526        let hsize = PrimitiveIdentifier(interner.get_or_intern_static("hsize"));
527        let pdfpagewidth = PrimitiveIdentifier(interner.get_or_intern_static("pdfpagewidth"));
528        let everymath = PrimitiveIdentifier(interner.get_or_intern_static("everymath"));
529        let everydisplay = PrimitiveIdentifier(interner.get_or_intern_static("everydisplay"));
530        let char = PrimitiveIdentifier(interner.get_or_intern_static("char"));
531        let tabskip = PrimitiveIdentifier(interner.get_or_intern_static("tabskip"));
532        let cr = PrimitiveIdentifier(interner.get_or_intern_static("cr"));
533        let crcr = PrimitiveIdentifier(interner.get_or_intern_static("crcr"));
534        let everycr = PrimitiveIdentifier(interner.get_or_intern_static("everycr"));
535        let span = PrimitiveIdentifier(interner.get_or_intern_static("span"));
536        let noalign = PrimitiveIdentifier(interner.get_or_intern_static("noalign"));
537        let omit = PrimitiveIdentifier(interner.get_or_intern_static("omit"));
538        let baselineskip = PrimitiveIdentifier(interner.get_or_intern_static("baselineskip"));
539        let lineskip = PrimitiveIdentifier(interner.get_or_intern_static("lineskip"));
540        let lineskiplimit = PrimitiveIdentifier(interner.get_or_intern_static("lineskiplimit"));
541        let parindent = PrimitiveIdentifier(interner.get_or_intern_static("parindent"));
542        let hrule = PrimitiveIdentifier(interner.get_or_intern_static("hrule"));
543        let vrule = PrimitiveIdentifier(interner.get_or_intern_static("vrule"));
544        let vskip = PrimitiveIdentifier(interner.get_or_intern_static("vskip"));
545        let hskip = PrimitiveIdentifier(interner.get_or_intern_static("hskip"));
546        let vfil = PrimitiveIdentifier(interner.get_or_intern_static("vfil"));
547        let hfil = PrimitiveIdentifier(interner.get_or_intern_static("hfil"));
548        let vfill = PrimitiveIdentifier(interner.get_or_intern_static("vfill"));
549        let hfill = PrimitiveIdentifier(interner.get_or_intern_static("hfill"));
550        let parskip = PrimitiveIdentifier(interner.get_or_intern_static("parskip"));
551        let delimiter = PrimitiveIdentifier(interner.get_or_intern_static("delimiter"));
552        let abovedisplayskip =
553            PrimitiveIdentifier(interner.get_or_intern_static("abovedisplayskip"));
554        let belowdisplayskip =
555            PrimitiveIdentifier(interner.get_or_intern_static("belowdisplayskip"));
556        let iffalse = PrimitiveIdentifier(interner.get_or_intern_static("iffalse"));
557        let iftrue = PrimitiveIdentifier(interner.get_or_intern_static("iftrue"));
558        let year = PrimitiveIdentifier(interner.get_or_intern_static("year"));
559        let month = PrimitiveIdentifier(interner.get_or_intern_static("month"));
560        let day = PrimitiveIdentifier(interner.get_or_intern_static("day"));
561        let time = PrimitiveIdentifier(interner.get_or_intern_static("time"));
562        let mathchar = PrimitiveIdentifier(interner.get_or_intern_static("mathchar"));
563        PrimitiveInterner {
564            interner: RwLock::new(interner),
565            globaldefs,
566            relax,
567            mag,
568            fam,
569            ifcase,
570            tracingifs,
571            tracingassigns,
572            tracingcommands,
573            tracinggroups,
574            r#else,
575            fi,
576            or,
577            global,
578            long,
579            outer,
580            protected,
581            def,
582            edef,
583            xdef,
584            gdef,
585            everyeof,
586            count,
587            tracingrestores,
588            noexpand,
589            endcsname,
590            unexpanded,
591            the,
592            toks,
593            everyhbox,
594            everyvbox,
595            everyjob,
596            vsize,
597            output,
598            badness,
599            outputpenalty,
600            dimen,
601            skip,
602            everypar,
603            indent,
604            noindent,
605            hangindent,
606            hangafter,
607            leftskip,
608            rightskip,
609            hsize,
610            pdfpagewidth,
611            everymath,
612            everydisplay,
613            char,
614            tabskip,
615            cr,
616            crcr,
617            everycr,
618            span,
619            noalign,
620            omit,
621            baselineskip,
622            lineskip,
623            lineskiplimit,
624            parindent,
625            hrule,
626            vrule,
627            vskip,
628            hskip,
629            vfil,
630            hfil,
631            vfill,
632            hfill,
633            parskip,
634            delimiter,
635            abovedisplayskip,
636            belowdisplayskip,
637            iffalse,
638            iftrue,
639            year,
640            month,
641            day,
642            time,
643            mathchar
644        }
645    }
646
647    fn new_id(&self, s: &'static str) -> PrimitiveIdentifier {
648        let mut lock = self.interner.write().unwrap();
649        PrimitiveIdentifier(lock.get_or_intern_static(s))
650    }
651}
652lazy_static!(
653    /// The global [`PrimitiveInterner`].
654    pub static ref PRIMITIVES:PrimitiveInterner = PrimitiveInterner::new();
655);
656
657/// A `Copy` identifier for a primitive command. Small and fast to compare.
658#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
659pub struct PrimitiveIdentifier(string_interner::symbol::SymbolU16);
660impl PrimitiveIdentifier {
661    /// Returns a struct implementing [`Display`](std::fmt::Display) for the given [`PrimitiveIdentifier`], and
662    /// optional `\escapechar` that will be prefixed - e.g.
663    /// `println!(`[`PRIMITIVES`](static@PRIMITIVES)`.the.`[`display`](Self::display)`(Some('\\'))`
664    /// will print `\the`.
665    pub fn display<C: Character>(self, escapechar: Option<C>) -> impl std::fmt::Display {
666        PrintableIdentifier(self, escapechar)
667    }
668    /// Returns the `u16` value of the identifier.
669    pub fn as_u16(&self) -> u16 {
670        use string_interner::Symbol;
671        self.0.to_usize() as u16
672    }
673    /// Returns the identifier for the given `u16` value, if it exists.
674    pub fn try_from_u16(u: u16) -> Option<Self> {
675        use string_interner::Symbol;
676        string_interner::symbol::SymbolU16::try_from_usize(u as usize).map(PrimitiveIdentifier)
677    }
678}
679
680struct PrintableIdentifier<C: Character>(PrimitiveIdentifier, Option<C>);
681impl<C: Character> std::fmt::Display for PrintableIdentifier<C> {
682    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
683        let lock = PRIMITIVES.interner.read().unwrap();
684        match self.1 {
685            None => (),
686            Some(c) => c.display_fmt(f),
687        }
688        write!(f, "{}", lock.resolve(self.0 .0).unwrap())
689    }
690}