Skip to main content

katex_parser/
function_registry.rs

1use std::collections::HashMap;
2
3use crate::ast::{Measurement, Mode, ParseNode};
4use crate::error::ParseError;
5use crate::macro_definition::MacroDefinition;
6use crate::settings::TrustContext;
7use crate::token::{token_location, Token};
8
9use crate::functions::*;
10
11#[allow(clippy::enum_variant_names)]
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13/// How a function argument should be parsed.
14pub enum ArgType {
15    ColorArg,
16    SizeArg,
17    UrlArg,
18    RawArg,
19    OriginalArg,
20    HboxArg,
21    PrimitiveArg,
22    MathArg,
23    TextArg,
24}
25
26/// Callbacks the parser exposes to function handlers. `&mut` methods mutate
27/// parser state; `&self` methods are pure reads of settings/state.
28pub trait FunctionParser {
29    fn report_nonstrict(
30        &self,
31        error_code: &str,
32        error_message: &str,
33        token: Option<&Token>,
34    ) -> Result<(), ParseError>;
35    fn use_strict_behavior(
36        &self,
37        error_code: &str,
38        error_message: &str,
39        token: Option<&Token>,
40    ) -> bool;
41    fn is_trusted(&self, context: TrustContext) -> bool;
42    fn current_color(&self) -> Result<Option<String>, ParseError>;
43    fn in_left_right(&self) -> bool;
44    fn is_expandable(&self, name: &str) -> bool;
45    fn get_macro(&self, name: &str) -> Option<MacroDefinition>;
46    fn set_macro(&mut self, name: &str, definition: Option<MacroDefinition>);
47    fn set_macro_definition(&mut self, name: &str, definition: MacroDefinition, global: bool);
48    fn parse_expression(
49        &mut self,
50        expr_list: bool,
51        break_on_token_text: Option<&str>,
52    ) -> Result<Vec<ParseNode>, ParseError>;
53    fn parse_math_mode(&mut self, closing: &str) -> Result<Vec<ParseNode>, ParseError>;
54    fn parse_left_right(&mut self, open: &str) -> Result<ParseNode, ParseError>;
55    fn parse_optional_size(&mut self) -> Result<Option<Measurement>, ParseError>;
56    fn parse_prefixed_function(&mut self, name: &str) -> Result<ParseNode, ParseError>;
57    fn parse_environment(&mut self, name: &str) -> Result<ParseNode, ParseError>;
58    fn pop_token(&mut self) -> Result<Token, ParseError>;
59    fn future_token(&mut self) -> Result<Token, ParseError>;
60    fn push_token(&mut self, token: Token);
61    fn consume_spaces(&mut self) -> Result<(), ParseError>;
62    fn consume_macro_arg(&mut self) -> Result<Vec<Token>, ParseError>;
63    fn expand_tokens(&mut self, tokens: Vec<Token>) -> Result<Vec<Token>, ParseError>;
64}
65
66/// Per-function-call context: static data about the function being parsed.
67#[derive(Debug, Clone)]
68pub struct FunctionContext {
69    pub func_name: String,
70    pub mode: Mode,
71    pub token: Option<Token>,
72    pub break_on_token_text: Option<String>,
73    pub display_mode: bool,
74}
75
76/// A function handler implementation.
77pub type FunctionHandler = fn(
78    parser: &mut dyn FunctionParser,
79    context: &FunctionContext,
80    args: &[ParseNode],
81    opt_args: &[Option<ParseNode>],
82) -> Result<ParseNode, ParseError>;
83
84#[derive(Debug, Clone)]
85/// The declaration of a function (name, arguments, handler).
86pub struct FunctionSpec {
87    pub names: Vec<String>,
88    pub num_args: usize,
89    pub num_optional_args: usize,
90    pub arg_types: Vec<ArgType>,
91    pub allowed_in_argument: bool,
92    pub allowed_in_text: bool,
93    pub allowed_in_math: bool,
94    pub infix: bool,
95    pub primitive: bool,
96    pub primitive_after_missing_optional: Option<usize>,
97    pub handler: Option<FunctionHandler>,
98}
99
100impl Default for FunctionSpec {
101    fn default() -> Self {
102        FunctionSpec {
103            names: Vec::new(),
104            num_args: 0,
105            num_optional_args: 0,
106            arg_types: Vec::new(),
107            allowed_in_argument: false,
108            allowed_in_text: false,
109            allowed_in_math: true,
110            infix: false,
111            primitive: false,
112            primitive_after_missing_optional: None,
113            handler: None,
114        }
115    }
116}
117
118impl FunctionSpec {
119    pub fn is_expandable(&self) -> bool {
120        !self.primitive
121    }
122}
123
124#[derive(Clone, Default)]
125/// A map from function names to their specs.
126pub struct FunctionRegistry {
127    entries: HashMap<String, FunctionSpec>,
128}
129
130impl FunctionRegistry {
131    pub fn new() -> Self {
132        FunctionRegistry {
133            entries: HashMap::new(),
134        }
135    }
136
137    pub fn register(&mut self, spec: FunctionSpec) {
138        for name in &spec.names {
139            self.entries.insert(name.clone(), spec.clone());
140        }
141    }
142
143    pub fn get(&self, name: &str) -> Option<&FunctionSpec> {
144        self.entries.get(name)
145    }
146
147    pub fn keys(&self) -> Vec<String> {
148        self.entries.keys().cloned().collect()
149    }
150}
151
152fn verb_spec() -> FunctionSpec {
153    FunctionSpec {
154        names: vec!["\\verb".to_string()],
155        allowed_in_text: true,
156        handler: Some(verb_handler),
157        ..Default::default()
158    }
159}
160
161fn verb_handler(
162    _parser: &mut dyn FunctionParser,
163    context: &FunctionContext,
164    _args: &[ParseNode],
165    _opt_args: &[Option<ParseNode>],
166) -> Result<ParseNode, ParseError> {
167    let loc = token_location(context.token.as_ref());
168    Err(ParseError::InvalidArgument {
169        message: "\\verb ended by end of line instead of matching delimiter".to_string(),
170        loc,
171    })
172}
173
174fn relax_spec() -> FunctionSpec {
175    FunctionSpec {
176        names: vec!["\\relax".to_string()],
177        allowed_in_argument: true,
178        allowed_in_text: true,
179        handler: Some(relax_handler),
180        ..Default::default()
181    }
182}
183
184fn relax_handler(
185    _parser: &mut dyn FunctionParser,
186    context: &FunctionContext,
187    _args: &[ParseNode],
188    _opt_args: &[Option<ParseNode>],
189) -> Result<ParseNode, ParseError> {
190    Ok(ParseNode::Internal {
191        mode: context.mode,
192    })
193}
194
195pub fn builtin_function_specs() -> Vec<FunctionSpec> {
196    vec![
197        verb_spec(),
198        relax_spec(),
199        sqrt_spec(),
200        standard_genfrac_spec(),
201        infix_genfrac_spec(),
202        general_genfrac_spec(),
203        above_spec(),
204        abovefrac_spec(),
205        text_spec(),
206        textcolor_spec(),
207        color_spec(),
208        styling_spec(),
209        font_spec(),
210        boldsymbol_spec(),
211        old_font_spec(),
212        mclass_spec(),
213        binrel_spec(),
214        stackrel_spec(),
215        big_operator_spec(),
216        mathop_spec(),
217        named_operator_spec(),
218        limited_named_operator_spec(),
219        integral_operator_spec(),
220        operatorname_spec(),
221        overline_spec(),
222        underline_spec(),
223        smash_spec(),
224        phantom_spec(),
225        vphantom_spec(),
226        pmb_spec(),
227        vcenter_spec(),
228        rule_spec(),
229        raisebox_spec(),
230        hbox_spec(),
231        lap_spec(),
232        mathchoice_spec(),
233        sizing_spec(),
234        char_spec(),
235        horiz_brace_spec(),
236        x_arrow_spec(),
237        accent_under_spec(),
238        accent_spec(),
239        text_accent_spec(),
240        kern_spec(),
241        colorbox_spec(),
242        fcolorbox_spec(),
243        fbox_spec(),
244        cancel_spec(),
245        sout_spec(),
246        angl_spec(),
247        href_spec(),
248        url_spec(),
249        html_spec(),
250        cr_spec(),
251        macro_prefix_spec(),
252        definition_spec(),
253        let_spec(),
254        futurelet_spec(),
255        includegraphics_spec(),
256        begin_end_spec(),
257        hline_spec(),
258        cd_internal_spec(),
259        cd_parent_spec(),
260        html_mathml_spec(),
261        math_mode_spec(),
262        math_closing_spec(),
263        delim_sizing_spec(),
264        left_right_closing_spec(),
265        left_right_spec(),
266        middle_spec(),
267    ]
268}
269
270/// Builds a function registry from the builtin specs plus caller-provided
271/// extension specs (e.g. `\eval` registered by the `eval` package).
272pub fn build_function_registry(extra_specs: &[FunctionSpec]) -> FunctionRegistry {
273    let mut registry = FunctionRegistry::new();
274    for spec in builtin_function_specs() {
275        registry.register(spec);
276    }
277    for spec in extra_specs {
278        registry.register(spec.clone());
279    }
280    registry
281}
282
283#[allow(dead_code)]
284static BUILTIN_FUNCTION_REGISTRY: std::sync::OnceLock<FunctionRegistry> = std::sync::OnceLock::new();
285
286#[allow(dead_code)]
287pub fn builtin_function_registry() -> &'static FunctionRegistry {
288    BUILTIN_FUNCTION_REGISTRY.get_or_init(|| build_function_registry(&[]))
289}
290
291#[allow(dead_code)]
292pub fn lookup_function(name: &str) -> Option<&'static FunctionSpec> {
293    builtin_function_registry().get(name)
294}