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