katex-parser 0.1.0

A port of the KaTeX parser: lexing and parsing LaTeX math expressions with macro expansion into a typed AST, plus a Unicode rendering backend
Documentation
use std::collections::HashMap;
use std::sync::OnceLock;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Whether a Unicode script is a subscript or superscript.
pub enum UnicodeScriptKind {
    UnicodeSubscript,
    UnicodeSuperscript,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnicodeScript {
    pub replacement: String,
    pub kind: UnicodeScriptKind,
}

fn unicode_scripts() -> &'static HashMap<String, UnicodeScript> {
    static TABLE: OnceLock<HashMap<String, UnicodeScript>> = OnceLock::new();
    TABLE.get_or_init(|| {
        let mut map = HashMap::new();
        fn sub(map: &mut HashMap<String, UnicodeScript>, c: &str, r: &str) {
            map.insert(
                c.to_string(),
                UnicodeScript {
                    replacement: r.to_string(),
                    kind: UnicodeScriptKind::UnicodeSubscript,
                },
            );
        }
        fn sup(map: &mut HashMap<String, UnicodeScript>, c: &str, r: &str) {
            map.insert(
                c.to_string(),
                UnicodeScript {
                    replacement: r.to_string(),
                    kind: UnicodeScriptKind::UnicodeSuperscript,
                },
            );
        }
        sub(&mut map, "", "+");
        sub(&mut map, "", "-");
        sub(&mut map, "", "=");
        sub(&mut map, "", "(");
        sub(&mut map, "", ")");
        sub(&mut map, "", "0");
        sub(&mut map, "", "1");
        sub(&mut map, "", "2");
        sub(&mut map, "", "3");
        sub(&mut map, "", "4");
        sub(&mut map, "", "5");
        sub(&mut map, "", "6");
        sub(&mut map, "", "7");
        sub(&mut map, "", "8");
        sub(&mut map, "", "9");
        sub(&mut map, "", "a");
        sub(&mut map, "", "e");
        sub(&mut map, "", "h");
        sub(&mut map, "", "i");
        sub(&mut map, "", "j");
        sub(&mut map, "", "k");
        sub(&mut map, "", "l");
        sub(&mut map, "", "m");
        sub(&mut map, "", "n");
        sub(&mut map, "", "o");
        sub(&mut map, "", "p");
        sub(&mut map, "", "r");
        sub(&mut map, "", "s");
        sub(&mut map, "", "t");
        sub(&mut map, "", "u");
        sub(&mut map, "", "v");
        sub(&mut map, "", "x");
        sub(&mut map, "", "β");
        sub(&mut map, "", "γ");
        sub(&mut map, "", "ρ");
        sub(&mut map, "", "ϕ");
        sub(&mut map, "", "χ");
        sup(&mut map, "", "+");
        sup(&mut map, "", "-");
        sup(&mut map, "", "=");
        sup(&mut map, "", "(");
        sup(&mut map, "", ")");
        sup(&mut map, "", "0");
        sup(&mut map, "¹", "1");
        sup(&mut map, "²", "2");
        sup(&mut map, "³", "3");
        sup(&mut map, "", "4");
        sup(&mut map, "", "5");
        sup(&mut map, "", "6");
        sup(&mut map, "", "7");
        sup(&mut map, "", "8");
        sup(&mut map, "", "9");
        sup(&mut map, "", "A");
        sup(&mut map, "", "B");
        sup(&mut map, "", "D");
        sup(&mut map, "", "E");
        sup(&mut map, "", "G");
        sup(&mut map, "", "H");
        sup(&mut map, "", "I");
        sup(&mut map, "", "J");
        sup(&mut map, "", "K");
        sup(&mut map, "", "L");
        sup(&mut map, "", "M");
        sup(&mut map, "", "N");
        sup(&mut map, "", "O");
        sup(&mut map, "", "P");
        sup(&mut map, "ᴿ", "R");
        sup(&mut map, "", "T");
        sup(&mut map, "", "U");
        sup(&mut map, "", "V");
        sup(&mut map, "", "W");
        sup(&mut map, "", "a");
        sup(&mut map, "", "b");
        sup(&mut map, "", "c");
        sup(&mut map, "", "d");
        sup(&mut map, "", "e");
        sup(&mut map, "", "f");
        sup(&mut map, "", "g");
        sup(&mut map, "ʰ", "h");
        sup(&mut map, "", "i");
        sup(&mut map, "ʲ", "j");
        sup(&mut map, "", "k");
        sup(&mut map, "ˡ", "l");
        sup(&mut map, "", "m");
        sup(&mut map, "", "n");
        sup(&mut map, "", "o");
        sup(&mut map, "", "p");
        sup(&mut map, "ʳ", "r");
        sup(&mut map, "ˢ", "s");
        sup(&mut map, "", "t");
        sup(&mut map, "", "u");
        sup(&mut map, "", "v");
        sup(&mut map, "ʷ", "w");
        sup(&mut map, "ˣ", "x");
        sup(&mut map, "ʸ", "y");
        sup(&mut map, "", "z");
        sup(&mut map, "", "β");
        sup(&mut map, "", "γ");
        sup(&mut map, "", "δ");
        sup(&mut map, "", "ϕ");
        sup(&mut map, "", "χ");
        sup(&mut map, "ᶿ", "θ");
        map
    })
}

pub(crate) fn lookup_unicode_script(text: &str) -> Option<&'static UnicodeScript> {
    unicode_scripts().get(text)
}

/// Returns the Unicode superscript or subscript character for `replacement`.
pub fn unicode_script_character(kind: UnicodeScriptKind, replacement: &str) -> Option<String> {
    unicode_scripts()
        .iter()
        .find(|(_, script)| script.kind == kind && script.replacement == replacement)
        .map(|(character, _)| character.clone())
}

/// True when the codepoint falls within one of the supported Unicode script
/// blocks. Mirrors KaTeX's `supportedCodepoint` (unicodeScripts.ts).
pub fn supported_codepoint(code: u32) -> bool {
    (0x0100..=0x024F).contains(&code) || // Latin Extended-A/B
    (0x0300..=0x036F).contains(&code) || // Combining Diacritical marks
    (0x0400..=0x04FF).contains(&code) || // Cyrillic
    (0x0530..=0x058F).contains(&code) || // Armenian
    (0x0900..=0x109F).contains(&code) || // Brahmic
    (0x10A0..=0x10FF).contains(&code) || // Georgian
    (0x3000..=0x30FF).contains(&code) || // CJK symbols, Hiragana, Katakana
    (0x4E00..=0x9FAF).contains(&code) || // CJK ideograms
    (0xFF00..=0xFF60).contains(&code) || // Fullwidth punctuation
    (0xAC00..=0xD7AF).contains(&code) // Hangul
}