1use ratex_lexer::token::{SourceLocation, Token};
2use unicode_normalization::UnicodeNormalization;
3
4use crate::error::{ParseError, ParseResult};
5use crate::functions::{self, ArgType, FunctionContext, FUNCTIONS};
6use crate::macro_expander::{MacroExpander, IMPLICIT_COMMANDS};
7use crate::parse_node::{AtomFamily, Mode, ParseNode};
8use crate::stack_safety::{validate_parse_nodes, DepthBudget, MAX_INPUT_DEPTH};
9
10static END_OF_EXPRESSION: &[&str] = &["}", "\\endgroup", "\\end", "\\right", "&"];
12
13pub struct Parser<'a> {
27 pub mode: Mode,
28 pub gullet: MacroExpander<'a>,
29 pub leftright_depth: i32,
30 depth_budget: DepthBudget,
31 next_token: Option<Token>,
32 pub equation_counter: usize,
33}
34
35impl<'a> Parser<'a> {
36 pub fn new(input: &'a str) -> Self {
37 let depth_budget = DepthBudget::new(MAX_INPUT_DEPTH);
38 Self {
39 mode: Mode::Math,
40 gullet: MacroExpander::new_with_budget(input, Mode::Math, depth_budget.clone()),
41 leftright_depth: 0,
42 depth_budget,
43 next_token: None,
44 equation_counter: 0,
45 }
46 }
47
48 pub fn fetch(&mut self) -> ParseResult<Token> {
52 if self.next_token.is_none() {
53 self.next_token = Some(self.gullet.expand_next_token()?);
54 }
55 Ok(self.next_token.clone().unwrap())
56 }
57
58 pub fn consume(&mut self) {
60 self.next_token = None;
61 }
62
63 pub fn expect(&mut self, text: &str, do_consume: bool) -> ParseResult<()> {
65 let tok = self.fetch()?;
66 if tok.text != text {
67 return Err(ParseError::new(
68 format!("Expected '{}', got '{}'", text, tok.text),
69 Some(&tok),
70 ));
71 }
72 if do_consume {
73 self.consume();
74 }
75 Ok(())
76 }
77
78 pub fn consume_spaces(&mut self) -> ParseResult<()> {
80 loop {
81 let tok = self.fetch()?;
82 if tok.text == " " {
83 self.consume();
84 } else {
85 break;
86 }
87 }
88 Ok(())
89 }
90
91 pub fn switch_mode(&mut self, new_mode: Mode) {
93 self.mode = new_mode;
94 self.gullet.switch_mode(new_mode);
95 }
96
97 pub(crate) fn depth_budget(&self) -> &DepthBudget {
98 &self.depth_budget
99 }
100
101 pub fn parse(&mut self) -> ParseResult<Vec<ParseNode>> {
105 self.gullet.begin_group();
106
107 let result = self.parse_expression_impl(false, None);
110
111 match result {
112 Ok(parse) => {
113 let eof_result = self.expect("EOF", true);
114 self.gullet.end_group();
115 eof_result?;
116 validate_parse_nodes(&parse, MAX_INPUT_DEPTH)?;
117 Ok(parse)
118 }
119 Err(e) => {
120 self.gullet.end_groups();
121 Err(e)
122 }
123 }
124 }
125
126 pub fn parse_expression(
130 &mut self,
131 break_on_infix: bool,
132 break_on_token_text: Option<&str>,
133 ) -> ParseResult<Vec<ParseNode>> {
134 let _guard = self
135 .depth_budget
136 .enter()
137 .map_err(|_| ParseError::recursion_limit_exceeded())?;
138
139 self.parse_expression_impl(break_on_infix, break_on_token_text)
140 }
141
142 fn parse_expression_impl(
143 &mut self,
144 break_on_infix: bool,
145 break_on_token_text: Option<&str>,
146 ) -> ParseResult<Vec<ParseNode>> {
147 let mut body = Vec::new();
148
149 loop {
150 if self.mode == Mode::Math {
151 self.consume_spaces()?;
152 }
153
154 let lex = self.fetch()?;
155
156 if END_OF_EXPRESSION.contains(&lex.text.as_str()) {
157 break;
158 }
159 if let Some(break_text) = break_on_token_text {
160 if lex.text == break_text {
161 break;
162 }
163 }
164 if break_on_infix {
165 if let Some(func) = FUNCTIONS.get(lex.text.as_str()) {
166 if func.infix {
167 break;
168 }
169 }
170 }
171
172 let atom = self.parse_atom(break_on_token_text)?;
173
174 match atom {
175 None => break,
176 Some(node) if node.type_name() == "internal" => continue,
177 Some(node) => body.push(node),
178 }
179 }
180
181 if self.mode == Mode::Text {
182 self.form_ligatures(&mut body);
183 }
184
185 self.handle_infix_nodes(body)
186 }
187
188 fn handle_infix_nodes(&mut self, body: Vec<ParseNode>) -> ParseResult<Vec<ParseNode>> {
190 let mut over_index: Option<usize> = None;
191 let mut func_name: Option<String> = None;
192
193 for (i, node) in body.iter().enumerate() {
194 if let ParseNode::Infix { replace_with, .. } = node {
195 if over_index.is_some() {
196 return Err(ParseError::msg("only one infix operator per group"));
197 }
198 over_index = Some(i);
199 func_name = Some(replace_with.clone());
200 }
201 }
202
203 if let (Some(idx), Some(fname)) = (over_index, func_name) {
204 let numer_body: Vec<ParseNode> = body[..idx].to_vec();
205 let denom_body: Vec<ParseNode> = body[idx + 1..].to_vec();
206
207 let numer = if numer_body.len() == 1 {
208 if let ParseNode::OrdGroup { .. } = &numer_body[0] {
209 numer_body.into_iter().next().unwrap()
210 } else {
211 ParseNode::OrdGroup {
212 mode: self.mode,
213 body: numer_body,
214 semisimple: None,
215 loc: None,
216 }
217 }
218 } else {
219 ParseNode::OrdGroup {
220 mode: self.mode,
221 body: numer_body,
222 semisimple: None,
223 loc: None,
224 }
225 };
226
227 let denom = if denom_body.len() == 1 {
228 if let ParseNode::OrdGroup { .. } = &denom_body[0] {
229 denom_body.into_iter().next().unwrap()
230 } else {
231 ParseNode::OrdGroup {
232 mode: self.mode,
233 body: denom_body,
234 semisimple: None,
235 loc: None,
236 }
237 }
238 } else {
239 ParseNode::OrdGroup {
240 mode: self.mode,
241 body: denom_body,
242 semisimple: None,
243 loc: None,
244 }
245 };
246
247 let node = if fname == "\\\\abovefrac" {
248 let infix_node = body[idx].clone();
250 self.call_function(&fname, vec![numer, infix_node, denom], vec![], None, None)?
251 } else {
252 self.call_function(&fname, vec![numer, denom], vec![], None, None)?
253 };
254 Ok(vec![node])
255 } else {
256 Ok(body)
257 }
258 }
259
260 fn form_ligatures(&self, group: &mut Vec<ParseNode>) {
262 let mut i = 0;
263 while i + 1 < group.len() {
264 let a_text = group[i].symbol_text().map(|s| s.to_string());
265 let b_text = group[i + 1].symbol_text().map(|s| s.to_string());
266
267 if let (Some(a), Some(b)) = (a_text, b_text) {
268 if group[i].type_name() == "textord" && group[i + 1].type_name() == "textord" {
269 if a == "-" && b == "-" {
270 if i + 2 < group.len() {
271 if let Some(c) = group[i + 2].symbol_text() {
272 if c == "-" && group[i + 2].type_name() == "textord" {
273 group[i] = ParseNode::TextOrd {
274 mode: Mode::Text,
275 text: "---".to_string(),
276 loc: None,
277 };
278 group.remove(i + 2);
279 group.remove(i + 1);
280 continue;
281 }
282 }
283 }
284 group[i] = ParseNode::TextOrd {
285 mode: Mode::Text,
286 text: "--".to_string(),
287 loc: None,
288 };
289 group.remove(i + 1);
290 continue;
291 }
292 if (a == "'" || a == "`") && b == a {
293 group[i] = ParseNode::TextOrd {
294 mode: Mode::Text,
295 text: format!("{}{}", a, a),
296 loc: None,
297 };
298 group.remove(i + 1);
299 continue;
300 }
301 }
302 }
303 i += 1;
304 }
305 }
306
307 pub fn parse_atom(
311 &mut self,
312 break_on_token_text: Option<&str>,
313 ) -> ParseResult<Option<ParseNode>> {
314 let mut base = self.parse_group("atom", break_on_token_text)?;
315
316 if let Some(ref b) = base {
317 if b.type_name() == "internal" {
318 return Ok(base);
319 }
320 }
321
322 if self.mode == Mode::Text {
323 return Ok(base);
324 }
325
326 let mut superscript: Option<ParseNode> = None;
327 let mut subscript: Option<ParseNode> = None;
328
329 loop {
330 self.consume_spaces()?;
331 let lex = self.fetch()?;
332
333 if lex.text == "\\limits" || lex.text == "\\nolimits" {
334 let is_limits = lex.text == "\\limits";
335 self.consume();
336 if let Some(base_node) = base.as_mut() {
337 match base_node {
338 ParseNode::Op {
339 limits,
340 always_handle_sup_sub,
341 ..
342 } => {
343 *limits = is_limits;
344 *always_handle_sup_sub = Some(is_limits);
345 }
346 ParseNode::OperatorName {
347 limits,
348 always_handle_sup_sub,
349 ..
350 } => {
351 *limits = is_limits;
352 *always_handle_sup_sub = is_limits;
353 }
354 _ => {}
355 }
356 }
357 } else if lex.text == "^" {
358 if superscript.is_some() {
359 return Err(ParseError::new("Double superscript", Some(&lex)));
360 }
361 superscript = Some(self.handle_sup_subscript("superscript")?);
362 } else if lex.text == "_" {
363 if subscript.is_some() {
364 return Err(ParseError::new("Double subscript", Some(&lex)));
365 }
366 subscript = Some(self.handle_sup_subscript("subscript")?);
367 } else if lex.text == "'" {
368 if superscript.is_some() {
369 return Err(ParseError::new("Double superscript", Some(&lex)));
370 }
371 let prime = ParseNode::TextOrd {
372 mode: self.mode,
373 text: "\\prime".to_string(),
374 loc: None,
375 };
376 let mut primes = vec![prime.clone()];
377 self.consume();
378 while self.fetch()?.text == "'" {
379 primes.push(prime.clone());
380 self.consume();
381 }
382 if self.fetch()?.text == "^" {
383 primes.push(self.handle_sup_subscript("superscript")?);
384 }
385 superscript = Some(ParseNode::OrdGroup {
386 mode: self.mode,
387 body: primes,
388 semisimple: None,
389 loc: None,
390 });
391 } else if let Some((mapped, is_sub)) = lex
392 .text
393 .chars()
394 .next()
395 .and_then(crate::unicode_sup_sub::unicode_sub_sup)
396 {
397 if is_sub && subscript.is_some() {
398 return Err(ParseError::new("Double subscript", Some(&lex)));
399 }
400 if !is_sub && superscript.is_some() {
401 return Err(ParseError::new("Double superscript", Some(&lex)));
402 }
403 let mut subsup_tokens = vec![Token::new(mapped, 0, 0)];
405 self.consume();
406 loop {
407 let tok = self.fetch()?;
408 match tok
409 .text
410 .chars()
411 .next()
412 .and_then(crate::unicode_sup_sub::unicode_sub_sup)
413 {
414 Some((m, sub)) if sub == is_sub => {
415 subsup_tokens.insert(0, Token::new(m, 0, 0));
416 self.consume();
417 }
418 _ => break,
419 }
420 }
421 let body = self.subparse(subsup_tokens)?;
422 let group = ParseNode::OrdGroup {
423 mode: Mode::Math,
424 body,
425 semisimple: None,
426 loc: None,
427 };
428 if is_sub {
429 subscript = Some(group);
430 } else {
431 superscript = Some(group);
432 }
433 } else {
434 break;
435 }
436 }
437
438 if superscript.is_some() || subscript.is_some() {
439 Ok(Some(ParseNode::SupSub {
440 mode: self.mode,
441 base: base.map(Box::new),
442 sup: superscript.map(Box::new),
443 sub: subscript.map(Box::new),
444 loc: None,
445 }))
446 } else {
447 Ok(base)
448 }
449 }
450
451 fn handle_sup_subscript(&mut self, name: &str) -> ParseResult<ParseNode> {
453 let symbol_token = self.fetch()?;
454 self.consume();
455 self.consume_spaces()?;
456
457 let group = self.parse_group(name, None)?;
458 match group {
459 Some(g) if g.type_name() != "internal" => Ok(g),
460 Some(_) => {
461 let g2 = self.parse_group(name, None)?;
463 g2.ok_or_else(|| {
464 ParseError::new(
465 format!("Expected group after '{}'", symbol_token.text),
466 Some(&symbol_token),
467 )
468 })
469 }
470 None => Err(ParseError::new(
471 format!("Expected group after '{}'", symbol_token.text),
472 Some(&symbol_token),
473 )),
474 }
475 }
476
477 pub fn parse_group(
481 &mut self,
482 name: &str,
483 break_on_token_text: Option<&str>,
484 ) -> ParseResult<Option<ParseNode>> {
485 self.parse_group_impl(name, break_on_token_text, false)
486 }
487
488 pub(crate) fn parse_structural_group(
489 &mut self,
490 name: &str,
491 break_on_token_text: Option<&str>,
492 ) -> ParseResult<Option<ParseNode>> {
493 let _guard = self
494 .depth_budget
495 .enter()
496 .map_err(|_| ParseError::recursion_limit_exceeded())?;
497 self.parse_group_impl(name, break_on_token_text, true)
498 }
499
500 fn parse_delimiter_group(&mut self, name: &str) -> ParseResult<Option<ParseNode>> {
501 let first_token = self.fetch()?;
502 let text = first_token.text.clone();
503
504 if text == "{" || text == "\\begingroup" {
505 return self.parse_structural_group(name, None);
506 }
507
508 let result = self.parse_symbol_inner()?;
509 if result.is_none() {
510 return Err(ParseError::new(
511 format!("Expected delimiter as {}", name),
512 Some(&first_token),
513 ));
514 }
515
516 Ok(result)
517 }
518
519 fn parse_group_impl(
520 &mut self,
521 name: &str,
522 break_on_token_text: Option<&str>,
523 depth_already_counted: bool,
524 ) -> ParseResult<Option<ParseNode>> {
525 let first_token = self.fetch()?;
526 let text = first_token.text.clone();
527
528 if text == "{" || text == "\\begingroup" {
529 self.consume();
530 let group_end = if text == "{" { "}" } else { "\\endgroup" };
531
532 if !depth_already_counted && self.depth_budget.remaining() == 0 {
533 return Err(ParseError::recursion_limit_exceeded());
534 }
535
536 self.gullet.begin_group();
537 let result: ParseResult<_> = (|| {
538 let expression = if depth_already_counted {
539 self.parse_expression_impl(false, Some(group_end))?
540 } else {
541 self.parse_expression(false, Some(group_end))?
542 };
543 let last_token = self.fetch()?;
544 self.expect(group_end, true)?;
545 Ok((expression, last_token))
546 })();
547 self.gullet.end_group();
548 let (expression, last_token) = result?;
549
550 let loc = Some(SourceLocation::range(&first_token.loc, &last_token.loc));
551 let semisimple = if text == "\\begingroup" {
552 Some(true)
553 } else {
554 None
555 };
556
557 Ok(Some(ParseNode::OrdGroup {
558 mode: self.mode,
559 body: expression,
560 semisimple,
561 loc,
562 }))
563 } else {
564 let result = match self.parse_function(break_on_token_text, Some(name))? {
565 some @ Some(_) => some,
566 None => self.parse_symbol_inner()?,
567 };
568
569 if result.is_none()
570 && text.starts_with('\\')
571 && !IMPLICIT_COMMANDS.contains(&text.as_str())
572 {
573 return Err(ParseError::new(
574 format!("Undefined control sequence: {}", text),
575 Some(&first_token),
576 ));
577 }
578
579 Ok(result)
580 }
581 }
582
583 pub fn parse_function(
587 &mut self,
588 break_on_token_text: Option<&str>,
589 name: Option<&str>,
590 ) -> ParseResult<Option<ParseNode>> {
591 let token = self.fetch()?;
592 let func = token.text.clone();
593
594 let func_data = match FUNCTIONS.get(func.as_str()) {
595 Some(f) => f,
596 None => return Ok(None),
597 };
598
599 self.consume();
600
601 if let Some(n) = name {
602 if n != "atom" && !func_data.allowed_in_argument {
603 return Err(ParseError::new(
604 format!("Got function '{}' with no arguments as {}", func, n),
605 Some(&token),
606 ));
607 }
608 }
609
610 functions::check_mode_compatibility(func_data, self.mode, &func, Some(&token))?;
611
612 if func == "\\hspace" {
616 self.gullet.consume_spaces();
617 if self.gullet.future().text == "*" {
618 self.gullet.pop_token();
619 }
620 }
621
622 let (args, opt_args) = self.parse_arguments(&func, func_data)?;
623
624 self.call_function(
625 &func,
626 args,
627 opt_args,
628 Some(token),
629 break_on_token_text.map(|s| s.to_string()).as_deref(),
630 )
631 .map(Some)
632 }
633
634 pub fn call_function(
636 &mut self,
637 name: &str,
638 args: Vec<ParseNode>,
639 opt_args: Vec<Option<ParseNode>>,
640 token: Option<Token>,
641 break_on_token_text: Option<&str>,
642 ) -> ParseResult<ParseNode> {
643 let func = FUNCTIONS
644 .get(name)
645 .ok_or_else(|| ParseError::msg(format!("No function handler for {}", name)))?;
646
647 let mut ctx = FunctionContext {
648 func_name: name.to_string(),
649 parser: self,
650 token: token.clone(),
651 break_on_token_text: break_on_token_text.map(|s| s.to_string()),
652 };
653
654 (func.handler)(&mut ctx, args, opt_args)
655 }
656
657 pub fn parse_arguments(
659 &mut self,
660 func: &str,
661 func_data: &functions::FunctionSpec,
662 ) -> ParseResult<(Vec<ParseNode>, Vec<Option<ParseNode>>)> {
663 let total_args = func_data.num_args + func_data.num_optional_args;
664 if total_args == 0 {
665 return Ok((Vec::new(), Vec::new()));
666 }
667
668 let mut args = Vec::new();
669 let mut opt_args = Vec::new();
670
671 for i in 0..total_args {
672 let arg_type = func_data
673 .arg_types
674 .as_ref()
675 .and_then(|types| types.get(i).copied());
676 let is_optional = i < func_data.num_optional_args;
677
678 let effective_type = if (func_data.primitive && arg_type.is_none())
679 || (func_data.node_type == "sqrt"
680 && i == 1
681 && opt_args
682 .first()
683 .is_some_and(|o: &Option<ParseNode>| o.is_none()))
684 {
685 Some(ArgType::Primitive)
686 } else {
687 arg_type
688 };
689 let structural = argument_consumes_depth(func, func_data, effective_type);
690
691 let arg = self.parse_group_of_type(
692 &format!("argument to '{}'", func),
693 effective_type,
694 is_optional,
695 structural,
696 )?;
697
698 if is_optional {
699 opt_args.push(arg);
700 } else if let Some(a) = arg {
701 args.push(a);
702 } else {
703 return Err(ParseError::msg(
704 "Null argument, please report this as a bug",
705 ));
706 }
707 }
708
709 Ok((args, opt_args))
710 }
711
712 fn parse_group_of_type(
714 &mut self,
715 name: &str,
716 arg_type: Option<ArgType>,
717 optional: bool,
718 structural: bool,
719 ) -> ParseResult<Option<ParseNode>> {
720 match arg_type {
721 Some(ArgType::Color) => self.parse_color_group(optional),
722 Some(ArgType::Size) => self.parse_size_group(optional),
723 Some(ArgType::Primitive) => {
724 if optional {
725 return Err(ParseError::msg("A primitive argument cannot be optional"));
726 }
727 let group = if structural {
728 self.parse_structural_group(name, None)?
729 } else {
730 self.parse_delimiter_group(name)?
731 };
732 match group {
733 Some(g) => Ok(Some(g)),
734 None => Err(ParseError::new(format!("Expected group as {}", name), None)),
735 }
736 }
737 Some(ArgType::Math) | Some(ArgType::Text) => {
738 let mode = match arg_type {
739 Some(ArgType::Math) => Some(Mode::Math),
740 Some(ArgType::Text) => Some(Mode::Text),
741 _ => None,
742 };
743 self.parse_argument_group(optional, mode)
744 }
745 Some(ArgType::HBox) => {
746 let group = self.parse_argument_group(optional, Some(Mode::Text))?;
747 match group {
748 Some(g) => Ok(Some(ParseNode::Styling {
749 mode: g.mode(),
750 style: crate::parse_node::StyleStr::Text,
751 body: vec![g],
752 loc: None,
753 })),
754 None => Ok(None),
755 }
756 }
757 Some(ArgType::Raw) => {
758 let token = self.parse_string_group("raw", optional)?;
759 match token {
760 Some(t) => Ok(Some(ParseNode::Raw {
761 mode: Mode::Text,
762 string: t.text,
763 loc: None,
764 })),
765 None => Ok(None),
766 }
767 }
768 Some(ArgType::Url) => self.parse_url_group(optional),
769 None | Some(ArgType::Original) => self.parse_argument_group(optional, None),
770 }
771 }
772
773 fn parse_color_group(&mut self, optional: bool) -> ParseResult<Option<ParseNode>> {
775 let res = self.parse_string_group("color", optional)?;
776 match res {
777 None => Ok(None),
778 Some(token) => {
779 let text = token.text.trim().to_string();
780 let re = regex_lite::Regex::new(
781 r"^(#[a-fA-F0-9]{3,4}|#[a-fA-F0-9]{6}|#[a-fA-F0-9]{8}|[a-fA-F0-9]{6}|[a-zA-Z]+|\d+(\.\d+)?(,\d+(\.\d+)?)*)$",
782 )
783 .unwrap();
784
785 if !re.is_match(&text) {
786 return Err(ParseError::new(
787 format!("Invalid color: '{}'", text),
788 Some(&token),
789 ));
790 }
791 let mut color = text;
792 if regex_lite::Regex::new(r"^[0-9a-fA-F]{6}$")
793 .unwrap()
794 .is_match(&color)
795 {
796 color = format!("#{}", color);
797 }
798
799 Ok(Some(ParseNode::ColorToken {
800 mode: self.mode,
801 color,
802 loc: None,
803 }))
804 }
805 }
806 }
807
808 pub fn parse_size_group(&mut self, optional: bool) -> ParseResult<Option<ParseNode>> {
810 let mut is_blank = false;
811
812 self.gullet.consume_spaces();
813 let res = if !optional && self.gullet.future().text != "{" {
814 Some(
815 self.parse_regex_group(
816 ®ex_lite::Regex::new(r"^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$")
817 .unwrap(),
818 "size",
819 )?,
820 )
821 } else {
822 self.parse_string_group("size", optional)?
823 };
824
825 let res = match res {
826 Some(r) => r,
827 None => return Ok(None),
828 };
829
830 let mut text = res.text.clone();
831 if !optional && text.is_empty() {
832 text = "0pt".to_string();
833 is_blank = true;
834 }
835
836 let size_re =
837 regex_lite::Regex::new(r"([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})").unwrap();
838 let m = size_re
839 .captures(&text)
840 .ok_or_else(|| ParseError::new(format!("Invalid size: '{}'", text), Some(&res)))?;
841
842 let sign = m.get(1).map_or("", |m| m.as_str());
843 let magnitude = m.get(2).map_or("", |m| m.as_str());
844 let unit = m.get(3).map_or("", |m| m.as_str());
845
846 let number: f64 = format!("{}{}", sign, magnitude).parse().unwrap_or(0.0);
847
848 if !is_valid_unit(unit) {
849 return Err(ParseError::new(
850 format!("Invalid unit: '{}'", unit),
851 Some(&res),
852 ));
853 }
854
855 Ok(Some(ParseNode::Size {
856 mode: self.mode,
857 value: crate::parse_node::Measurement {
858 number,
859 unit: unit.to_string(),
860 },
861 is_blank,
862 loc: None,
863 }))
864 }
865
866 fn parse_url_group(&mut self, optional: bool) -> ParseResult<Option<ParseNode>> {
869 self.gullet.lexer.set_catcode('%', 13);
870 self.gullet.lexer.set_catcode('~', 12);
871 let res = self.parse_string_group("url", optional);
872 self.gullet.lexer.set_catcode('%', 14);
873 self.gullet.lexer.set_catcode('~', 13);
874 let res = res?;
875 match res {
876 None => Ok(None),
877 Some(token) => {
878 let url = token.text;
879 Ok(Some(ParseNode::Url {
880 mode: self.mode,
881 url,
882 loc: None,
883 }))
884 }
885 }
886 }
887
888 fn parse_string_group(
890 &mut self,
891 _mode_name: &str,
892 optional: bool,
893 ) -> ParseResult<Option<Token>> {
894 let arg_token = self.gullet.scan_argument(optional)?;
895 let arg_token = match arg_token {
896 Some(t) => t,
897 None => return Ok(None),
898 };
899
900 let mut s = String::new();
901 loop {
902 let next = self.fetch()?;
903 if next.text == "EOF" {
904 break;
905 }
906 s.push_str(&next.text);
907 self.consume();
908 }
909 self.consume(); let mut result = arg_token;
912 result.text = s;
913 Ok(Some(result))
914 }
915
916 fn parse_regex_group(
918 &mut self,
919 regex: ®ex_lite::Regex,
920 mode_name: &str,
921 ) -> ParseResult<Token> {
922 let first_token = self.fetch()?;
923 let mut last_token = first_token.clone();
924 let mut s = String::new();
925
926 loop {
927 let next = self.fetch()?;
928 if next.text == "EOF" {
929 break;
930 }
931 let candidate = format!("{}{}", s, next.text);
932 if regex.is_match(&candidate) {
933 last_token = next;
934 s = candidate;
935 self.consume();
936 } else {
937 break;
938 }
939 }
940
941 if s.is_empty() {
942 return Err(ParseError::new(
943 format!("Invalid {}: '{}'", mode_name, first_token.text),
944 Some(&first_token),
945 ));
946 }
947
948 Ok(first_token.range(&last_token, s))
949 }
950
951 pub fn parse_argument_group(
953 &mut self,
954 optional: bool,
955 mode: Option<Mode>,
956 ) -> ParseResult<Option<ParseNode>> {
957 let arg_token = self.gullet.scan_argument(optional)?;
958 let arg_token = match arg_token {
959 Some(t) => t,
960 None => return Ok(None),
961 };
962
963 let outer_mode = self.mode;
964 if let Some(m) = mode {
965 self.switch_mode(m);
966 }
967
968 self.gullet.begin_group();
969 let result: ParseResult<_> = (|| {
970 let expression = self.parse_expression(false, Some("EOF"))?;
971 self.expect("EOF", true)?;
972 Ok(expression)
973 })();
974 self.gullet.end_group();
975 let result_mode = self.mode;
976 let expression = result;
977
978 if mode.is_some() {
979 self.switch_mode(outer_mode);
980 }
981
982 let expression = expression?;
983
984 let result = ParseNode::OrdGroup {
985 mode: result_mode,
986 loc: Some(arg_token.loc.clone()),
987 body: expression,
988 semisimple: None,
989 };
990
991 Ok(Some(result))
992 }
993
994 fn parse_symbol_inner(&mut self) -> ParseResult<Option<ParseNode>> {
998 let nucleus = self.fetch()?;
999 let text = nucleus.text.clone();
1000
1001 if let Some(stripped) = text.strip_prefix("\\verb") {
1002 self.consume();
1003 let (star, rest) = if let Some(r) = stripped.strip_prefix('*') {
1004 (true, r)
1005 } else {
1006 (false, stripped)
1007 };
1008 let chars: Vec<char> = rest.chars().collect();
1009 if chars.len() < 2 {
1010 return Err(ParseError::new("\\verb assertion failed", Some(&nucleus)));
1011 }
1012 let body: String = chars[1..chars.len() - 1].iter().collect();
1013 return Ok(Some(ParseNode::Verb {
1014 mode: Mode::Text,
1015 body,
1016 star,
1017 loc: Some(nucleus.loc.clone()),
1018 }));
1019 }
1020
1021 let font_mode = match self.mode {
1022 Mode::Math => ratex_font::symbols::Mode::Math,
1023 Mode::Text => ratex_font::symbols::Mode::Text,
1024 };
1025
1026 if text == "^" || text == "_" {
1028 return Ok(None);
1029 }
1030
1031 if text == "\\" {
1033 return Ok(None);
1034 }
1035
1036 if let Some(sym_info) = ratex_font::symbols::get_symbol(&text, font_mode) {
1037 let loc = Some(SourceLocation::range(&nucleus.loc, &nucleus.loc));
1038 let group = sym_info.group;
1039
1040 let node = if group.is_atom() {
1041 let family = match group {
1042 ratex_font::symbols::Group::Bin => AtomFamily::Bin,
1043 ratex_font::symbols::Group::Close => AtomFamily::Close,
1044 ratex_font::symbols::Group::Inner => AtomFamily::Inner,
1045 ratex_font::symbols::Group::Open => AtomFamily::Open,
1046 ratex_font::symbols::Group::Punct => AtomFamily::Punct,
1047 ratex_font::symbols::Group::Rel => AtomFamily::Rel,
1048 _ => unreachable!(),
1049 };
1050 ParseNode::Atom {
1051 mode: self.mode,
1052 family,
1053 text: text.clone(),
1054 loc,
1055 }
1056 } else {
1057 match group {
1058 ratex_font::symbols::Group::MathOrd => ParseNode::MathOrd {
1059 mode: self.mode,
1060 text: text.clone(),
1061 loc,
1062 },
1063 ratex_font::symbols::Group::TextOrd => ParseNode::TextOrd {
1064 mode: self.mode,
1065 text: text.clone(),
1066 loc,
1067 },
1068 ratex_font::symbols::Group::OpToken => ParseNode::OpToken {
1069 mode: self.mode,
1070 text: text.clone(),
1071 loc,
1072 },
1073 ratex_font::symbols::Group::AccentToken => ParseNode::AccentToken {
1074 mode: self.mode,
1075 text: text.clone(),
1076 loc,
1077 },
1078 ratex_font::symbols::Group::Spacing => ParseNode::SpacingNode {
1079 mode: self.mode,
1080 text: text.clone(),
1081 loc,
1082 },
1083 _ => ParseNode::MathOrd {
1084 mode: self.mode,
1085 text: text.clone(),
1086 loc,
1087 },
1088 }
1089 };
1090
1091 self.consume();
1092 return Ok(Some(node));
1093 }
1094
1095 if let Some(node) = self.try_parse_unicode_accent(&text, &nucleus)? {
1098 self.consume();
1099 return Ok(Some(node));
1100 }
1101
1102 let first_char = text.chars().next();
1105 if let Some(ch) = first_char {
1106 if ch as u32 >= 0x80 {
1107 let node = ParseNode::TextOrd {
1108 mode: Mode::Text,
1109 text: text.clone(),
1110 loc: Some(SourceLocation::range(&nucleus.loc, &nucleus.loc)),
1111 };
1112 self.consume();
1113 return Ok(Some(node));
1114 }
1115 }
1116
1117 Ok(None)
1118 }
1119
1120 fn try_parse_unicode_accent(
1124 &self,
1125 text: &str,
1126 nucleus: &Token,
1127 ) -> ParseResult<Option<ParseNode>> {
1128 let nfd: String = text.nfd().collect();
1129 let chars: Vec<char> = nfd.chars().collect();
1130
1131 if chars.len() < 2 {
1132 return Ok(None);
1133 }
1134
1135 let mut split_idx = chars.len() - 1;
1137 while split_idx > 0 && is_supported_combining_accent(chars[split_idx]) {
1138 split_idx -= 1;
1139 }
1140
1141 if split_idx == chars.len() - 1 {
1143 return Ok(None);
1144 }
1145
1146 let base_char = chars[0];
1148 if !is_latin_base_char(base_char) {
1149 return Ok(None);
1150 }
1151
1152 let accent_count = chars.len() - split_idx - 1;
1153 self.depth_budget
1154 .ensure_additional(accent_count)
1155 .map_err(|_| ParseError::new("Recursion limit exceeded", Some(nucleus)))?;
1156
1157 let loc = Some(SourceLocation::range(&nucleus.loc, &nucleus.loc));
1158
1159 let mut base_str: String = chars[..split_idx + 1].iter().collect();
1161
1162 if base_str.len() == 1 {
1164 match base_str.as_str() {
1165 "i" => base_str = "\u{0131}".to_string(), "j" => base_str = "\u{0237}".to_string(), _ => {}
1168 }
1169 }
1170
1171 let font_mode = match self.mode {
1172 Mode::Math => ratex_font::symbols::Mode::Math,
1173 Mode::Text => ratex_font::symbols::Mode::Text,
1174 };
1175
1176 let mut node = if base_str.chars().count() == 1 {
1177 let ch = base_str.chars().next().unwrap();
1178 if let Some(sym) = ratex_font::symbols::get_symbol(&base_str, font_mode) {
1179 match sym.group {
1180 ratex_font::symbols::Group::TextOrd => ParseNode::TextOrd {
1181 mode: self.mode,
1182 text: base_str.clone(),
1183 loc: loc.clone(),
1184 },
1185 _ => ParseNode::MathOrd {
1186 mode: self.mode,
1187 text: base_str.clone(),
1188 loc: loc.clone(),
1189 },
1190 }
1191 } else if (ch as u32) >= 0x80 {
1192 ParseNode::TextOrd {
1194 mode: Mode::Text,
1195 text: base_str.clone(),
1196 loc: loc.clone(),
1197 }
1198 } else {
1199 ParseNode::MathOrd {
1200 mode: self.mode,
1201 text: base_str.clone(),
1202 loc: loc.clone(),
1203 }
1204 }
1205 } else {
1206 return self
1207 .try_parse_unicode_accent(&base_str, nucleus)
1208 .map(|opt| {
1209 opt.or_else(|| {
1210 Some(ParseNode::TextOrd {
1211 mode: Mode::Text,
1212 text: base_str.clone(),
1213 loc: loc.clone(),
1214 })
1215 })
1216 });
1217 };
1218
1219 for &combining in &chars[split_idx + 1..] {
1221 let label = combining_to_accent_label(combining, self.mode);
1222 node = ParseNode::Accent {
1223 mode: self.mode,
1224 label,
1225 is_stretchy: Some(false),
1226 is_shifty: Some(true),
1227 base: Box::new(node),
1228 loc: loc.clone(),
1229 };
1230 }
1231
1232 Ok(Some(node))
1233 }
1234
1235 pub fn subparse(&mut self, tokens: Vec<Token>) -> ParseResult<Vec<ParseNode>> {
1237 let old_token = self.next_token.take();
1238
1239 self.gullet.push_token(Token::new("}", 0, 0));
1240 self.gullet.push_tokens(tokens);
1241 let parse = self.parse_expression(false, None)?;
1242 self.expect("}", true)?;
1243
1244 self.next_token = old_token;
1245 Ok(parse)
1246 }
1247}
1248
1249fn is_latin_base_char(ch: char) -> bool {
1250 matches!(ch,
1251 'A'..='Z' | 'a'..='z'
1252 | '\u{0131}' | '\u{0237}' | '\u{00C6}' | '\u{00D0}' | '\u{00D8}' | '\u{00DE}' | '\u{00DF}' | '\u{00E6}' | '\u{00F0}' | '\u{00F8}' | '\u{00FE}' )
1264}
1265
1266fn is_supported_combining_accent(ch: char) -> bool {
1267 matches!(
1268 ch,
1269 '\u{0300}'
1270 | '\u{0301}'
1271 | '\u{0302}'
1272 | '\u{0303}'
1273 | '\u{0304}'
1274 | '\u{0306}'
1275 | '\u{0307}'
1276 | '\u{0308}'
1277 | '\u{030A}'
1278 | '\u{030B}'
1279 | '\u{030C}'
1280 | '\u{0327}'
1281 )
1282}
1283
1284fn combining_to_accent_label(ch: char, mode: Mode) -> String {
1285 match mode {
1286 Mode::Math => match ch {
1287 '\u{0300}' => "\\grave".to_string(),
1288 '\u{0301}' => "\\acute".to_string(),
1289 '\u{0302}' => "\\hat".to_string(),
1290 '\u{0303}' => "\\tilde".to_string(),
1291 '\u{0304}' => "\\bar".to_string(),
1292 '\u{0306}' => "\\breve".to_string(),
1293 '\u{0307}' => "\\dot".to_string(),
1294 '\u{0308}' => "\\ddot".to_string(),
1295 '\u{030A}' => "\\mathring".to_string(),
1296 '\u{030B}' => "\\H".to_string(),
1297 '\u{030C}' => "\\check".to_string(),
1298 '\u{0327}' => "\\c".to_string(),
1299 _ => format!("\\char\"{:X}", ch as u32),
1300 },
1301 Mode::Text => match ch {
1302 '\u{0300}' => "\\`".to_string(),
1303 '\u{0301}' => "\\'".to_string(),
1304 '\u{0302}' => "\\^".to_string(),
1305 '\u{0303}' => "\\~".to_string(),
1306 '\u{0304}' => "\\=".to_string(),
1307 '\u{0306}' => "\\u".to_string(),
1308 '\u{0307}' => "\\.".to_string(),
1309 '\u{0308}' => "\\\"".to_string(),
1310 '\u{030A}' => "\\r".to_string(),
1311 '\u{030B}' => "\\H".to_string(),
1312 '\u{030C}' => "\\v".to_string(),
1313 '\u{0327}' => "\\c".to_string(),
1314 _ => format!("\\char\"{:X}", ch as u32),
1315 },
1316 }
1317}
1318
1319fn argument_consumes_depth(
1320 func_name: &str,
1321 func_data: &functions::FunctionSpec,
1322 arg_type: Option<ArgType>,
1323) -> bool {
1324 match arg_type {
1325 Some(ArgType::Color | ArgType::Size | ArgType::Raw | ArgType::Url) => false,
1326 Some(ArgType::Primitive) => {
1327 !matches!(func_name, "\\left" | "\\right" | "\\middle")
1328 && func_data.node_type != "delimsizing"
1329 }
1330 Some(ArgType::Math | ArgType::Text | ArgType::HBox | ArgType::Original) | None => true,
1331 }
1332}
1333
1334fn is_valid_unit(unit: &str) -> bool {
1335 matches!(
1336 unit,
1337 "pt" | "mm"
1338 | "cm"
1339 | "in"
1340 | "bp"
1341 | "pc"
1342 | "dd"
1343 | "cc"
1344 | "nd"
1345 | "nc"
1346 | "sp"
1347 | "px"
1348 | "ex"
1349 | "em"
1350 | "mu"
1351 )
1352}
1353
1354fn strip_outer_math_delimiters(input: &str) -> &str {
1358 let s = input.trim();
1359 if s.len() >= 4 && s.starts_with("$$") && s.ends_with("$$") {
1360 return s[2..s.len() - 2].trim();
1361 }
1362 if s.len() >= 2 && s.starts_with('$') && s.ends_with('$') {
1363 return s[1..s.len() - 1].trim();
1364 }
1365 s
1366}
1367
1368pub fn parse(input: &str) -> ParseResult<Vec<ParseNode>> {
1370 Parser::new(strip_outer_math_delimiters(input)).parse()
1371}
1372
1373#[cfg(test)]
1374mod tests {
1375 use super::*;
1376
1377 #[test]
1378 fn test_parse_single_char() {
1379 let result = parse("x").unwrap();
1380 assert_eq!(result.len(), 1);
1381 assert_eq!(result[0].type_name(), "mathord");
1382 }
1383
1384 #[test]
1385 fn test_parse_strips_outer_dollar_inline_math() {
1386 let inner = r"C_p[\ce{H2O(l)}] = \pu{75.3 J // mol K}";
1387 let wrapped = format!("${inner}$");
1388 let a = parse(&wrapped).expect("wrapped");
1389 let b = parse(inner).expect("inner");
1390 assert_eq!(a.len(), b.len());
1391 for (x, y) in a.iter().zip(b.iter()) {
1392 assert_eq!(x.type_name(), y.type_name());
1393 }
1394 }
1395
1396 #[test]
1397 fn test_parse_addition() {
1398 let result = parse("a+b").unwrap();
1399 assert_eq!(result.len(), 3);
1400 assert_eq!(result[0].type_name(), "mathord"); assert_eq!(result[1].type_name(), "atom"); assert_eq!(result[2].type_name(), "mathord"); }
1404
1405 #[test]
1406 fn test_parse_superscript() {
1407 let result = parse("x^2").unwrap();
1408 assert_eq!(result.len(), 1);
1409 assert_eq!(result[0].type_name(), "supsub");
1410 }
1411
1412 #[test]
1413 fn test_parse_subscript() {
1414 let result = parse("a_i").unwrap();
1415 assert_eq!(result.len(), 1);
1416 assert_eq!(result[0].type_name(), "supsub");
1417 }
1418
1419 #[test]
1420 fn test_parse_supsub() {
1421 let result = parse("x^2_i").unwrap();
1422 assert_eq!(result.len(), 1);
1423 assert_eq!(result[0].type_name(), "supsub");
1424 if let ParseNode::SupSub { sup, sub, .. } = &result[0] {
1425 assert!(sup.is_some());
1426 assert!(sub.is_some());
1427 } else {
1428 panic!("Expected SupSub");
1429 }
1430 }
1431
1432 #[test]
1433 fn test_parse_group() {
1434 let result = parse("{a+b}").unwrap();
1435 assert_eq!(result.len(), 1);
1436 assert_eq!(result[0].type_name(), "ordgroup");
1437 }
1438
1439 #[test]
1440 fn test_parse_frac() {
1441 let result = parse("\\frac{a}{b}").unwrap();
1442 assert_eq!(result.len(), 1);
1443 assert_eq!(result[0].type_name(), "genfrac");
1444 }
1445
1446 #[test]
1447 fn test_parse_sqrt() {
1448 let result = parse("\\sqrt{x}").unwrap();
1449 assert_eq!(result.len(), 1);
1450 assert_eq!(result[0].type_name(), "sqrt");
1451 }
1452
1453 #[test]
1454 fn test_parse_sqrt_optional() {
1455 let result = parse("\\sqrt[3]{x}").unwrap();
1456 assert_eq!(result.len(), 1);
1457 if let ParseNode::Sqrt { index, .. } = &result[0] {
1458 assert!(index.is_some());
1459 } else {
1460 panic!("Expected Sqrt");
1461 }
1462 }
1463
1464 #[test]
1465 fn test_parse_nested() {
1466 let result = parse("\\frac{\\sqrt{a^2+b^2}}{c}").unwrap();
1467 assert_eq!(result.len(), 1);
1468 assert_eq!(result[0].type_name(), "genfrac");
1469 }
1470
1471 #[test]
1472 fn test_parse_empty() {
1473 let result = parse("").unwrap();
1474 assert_eq!(result.len(), 0);
1475 }
1476
1477 #[test]
1478 fn test_parse_double_superscript_error() {
1479 let result = parse("x^2^3");
1480 assert!(result.is_err());
1481 }
1482
1483 #[test]
1484 fn test_parse_unclosed_brace_error() {
1485 let result = parse("{x");
1486 assert!(result.is_err());
1487 }
1488
1489 #[test]
1490 fn test_parse_json_output() {
1491 let result = parse("x^2").unwrap();
1492 let json = serde_json::to_string_pretty(&result).unwrap();
1493 assert!(json.contains("supsub"));
1494 }
1495}