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