Skip to main content

ratex_parser/functions/
mclass.rs

1use std::collections::HashMap;
2
3use crate::error::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        &[
11            "\\mathord", "\\mathbin", "\\mathrel", "\\mathopen",
12            "\\mathclose", "\\mathpunct", "\\mathinner",
13        ],
14        "mclass",
15        1, 0, None,
16        false, false, true, false, true,
17        handle_mclass,
18    );
19
20    define_function_full(
21        map,
22        &["\\stackrel", "\\overset", "\\underset"],
23        "mclass",
24        2, 0, None,
25        false, false, true, false, false,
26        handle_stackrel,
27    );
28
29    // \@binrel{x}{body} — infer mbin/mrel/mord from x, wrap body
30    define_function_full(
31        map,
32        &["\\@binrel"],
33        "mclass",
34        2, 0, None,
35        false, false, true, false, false,
36        handle_binrel,
37    );
38}
39
40fn handle_mclass(
41    ctx: &mut FunctionContext,
42    args: Vec<ParseNode>,
43    _opt_args: Vec<Option<ParseNode>>,
44) -> ParseResult<ParseNode> {
45    let mclass = format!("m{}", &ctx.func_name[5..]);
46    let body = ParseNode::ord_argument(args.into_iter().next().unwrap());
47    let is_char_box = body.len() == 1 && body[0].is_symbol_node();
48
49    Ok(ParseNode::MClass {
50        mode: ctx.parser.mode,
51        mclass,
52        body,
53        is_character_box: is_char_box,
54        loc: None,
55    })
56}
57
58fn binrel_class(arg: &ParseNode) -> String {
59    let atom = if let ParseNode::OrdGroup { body, .. } = arg {
60        if !body.is_empty() {
61            &body[0]
62        } else {
63            arg
64        }
65    } else {
66        arg
67    };
68
69    if let ParseNode::Atom { family, .. } = atom {
70        match family {
71            crate::parse_node::AtomFamily::Bin => return "mbin".to_string(),
72            crate::parse_node::AtomFamily::Rel => return "mrel".to_string(),
73            _ => {}
74        }
75    }
76    "mord".to_string()
77}
78
79fn handle_binrel(
80    ctx: &mut FunctionContext,
81    args: Vec<ParseNode>,
82    _opt_args: Vec<Option<ParseNode>>,
83) -> ParseResult<ParseNode> {
84    let mut args = args.into_iter();
85    let atom = args.next().unwrap();
86    let body = args.next().unwrap();
87    let mclass = binrel_class(&atom);
88
89    Ok(ParseNode::MClass {
90        mode: ctx.parser.mode,
91        mclass,
92        body: ParseNode::ord_argument(body),
93        is_character_box: false,
94        loc: None,
95    })
96}
97
98fn handle_stackrel(
99    ctx: &mut FunctionContext,
100    args: Vec<ParseNode>,
101    _opt_args: Vec<Option<ParseNode>>,
102) -> ParseResult<ParseNode> {
103    let mut args = args.into_iter();
104    let shifted_arg = args.next().unwrap();
105    let base_arg = args.next().unwrap();
106
107    let mclass = if ctx.func_name == "\\stackrel" {
108        "mrel".to_string()
109    } else {
110        binrel_class(&base_arg)
111    };
112
113    let op_node = ParseNode::Op {
114        mode: ctx.parser.mode,
115        limits: true,
116        always_handle_sup_sub: Some(true),
117        suppress_base_shift: Some(ctx.func_name != "\\stackrel"),
118        parent_is_sup_sub: false,
119        symbol: false,
120        name: None,
121        body: Some(ParseNode::ord_argument(base_arg)),
122        loc: None,
123    };
124
125    let (sup, sub) = if ctx.func_name == "\\underset" {
126        (None, Some(Box::new(shifted_arg)))
127    } else {
128        (Some(Box::new(shifted_arg)), None)
129    };
130
131    let supsub = ParseNode::SupSub {
132        mode: ctx.parser.mode,
133        base: Some(Box::new(op_node)),
134        sup,
135        sub,
136        loc: None,
137    };
138
139    Ok(ParseNode::MClass {
140        mode: ctx.parser.mode,
141        mclass,
142        body: vec![supsub],
143        is_character_box: false,
144        loc: None,
145    })
146}