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 crate::ast::{Measurement, ParseNode};
use crate::error::ParseError;
use crate::function_registry::{ArgType, FunctionContext, FunctionParser, FunctionSpec};

use super::require_function_arg;

pub(crate) fn kern_spec() -> FunctionSpec {
    FunctionSpec {
        names: vec![
            "\\kern".to_string(),
            "\\mkern".to_string(),
            "\\hskip".to_string(),
            "\\mskip".to_string(),
        ],
        num_args: 1,
        arg_types: vec![ArgType::SizeArg],
        primitive: true,
        allowed_in_text: true,
        handler: Some(kern_handler),
        ..Default::default()
    }
}

fn kern_dimension(args: &[ParseNode], func_name: &str) -> Result<Measurement, ParseError> {
    match require_function_arg(args, 0, func_name)? {
        ParseNode::Size { value, .. } => Ok(value),
        _ => Err(ParseError::InternalInvariant {
            message: format!("Expected size argument for {func_name}"),
        }),
    }
}

fn is_math_spacing_function(func_name: &str) -> bool {
    func_name == "\\mkern" || func_name == "\\mskip"
}

fn kern_handler(
    parser: &mut dyn FunctionParser,
    context: &FunctionContext,
    args: &[ParseNode],
    _opt_args: &[Option<ParseNode>],
) -> Result<ParseNode, ParseError> {
    let dimension = kern_dimension(args, &context.func_name)?;
    let uses_mu = dimension.unit == "mu";
    if is_math_spacing_function(&context.func_name) {
        if !uses_mu {
            parser.report_nonstrict(
                "mathVsTextUnits",
                &format!(
                    "LaTeX's {} supports only mu units, not {} units",
                    context.func_name, dimension.unit
                ),
                context.token.as_ref(),
            )?;
        }
        if context.mode != crate::ast::Mode::Math {
            parser.report_nonstrict(
                "mathVsTextUnits",
                &format!("LaTeX's {} works only in math mode", context.func_name),
                context.token.as_ref(),
            )?;
        }
    } else if uses_mu {
        parser.report_nonstrict(
            "mathVsTextUnits",
            &format!("LaTeX's {} doesn't support mu units", context.func_name),
            context.token.as_ref(),
        )?;
    }
    Ok(ParseNode::Kern {
        mode: context.mode,
        dimension,
    })
}