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