retex-engine 1.3.0-alpha.2

The TeX typesetting engine for retex
Documentation
//! Lookup tables over LuaTeX's primitives ([`crate::primitives_gen`]).

use std::collections::HashMap;
use std::sync::OnceLock;

use crate::cmd::Cmd;
use crate::primitives_gen::{Group, PRIMITIVES, Primitive};

struct Tables {
    by_name: HashMap<&'static [u8], &'static Primitive>,
    by_code: HashMap<(Cmd, u32), &'static str>,
}

fn tables() -> &'static Tables {
    static T: OnceLock<Tables> = OnceLock::new();
    T.get_or_init(|| {
        let mut by_name = HashMap::with_capacity(PRIMITIVES.len());
        let mut by_code = HashMap::with_capacity(PRIMITIVES.len());
        for p in PRIMITIVES {
            by_name.insert(p.name.as_bytes(), p);
            if p.cmd == Cmd::UndefinedCs {
                continue;
            }
            // When several names share a code, LuaTeX prints the one defined
            // last; `\-` and `\explicitdiscretionary` is the only such pair and
            // the longer name wins.
            by_code
                .entry((p.cmd, p.chr))
                .and_modify(|n: &mut &'static str| {
                    if p.name.len() > n.len() {
                        *n = p.name;
                    }
                })
                .or_insert(p.name);
        }
        Tables { by_name, by_code }
    })
}

/// The primitive with the given name, if any.
pub fn lookup(name: &[u8]) -> Option<&'static Primitive> {
    tables().by_name.get(name).copied()
}

/// The printed name of the primitive with this command and modifier.
pub fn name_of(cmd: Cmd, chr: u32) -> Option<&'static str> {
    tables().by_code.get(&(cmd, chr)).copied()
}

/// The primitives of the given `tex.extraprimitives` group.
pub fn group(g: Group) -> impl Iterator<Item = &'static Primitive> {
    PRIMITIVES.iter().filter(move |p| p.group == g)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn lookups() {
        let p = lookup(b"relax").unwrap();
        assert_eq!(p.cmd, Cmd::Relax);
        assert_eq!(
            name_of(Cmd::Discretionary, 1),
            Some("explicitdiscretionary")
        );
        assert_eq!(name_of(Cmd::IfTest, 12), Some("ifx"));
        assert_eq!(group(Group::Core).count(), 1);
        assert_eq!(group(Group::Tex).count(), 340);
    }
}