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)]
13pub enum ArgType {
15 ColorArg,
16 SizeArg,
17 UrlArg,
18 RawArg,
19 OriginalArg,
20 HboxArg,
21 PrimitiveArg,
22 MathArg,
23 TextArg,
24}
25
26pub 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#[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
76pub 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)]
85pub 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)]
125pub 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
270pub 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}