1pub mod directives;
2pub mod rules;
3
4use crate::quickparse::tokens::TeXToken;
5use flams_utils::{
6 CondSerialize,
7 parsing::{SourceParser, StrParser, StringOrStr},
8 prelude::*,
9 sourcerefs::{StringPosition, StringRange},
10};
11use ftml_uris::Language;
12use rules::{AnyEnv, AnyMacro, EnvironmentResult, EnvironmentRule, MacroResult, MacroRule};
13use smallvec::SmallVec;
14use std::convert::Into;
15use std::{borrow::Cow, collections::hash_map::Entry};
16use tex_engine::utils::HMap;
17
18use super::stex::DiagnosticLevel;
19
20pub trait FromLaTeXToken<'a, Pos: StringPosition>: Sized + std::fmt::Debug {
21 fn from_comment(r: StringRange<Pos>) -> Option<Self>;
22 fn from_group(r: StringRange<Pos>, children: Vec<Self>) -> Option<Self>;
23 fn from_math(display: bool, r: StringRange<Pos>, children: Vec<Self>) -> Option<Self>;
24 fn from_control_sequence(start: Pos, name: &'a str) -> Option<Self>;
25 fn from_text(r: StringRange<Pos>, text: &'a str) -> Option<Self>;
26 fn from_macro_application(m: Macro<'a, Pos>) -> Option<Self>;
27 fn from_environment(e: Environment<'a, Pos, Self>) -> Option<Self>;
28}
29
30#[derive(Debug)]
31pub enum LaTeXToken<'a, Pos: StringPosition> {
32 Comment(StringRange<Pos>),
33 Group {
34 range: StringRange<Pos>,
35 children: Vec<Self>,
36 },
37 Math {
38 display: bool,
39 range: StringRange<Pos>,
40 children: Vec<Self>,
41 },
42 ControlSequence {
43 start: Pos,
44 name: &'a str,
45 },
46 Text {
47 range: StringRange<Pos>,
48 text: &'a str,
49 },
50 MacroApplication(Macro<'a, Pos>),
51 Environment(Environment<'a, Pos, Self>),
52}
53
54impl<'a, Pos: StringPosition> FromLaTeXToken<'a, Pos> for LaTeXToken<'a, Pos> {
55 #[inline]
56 fn from_comment(r: StringRange<Pos>) -> Option<Self> {
57 Some(LaTeXToken::Comment(r))
58 }
59 #[inline]
60 fn from_group(r: StringRange<Pos>, children: Vec<Self>) -> Option<Self> {
61 Some(LaTeXToken::Group { range: r, children })
62 }
63 #[inline]
64 fn from_math(display: bool, r: StringRange<Pos>, children: Vec<Self>) -> Option<Self> {
65 Some(LaTeXToken::Math {
66 display,
67 range: r,
68 children,
69 })
70 }
71 #[inline]
72 fn from_control_sequence(start: Pos, name: &'a str) -> Option<Self> {
73 Some(LaTeXToken::ControlSequence { start, name })
74 }
75 #[inline]
76 fn from_text(range: StringRange<Pos>, text: &'a str) -> Option<Self> {
77 Some(LaTeXToken::Text { range, text })
78 }
79 #[inline]
80 fn from_macro_application(m: Macro<'a, Pos>) -> Option<Self> {
81 Some(LaTeXToken::MacroApplication(m))
82 }
83 #[inline]
84 fn from_environment(e: Environment<'a, Pos, Self>) -> Option<Self> {
85 Some(LaTeXToken::Environment(e))
86 }
87}
88
89#[derive(Debug)]
90pub struct Macro<'a, Pos: StringPosition> {
91 pub token_range: StringRange<Pos>,
92 pub range: StringRange<Pos>,
93 pub name: &'a str,
94}
95
96#[derive(Debug)]
97pub struct Environment<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>> {
98 pub begin: Macro<'a, Pos>,
99 pub end: Option<Macro<'a, Pos>>,
100 pub name: &'a str,
101 pub name_range: StringRange<Pos>,
102 pub children: Vec<T>,
104}
105
106pub struct OptArg<'a, Pos: StringPosition> {
107 inner: Option<&'a str>,
108 range: StringRange<Pos>,
109}
110
111impl<'a, Pos: StringPosition> OptArg<'a, Pos> {
112 #[inline]
113 pub const fn is_some(&self) -> bool {
114 self.inner.is_some()
115 }
116 pub fn into_name(self) -> Option<(&'a str, StringRange<Pos>)> {
117 self.inner.map(|i| (i, self.range))
118 }
119 pub fn as_keyvals(&'a self) -> VecMap<&'a str, OptVal<'a, Pos>> {
120 let mut map = VecMap::default();
121 if let Some(s) = &self.inner {
122 let mut curr = self.range;
123 for e in s.split_noparens_bytes(b'{', b'}', b',') {
124 if let Some((a, b)) = e.split_once('=') {
125 curr.end += Pos::len(a);
126 let key_range = curr;
127 curr.end.inc_by('=');
128 curr.start = curr.end;
129 curr.end += Pos::len(b);
130 let val_range = curr;
131 curr.end.inc_by(',');
132 curr.start = curr.end;
133 let a = a.trim();
134 map.insert(
135 a,
136 OptVal {
137 key: a,
138 key_range,
139 val: b.trim(),
140 val_range,
141 },
142 );
143 } else {
144 curr.end += Pos::len(e);
145 let key_range = curr;
146 curr.end.inc_by(',');
147 curr.start = curr.end;
148 map.insert(
149 e.trim(),
150 OptVal {
151 key: e,
152 key_range,
153 val: "",
154 val_range: curr,
155 },
156 );
157 }
158 }
159 }
160 map
161 }
162}
163
164pub struct OptVal<'a, Pos: StringPosition> {
165 pub key: &'a str,
166 pub key_range: StringRange<Pos>,
167 pub val: &'a str,
168 pub val_range: StringRange<Pos>,
169}
170
171#[derive(Debug)]
172pub struct OptMapVal<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>> {
173 pub key_range: StringRange<Pos>,
174 pub val_range: StringRange<Pos>,
175 pub val: Vec<T>,
176 pub str: &'a str,
177}
178
179#[derive(Debug)]
180pub struct OptMap<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>> {
181 pub inner: VecMap<&'a str, OptMapVal<'a, Pos, T>>,
182}
183
184pub struct Group<
185 'a,
186 Pos: StringPosition,
187 T: FromLaTeXToken<'a, Pos>,
188 State: ParserState<'a, Pos, T>,
189> {
190 previous_letters: Option<String>,
191 #[allow(clippy::type_complexity)]
192 pub macro_rule_changes: HMap<Cow<'a, str>, Option<AnyMacro<'a, Pos, T, State>>>,
193 #[allow(clippy::type_complexity)]
194 pub environment_rule_changes: HMap<Cow<'a, str>, Option<AnyEnv<'a, Pos, T, State>>>,
195}
196
197pub trait GroupState<
198 'a,
199 Pos: StringPosition,
200 T: FromLaTeXToken<'a, Pos>,
201 State: ParserState<'a, Pos, T>,
202>
203{
204 fn new(parent: Option<&mut Self>) -> Self;
205 fn inner(&self) -> &Group<'a, Pos, T, State>;
206 fn inner_mut(&mut self) -> &mut Group<'a, Pos, T, State>;
207 fn close(self, parser: &mut LaTeXParser<'a, Pos, T, State>);
208 fn add_macro_rule(&mut self, name: Cow<'a, str>, old: Option<AnyMacro<'a, Pos, T, State>>);
209 fn add_environment_rule(&mut self, name: Cow<'a, str>, old: Option<AnyEnv<'a, Pos, T, State>>);
210 fn letter_change(&mut self, old: &str);
211}
212
213impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
214 GroupState<'a, Pos, T, State> for Group<'a, Pos, T, State>
215{
216 fn new(_: Option<&mut Self>) -> Self {
217 Group {
218 previous_letters: None,
219 macro_rule_changes: HMap::default(),
220 environment_rule_changes: HMap::default(),
221 }
222 }
223 fn inner(&self) -> &Self {
224 self
225 }
226 fn inner_mut(&mut self) -> &mut Self {
227 self
228 }
229
230 fn add_macro_rule(&mut self, name: Cow<'a, str>, old: Option<AnyMacro<'a, Pos, T, State>>) {
231 if let Entry::Vacant(e) = self.macro_rule_changes.entry(name) {
232 e.insert(old);
233 }
234 }
235 fn add_environment_rule(&mut self, name: Cow<'a, str>, old: Option<AnyEnv<'a, Pos, T, State>>) {
236 if let Entry::Vacant(e) = self.environment_rule_changes.entry(name) {
237 e.insert(old);
238 }
239 }
240
241 fn letter_change(&mut self, old: &str) {
242 if self.previous_letters.is_none() {
243 self.previous_letters = Some(old.to_string());
244 }
245 }
246
247 fn close(self, parser: &mut LaTeXParser<'a, Pos, T, State>) {
248 if let Some(l) = self.previous_letters {
249 parser.tokenizer.letters = l;
250 }
251 for (n, r) in self.macro_rule_changes {
252 if let Some(r) = r {
253 parser.macro_rules.insert(n, r);
254 } else {
255 parser.macro_rules.remove(&n);
256 }
257 }
258 }
259}
260
261pub trait ParserState<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>>: Sized {
262 type Group: GroupState<'a, Pos, T, Self>;
263 type MacroArg: Clone;
264 #[inline]
265 fn from_comment(&self, r: StringRange<Pos>) -> Option<T> {
266 T::from_comment(r)
267 }
268 #[inline]
269 fn from_group(&self, r: StringRange<Pos>, children: Vec<T>) -> Option<T> {
270 T::from_group(r, children)
271 }
272 #[inline]
273 fn from_math(&self, display: bool, r: StringRange<Pos>, children: Vec<T>) -> Option<T> {
274 T::from_math(display, r, children)
275 }
276 #[inline]
277 fn from_control_sequence(&self, start: Pos, name: &'a str) -> Option<T> {
278 T::from_control_sequence(start, name)
279 }
280 #[inline]
281 fn from_text(
282 &self,
283 r: StringRange<Pos>,
284 text: &'a str,
285 in_document: bool,
286 in_math: bool,
287 groups: &mut Groups<'a, '_, Pos, T, Self>,
288 ) -> Option<T> {
289 T::from_text(r, text)
290 }
291 #[inline]
292 fn from_macro_application(&self, m: Macro<'a, Pos>) -> Option<T> {
293 T::from_macro_application(m)
294 }
295 #[inline]
296 fn from_environment(&self, e: Environment<'a, Pos, T>) -> Option<T> {
297 T::from_environment(e)
298 }
299}
300
301impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>> ParserState<'a, Pos, T> for () {
302 type Group = Group<'a, Pos, T, Self>;
303 type MacroArg = ();
304}
305
306impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
307 Group<'a, Pos, T, State>
308{
309}
310
311pub struct LaTeXParser<
312 'a,
313 Pos: StringPosition,
314 T: FromLaTeXToken<'a, Pos>,
315 State: ParserState<'a, Pos, T>,
316> {
317 pub tokenizer: super::tokenizer::TeXTokenizer<'a, Pos>,
318 macro_rules: HMap<Cow<'a, str>, AnyMacro<'a, Pos, T, State>>,
319 pub groups: Vec<State::Group>,
320 environment_rules: HMap<Cow<'a, str>, AnyEnv<'a, Pos, T, State>>,
321 directives: HMap<&'a str, fn(&mut Self, &'a str)>,
322 buf: Vec<T>,
323 pub state: State,
324 pub in_document: bool,
325}
326
327macro_rules! count {
328 () => (0usize);
329 ( $e:expr; $($n:expr;)* ) => (1usize + count!($($n;)*));
330}
331
332macro_rules! default_rules {
333 ($( $($name:ident)? $(($l:literal,$lname:ident))? ),*) => {
334 #[must_use]
335 pub fn default_rules() -> [(&'static str,MacroRule<'a,Pos, T, State>);count!($( $($name;)? $($lname;)? )*)] {[
336 $($((stringify!($name),rules::$name))?$(($l.into(),rules::$lname))?),*
337 ]}
338 }
339}
340
341macro_rules! default_envs {
342 ($( $($name:ident)? $(($l:literal,$lname:ident))? ),*) => {
343 #[must_use]
344 pub fn default_env_rules() -> [(&'static str,EnvironmentRule<'a,Pos, T, State>);count!($( $($name;)? $($lname;)? )*)] {[
345 $(paste::paste!(
346 $((stringify!($name),(rules::[<$name _open>],rules::[<$name _close>])))?
347 $(($l.into(),(rules::$lname,rules::rules::[<$lname _close>])))?
348 )),*
349 ]}
350 }
351}
352
353pub struct Groups<
354 'a,
355 'b,
356 Pos: StringPosition,
357 T: FromLaTeXToken<'a, Pos>,
358 State: ParserState<'a, Pos, T>,
359> {
360 pub groups: &'b mut Vec<State::Group>,
361 pub rules: &'b mut HMap<Cow<'a, str>, AnyMacro<'a, Pos, T, State>>,
362 pub environment_rules: &'b mut HMap<Cow<'a, str>, AnyEnv<'a, Pos, T, State>>,
363 pub tokenizer: &'b mut super::tokenizer::TeXTokenizer<'a, Pos>,
364}
365
366impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
367 Groups<'a, '_, Pos, T, State>
368{
369 pub fn add_macro_rule(
370 &mut self,
371 name: Cow<'a, str>,
372 rule: Option<AnyMacro<'a, Pos, T, State>>,
373 ) {
374 let old = if let Some(rule) = rule {
375 self.rules.insert(name.clone(), rule)
376 } else {
377 self.rules.remove(&name)
378 };
379 if let Some(g) = self.groups.last_mut() {
380 g.add_macro_rule(name, old);
381 }
382 }
383
384 pub fn add_environment_rule(
385 &mut self,
386 name: Cow<'a, str>,
387 rule: Option<AnyEnv<'a, Pos, T, State>>,
388 ) {
389 let old = if let Some(rule) = rule {
390 self.environment_rules.insert(name.clone(), rule)
391 } else {
392 self.environment_rules.remove(&name)
393 };
394 if let Some(g) = self.groups.last_mut() {
395 g.add_environment_rule(name, old);
396 }
397 }
398}
399
400impl<'a, Pos: StringPosition, T: FromLaTeXToken<'a, Pos>, State: ParserState<'a, Pos, T>>
404 LaTeXParser<'a, Pos, T, State>
405{
406 pub fn new(
407 input: &'a str,
408 state: State,
409 err: &'a mut dyn FnMut(String, StringRange<Pos>, DiagnosticLevel),
410 ) -> Self {
411 Self::with_rules(
412 input,
413 state,
414 err,
415 Self::default_rules().into_iter(),
416 Self::default_env_rules().into_iter(),
417 )
418 }
419
420 pub fn with_rules(
421 input: &'a str,
422 state: State,
423 err: &'a mut dyn FnMut(String, StringRange<Pos>, DiagnosticLevel),
424 rules: impl Iterator<Item = (&'a str, MacroRule<'a, Pos, T, State>)>,
425 envs: impl Iterator<Item = (&'a str, EnvironmentRule<'a, Pos, T, State>)>,
426 ) -> Self {
427 let mut macro_rules = HMap::default();
428 let mut environment_rules = HMap::default();
429 for (k, v) in rules {
430 macro_rules.insert(Cow::Borrowed(k), AnyMacro::Ptr(v));
431 }
432 for (k, v) in envs {
433 environment_rules.insert(Cow::Borrowed(k), AnyEnv::Ptr(v));
434 }
435 let mut directives = HMap::default();
436 directives.insert("copycmd", directives::copycmd as _);
437 directives.insert("verbcmd", directives::verbcmd as _);
438 directives.insert("verbenv", directives::verbenv as _);
439 directives.insert("nolint", directives::nolint as _);
440 directives.insert("dolint", directives::dolint as _);
441 directives.insert("macro", directives::macro_dir as _);
442 directives.insert("env", directives::env_dir as _);
443
444 LaTeXParser {
445 tokenizer: super::tokenizer::TeXTokenizer::new(input, err),
446 macro_rules,
447 groups: vec![State::Group::new(None)],
448 environment_rules,
449 directives,
450 buf: Vec::new(),
451 state,
452 in_document: false,
453 }
454 }
455
456 #[inline]
457 pub const fn split<'b>(&'b mut self) -> (&'b mut State, Groups<'a, 'b, Pos, T, State>) {
458 (
459 &mut self.state,
460 Groups {
461 groups: &mut self.groups,
462 rules: &mut self.macro_rules,
463 environment_rules: &mut self.environment_rules,
464 tokenizer: &mut self.tokenizer,
465 },
466 )
467 }
468
469 pub fn add_macro_rule(
470 &mut self,
471 name: Cow<'a, str>,
472 rule: Option<AnyMacro<'a, Pos, T, State>>,
473 ) {
474 let old = if let Some(rule) = rule {
475 self.macro_rules.insert(name.clone(), rule)
476 } else {
477 self.macro_rules.remove(&name)
478 };
479 if let Some(g) = self.groups.last_mut() {
480 g.add_macro_rule(name, old);
481 }
482 }
483
484 pub fn add_environment_rule(
485 &mut self,
486 name: Cow<'a, str>,
487 rule: Option<AnyEnv<'a, Pos, T, State>>,
488 ) {
489 let old = if let Some(rule) = rule {
490 self.environment_rules.insert(name.clone(), rule)
491 } else {
492 self.environment_rules.remove(&name)
493 };
494 if let Some(g) = self.groups.last_mut() {
495 g.add_environment_rule(name, old);
496 }
497 }
498
499 default_rules!(
500 begin,
501 end,
502 begingroup,
503 endgroup,
504 makeatletter,
505 makeatother,
506 ExplSyntaxOn,
507 ExplSyntaxOff,
508 lstinline,
509 verb,
510 stexcodeinline,
511 stexinline,
512 newcommand,
513 renewcommand,
514 providecommand,
515 newenvironment,
516 renewenvironment,
517 provideenvironment,
518 NewDocumentCommand,
519 DeclareDocumentCommand,
520 DeclareRobustCommand,
521 NewDocumentEnvironment,
522 DeclareDocumentEnvironment,
523 ("ref", r#ref),
524 label,
525 cite,
526 includegraphics,
527 url,
528 lstdefinelanguage,
529 hbox,
530 vbox,
531 fbox,
532 mvbox,
533 text,
534 texttt,
535 textrm,
536 textbf,
537 ensuremath,
538 scalebox,
539 raisebox,
540 def,
541 edef,
542 gdef,
543 xdef
544 );
545
546 default_envs!(document, verbatim, lstlisting, stexcode);
547
548 #[inline]
549 pub fn curr_pos(&self) -> Pos {
550 self.tokenizer.reader.curr_pos()
551 }
552
553 fn default(&mut self, t: TeXToken<Pos, &'a str>) -> Option<T> {
554 match t {
555 TeXToken::Comment(r) => self.state.from_comment(r),
556 TeXToken::Text { range, text } => {
557 let mut groups = Groups {
558 groups: &mut self.groups,
559 rules: &mut self.macro_rules,
560 environment_rules: &mut self.environment_rules,
561 tokenizer: &mut self.tokenizer,
562 };
563 self.state.from_text(
564 range,
565 text,
566 self.in_document,
567 matches!(
568 groups.tokenizer.mode,
569 crate::quickparse::tokenizer::Mode::Math { .. }
570 ),
571 &mut groups,
572 )
573 }
574 TeXToken::BeginGroupChar(start) => {
575 let children = self.group();
576 self.state.from_group(
577 StringRange {
578 start,
579 end: self.tokenizer.reader.curr_pos(),
580 },
581 children,
582 )
583 }
584 TeXToken::BeginMath { display, start } => {
585 let children = self.math(display);
586 self.state.from_math(
587 display,
588 StringRange {
589 start,
590 end: self.tokenizer.reader.curr_pos(),
591 },
592 children,
593 )
594 }
595 TeXToken::Directive(s) => {
596 self.directive(s);
597 None
598 }
599 TeXToken::EndGroupChar(p) => {
600 self.tokenizer
601 .problem(p, "Unmatched close group", DiagnosticLevel::Error);
602 None
603 }
604 TeXToken::EndMath { start, .. } => {
605 self.tokenizer
606 .problem(start, "Unmatched math close", DiagnosticLevel::Error);
607 None
608 }
609 TeXToken::ControlSequence { start, name } => self.cs(name, start),
610 }
611 }
612
613 pub fn open_group(&mut self) {
614 let g = State::Group::new(self.groups.last_mut());
615 self.groups.push(g);
616 }
617
618 pub fn close_group(&mut self) {
619 match self.groups.pop() {
620 None => self
621 .tokenizer
622 .problem(self.curr_pos(), "Unmatched }", DiagnosticLevel::Error),
623 Some(g) => g.close(self),
624 }
625 }
626 pub fn add_letters(&mut self, s: &str) {
627 if let Some(g) = self.groups.last_mut() {
628 g.letter_change(&self.tokenizer.letters);
629 }
630 self.tokenizer.letters.push_str(s);
631 }
632 pub fn remove_letters(&mut self, s: &str) {
633 if let Some(g) = self.groups.last_mut() {
634 g.letter_change(&self.tokenizer.letters);
635 }
636 self.tokenizer.letters.retain(|x| !s.contains(x));
637 }
638
639 fn cs(&mut self, name: &'a str, start: Pos) -> Option<T> {
640 match self.macro_rules.get(name).cloned() {
641 Some(r) => {
642 let r#macro = Macro {
643 range: StringRange {
644 start,
645 end: self.curr_pos(),
646 },
647 token_range: StringRange {
648 start,
649 end: self.curr_pos(),
650 },
651 name,
652 };
654 match r.call(r#macro, self) {
655 MacroResult::Success(t) => Some(t),
656 MacroResult::Simple(m) => self.state.from_macro_application(m),
657 MacroResult::Other(v) => {
658 self.buf.extend(v.into_iter().rev());
659 self.buf.pop()
660 }
661 }
662 }
663 None => self.state.from_control_sequence(start, name),
664 }
665 }
666
667 pub(in crate::quickparse) fn environment(
668 &mut self,
669 begin: Macro<'a, Pos>,
670 name: &'a str,
671 name_range: StringRange<Pos>,
672 ) -> EnvironmentResult<'a, Pos, T> {
673 if name == "document" {
674 self.in_document = true;
675 }
676 let mut env = Environment {
677 begin,
678 end: None,
679 name,
680 name_range,
681 children: Vec::new(),
683 };
685 self.open_group();
686 let close = self.environment_rules.get(env.name).cloned().map(|e| {
687 e.open(&mut env, self);
688 let close = e.close();
689 close
690 });
691 while let Some(next) = self.tokenizer.next() {
692 if let TeXToken::ControlSequence {
693 start,
694 name: endname,
695 } = &next
696 {
697 if *endname == "end" {
698 let mut end_macro = Macro {
699 range: StringRange {
700 start: *start,
701 end: self.curr_pos(),
702 },
703 token_range: StringRange {
704 start: *start,
705 end: self.curr_pos(),
706 },
707 name: env.name,
708 };
710 match self.read_name(&mut end_macro).map(|(n, _)| n) {
711 Some(n) if n == env.name => {
712 if env.name == "document" {
713 self.in_document = false;
714 }
715 env.end = Some(end_macro);
716 return if let Some(close) = close {
717 let ret = close(env, self);
718 self.close_group();
719 ret
720 } else {
721 self.close_group();
722 EnvironmentResult::Simple(env)
723 };
724 }
725 Some(n) => {
726 self.tokenizer.problem(
727 end_macro.range.start,
728 format!("Expected \\end{{{}}}, found \\end{{{n}}}", env.name),
729 DiagnosticLevel::Error,
730 );
731 break;
732 }
733 None => {
734 self.tokenizer.problem(
735 end_macro.range.start,
736 "Expected environment name after \\end",
737 DiagnosticLevel::Error,
738 );
739 break;
740 }
741 }
742 }
743 }
744 if let Some(n) = self.default(next) {
745 env.children.push(n);
746 }
747 }
748 self.close_group();
749 self.tokenizer.problem(
750 env.begin.range.start,
751 "Unclosed environment",
752 DiagnosticLevel::Error,
753 );
754 EnvironmentResult::Simple(env)
755 }
756
757 fn directive(&mut self, s: &'a str) {
758 let name_args = s.trim().split_once(|c: char| c.is_ascii_whitespace());
759 if let Some((d, args)) =
765 name_args.and_then(|(name, args)| self.directives.get(name).map(|d| (d, args)))
766 {
768 d(self, args.trim_start());
772 } else if let Some(d) = self.directives.get(s.trim()) {
773 d(self, "");
774 } else {
775 self.tokenizer.problem(
776 self.curr_pos(),
777 format!("Unknown directive {s}"),
778 DiagnosticLevel::Error,
779 );
780 }
781 }
782
783 fn math(&mut self, _display: bool) -> Vec<T> {
784 let start = self.curr_pos();
785 self.open_group();
786 let mut v = Vec::new();
787 while let Some(next) = self.tokenizer.next() {
788 if matches!(next, TeXToken::EndMath { .. }) {
789 self.close_group();
790 return v;
791 }
792 if let Some(n) = self.default(next) {
793 v.push(n);
794 }
795 }
796 self.tokenizer
797 .problem(start, "Unclosed math group", DiagnosticLevel::Error);
798 self.close_group();
799 v
800 }
801
802 fn group(&mut self) -> Vec<T> {
803 let start = self.curr_pos();
804 self.open_group();
805 let mut v = Vec::new();
806 while let Some(next) = self.tokenizer.next() {
807 if matches!(next, TeXToken::EndGroupChar(_)) {
808 self.close_group();
809 return v;
810 }
811 if let Some(n) = self.default(next) {
812 v.push(n);
813 }
814 }
815 self.tokenizer
816 .problem(start, "Unclosed group", DiagnosticLevel::Error);
817 v
818 }
819
820 fn group_i(&mut self) -> Vec<T> {
821 let start = self.curr_pos();
822 let mut v = Vec::new();
823 while !self.tokenizer.reader.starts_with('}') {
824 let Some(next) = self.tokenizer.next() else {
825 self.tokenizer
826 .problem(start, "Unclosed group", DiagnosticLevel::Error);
827 return v;
828 };
829 if matches!(next, TeXToken::EndGroupChar(_)) {
830 return v;
831 }
832 if let Some(n) = self.default(next) {
833 v.push(n);
834 }
835 }
836 if self.tokenizer.reader.starts_with('}') {
837 self.tokenizer.reader.next_char();
838 } else {
839 self.tokenizer
840 .problem(start, "Unclosed group", DiagnosticLevel::Error);
841 }
842 v
843 }
844
845 pub fn get_argument(&mut self, in_macro: &mut Macro<'a, Pos>) -> (StringRange<Pos>, Vec<T>) {
846 self.tokenizer.reader.trim_start();
847 let start = self.curr_pos();
848 if self.tokenizer.reader.starts_with('{') {
849 self.tokenizer.reader.next_char();
850 let v = self.group_i();
851 in_macro.range.end = self.curr_pos();
852 let range = StringRange {
853 start,
854 end: self.curr_pos(),
855 };
856 (range, v)
857 } else if self.tokenizer.reader.starts_with('\\') {
858 let t = self.tokenizer.next().unwrap_or_else(|| unreachable!());
859 in_macro.range.end = self.curr_pos();
860 let range = StringRange {
861 start,
862 end: self.curr_pos(),
863 };
864 self.default(t)
865 .map_or_else(|| (range, Vec::new()), |t| (range, vec![t]))
866 } else {
867 let n = self.tokenizer.next();
868 if n.is_none() {
869 self.tokenizer
870 .problem(start, "Expected argument", DiagnosticLevel::Error);
871 }
872 in_macro.range.end = self.curr_pos();
873 let range = StringRange {
874 start,
875 end: self.curr_pos(),
876 };
877 (range, Vec::new())
878 }
879 }
880
881 pub fn get_argument_with_str(
882 &mut self,
883 in_macro: &mut Macro<'a, Pos>,
884 ) -> (StringRange<Pos>, Vec<T>, &'a str) {
885 self.tokenizer.reader.trim_start();
886 let start = self.curr_pos();
887 if self.tokenizer.reader.starts_with('{') {
888 self.tokenizer.reader.next_char();
889 let full = self.tokenizer.reader.rest();
890 let v = self.group_i();
891 in_macro.range.end = self.curr_pos();
892 let range = StringRange {
893 start,
894 end: self.curr_pos(),
895 };
896 let new_rest = self.tokenizer.reader.rest();
897 let text = full.strip_suffix(new_rest).expect("impossible").trim_end();
898 let text = text.strip_suffix('}').unwrap_or_else(|| text.trim());
899 (range, v, text)
900 } else if self.tokenizer.reader.starts_with('\\') {
901 let full = self.tokenizer.reader.rest();
902 let t = self.tokenizer.next().unwrap_or_else(|| unreachable!());
903 in_macro.range.end = self.curr_pos();
904 let range = StringRange {
905 start,
906 end: self.curr_pos(),
907 };
908 let new_rest = self.tokenizer.reader.rest();
909 let text = full.strip_suffix(new_rest).expect("impossible").trim_end();
910 self.default(t)
911 .map_or_else(|| (range, Vec::new(), text), |t| (range, vec![t], text))
912 } else {
913 let full = self.tokenizer.reader.rest().trim_start();
914 let n = self.tokenizer.next();
915 if n.is_none() {
916 self.tokenizer
917 .problem(start, "Expected argument", DiagnosticLevel::Error);
918 }
919 in_macro.range.end = self.curr_pos();
920 let range = StringRange {
921 start,
922 end: self.curr_pos(),
923 };
924 let new_rest = self.tokenizer.reader.rest();
925 let text = full.strip_suffix(new_rest).expect("impossible").trim_end();
926 (range, Vec::new(), text)
927 }
928 }
929
930 pub fn read_argument(&mut self, in_macro: &mut Macro<'a, Pos>) {
931 self.tokenizer.reader.trim_start();
932 if self.tokenizer.reader.starts_with('{') {
933 self.tokenizer.reader.next_char();
934 let _v = self.group_i();
935 } else if self.tokenizer.reader.starts_with('\\') {
936 let _t = self.tokenizer.next().unwrap_or_else(|| unreachable!());
937 } else {
938 let _ = self.tokenizer.next();
939 }
940 in_macro.range.end = self.curr_pos();
941 }
942
943 pub fn read_opt_str(&mut self, in_macro: &mut Macro<'a, Pos>) -> OptArg<'a, Pos> {
944 self.tokenizer.reader.trim_start();
945 if self.tokenizer.reader.starts_with('[') {
946 self.tokenizer.reader.next_char();
947 self.tokenizer.reader.trim_start();
948 let tstart = self.curr_pos();
949 let s = self
950 .tokenizer
951 .reader
952 .read_until_byte_with_brackets(b']', b'{', b'}');
953 let range = StringRange {
954 start: tstart,
955 end: self.curr_pos(),
956 };
957 self.tokenizer.reader.next_char();
958 in_macro.range.end = self.curr_pos();
959 OptArg {
960 inner: Some(s),
961 range,
962 }
963 } else {
964 let range = StringRange {
965 start: self.curr_pos(),
966 end: self.curr_pos(),
967 };
968 OptArg { inner: None, range }
969 }
970 }
971
972 pub fn read_name(&mut self, r#in: &mut Macro<'a, Pos>) -> Option<(&'a str, StringRange<Pos>)> {
973 self.tokenizer.reader.trim_start();
974 if self.tokenizer.reader.starts_with('{') {
975 self.tokenizer.reader.next_char();
976 self.tokenizer.reader.trim_start();
977 let tstart = self.curr_pos();
978 let s = self
979 .tokenizer
980 .reader
981 .read_until_byte_with_brackets(b'}', b'{', b'}');
982 let range = StringRange {
983 start: tstart,
984 end: self.curr_pos(),
985 };
986 self.tokenizer.reader.next_char();
987 r#in.range.end = self.curr_pos();
988 Some((s, range))
989 } else {
990 None
991 }
992 }
993
994 pub fn read_names(&mut self, r#in: &mut Macro<'a, Pos>) -> Vec<(&'a str, StringRange<Pos>)> {
995 self.tokenizer.reader.trim_start();
996 if self.tokenizer.reader.starts_with('{') {
997 let mut ret = Vec::new();
998 loop {
999 self.tokenizer.reader.next_char();
1000 self.tokenizer.reader.trim_start();
1001 let tstart = self.curr_pos();
1002 let s = self
1003 .tokenizer
1004 .reader
1005 .read_until_with_brackets('{', '}', |c| c == '}' || c == ',');
1006 let range = StringRange {
1007 start: tstart,
1008 end: self.curr_pos(),
1009 };
1010 ret.push((s, range));
1011 if self.tokenizer.reader.starts_with('}') {
1012 break;
1013 }
1014 }
1015 self.tokenizer.reader.next_char();
1016
1017 r#in.range.end = self.curr_pos();
1018 ret
1019 } else {
1020 Vec::new()
1021 }
1022 }
1023
1024 pub fn skip_opt(&mut self, in_macro: &mut Macro<'a, Pos>) -> bool {
1025 self.tokenizer.reader.trim_start();
1026 if self.tokenizer.reader.starts_with('[') {
1027 self.tokenizer.reader.next_char();
1028 self.tokenizer.reader.trim_start();
1029 let _s = self
1030 .tokenizer
1031 .reader
1032 .read_until_byte_with_brackets(b']', b'{', b'}');
1033 self.tokenizer.reader.next_char();
1034 in_macro.range.end = self.curr_pos();
1035 true
1036 } else {
1037 false
1038 }
1039 }
1040 pub fn skip_arg(&mut self, in_macro: &mut Macro<'a, Pos>) {
1041 self.tokenizer.reader.trim_start();
1042 if self.tokenizer.reader.starts_with('{') {
1043 self.tokenizer.reader.next_char();
1044 self.tokenizer.reader.trim_start();
1045 let _s = self
1046 .tokenizer
1047 .reader
1048 .read_until_byte_with_brackets(b'}', b'{', b'}');
1049 self.tokenizer.reader.next_char();
1050 } else {
1051 let _ = self.tokenizer.next();
1052 }
1053 in_macro.range.end = self.curr_pos();
1054 }
1055
1056 pub fn skip_comments(&mut self) {
1057 self.tokenizer.reader.trim_start();
1058 while self.tokenizer.reader.starts_with('%') {
1059 let _ = self.tokenizer.next();
1060 self.tokenizer.reader.trim_start();
1061 }
1062 }
1063}
1064
1065pub trait KeyValValues<
1066 'a,
1067 Pos: StringPosition,
1068 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1069 State: ParserState<'a, Pos, T>,
1070>: Sized + Default
1071{
1072 fn parse_opt(parser: &mut LaTeXParser<'a, Pos, T, State>) -> Option<Self> {
1073 parser.skip_comments();
1074 if !parser.tokenizer.reader.starts_with('[') {
1075 return None;
1076 }
1077 let mut ret = Self::default();
1078 parser.tokenizer.reader.next_char();
1079 loop {
1080 parser.skip_comments();
1081 let key_start = parser.curr_pos();
1082 let key = parser
1083 .tokenizer
1084 .reader
1085 .read_until(|c| c == ']' || c == ',' || c == '=' || c == '%')
1086 .trim();
1087 let key_end = parser.curr_pos();
1088 parser.skip_comments();
1089 match parser.tokenizer.reader.next_char() {
1090 Some(']') => {
1091 if !key.is_empty() {
1092 let kvp = KeyValParser {
1093 start: parser.curr_pos(),
1094 key,
1095 key_range: StringRange {
1096 start: key_start,
1097 end: key_end,
1098 },
1099 value_end: parser.curr_pos(),
1100 has_value: false,
1101 parser,
1102 };
1103 ret.next(kvp, key);
1104 }
1105 break;
1106 }
1107 Some(',') if !key.is_empty() => {
1108 let kvp = KeyValParser {
1109 start: parser.curr_pos(),
1110 key,
1111 key_range: StringRange {
1112 start: key_start,
1113 end: key_end,
1114 },
1115 value_end: parser.curr_pos(),
1116 has_value: false,
1117 parser,
1118 };
1119 ret.next(kvp, key);
1120 }
1121 Some(',') => (),
1122 Some('=') => {
1123 parser.skip_comments();
1124 let start = parser.curr_pos();
1125 let kvp = KeyValParser {
1126 start,
1127 key,
1128 key_range: StringRange {
1129 start: key_start,
1130 end: key_end,
1131 },
1132 value_end: parser.curr_pos(),
1133 has_value: true,
1134 parser,
1135 };
1136 ret.next(kvp, key);
1137 parser.skip_comments();
1138 match parser.tokenizer.reader.next_char() {
1139 Some(',') => (),
1140 Some(']') => break,
1141 c => {
1142 parser.tokenizer.problem(
1143 start,
1144 format!("Unexpected end of key-value list: {c:?}"),
1145 DiagnosticLevel::Error,
1146 );
1147 break;
1148 }
1149 }
1150 }
1151 _ => {
1152 parser.tokenizer.problem(
1153 key_start,
1154 "Unexpected end of key-value list 2",
1155 DiagnosticLevel::Error,
1156 );
1157 break;
1158 }
1159 }
1160 }
1161 Some(ret)
1162 }
1163 fn next(&mut self, parser: KeyValParser<'a, '_, Pos, T, State>, key: &str);
1164}
1165
1166pub trait KeyValKind<
1167 'a,
1168 Pos: StringPosition,
1169 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1170 State: ParserState<'a, Pos, T>,
1171>: Sized
1172{
1173 fn next_val(parser: &mut KeyValParser<'a, '_, Pos, T, State>, key: &str) -> Option<Self>;
1174}
1175impl<
1176 'a,
1177 Pos: StringPosition,
1178 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1179 State: ParserState<'a, Pos, T>,
1180 K: KeyValKind<'a, Pos, T, State>,
1181> KeyValValues<'a, Pos, T, State> for Vec<K>
1182{
1183 fn next(&mut self, mut parser: KeyValParser<'a, '_, Pos, T, State>, key: &str) {
1184 if let Some(v) = K::next_val(&mut parser, key) {
1185 self.push(v);
1186 } else {
1187 parser.parser.tokenizer.problem(
1188 parser.start,
1189 format!("Unexpected key {key}"),
1190 DiagnosticLevel::Error,
1191 );
1192 parser.skip_value();
1193 }
1194 }
1195}
1196
1197#[derive(Clone, Debug, serde::Serialize)]
1198pub struct ParsedKeyValue<Pos: StringPosition, T: CondSerialize> {
1199 pub key_range: StringRange<Pos>,
1200 pub val_range: StringRange<Pos>,
1201 pub val: T,
1202}
1203
1204pub trait KeyValParsable<
1205 'a,
1206 Pos: StringPosition + 'a,
1207 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1208 State: ParserState<'a, Pos, T>,
1209>: Sized + 'a + CondSerialize
1210{
1211 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self>;
1212 fn parse_key_val(
1213 parser: &mut KeyValParser<'a, '_, Pos, T, State>,
1214 ) -> Option<ParsedKeyValue<Pos, Self>> {
1215 Self::parse_key_val_inner(parser).map(|val| ParsedKeyValue {
1216 key_range: parser.key_range,
1217 val_range: StringRange {
1218 start: parser.start,
1219 end: parser.value_end,
1220 },
1221 val,
1222 })
1223 }
1224}
1225
1226impl<
1227 'a,
1228 Pos: StringPosition + 'a,
1229 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1230 State: ParserState<'a, Pos, T>,
1231> KeyValParsable<'a, Pos, T, State> for ()
1232{
1233 #[inline]
1234 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1235 parser.skip_value();
1236 Some(())
1237 }
1238}
1239
1240impl<
1241 'a,
1242 Pos: StringPosition + 'a,
1243 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1244 State: ParserState<'a, Pos, T>,
1245> KeyValParsable<'a, Pos, T, State> for Language
1246{
1247 #[inline]
1248 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1249 let Some(s) = parser.read_value_str_normalized() else {
1250 parser.problem("Missing value", DiagnosticLevel::Error);
1251 return None;
1252 };
1253 let Ok(l) = s.parse() else {
1254 parser.problem("Invalid language", DiagnosticLevel::Error);
1255 return None;
1256 };
1257 Some(l)
1258 }
1259}
1260impl<
1261 'a,
1262 Pos: StringPosition + 'a,
1263 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1264 State: ParserState<'a, Pos, T>,
1265> KeyValParsable<'a, Pos, T, State> for bool
1266{
1267 #[inline]
1268 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1269 let Some(s) = parser.read_value_str_normalized() else {
1270 parser.problem("Missing value", DiagnosticLevel::Error);
1271 return None;
1272 };
1273 let Ok(l) = s.parse() else {
1274 parser.problem("Invalid boolean", DiagnosticLevel::Error);
1275 return None;
1276 };
1277 Some(l)
1278 }
1279}
1280impl<
1281 'a,
1282 Pos: StringPosition + 'a,
1283 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1284 State: ParserState<'a, Pos, T>,
1285> KeyValParsable<'a, Pos, T, State> for f32
1286{
1287 #[inline]
1288 #[allow(clippy::cast_precision_loss)]
1289 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1290 let Some(s) = parser.read_value_str_normalized() else {
1291 parser.problem("Missing value", DiagnosticLevel::Error);
1292 return None;
1293 };
1294 if s.contains('.') {
1295 let Ok(l) = s.parse() else {
1296 parser.problem("Invalid boolean", DiagnosticLevel::Error);
1297 return None;
1298 };
1299 Some(l)
1300 } else {
1301 let Ok(l) = s.parse::<i32>() else {
1302 parser.problem("Invalid boolean", DiagnosticLevel::Error);
1303 return None;
1304 };
1305 Some(l as _)
1306 }
1307 }
1308}
1309
1310impl<
1311 'a,
1312 Pos: StringPosition + 'a,
1313 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1314 State: ParserState<'a, Pos, T>,
1315> KeyValParsable<'a, Pos, T, State> for Box<str>
1316{
1317 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1318 parser.read_value_str_normalized().map(|s| match s {
1319 Cow::Borrowed(s) => s.to_string().into_boxed_str(),
1320 Cow::Owned(s) => s.into_boxed_str(),
1321 })
1322 }
1323}
1324impl<
1325 'a,
1326 Pos: StringPosition + 'a,
1327 T: FromLaTeXToken<'a, Pos> + CondSerialize + 'a,
1328 State: ParserState<'a, Pos, T>,
1329> KeyValParsable<'a, Pos, T, State> for Vec<T>
1330{
1331 #[inline]
1332 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1333 Some(parser.tokens())
1334 }
1335}
1336impl<
1337 'a,
1338 Pos: StringPosition + 'a,
1339 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1340 State: ParserState<'a, Pos, T>,
1341> KeyValParsable<'a, Pos, T, State> for u8
1342{
1343 fn parse_key_val_inner(parser: &mut KeyValParser<'a, '_, Pos, T, State>) -> Option<Self> {
1344 parser.read_value_str().and_then(|s| s.parse().ok())
1345 }
1346}
1347
1348pub struct KeyValParser<
1349 'a,
1350 'b,
1351 Pos: StringPosition + 'a,
1352 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1353 State: ParserState<'a, Pos, T>,
1354> {
1355 pub start: Pos,
1356 pub key_range: StringRange<Pos>,
1357 pub key: &'a str,
1358 value_end: Pos,
1359 pub has_value: bool,
1360 pub parser: &'b mut LaTeXParser<'a, Pos, T, State>,
1361}
1362impl<
1363 'a,
1364 Pos: StringPosition + 'a,
1365 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1366 State: ParserState<'a, Pos, T>,
1367> KeyValParser<'a, '_, Pos, T, State>
1368{
1369 #[inline]
1370 pub fn parse<R: KeyValParsable<'a, Pos, T, State> + CondSerialize>(
1371 &mut self,
1372 ) -> Option<ParsedKeyValue<Pos, R>> {
1373 R::parse_key_val(self)
1374 }
1375
1376 #[inline]
1377 pub const fn to_key_value<Tp: CondSerialize>(&self, val: Tp) -> ParsedKeyValue<Pos, Tp> {
1378 ParsedKeyValue {
1379 key_range: self.key_range,
1380 val_range: StringRange {
1381 start: self.start,
1382 end: self.value_end,
1383 },
1384 val,
1385 }
1386 }
1387 #[inline]
1388 pub fn problem<D: std::fmt::Display>(&mut self, msg: D, severity: DiagnosticLevel) {
1389 self.parser.tokenizer.problem(self.start, msg, severity);
1390 }
1391 #[inline]
1392 pub fn tokens(&mut self) -> Vec<T> {
1393 self.read_value_str()
1394 .map_or_else(Vec::new, |s| self.parser.reparse(s, self.start))
1395 }
1396 pub fn read_value_str(&mut self) -> Option<&'a str> {
1397 if !self.has_value {
1398 return None;
1399 }
1400 self.parser.skip_comments();
1401 let value_start = self.parser.curr_pos();
1402 let str = self
1403 .parser
1404 .tokenizer
1405 .reader
1406 .read_until_with_brackets('{', '}', |c| c == ']' || c == ',');
1407 self.value_end = self.parser.curr_pos();
1408 Some(str)
1409 }
1410 pub fn read_value_str_normalized(&mut self) -> Option<Cow<'a, str>> {
1411 if !self.has_value {
1412 return None;
1413 }
1414 self.parser.skip_comments();
1415 let had_braces = self.parser.tokenizer.reader.starts_with('{');
1416 if had_braces {
1417 self.parser.tokenizer.reader.next_char();
1418 self.parser.skip_comments();
1419 }
1420 let get_next = if had_braces {
1421 |s: &mut Self| {
1422 s.parser
1423 .tokenizer
1424 .reader
1425 .read_until_with_brackets('{', '}', |c| c == '}' || c == '%')
1426 }
1427 } else {
1428 |s: &mut Self| {
1429 s.parser
1430 .tokenizer
1431 .reader
1432 .read_until_with_brackets('{', '}', |c| c == ']' || c == ',' || c == '%')
1433 }
1434 };
1435 let value_start = self.parser.curr_pos();
1436 let first_str = get_next(self); if self.parser.tokenizer.reader.starts_with('%') {
1438 let mut nexts = SmallVec::<_, 2>::new();
1439 let mut end_pos = self.parser.curr_pos();
1440 loop {
1441 self.parser.skip_comments();
1442 let next = get_next(self);
1443 end_pos = self.parser.curr_pos();
1444 if !next.is_empty() {
1445 nexts.push(next);
1446 }
1447 if self.parser.tokenizer.reader.starts_with('%') {
1448 continue;
1449 }
1450 break;
1451 }
1452 self.value_end = end_pos;
1453 if had_braces {
1454 self.parser.tokenizer.reader.next_char();
1455 }
1456 if nexts.iter().all(|s| s.trim().is_empty()) {
1457 Some(normalize_ws(first_str))
1458 } else {
1459 Some(Cow::Owned(join_strs(first_str, nexts)))
1460 }
1461 } else {
1462 self.value_end = self.parser.curr_pos();
1463 if had_braces {
1464 self.parser.tokenizer.reader.next_char();
1465 }
1466 Some(normalize_ws(first_str))
1467 }
1468 }
1469
1470 pub fn read_value_strs_normalized(&mut self) -> Vec<(Cow<'a, str>, StringRange<Pos>)> {
1471 if !self.has_value {
1472 return Vec::new();
1473 }
1474 self.parser.skip_comments();
1475 if !self.parser.tokenizer.reader.starts_with('{') {
1476 return self.read_value_str_normalized().map_or_else(Vec::new, |s| {
1477 vec![(
1478 s,
1479 StringRange {
1480 start: self.start,
1481 end: self.value_end,
1482 },
1483 )]
1484 });
1485 }
1486 self.parser.tokenizer.reader.next_char();
1487 self.parser.skip_comments();
1488 let mut ret = Vec::new();
1489 loop {
1490 let value_start = self.parser.curr_pos();
1491 let first_str = self
1492 .parser
1493 .tokenizer
1494 .reader
1495 .read_until_with_brackets('{', '}', |c| c == '}' || c == '%' || c == ',');
1496 if self.parser.tokenizer.reader.starts_with('%') {
1497 let mut nexts = SmallVec::<_, 2>::new();
1498 let mut end_pos = self.parser.curr_pos();
1499 loop {
1500 self.parser.skip_comments();
1501 let next =
1502 self.parser
1503 .tokenizer
1504 .reader
1505 .read_until_with_brackets('{', '}', |c| {
1506 c == '}' || c == '%' || c == ','
1507 });
1508 end_pos = self.parser.curr_pos();
1509 nexts.push(next);
1510 if self.parser.tokenizer.reader.starts_with('%') {
1511 continue;
1512 }
1513 break;
1514 }
1515 let range = StringRange {
1516 start: value_start,
1517 end: end_pos,
1518 };
1519 if nexts.iter().all(|s| s.trim().is_empty()) {
1520 ret.push((normalize_ws(first_str), range));
1521 } else {
1522 ret.push((Cow::Owned(join_strs(first_str, nexts)), range));
1523 }
1524 if self
1525 .parser
1526 .tokenizer
1527 .reader
1528 .next_char()
1529 .is_some_and(|c| c == ',')
1530 {
1531 continue;
1532 }
1533 break;
1534 }
1535 let range = StringRange {
1536 start: value_start,
1537 end: self.parser.curr_pos(),
1538 };
1539 ret.push((normalize_ws(first_str), range));
1540 if self
1541 .parser
1542 .tokenizer
1543 .reader
1544 .next_char()
1545 .is_some_and(|c| c == ',')
1546 {
1547 continue;
1548 }
1549 break;
1550 }
1551 self.value_end = self.parser.curr_pos();
1552 ret
1553 }
1554
1555 pub fn skip_value(&mut self) {
1556 self.read_value_str();
1557 }
1558}
1559
1560impl<
1561 'a,
1562 Pos: StringPosition,
1563 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1564 State: ParserState<'a, Pos, T>,
1565> LaTeXParser<'a, Pos, T, State>
1566{
1567 pub fn reparse(&mut self, s: &'a str, at: Pos) -> Vec<T> {
1568 let mut new = StrParser::new(s);
1569 new.pos = at;
1570 let mut old = std::mem::replace(&mut self.tokenizer.reader, new);
1571 let mut val = Vec::new();
1572 while self.tokenizer.reader.peek_head().is_some() {
1573 let Some(next) = self.tokenizer.next() else {
1574 self.tokenizer
1575 .problem(at, "Unclosed optional argument", DiagnosticLevel::Error);
1576 break;
1577 };
1578 if let Some(n) = self.default(next) {
1579 val.push(n);
1580 }
1581 self.tokenizer.reader.trim_start();
1582 }
1583 old.pos = self.curr_pos();
1584 self.tokenizer.reader = old;
1585 val
1586 }
1587
1588 #[allow(clippy::too_many_lines)]
1589 pub fn read_opt_map(&mut self, in_macro: &mut Macro<'a, Pos>) -> OptMap<'a, Pos, T> {
1590 self.skip_comments();
1591 if self.tokenizer.reader.starts_with('[') {
1592 self.tokenizer.reader.next_char();
1593 let mut map = VecMap::new();
1594 loop {
1595 self.skip_comments();
1596 let key_start = self.curr_pos();
1597 let key = self
1598 .tokenizer
1599 .reader
1600 .read_until(|c| c == ']' || c == ',' || c == '=' || c == '%')
1601 .trim();
1602 let key_end = self.curr_pos();
1603 self.skip_comments();
1604 match self.tokenizer.reader.next_char() {
1605 Some(']') => {
1606 if !key.is_empty() {
1607 map.insert(
1608 key,
1609 OptMapVal {
1610 key_range: StringRange {
1611 start: key_start,
1612 end: key_end,
1613 },
1614 val_range: StringRange {
1615 start: self.curr_pos(),
1616 end: self.curr_pos(),
1617 },
1618 val: Vec::new(),
1619 str: "",
1620 },
1621 );
1622 }
1623 break;
1624 }
1625 Some(',') if !key.is_empty() => {
1626 map.insert(
1627 key,
1628 OptMapVal {
1629 key_range: StringRange {
1630 start: key_start,
1631 end: key_end,
1632 },
1633 val_range: StringRange {
1634 start: self.curr_pos(),
1635 end: self.curr_pos(),
1636 },
1637 val: Vec::new(),
1638 str: "",
1639 },
1640 );
1641 }
1642 Some(',') => (),
1643 Some('=') => {
1644 self.skip_comments();
1645 let value_start = self.curr_pos();
1646 let str = self
1647 .tokenizer
1648 .reader
1649 .read_until_with_brackets('{', '}', |c| c == ']' || c == ',');
1650 let val = self.reparse(str, value_start);
1651 map.insert(
1652 key,
1653 OptMapVal {
1654 key_range: StringRange {
1655 start: key_start,
1656 end: key_end,
1657 },
1658 val_range: StringRange {
1659 start: value_start,
1660 end: self.curr_pos(),
1661 },
1662 val,
1663 str,
1664 },
1665 );
1666 }
1667 _ => {
1668 self.tokenizer.problem(
1669 key_start,
1670 format!(
1671 "value for key \"{key}\" in {} ended unexpectedly",
1672 in_macro.name
1673 ),
1674 DiagnosticLevel::Error,
1675 );
1676 break;
1677 }
1678 }
1679 }
1680 OptMap { inner: map }
1681 } else {
1682 OptMap {
1683 inner: VecMap::new(),
1684 }
1685 }
1686 }
1687
1688 pub fn read_opt_name_normalized(
1689 &mut self,
1690 r#in: &mut Macro<'a, Pos>,
1691 ) -> Option<(Cow<'a, str>, StringRange<Pos>)> {
1692 self.skip_comments();
1693 if self.tokenizer.reader.starts_with('[') {
1694 self.tokenizer.reader.next_char();
1695 self.tokenizer.reader.trim_start();
1696 let tstart = self.curr_pos();
1697 let first_str = self
1698 .tokenizer
1699 .reader
1700 .read_until_with_brackets('{', '}', |c| c == ']' || c == '%');
1701 let first_end = self.curr_pos();
1702 if self.tokenizer.reader.starts_with('%') {
1703 let mut nexts = SmallVec::<_, 2>::new();
1704 let mut end_pos = self.curr_pos();
1705 loop {
1706 self.skip_comments();
1707 let next = self
1708 .tokenizer
1709 .reader
1710 .read_until_with_brackets('{', '}', |c| c == ']' || c == '%');
1711 end_pos = self.curr_pos();
1712 nexts.push(next);
1713 if self.tokenizer.reader.starts_with('%') {
1714 continue;
1715 }
1716 self.tokenizer.reader.next_char();
1717 break;
1718 }
1719 let range = StringRange {
1720 start: tstart,
1721 end: end_pos,
1722 };
1723 r#in.range.end = self.curr_pos();
1724 if nexts.iter().all(|s| s.trim().is_empty()) {
1725 Some((normalize_ws(first_str), range))
1726 } else {
1727 Some((Cow::Owned(join_strs(first_str, nexts)), range))
1728 }
1729 } else {
1730 self.tokenizer.reader.next_char();
1731 let range = StringRange {
1732 start: tstart,
1733 end: first_end,
1734 };
1735 r#in.range.end = self.curr_pos();
1736 Some((normalize_ws(first_str), range))
1737 }
1738 } else {
1739 None
1740 }
1741 }
1742
1743 pub fn read_name_normalized(
1744 &mut self,
1745 r#in: &mut Macro<'a, Pos>,
1746 ) -> Option<(Cow<'a, str>, StringRange<Pos>)> {
1747 self.skip_comments();
1748 if self.tokenizer.reader.starts_with('{') {
1749 self.tokenizer.reader.next_char();
1750 self.skip_comments();
1751 let tstart = self.curr_pos();
1752 let first_str = self
1753 .tokenizer
1754 .reader
1755 .read_until_with_brackets('{', '}', |c| c == '}' || c == '%');
1756 let first_end = self.curr_pos();
1757 if self.tokenizer.reader.starts_with('%') {
1758 let mut nexts = SmallVec::<_, 2>::new();
1759 let mut end_pos = self.curr_pos();
1760 loop {
1761 self.skip_comments();
1762 let next = self
1763 .tokenizer
1764 .reader
1765 .read_until_with_brackets('{', '}', |c| c == '}' || c == '%');
1766 end_pos = self.curr_pos();
1767 nexts.push(next);
1768 if self.tokenizer.reader.starts_with('%') {
1769 continue;
1770 }
1771 self.tokenizer.reader.next_char();
1772 break;
1773 }
1774 let range = StringRange {
1775 start: tstart,
1776 end: end_pos,
1777 };
1778 r#in.range.end = self.curr_pos();
1779 if nexts.iter().all(|s| s.trim().is_empty()) {
1780 Some((normalize_ws(first_str), range))
1781 } else {
1782 Some((Cow::Owned(join_strs(first_str, nexts)), range))
1783 }
1784 } else {
1785 self.tokenizer.reader.next_char();
1786 let range = StringRange {
1787 start: tstart,
1788 end: first_end,
1789 };
1790 r#in.range.end = self.curr_pos();
1791 Some((normalize_ws(first_str), range))
1792 }
1793 } else {
1794 let start = self.curr_pos();
1795 let c = self.tokenizer.reader.read_n(1);
1796 Some((
1797 Cow::Borrowed(c),
1798 StringRange {
1799 start,
1800 end: self.curr_pos(),
1801 },
1802 ))
1803 }
1804 }
1805
1806 pub fn read_names_normalized(
1807 &mut self,
1808 r#in: &mut Macro<'a, Pos>,
1809 ) -> Vec<(Cow<'a, str>, StringRange<Pos>)> {
1810 self.skip_comments();
1811 if self.tokenizer.reader.starts_with('{') {
1812 self.tokenizer.reader.next_char();
1813 let mut ret = Vec::new();
1814 loop {
1815 self.skip_comments();
1816 let tstart = self.curr_pos();
1817 let first_str = self
1818 .tokenizer
1819 .reader
1820 .read_until_with_brackets('{', '}', |c| c == '}' || c == ',' || c == '%');
1821 let first_end = self.curr_pos();
1822 if self.tokenizer.reader.starts_with('%') {
1823 let mut nexts = SmallVec::<_, 2>::new();
1824 let mut end_pos = self.curr_pos();
1825 loop {
1826 self.skip_comments();
1827 let next = self
1828 .tokenizer
1829 .reader
1830 .read_until_with_brackets('{', '}', |c| {
1831 c == '}' || c == '%' || c == ','
1832 });
1833 end_pos = self.curr_pos();
1834 nexts.push(next);
1835 if self.tokenizer.reader.starts_with('%') {
1836 continue;
1837 }
1838 break;
1839 }
1840 let range = StringRange {
1841 start: tstart,
1842 end: end_pos,
1843 };
1844 if nexts.iter().all(|s| s.trim().is_empty()) {
1845 ret.push((normalize_ws(first_str), range));
1846 } else {
1847 ret.push((Cow::Owned(join_strs(first_str, nexts)), range));
1848 }
1849 if self.tokenizer.reader.next_char().is_some_and(|c| c == ',') {
1850 continue;
1851 }
1852 break;
1853 }
1854 let range = StringRange {
1855 start: tstart,
1856 end: first_end,
1857 };
1858 ret.push((normalize_ws(first_str), range));
1859 if self.tokenizer.reader.next_char().is_some_and(|c| c == ',') {
1860 continue;
1861 }
1862 break;
1863 }
1864 r#in.range.end = self.curr_pos();
1865 ret
1866 } else {
1867 Vec::new()
1868 }
1869 }
1870}
1871
1872fn normalize_ws(s: &str) -> Cow<'_, str> {
1873 if s.contains(['\t', ' ', '\r', '\n']) {
1874 let v = s
1875 .trim()
1876 .split_ascii_whitespace()
1877 .collect::<SmallVec<_, 2>>();
1878 Cow::Owned(v.join(" "))
1879 } else {
1880 Cow::Borrowed(s)
1881 }
1882}
1883
1884fn join_strs(first: &str, rest: SmallVec<&str, 2>) -> String {
1885 let mut ret_str = first.trim_start().to_string();
1886 for r in rest {
1887 ret_str.push_str(r.trim_start());
1888 }
1889 let v = ret_str
1890 .trim_end()
1891 .split_ascii_whitespace()
1892 .collect::<SmallVec<_, 2>>();
1893 v.join(" ")
1894}
1895
1896impl<
1897 'a,
1898 Pos: StringPosition,
1899 T: FromLaTeXToken<'a, Pos> + CondSerialize,
1900 State: ParserState<'a, Pos, T>,
1901> Iterator for LaTeXParser<'a, Pos, T, State>
1902{
1903 type Item = T;
1904 fn next(&mut self) -> Option<T> {
1905 if let Some(t) = self.buf.pop() {
1906 return Some(t);
1907 }
1908 while let Some(t) = self.tokenizer.next() {
1909 if let Some(n) = self.default(t) {
1910 return Some(n);
1911 }
1912 }
1913 None
1914 }
1915}
1916
1917