Skip to main content

ratex_parser/functions/
char_cmd.rs

1use std::collections::HashMap;
2
3use crate::error::{ParseError, ParseResult};
4use crate::functions::{define_function_full, FunctionContext, FunctionSpec};
5use crate::parse_node::ParseNode;
6
7pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
8    define_function_full(
9        map,
10        &["\\@char"],
11        "textord",
12        1,
13        0,
14        None,
15        false,
16        true,
17        true,
18        false,
19        false,
20        handle_at_char,
21    );
22}
23
24fn handle_at_char(
25    ctx: &mut FunctionContext,
26    args: Vec<ParseNode>,
27    _opt_args: Vec<Option<ParseNode>>,
28) -> ParseResult<ParseNode> {
29    let arg = args.into_iter().next().unwrap();
30    let body = ParseNode::ord_argument(arg);
31
32    let mut number_str = String::new();
33    for node in &body {
34        if let Some(text) = node.symbol_text() {
35            number_str.push_str(text);
36        }
37    }
38
39    let code: u32 = number_str
40        .parse()
41        .map_err(|_| ParseError::msg(format!("\\@char has non-numeric argument {}", number_str)))?;
42
43    let text = char::from_u32(code)
44        .ok_or_else(|| ParseError::msg(format!("\\@char with invalid code point {}", code)))?
45        .to_string();
46
47    // Match KaTeX `src/functions/char.ts`: `\@char` always yields `textord`, even when the
48    // codepoint is also listed as `\\sum` / op-token in `symbols.js`. `\char"2211` must not
49    // become a large `Size1` operator (that glyph is much wider than KaTeX’s textord path).
50    Ok(ParseNode::TextOrd {
51        mode: ctx.parser.mode,
52        text,
53        loc: None,
54    })
55}