1use std::collections::HashMap;
2
3use ratex_lexer::token::{SourceLocation, Token};
4use ratex_lexer::Lexer;
5
6use crate::error::{ParseError, ParseResult};
7use crate::functions::FUNCTIONS;
8use crate::parse_node::Mode;
9
10pub static IMPLICIT_COMMANDS: &[&str] = &["^", "_", "\\limits", "\\nolimits"];
13
14pub type FnMacroHandler = fn(&mut MacroExpander) -> ParseResult<Vec<Token>>;
17
18#[derive(Clone)]
20pub enum MacroDefinition {
21 Text(String),
23 Tokens { tokens: Vec<Token>, num_args: usize },
25 Function(FnMacroHandler),
27}
28
29impl std::fmt::Debug for MacroDefinition {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 Self::Text(s) => write!(f, "Text({:?})", s),
33 Self::Tokens { tokens, num_args } => {
34 write!(f, "Tokens {{ {} tokens, {} args }}", tokens.len(), num_args)
35 }
36 Self::Function(_) => write!(f, "Function(...)"),
37 }
38 }
39}
40
41struct MacroExpansion {
43 tokens: Vec<Token>,
44 num_args: usize,
45 unexpandable: bool,
46}
47
48pub struct MacroExpander<'a> {
56 pub lexer: Lexer<'a>,
57 pub mode: Mode,
58 stack: Vec<Token>,
59 macros: MacroNamespace,
60 expansion_count: usize,
61 max_expand: usize,
62}
63
64struct MacroNamespace {
66 current: HashMap<String, MacroDefinition>,
67 group_stack: Vec<HashMap<String, Option<MacroDefinition>>>,
68}
69
70impl MacroNamespace {
71 fn new() -> Self {
72 Self {
73 current: HashMap::new(),
74 group_stack: Vec::new(),
75 }
76 }
77
78 fn get(&self, name: &str) -> Option<&MacroDefinition> {
79 self.current.get(name)
80 }
81
82 fn set(&mut self, name: String, def: MacroDefinition) {
83 if let Some(undo) = self.group_stack.last_mut() {
84 undo.entry(name.clone())
85 .or_insert_with(|| self.current.get(&name).cloned());
86 }
87 self.current.insert(name, def);
88 }
89
90 fn set_global(&mut self, name: String, def: MacroDefinition) {
91 self.current.insert(name, def);
92 }
93
94 fn has(&self, name: &str) -> bool {
95 self.current.contains_key(name)
96 }
97
98 fn begin_group(&mut self) {
99 self.group_stack.push(HashMap::new());
100 }
101
102 fn end_group(&mut self) {
103 if let Some(undo) = self.group_stack.pop() {
104 for (name, old_val) in undo {
105 match old_val {
106 Some(def) => {
107 self.current.insert(name, def);
108 }
109 None => {
110 self.current.remove(&name);
111 }
112 }
113 }
114 }
115 }
116
117 fn end_groups(&mut self) {
118 while !self.group_stack.is_empty() {
119 self.end_group();
120 }
121 }
122}
123
124fn lex_string_to_stack_tokens(text: &str) -> Vec<Token> {
126 let mut body_lexer = Lexer::new(text);
127 let mut tokens = Vec::new();
128 loop {
129 let tok = body_lexer.lex();
130 if tok.is_eof() {
131 break;
132 }
133 tokens.push(tok);
134 }
135 tokens.reverse();
136 tokens
137}
138
139fn dotsc_space_after(next: &str) -> bool {
140 matches!(
141 next,
142 ")" | "]"
143 | "\\rbrack"
144 | "\\}"
145 | "\\rbrace"
146 | "\\rangle"
147 | "\\rceil"
148 | "\\rfloor"
149 | "\\rgroup"
150 | "\\rmoustache"
151 | "\\right"
152 | "\\bigr"
153 | "\\biggr"
154 | "\\Bigr"
155 | "\\Biggr"
156 | "$"
157 | ";"
158 | "."
159 )
160}
161
162impl<'a> MacroExpander<'a> {
163 pub fn new(input: &'a str, mode: Mode) -> Self {
164 let mut me = Self {
165 lexer: Lexer::new(input),
166 mode,
167 stack: Vec::new(),
168 macros: MacroNamespace::new(),
169 expansion_count: 0,
170 max_expand: 1000,
171 };
172 me.load_builtins();
173 me
174 }
175
176 fn load_builtins(&mut self) {
177 let builtins: &[(&str, &str)] = &[
178 ("\\bgroup", "{"),
180 ("\\egroup", "}"),
181
182 ("\\lq", "`"),
184 ("\\rq", "'"),
185 ("\\aa", "\\r a"),
187 ("\\AA", "\\r A"),
188
189 ("~", "\\nobreakspace"),
191
192 ("\\hphantom", "\\smash{\\phantom{#1}}"),
194
195 ("\\not", "\\html@mathml{\\mathrel{\\mathrlap\\@not}\\nobreak}{\\char\"338}"),
197 ("\\neq", "\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`≠}}"),
198 ("\\ne", "\\neq"),
199 ("\u{2260}", "\\neq"),
200 ("\\notin", "\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`∉}}"),
201 ("\u{2209}", "\\notin"),
202 ("\\notni", "\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u{220C}}}"),
203 ("\u{220C}", "\\notni"),
204 ("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"),
208 ("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"),
209 ("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"),
210
211 ("\\varGamma", "\\mathit{\\Gamma}"),
213 ("\\varDelta", "\\mathit{\\Delta}"),
214 ("\\varTheta", "\\mathit{\\Theta}"),
215 ("\\varLambda", "\\mathit{\\Lambda}"),
216 ("\\varXi", "\\mathit{\\Xi}"),
217 ("\\varPi", "\\mathit{\\Pi}"),
218 ("\\varSigma", "\\mathit{\\Sigma}"),
219 ("\\varUpsilon", "\\mathit{\\Upsilon}"),
220 ("\\varPhi", "\\mathit{\\Phi}"),
221 ("\\varPsi", "\\mathit{\\Psi}"),
222 ("\\varOmega", "\\mathit{\\Omega}"),
223
224 ("\\,", "\\TextOrMath{\\kern{.1667em}}{\\mskip{3mu}}"),
226 ("\\thinspace", "\\,"),
227 ("\\>", "\\mskip{4mu}"),
228 ("\\:", "\\TextOrMath{\\kern{.2222em}}{\\mskip{4mu}}"),
229 ("\\medspace", "\\:"),
230 ("\\;", "\\TextOrMath{\\kern{.2777em}}{\\mskip{5mu}}"),
231 ("\\thickspace", "\\;"),
232 ("\\!", "\\TextOrMath{\\kern{-.1667em}}{\\mskip{-3mu}}"),
233 ("\\negthinspace", "\\!"),
234 ("\\negmedspace", "\\TextOrMath{\\kern{-.2222em}}{\\mskip{-4mu}}"),
235 ("\\negthickspace", "\\TextOrMath{\\kern{-.2777em}}{\\mskip{-5mu}}"),
236 ("\\enspace", "\\kern.5em "),
237 ("\\enskip", "\\hskip.5em\\relax"),
238 ("\\quad", "\\hskip1em\\relax"),
239 ("\\qquad", "\\hskip2em\\relax"),
240
241 ("\\newline", "\\\\\\relax"),
243
244 ("\\@hspace", "\\hskip #1\\relax"),
246 ("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"),
247
248 ("\\llap", "\\mathllap{\\textrm{#1}}"),
250 ("\\rlap", "\\mathrlap{\\textrm{#1}}"),
251 ("\\clap", "\\mathclap{\\textrm{#1}}"),
252
253 ("\\TeX", "\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}"),
255 ("\\LaTeX", "\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{0.21em}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),
256 ("\\KaTeX", "\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{0.21em}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),
257
258 ("\\imath", "\\html@mathml{\\@imath}{\u{0131}}"),
260 ("\\jmath", "\\html@mathml{\\@jmath}{\u{0237}}"),
261
262 ("\\minuso", "\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u{29B5}}}"),
264 ("\\clap", "\\mathclap{\\textrm{#1}}"),
265
266 ("\\mathstrut", "\\vphantom{(}"),
268 ("\\underbar", "\\underline{\\text{#1}}"),
269
270 ("\\Bbbk", "\\Bbb{k}"),
272
273 ("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"),
275
276 ("\\boxed", "\\fbox{$\\displaystyle{#1}$}"),
278
279 ("\\colon", "\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),
281
282 ("\\dots", "\\cdots"),
284 ("\\cdots", "\\@cdots"),
285 ("\\dotsb", "\\cdots"),
286 ("\\dotsm", "\\cdots"),
287 ("\\dotsi", "\\!\\cdots"),
288 ("\\dotsx", "\\ldots\\,"),
289 ("\\dotso", "\\ldots"), ("\\DOTSI", "\\relax"),
291 ("\\DOTSB", "\\relax"),
292 ("\\DOTSX", "\\relax"),
293
294 ("\\gvertneqq", "\\@gvertneqq"),
296 ("\\lvertneqq", "\\@lvertneqq"),
297 ("\\ngeqq", "\\@ngeqq"),
298 ("\\ngeqslant", "\\@ngeqslant"),
299 ("\\nleqq", "\\@nleqq"),
300 ("\\nleqslant", "\\@nleqslant"),
301 ("\\nshortmid", "\\@nshortmid"),
302 ("\\nshortparallel", "\\@nshortparallel"),
303 ("\\nsubseteqq", "\\@nsubseteqq"),
304 ("\\nsupseteqq", "\\@nsupseteqq"),
305 ("\\ulcorner", "\\@ulcorner"),
306 ("\\urcorner", "\\@urcorner"),
307 ("\\llcorner", "\\@llcorner"),
308 ("\\lrcorner", "\\@lrcorner"),
309 ("\\varsubsetneq", "\\@varsubsetneq"),
310 ("\\varsubsetneqq", "\\@varsubsetneqq"),
311 ("\\varsupsetneq", "\\@varsupsetneq"),
312 ("\\varsupsetneqq", "\\@varsupsetneqq"),
313
314 ("\\lBrace", "\\mathopen{\\{\\mkern-3.2mu[}"),
317 ("\\rBrace", "\\mathclose{]\\mkern-3.2mu\\}}"),
318 ("\\llbracket", "\\mathopen{[\\mkern-3.2mu[}"),
319 ("\\rrbracket", "\\mathclose{]\\mkern-3.2mu]}"),
320 ("\\copyright", "\\textcircled{c}"),
321 ("\\textregistered", "\\textcircled{\\scriptsize R}"),
322 ("\\textcopyright", "\\html@mathml{\\textcircled{c}}{\\char`©}"),
323
324 ("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),
327
328 ("\u{2258}", "\\mathrel{\u{E258}}"),
334 ("\u{2259}", "\\stackrel{\\tiny\\wedge}{=}"),
335 ("\u{225A}", "\\stackrel{\\tiny\\vee}{=}"),
336 ("\u{225B}", "\\stackrel{\\scriptsize\\star}{=}"),
337 ("\u{225D}", "\\stackrel{\\tiny\\mathrm{def}}{=}"),
338 ("\u{225E}", "\\mathrel{\u{E25E}}"),
339 ("\u{225F}", "\\stackrel{\\tiny?}{=}"),
340 ("\u{27C2}", "\\perp"),
342 ("\u{203C}", "\\mathclose{!\\mkern-0.8mu!}"),
343 ("\u{231C}", "\\ulcorner"),
344 ("\u{231D}", "\\urcorner"),
345 ("\u{231E}", "\\llcorner"),
346 ("\u{231F}", "\\lrcorner"),
347 ("\u{00A9}", "\\copyright"),
348 ("\u{00AE}", "\\textregistered"),
349 ("\u{2237}", "\\dblcolon"),
351 ("\u{2239}", "\\eqcolon"),
352 ("\u{2254}", "\\coloneqq"),
353 ("\u{2255}", "\\eqqcolon"),
354 ("\u{2A74}", "\\Coloneqq"),
355 ("\u{27E6}", "\\llbracket"),
357 ("\u{27E7}", "\\rrbracket"),
358 ("\u{2983}", "\\lBrace"),
359 ("\u{2984}", "\\rBrace"),
360 ("\u{29B5}", "\\minuso"),
362
363 ("\\dddot", "{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}"),
365 ("\\ddddot", "{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}"),
366
367 ("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}"),
369 ("\u{22ee}", "\\vdots"),
370
371 ("\\bmod", "\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),
373 ("\\pod", "\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),
374 ("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"),
375 ("\\mod", "\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),
376
377 ("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"),
379 ("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"),
380 ("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"),
381 ("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"),
382 ("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\mathrm{lim}}}"),
383 ("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\mathrm{lim}}}"),
384 ("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\mathrm{lim}}}"),
385 ("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\mathrm{lim}}}"),
386
387 ("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"),
389 ("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"),
390 ("\\plim", "\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),
391
392 ("\\ordinarycolon", ":"),
394 ("\\vcentcolon", "\\mathrel{\\mathop\\ordinarycolon}"),
395 ("\\dblcolon", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char\"2237}}"),
396 ("\\coloneqq", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char\"2254}}"),
397 ("\\Coloneqq", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char\"2237\\char\"3d}}"),
398 ("\\coloneq", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char\"3a\\char\"2212}}"),
399 ("\\Coloneq", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char\"2237\\char\"2212}}"),
400 ("\\eqqcolon", "\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char\"2255}}"),
401 ("\\Eqqcolon", "\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char\"3d\\char\"2237}}"),
402 ("\\eqcolon", "\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char\"2239}}"),
403 ("\\Eqcolon", "\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char\"2212\\char\"2237}}"),
404 ("\\colonapprox", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char\"3a\\char\"2248}}"),
405 ("\\Colonapprox", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char\"2237\\char\"2248}}"),
406 ("\\colonsim", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char\"3a\\char\"223c}}"),
407 ("\\Colonsim", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char\"2237\\char\"223c}}"),
408
409 ("\\ratio", "\\vcentcolon"),
411 ("\\coloncolon", "\\dblcolon"),
412 ("\\colonequals", "\\coloneqq"),
413 ("\\coloncolonequals", "\\Coloneqq"),
414 ("\\equalscolon", "\\eqqcolon"),
415 ("\\equalscoloncolon", "\\Eqqcolon"),
416 ("\\colonminus", "\\coloneq"),
417 ("\\coloncolonminus", "\\Coloneq"),
418 ("\\minuscolon", "\\eqcolon"),
419 ("\\minuscoloncolon", "\\Eqcolon"),
420 ("\\coloncolonapprox", "\\Colonapprox"),
421 ("\\coloncolonsim", "\\Colonsim"),
422 ("\\simcolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),
423 ("\\simcoloncolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),
424 ("\\approxcolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),
425 ("\\approxcoloncolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),
426
427 ("\\bra", "\\mathinner{\\langle{#1}|}"),
429 ("\\ket", "\\mathinner{|{#1}\\rangle}"),
430 ("\\braket", "\\mathinner{\\langle{#1}\\rangle}"),
431 ("\\Braket", "\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),
432 ("\\Bra", "\\left\\langle#1\\right|"),
433 ("\\Ket", "\\left|#1\\right\\rangle"),
434
435 ("\\darr", "\\downarrow"),
437 ("\\dArr", "\\Downarrow"),
438 ("\\Darr", "\\Downarrow"),
439 ("\\lang", "\\langle"),
440 ("\\rang", "\\rangle"),
441 ("\\uarr", "\\uparrow"),
442 ("\\uArr", "\\Uparrow"),
443 ("\\Uarr", "\\Uparrow"),
444 ("\\N", "\\mathbb{N}"),
445 ("\\R", "\\mathbb{R}"),
446 ("\\Z", "\\mathbb{Z}"),
447 ("\\alef", "\\aleph"),
448 ("\\alefsym", "\\aleph"),
449 ("\\Alpha", "\\mathrm{A}"),
450 ("\\Beta", "\\mathrm{B}"),
451 ("\\bull", "\\bullet"),
452 ("\\Chi", "\\mathrm{X}"),
453 ("\\clubs", "\\clubsuit"),
454 ("\\cnums", "\\mathbb{C}"),
455 ("\\Complex", "\\mathbb{C}"),
456 ("\\Dagger", "\\ddagger"),
457 ("\\diamonds", "\\diamondsuit"),
458 ("\\empty", "\\emptyset"),
459 ("\\Epsilon", "\\mathrm{E}"),
460 ("\\Eta", "\\mathrm{H}"),
461 ("\\exist", "\\exists"),
462 ("\\harr", "\\leftrightarrow"),
463 ("\\hArr", "\\Leftrightarrow"),
464 ("\\Harr", "\\Leftrightarrow"),
465 ("\\hearts", "\\heartsuit"),
466 ("\\image", "\\Im"),
467 ("\\infin", "\\infty"),
468 ("\\Iota", "\\mathrm{I}"),
469 ("\\isin", "\\in"),
470 ("\\Kappa", "\\mathrm{K}"),
471 ("\\larr", "\\leftarrow"),
472 ("\\lArr", "\\Leftarrow"),
473 ("\\Larr", "\\Leftarrow"),
474 ("\\lrarr", "\\leftrightarrow"),
475 ("\\lrArr", "\\Leftrightarrow"),
476 ("\\Lrarr", "\\Leftrightarrow"),
477 ("\\Mu", "\\mathrm{M}"),
478 ("\\natnums", "\\mathbb{N}"),
479 ("\\Nu", "\\mathrm{N}"),
480 ("\\Omicron", "\\mathrm{O}"),
481 ("\\plusmn", "\\pm"),
482 ("\\rarr", "\\rightarrow"),
483 ("\\rArr", "\\Rightarrow"),
484 ("\\Rarr", "\\Rightarrow"),
485 ("\\real", "\\Re"),
486 ("\\reals", "\\mathbb{R}"),
487 ("\\Reals", "\\mathbb{R}"),
488 ("\\Rho", "\\mathrm{P}"),
489 ("\\sdot", "\\cdot"),
490 ("\\sect", "\\S"),
491 ("\\spades", "\\spadesuit"),
492 ("\\sub", "\\subset"),
493 ("\\sube", "\\subseteq"),
494 ("\\supe", "\\supseteq"),
495 ("\\Tau", "\\mathrm{T}"),
496 ("\\thetasym", "\\vartheta"),
497 ("\\weierp", "\\wp"),
498 ("\\Zeta", "\\mathrm{Z}"),
499
500 ("\\blue", "\\textcolor{##6495ed}{#1}"),
502 ("\\orange", "\\textcolor{##ffa500}{#1}"),
503 ("\\pink", "\\textcolor{##ff00af}{#1}"),
504 ("\\red", "\\textcolor{##df0030}{#1}"),
505 ("\\green", "\\textcolor{##28ae7b}{#1}"),
506 ("\\gray", "\\textcolor{gray}{#1}"),
507 ("\\purple", "\\textcolor{##9d38bd}{#1}"),
508
509 ("\u{212C}", "\\mathscr{B}"),
511 ("\u{2130}", "\\mathscr{E}"),
512 ("\u{2131}", "\\mathscr{F}"),
513 ("\u{210B}", "\\mathscr{H}"),
514 ("\u{2110}", "\\mathscr{I}"),
515 ("\u{2112}", "\\mathscr{L}"),
516 ("\u{2133}", "\\mathscr{M}"),
517 ("\u{211B}", "\\mathscr{R}"),
518 ("\u{212D}", "\\mathfrak{C}"),
519 ("\u{210C}", "\\mathfrak{H}"),
520 ("\u{2128}", "\\mathfrak{Z}"),
521
522 ("\\notni", "\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u{220C}}}"),
524
525 ("\\angln", "{\\angl n}"),
527
528 ("\\set", "\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),
530 ("\\Set", "\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),
531
532 (
534 "\\tripledash",
535 "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}",
536 ),
537 ];
538
539 for &(name, expansion) in builtins {
540 self.macros.set(
541 name.to_string(),
542 MacroDefinition::Text(expansion.to_string()),
543 );
544 }
545
546 self.load_function_macros();
547 }
548
549 fn load_function_macros(&mut self) {
550 self.macros.set(
552 "\\noexpand".to_string(),
553 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
554 let mut tok = me.pop_token();
555 if me.is_expandable(&tok.text) {
556 tok.noexpand = true;
557 tok.treat_as_relax = true;
558 }
559 Ok(vec![tok])
560 }),
561 );
562
563 self.macros.set(
567 "\\@firstoftwo".to_string(),
568 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
569 let args = me.consume_args(2)?;
570 Ok(args.into_iter().next().unwrap())
571 }),
572 );
573
574 self.macros.set(
576 "\\@secondoftwo".to_string(),
577 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
578 let args = me.consume_args(2)?;
579 Ok(args.into_iter().nth(1).unwrap())
580 }),
581 );
582
583 self.macros.set(
585 "\\@ifnextchar".to_string(),
586 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
587 let args = me.consume_args(3)?;
588 me.consume_spaces();
589 let next = me.future().text.clone();
590 let char_arg = &args[0];
591 let char_text = char_arg.first().map_or("", |t| t.text.as_str());
593 if next == char_text {
594 Ok(args[1].clone())
595 } else {
596 Ok(args[2].clone())
597 }
598 }),
599 );
600
601 self.macros.set(
603 "\\@ifstar".to_string(),
604 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
605 let args = me.consume_args(2)?;
606 let next = me.future().text.clone();
607 if next == "*" {
608 me.pop_token();
609 Ok(args[0].clone())
610 } else {
611 Ok(args[1].clone())
612 }
613 }),
614 );
615
616 self.macros.set(
618 "\\TextOrMath".to_string(),
619 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
620 let args = me.consume_args(2)?;
621 if me.mode == Mode::Text {
622 Ok(args[0].clone())
623 } else {
624 Ok(args[1].clone())
625 }
626 }),
627 );
628
629 self.macros.set(
632 "\\dotsc".to_string(),
633 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
634 let next = me.future().text.clone();
635 let text = if dotsc_space_after(&next) {
636 "\\ldots\\,"
637 } else {
638 "\\ldots"
639 };
640 Ok(lex_string_to_stack_tokens(text))
641 }),
642 );
643
644 self.macros.set(
648 "\\newcommand".to_string(),
649 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
650 handle_newcommand(me, false, true)
651 }),
652 );
653
654 self.macros.set(
656 "\\renewcommand".to_string(),
657 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
658 handle_newcommand(me, true, false)
659 }),
660 );
661
662 self.macros.set(
664 "\\providecommand".to_string(),
665 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
666 handle_newcommand(me, true, true)
667 }),
668 );
669
670 self.macros.set(
672 "\\char".to_string(),
673 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
674 let mut tok = me.pop_token();
675 let mut number: i64;
676 let base: Option<u32>;
677
678 if tok.text == "'" {
679 base = Some(8);
680 tok = me.pop_token();
681 } else if tok.text == "\"" {
682 base = Some(16);
683 tok = me.pop_token();
684 } else if tok.text == "`" {
685 tok = me.pop_token();
686 if tok.text.starts_with('\\') {
687 number = tok.text.chars().nth(1).map_or(0, |c| c as i64);
688 } else {
689 number = tok.text.chars().next().map_or(0, |c| c as i64);
690 }
691 let s = number.to_string();
693 let loc = tok.loc.clone();
694 let mut result = vec![Token::new("}", loc.start, loc.end)];
695 for ch in s.chars().rev() {
696 result.push(Token::new(ch.to_string(), loc.start, loc.end));
697 }
698 result.push(Token::new("{", loc.start, loc.end));
699 result.push(Token::new("\\@char", loc.start, loc.end));
700 return Ok(result);
701 } else {
702 base = Some(10);
703 }
704
705 if let Some(b) = base {
706 number = i64::from_str_radix(&tok.text, b).unwrap_or(0);
707 loop {
708 let next = me.future().text.clone();
709 if let Ok(d) = i64::from_str_radix(&next, b) {
710 me.pop_token();
711 number = number * (b as i64) + d;
712 } else {
713 break;
714 }
715 }
716 } else {
717 number = 0;
718 }
719
720 let s = number.to_string();
721 let loc = tok.loc.clone();
722 let mut result = vec![Token::new("}", loc.start, loc.end)];
723 for ch in s.chars().rev() {
724 result.push(Token::new(ch.to_string(), loc.start, loc.end));
725 }
726 result.push(Token::new("{", loc.start, loc.end));
727 result.push(Token::new("\\@char", loc.start, loc.end));
728 Ok(result)
729 }),
730 );
731
732 self.macros.set(
734 "\\operatorname".to_string(),
735 MacroDefinition::Text("\\@ifstar\\operatornamewithlimits\\operatorname@".to_string()),
736 );
737
738 self.macros.set(
740 "\\message".to_string(),
741 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
742 let _args = me.consume_args(1)?;
743 Ok(vec![])
744 }),
745 );
746
747 self.macros.set(
749 "\\errmessage".to_string(),
750 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
751 let _args = me.consume_args(1)?;
752 Ok(vec![])
753 }),
754 );
755
756 for name in &["\\htmlClass", "\\htmlData", "\\htmlId"] {
760 let name = (*name).to_string();
761 self.macros.set(
762 name.clone(),
763 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
764 let args = me.consume_args(2)?;
765 let content = args[1].iter().cloned().rev().collect::<Vec<_>>();
766 Ok(content)
767 }),
768 );
769 }
770
771 self.macros.set(
773 "\\bra@ket".to_string(),
774 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
775 let args = me.consume_args(4)?;
776 let left = args[0].clone();
777 let middle = args[1].clone();
778 let middle_double = args[2].clone();
779 let right = args[3].clone();
780
781 let content = me.consume_args(1)?;
782 let content = content.into_iter().next().unwrap();
783
784 let logical: Vec<Token> = content.into_iter().rev().collect();
787 let mut new_logical: Vec<Token> = Vec::new();
788 let mut depth: i32 = 0;
789 let mut i = 0;
790 while i < logical.len() {
791 let t = &logical[i];
792 if t.text == "{" {
793 depth += 1;
794 new_logical.push(t.clone());
795 } else if t.text == "}" {
796 depth -= 1;
797 new_logical.push(t.clone());
798 } else if depth == 0 && t.text == "|" {
799 if !middle_double.is_empty()
801 && i + 1 < logical.len()
802 && logical[i + 1].text == "|"
803 {
804 new_logical.extend(middle_double.iter().rev().cloned());
806 i += 2;
807 continue;
808 }
809 new_logical.extend(middle.iter().rev().cloned());
811 } else {
812 new_logical.push(t.clone());
813 }
814 i += 1;
815 }
816
817 let content_rev: Vec<Token> = new_logical.into_iter().rev().collect();
819
820 let mut to_expand = Vec::new();
822 to_expand.extend(right);
823 to_expand.extend(content_rev);
824 to_expand.extend(left);
825
826 me.begin_group();
827 let expanded = me.expand_tokens(to_expand)?;
828 me.end_group();
829
830 Ok(expanded)
831 }),
832 );
833
834 self.macros.set(
837 "\\bra@set".to_string(),
838 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
839 let args = me.consume_args(4)?;
840 let left = args[0].clone();
841 let middle = args[1].clone();
842 let middle_double = args[2].clone();
843 let right = args[3].clone();
844
845 let content = me.consume_args(1)?;
846 let mut content = content.into_iter().next().unwrap();
847
848 let mut depth: i32 = 0;
851 let mut _first_pipe_idx: Option<usize> = None;
852 for i in (0..content.len()).rev() {
854 let t = &content[i];
855 if t.text == "{" {
856 depth += 1;
857 } else if t.text == "}" {
858 depth -= 1;
859 } else if depth == 0 && t.text == "|" {
860 if !middle_double.is_empty() && i > 0 && content[i - 1].text == "|" {
862 _first_pipe_idx = Some(i);
863 content.remove(i);
865 content.remove(i - 1);
866 let insert_at = if i >= 2 { i - 1 } else { 0 };
867 for (j, tok) in middle_double.iter().enumerate() {
868 content.insert(insert_at + j, tok.clone());
869 }
870 break;
871 }
872 _first_pipe_idx = Some(i);
873 content.remove(i);
874 for (j, tok) in middle.iter().enumerate() {
875 content.insert(i + j, tok.clone());
876 }
877 break;
878 }
879 }
880
881 let mut to_expand = Vec::new();
883 to_expand.extend(right);
884 to_expand.extend(content);
885 to_expand.extend(left);
886
887 me.begin_group();
888 let expanded = me.expand_tokens(to_expand)?;
889 me.end_group();
890
891 Ok(expanded)
892 }),
893 );
894
895 self.macros.set(
897 "\\ce".to_string(),
898 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
899 let args = me.consume_args(1)?;
900 let s = crate::mhchem::mhchem_arg_tokens_to_string(&args[0]);
901 let tex = crate::mhchem::chem_parse_str(&s, "ce")
902 .map_err(|e| ParseError::msg(format!("\\ce: {e}")))?;
903 Ok(lex_string_to_stack_tokens(&tex))
904 }),
905 );
906 self.macros.set(
907 "\\pu".to_string(),
908 MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
909 let args = me.consume_args(1)?;
910 let s = crate::mhchem::mhchem_arg_tokens_to_string(&args[0]);
911 let tex = crate::mhchem::chem_parse_str(&s, "pu")
912 .map_err(|e| ParseError::msg(format!("\\pu: {e}")))?;
913 Ok(lex_string_to_stack_tokens(&tex))
914 }),
915 );
916 }
917
918 pub fn set_macro(&mut self, name: String, def: MacroDefinition) {
919 self.macros.set(name, def);
920 }
921
922 pub fn set_macro_global(&mut self, name: String, def: MacroDefinition) {
923 self.macros.set_global(name, def);
924 }
925
926 pub fn set_text_macro(&mut self, name: &str, text: &str) {
927 self.macros
928 .set(name.to_string(), MacroDefinition::Text(text.to_string()));
929 }
930
931 pub fn get_macro(&self, name: &str) -> Option<&MacroDefinition> {
932 self.macros.get(name)
933 }
934
935 pub fn expand_tokens(&mut self, tokens: Vec<Token>) -> ParseResult<Vec<Token>> {
937 let saved_stack = std::mem::take(&mut self.stack);
938 self.stack = tokens;
939
940 let mut result = Vec::new();
941 loop {
942 if self.stack.is_empty() {
943 break;
944 }
945 let expanded = self.expand_once(false)?;
946 if !expanded {
947 if let Some(tok) = self.stack.pop() {
948 if tok.is_eof() {
949 break;
950 }
951 result.push(tok);
952 }
953 }
954 }
955
956 self.stack = saved_stack;
957 result.reverse();
958 Ok(result)
959 }
960
961 pub fn switch_mode(&mut self, new_mode: Mode) {
962 self.mode = new_mode;
963 }
964
965 pub fn begin_group(&mut self) {
966 self.macros.begin_group();
967 }
968
969 pub fn end_group(&mut self) {
970 self.macros.end_group();
971 }
972
973 pub fn end_groups(&mut self) {
974 self.macros.end_groups();
975 }
976
977 pub fn future(&mut self) -> &Token {
979 if self.stack.is_empty() {
980 let tok = self.lexer.lex();
981 self.stack.push(tok);
982 }
983 self.stack.last().unwrap()
984 }
985
986 pub fn pop_token(&mut self) -> Token {
988 self.future();
989 self.stack.pop().unwrap()
990 }
991
992 pub fn set_top_text(&mut self, text: String) {
994 self.future();
995 if let Some(tok) = self.stack.last_mut() {
996 tok.text = text;
997 }
998 }
999
1000 pub fn push_token(&mut self, token: Token) {
1002 self.stack.push(token);
1003 }
1004
1005 pub fn push_tokens(&mut self, tokens: Vec<Token>) {
1007 self.stack.extend(tokens);
1008 }
1009
1010 pub fn consume_spaces(&mut self) {
1012 loop {
1013 let is_space = self.future().text == " ";
1014 if is_space {
1015 self.stack.pop();
1016 } else {
1017 break;
1018 }
1019 }
1020 }
1021
1022 fn expand_once(&mut self, expandable_only: bool) -> ParseResult<bool> {
1025 let top_token = self.pop_token();
1026 let name = &top_token.text;
1027
1028 if top_token.noexpand {
1029 self.push_token(top_token);
1030 return Ok(false);
1031 }
1032
1033 if let Some(MacroDefinition::Function(handler)) = self.macros.get(name).cloned() {
1035 self.count_expansion(1)?;
1036 let tokens = handler(self)?;
1037 self.stack.extend(tokens);
1038 return Ok(true);
1039 }
1040
1041 let expansion = self.get_expansion(name);
1042 match expansion {
1043 None => {
1044 if expandable_only && name.starts_with('\\') && !self.is_defined(name) {
1045 return Err(ParseError::new(
1046 format!("Undefined control sequence: {}", name),
1047 Some(&top_token),
1048 ));
1049 }
1050 self.push_token(top_token);
1051 Ok(false)
1052 }
1053 Some(exp) if expandable_only && exp.unexpandable => {
1054 self.push_token(top_token);
1055 Ok(false)
1056 }
1057 Some(exp) => {
1058 self.count_expansion(1)?;
1059 let mut tokens = exp.tokens;
1060 if exp.num_args > 0 {
1061 let args = self.consume_args(exp.num_args)?;
1062 tokens = self.substitute_args(tokens, &args);
1063 }
1064 self.stack.extend(tokens);
1065 Ok(true)
1066 }
1067 }
1068 }
1069
1070 fn substitute_args(&self, mut tokens: Vec<Token>, args: &[Vec<Token>]) -> Vec<Token> {
1071 let mut i = tokens.len();
1072 while i > 0 {
1073 i -= 1;
1074 if tokens[i].text == "#" && i > 0 {
1075 let next = &tokens[i - 1];
1076 if next.text == "#" {
1077 tokens.remove(i);
1078 i -= 1;
1079 } else if let Ok(n) = next.text.parse::<usize>() {
1080 if n >= 1 && n <= args.len() {
1081 tokens.remove(i);
1082 tokens.remove(i - 1);
1083 let arg_tokens = &args[n - 1];
1084 for (j, t) in arg_tokens.iter().enumerate() {
1085 tokens.insert(i - 1 + j, t.clone());
1086 }
1087 i = i.saturating_sub(1);
1088 }
1089 }
1090 }
1091 }
1092 tokens
1093 }
1094
1095 fn get_expansion(&self, name: &str) -> Option<MacroExpansion> {
1096 let def = self.macros.get(name)?;
1097
1098 if name.len() == 1 {
1099 let ch = name.chars().next().unwrap();
1100 let catcode = self.lexer_catcode(ch);
1101 if catcode != 0 && catcode != 13 {
1102 return None;
1103 }
1104 }
1105
1106 match def {
1107 MacroDefinition::Text(text) => {
1108 let mut num_args = 0;
1109 let stripped = text.replace("##", "");
1110 while stripped.contains(&format!("#{}", num_args + 1)) {
1111 num_args += 1;
1112 }
1113 let mut body_lexer = Lexer::new(text);
1114 let mut tokens = Vec::new();
1115 loop {
1116 let tok = body_lexer.lex();
1117 if tok.is_eof() {
1118 break;
1119 }
1120 tokens.push(tok);
1121 }
1122 tokens.reverse();
1123 Some(MacroExpansion {
1124 tokens,
1125 num_args,
1126 unexpandable: false,
1127 })
1128 }
1129 MacroDefinition::Tokens { tokens, num_args } => Some(MacroExpansion {
1130 tokens: tokens.clone(),
1131 num_args: *num_args,
1132 unexpandable: false,
1133 }),
1134 MacroDefinition::Function(_) => {
1135 Some(MacroExpansion {
1137 tokens: vec![],
1138 num_args: 0,
1139 unexpandable: false,
1140 })
1141 }
1142 }
1143 }
1144
1145 fn lexer_catcode(&self, ch: char) -> u8 {
1146 self.lexer.get_catcode(ch)
1147 }
1148
1149 fn count_expansion(&mut self, amount: usize) -> ParseResult<()> {
1150 self.expansion_count += amount;
1151 if self.expansion_count > self.max_expand {
1152 Err(ParseError::msg(
1153 "Too many expansions: infinite loop or need to increase maxExpand setting",
1154 ))
1155 } else {
1156 Ok(())
1157 }
1158 }
1159
1160 pub fn expand_next_token(&mut self) -> ParseResult<Token> {
1162 loop {
1163 let expanded = self.expand_once(false)?;
1164 if !expanded {
1165 let mut token = self.stack.pop().unwrap();
1166 if token.treat_as_relax {
1167 token.text = "\\relax".to_string();
1168 }
1169 return Ok(token);
1170 }
1171 }
1172 }
1173
1174 pub fn consume_arg(&mut self, delims: Option<&[&str]>) -> ParseResult<ConsumedArg> {
1176 let is_delimited = delims.is_some_and(|d| !d.is_empty());
1177 if !is_delimited {
1178 self.consume_spaces();
1179 }
1180
1181 let start = self.future().clone();
1182 let mut tokens = Vec::new();
1183 let mut depth: i32 = 0;
1184 let mut end_tok;
1185
1186 loop {
1187 let tok = self.pop_token();
1188 end_tok = tok.clone();
1189 tokens.push(tok.clone());
1190
1191 if tok.text == "{" {
1192 depth += 1;
1193 } else if tok.text == "}" {
1194 depth -= 1;
1195 if depth == -1 {
1196 return Err(ParseError::new("Extra }", Some(&tok)));
1197 }
1198 } else if tok.is_eof() {
1199 return Err(ParseError::new(
1200 "Unexpected end of input in a macro argument",
1201 Some(&tok),
1202 ));
1203 }
1204
1205 if depth == 0 && !is_delimited {
1206 break;
1207 }
1208
1209 if let Some(delims) = delims {
1210 if is_delimited && depth == 0 {
1211 if let Some(last) = delims.last() {
1212 if tok.text == *last {
1213 tokens.pop();
1214 break;
1215 }
1216 }
1217 }
1218 }
1219 }
1220
1221 if start.text == "{" && tokens.last().is_some_and(|t| t.text == "}") {
1222 tokens.pop();
1223 tokens.remove(0);
1224 }
1225
1226 tokens.reverse();
1227
1228 Ok(ConsumedArg {
1229 tokens,
1230 start,
1231 end: end_tok,
1232 })
1233 }
1234
1235 fn consume_args(&mut self, num_args: usize) -> ParseResult<Vec<Vec<Token>>> {
1237 let mut args = Vec::with_capacity(num_args);
1238 for _ in 0..num_args {
1239 let arg = self.consume_arg(None)?;
1240 args.push(arg.tokens);
1241 }
1242 Ok(args)
1243 }
1244
1245 pub fn scan_argument(&mut self, is_optional: bool) -> ParseResult<Option<Token>> {
1248 if is_optional {
1249 self.consume_spaces();
1250 if self.future().text != "[" {
1251 return Ok(None);
1252 }
1253 let start = self.pop_token();
1254 let arg = self.consume_arg(Some(&["]"]))?;
1255 let end = &arg.end;
1256 let end_loc = end.loc.clone();
1257
1258 self.push_token(Token::new("EOF", end_loc.start, end_loc.end));
1259 self.push_tokens(arg.tokens);
1260
1261 let result = Token {
1262 text: String::new(),
1263 loc: SourceLocation::range(&start.loc, &end_loc),
1264 noexpand: false,
1265 treat_as_relax: false,
1266 };
1267 Ok(Some(result))
1268 } else {
1269 let arg = self.consume_arg(None)?;
1270 let end_loc = arg.end.loc.clone();
1271
1272 self.push_token(Token::new("EOF", end_loc.start, end_loc.end));
1273 self.push_tokens(arg.tokens);
1274
1275 let result = Token {
1276 text: String::new(),
1277 loc: SourceLocation::range(&arg.start.loc, &end_loc),
1278 noexpand: false,
1279 treat_as_relax: false,
1280 };
1281 Ok(Some(result))
1282 }
1283 }
1284
1285 pub fn is_defined(&self, name: &str) -> bool {
1287 self.macros.has(name)
1288 || FUNCTIONS.contains_key(name)
1289 || is_known_symbol(name)
1290 || IMPLICIT_COMMANDS.contains(&name)
1291 }
1292
1293 pub fn is_expandable(&self, name: &str) -> bool {
1295 if let Some(_def) = self.macros.get(name) {
1296 return true;
1297 }
1298 if let Some(func) = FUNCTIONS.get(name) {
1299 return !func.primitive;
1300 }
1301 false
1302 }
1303}
1304
1305pub struct ConsumedArg {
1306 pub tokens: Vec<Token>,
1307 pub start: Token,
1308 pub end: Token,
1309}
1310
1311fn handle_newcommand(
1312 me: &mut MacroExpander,
1313 exists_ok: bool,
1314 nonexists_ok: bool,
1315) -> ParseResult<Vec<Token>> {
1316 let name_arg = me.consume_arg(None)?;
1317 let name = name_arg
1319 .tokens
1320 .last()
1321 .map_or_else(String::new, |t| t.text.clone());
1322
1323 let exists = me.is_defined(&name);
1324 if exists && !exists_ok {
1325 return Err(ParseError::msg(format!(
1326 "\\newcommand{{{}}} attempting to redefine {}; use \\renewcommand",
1327 name, name
1328 )));
1329 }
1330 if !exists && !nonexists_ok {
1331 return Err(ParseError::msg(format!(
1332 "\\renewcommand{{{}}} when command {} does not yet exist; use \\newcommand",
1333 name, name
1334 )));
1335 }
1336
1337 me.consume_spaces();
1338 let mut num_args = 0usize;
1339 if me.future().text == "[" {
1340 me.pop_token();
1341 let narg_tok = me.pop_token();
1342 num_args = narg_tok.text.parse().unwrap_or(0);
1343 let close = me.pop_token();
1344 if close.text != "]" {
1345 return Err(ParseError::msg("Expected ] in \\newcommand"));
1346 }
1347 }
1348
1349 let body_arg = me.consume_arg(None)?;
1350 let tokens = body_arg.tokens;
1351
1352 me.set_macro(name, MacroDefinition::Tokens { tokens, num_args });
1353 Ok(vec![])
1354}
1355
1356fn is_known_symbol(name: &str) -> bool {
1357 use ratex_font::symbols;
1358 symbols::get_symbol(name, symbols::Mode::Math).is_some()
1359 || symbols::get_symbol(name, symbols::Mode::Text).is_some()
1360}