1use crate::atom::AtomClass;
13use crate::node::{
14 AccentKind, DelimSize, FracSpec, LineAlign, MathFont, PhantomKind, SpaceKind, StackKind,
15 TextStyle,
16};
17use crate::style::Style;
18
19pub const TIER2_TRACKING: &str = "franken_manim fm-j5t (the tier-2 construct program)";
21
22pub const UNTIERED_TRACKING: &str = "https://github.com/Dicklesworthstone/franken_manim/issues";
24
25pub const LAYOUT_PENDING_TRACKING: &str = "franken_manim fm-kg9 (the fmd-math extensions bead)";
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum Tier {
32 T1,
34 T2,
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum ConstructStatus {
44 Supported,
46 UnsupportedT2,
49 Unknown,
52}
53
54#[derive(Clone, Debug, PartialEq)]
56pub(crate) enum Cmd {
57 Sym {
60 ch: char,
61 class: AtomClass,
62 delim: bool,
63 },
64 BigOp { ch: char, integral: bool },
66 OpName {
68 rendered: &'static str,
69 limits: bool,
70 },
71 OperatorName,
73 Accent(AccentKind),
75 Frac(FracSpec),
77 OverInfix(FracSpec),
80 Radical,
82 Text,
84 TextStyled(TextStyle),
86 Alphabet(MathFont),
88 StyleSwitch(Style),
90 AlignDecl(LineAlign),
92 LineSpacing(f64),
94 SizeChange(f64),
97 Spacing(SpaceKind),
99 Phantom(PhantomKind),
101 Stack(StackKind),
103 Substack,
107 Color,
109 SizedDelim {
112 size: DelimSize,
113 class: Option<AtomClass>,
114 },
115 Left,
117 Right,
119 Limits,
121 NoLimits,
123 Begin,
125 End,
127 Ding,
130 XArrow {
132 mapsto: bool,
134 },
135 UnsupportedT2,
137}
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
141pub(crate) struct EnvDef {
142 pub(crate) has_spec: bool,
144}
145
146const fn sym(ch: char, class: AtomClass) -> Cmd {
147 Cmd::Sym {
148 ch,
149 class,
150 delim: false,
151 }
152}
153
154const fn delim_sym(ch: char, class: AtomClass) -> Cmd {
155 Cmd::Sym {
156 ch,
157 class,
158 delim: true,
159 }
160}
161
162#[allow(clippy::too_many_lines)]
164pub(crate) fn lookup(name: &str) -> Option<Cmd> {
165 use AtomClass::{Bin, Close, Inner, Open, Ord, Punct, Rel};
166 Some(match name {
167 "alpha" => sym('α', Ord),
169 "beta" => sym('β', Ord),
170 "gamma" => sym('γ', Ord),
171 "delta" => sym('δ', Ord),
172 "epsilon" => sym('ϵ', Ord),
173 "varepsilon" => sym('ε', Ord),
174 "zeta" => sym('ζ', Ord),
175 "eta" => sym('η', Ord),
176 "theta" => sym('θ', Ord),
177 "vartheta" => sym('ϑ', Ord),
178 "iota" => sym('ι', Ord),
179 "kappa" => sym('κ', Ord),
180 "lambda" => sym('λ', Ord),
181 "mu" => sym('μ', Ord),
182 "nu" => sym('ν', Ord),
183 "xi" => sym('ξ', Ord),
184 "pi" => sym('π', Ord),
185 "varpi" => sym('ϖ', Ord),
186 "rho" => sym('ρ', Ord),
187 "varrho" => sym('ϱ', Ord),
188 "sigma" => sym('σ', Ord),
189 "varsigma" => sym('ς', Ord),
190 "tau" => sym('τ', Ord),
191 "upsilon" => sym('υ', Ord),
192 "phi" => sym('ϕ', Ord),
193 "varphi" => sym('φ', Ord),
194 "chi" => sym('χ', Ord),
195 "psi" => sym('ψ', Ord),
196 "omega" => sym('ω', Ord),
197 "Gamma" => sym('Γ', Ord),
199 "Delta" => sym('Δ', Ord),
200 "Theta" => sym('Θ', Ord),
201 "Lambda" => sym('Λ', Ord),
202 "Xi" => sym('Ξ', Ord),
203 "Pi" => sym('Π', Ord),
204 "Sigma" => sym('Σ', Ord),
205 "Upsilon" => sym('ϒ', Ord),
206 "Phi" => sym('Φ', Ord),
207 "Psi" => sym('Ψ', Ord),
208 "Omega" => sym('Ω', Ord),
209 "infty" => sym('∞', Ord),
211 "partial" => sym('∂', Ord),
212 "nabla" => sym('∇', Ord),
213 "hbar" => sym('ℏ', Ord),
214 "ell" => sym('ℓ', Ord),
215 "wp" => sym('℘', Ord),
216 "Re" => sym('ℜ', Ord),
217 "Im" => sym('ℑ', Ord),
218 "aleph" => sym('ℵ', Ord),
219 "imath" | "i" => sym('ı', Ord),
220 "jmath" | "j" => sym('ȷ', Ord),
221 "emptyset" => sym('∅', Ord),
222 "varnothing" => sym('⌀', Ord),
223 "exists" => sym('∃', Ord),
224 "forall" => sym('∀', Ord),
225 "neg" | "lnot" => sym('¬', Ord),
226 "prime" => sym('′', Ord),
227 "angle" => sym('∠', Ord),
228 "triangle" => sym('△', Ord),
229 "checkmark" => sym('✓', Ord),
230 "copyright" => sym('©', Ord),
236 "female" => sym('\u{2640}', Ord),
237 "male" | "mars" => sym('\u{2642}', Ord),
238 "earth" => sym('\u{2641}', Ord),
239 "ding" => Cmd::Ding,
240 "xrightarrow" => Cmd::XArrow { mapsto: false },
241 "xmapsto" => Cmd::XArrow { mapsto: true },
242 "top" => sym('⊤', Ord),
243 "bot" => sym('⊥', Ord),
244 "flat" => sym('♭', Ord),
245 "natural" => sym('♮', Ord),
246 "sharp" => sym('♯', Ord),
247 "clubsuit" => sym('♣', Ord),
248 "diamondsuit" => sym('♢', Ord),
249 "heartsuit" => sym('♡', Ord),
250 "spadesuit" => sym('♠', Ord),
251 "backslash" => delim_sym('\\', Ord),
252 "vert" => delim_sym('|', Ord),
253 "Vert" => delim_sym('‖', Ord),
254 "ldots" | "dots" | "dotsc" | "dotso" => sym('…', Inner),
257 "cdots" | "dotsb" | "hdots" => sym('⋯', Inner),
258 "vdots" => sym('⋮', Ord),
259 "ddots" => sym('⋱', Inner),
260 "pm" => sym('±', Bin),
262 "mp" => sym('∓', Bin),
263 "cdot" => sym('⋅', Bin),
264 "times" => sym('×', Bin),
265 "div" => sym('÷', Bin),
266 "ast" => sym('∗', Bin),
267 "star" => sym('⋆', Bin),
268 "circ" => sym('∘', Bin),
269 "bullet" => sym('∙', Bin),
270 "oplus" => sym('⊕', Bin),
271 "ominus" => sym('⊖', Bin),
272 "otimes" => sym('⊗', Bin),
273 "oslash" => sym('⊘', Bin),
274 "odot" => sym('⊙', Bin),
275 "cup" => sym('∪', Bin),
276 "cap" => sym('∩', Bin),
277 "sqcup" => sym('⊔', Bin),
278 "sqcap" => sym('⊓', Bin),
279 "vee" | "lor" => sym('∨', Bin),
280 "wedge" | "land" => sym('∧', Bin),
281 "setminus" | "smallsetminus" => sym('∖', Bin),
282 "wr" => sym('≀', Bin),
283 "diamond" => sym('⋄', Bin),
284 "triangleleft" => sym('◁', Bin),
285 "triangleright" => sym('▷', Bin),
286 "uplus" => sym('⊎', Bin),
287 "amalg" => sym('⨿', Bin),
288 "dagger" => sym('†', Bin),
289 "ddagger" => sym('‡', Bin),
290 "minus" => sym('−', Bin),
293 "le" | "leq" => sym('≤', Rel),
295 "ge" | "geq" => sym('≥', Rel),
296 "ne" | "neq" => sym('≠', Rel),
297 "equiv" => sym('≡', Rel),
298 "approx" => sym('≈', Rel),
299 "sim" => sym('∼', Rel),
300 "simeq" => sym('≃', Rel),
301 "cong" => sym('≅', Rel),
302 "doteq" => sym('≐', Rel),
303 "propto" => sym('∝', Rel),
304 "perp" => sym('⊥', Rel),
305 "mid" => sym('∣', Rel),
306 "nmid" => sym('∤', Rel),
307 "parallel" => sym('∥', Rel),
308 "in" => sym('∈', Rel),
309 "ni" => sym('∋', Rel),
310 "notin" => sym('∉', Rel),
311 "subset" => sym('⊂', Rel),
312 "supset" => sym('⊃', Rel),
313 "subseteq" => sym('⊆', Rel),
314 "supseteq" => sym('⊇', Rel),
315 "sqsubseteq" => sym('⊑', Rel),
316 "sqsupseteq" => sym('⊒', Rel),
317 "ll" => sym('≪', Rel),
318 "gg" => sym('≫', Rel),
319 "prec" => sym('≺', Rel),
320 "succ" => sym('≻', Rel),
321 "preceq" => sym('⪯', Rel),
322 "succeq" => sym('⪰', Rel),
323 "asymp" => sym('≍', Rel),
324 "bowtie" => sym('⋈', Rel),
325 "models" => sym('⊨', Rel),
326 "vdash" => sym('⊢', Rel),
327 "dashv" => sym('⊣', Rel),
328 "smile" => sym('⌣', Rel),
329 "frown" => sym('⌢', Rel),
330 "colon" => sym(':', Punct),
331 "rightarrow" | "to" => sym('→', Rel),
333 "leftarrow" | "gets" => sym('←', Rel),
334 "leftrightarrow" => sym('↔', Rel),
335 "Rightarrow" => sym('⇒', Rel),
336 "Leftarrow" => sym('⇐', Rel),
337 "Leftrightarrow" => sym('⇔', Rel),
338 "longrightarrow" => sym('⟶', Rel),
339 "longleftarrow" => sym('⟵', Rel),
340 "longleftrightarrow" => sym('⟷', Rel),
341 "Longrightarrow" | "implies" => sym('⟹', Rel),
342 "Longleftarrow" | "impliedby" => sym('⟸', Rel),
343 "Longleftrightarrow" | "iff" => sym('⟺', Rel),
344 "mapsto" => sym('↦', Rel),
345 "longmapsto" => sym('⟼', Rel),
346 "uparrow" => delim_sym('↑', Rel),
347 "downarrow" => delim_sym('↓', Rel),
348 "updownarrow" => delim_sym('↕', Rel),
349 "Uparrow" => delim_sym('⇑', Rel),
350 "Downarrow" => delim_sym('⇓', Rel),
351 "Updownarrow" => delim_sym('⇕', Rel),
352 "nearrow" => sym('↗', Rel),
353 "searrow" => sym('↘', Rel),
354 "swarrow" => sym('↙', Rel),
355 "nwarrow" => sym('↖', Rel),
356 "hookrightarrow" => sym('↪', Rel),
357 "circlearrowleft" => sym('↺', Rel),
358 "circlearrowright" => sym('↻', Rel),
359 "hookleftarrow" => sym('↩', Rel),
360 "rightharpoonup" => sym('⇀', Rel),
361 "rightharpoondown" => sym('⇁', Rel),
362 "leftharpoonup" => sym('↼', Rel),
363 "leftharpoondown" => sym('↽', Rel),
364 "rightleftharpoons" => sym('⇌', Rel),
365 "langle" => delim_sym('⟨', Open),
367 "rangle" => delim_sym('⟩', Close),
368 "lceil" => delim_sym('⌈', Open),
369 "rceil" => delim_sym('⌉', Close),
370 "lfloor" => delim_sym('⌊', Open),
371 "rfloor" => delim_sym('⌋', Close),
372 "lbrace" => delim_sym('{', Open),
373 "rbrace" => delim_sym('}', Close),
374 "lbrack" => delim_sym('[', Open),
375 "rbrack" => delim_sym(']', Close),
376 "sum" => Cmd::BigOp {
378 ch: '∑',
379 integral: false,
380 },
381 "prod" => Cmd::BigOp {
382 ch: '∏',
383 integral: false,
384 },
385 "coprod" => Cmd::BigOp {
386 ch: '∐',
387 integral: false,
388 },
389 "int" => Cmd::BigOp {
390 ch: '∫',
391 integral: true,
392 },
393 "oint" => Cmd::BigOp {
394 ch: '∮',
395 integral: true,
396 },
397 "iint" => Cmd::BigOp {
398 ch: '∬',
399 integral: true,
400 },
401 "oiint" => Cmd::BigOp {
402 ch: '∯',
403 integral: true,
404 },
405 "iiint" => Cmd::BigOp {
406 ch: '∭',
407 integral: true,
408 },
409 "bigcup" => Cmd::BigOp {
410 ch: '⋃',
411 integral: false,
412 },
413 "bigcap" => Cmd::BigOp {
414 ch: '⋂',
415 integral: false,
416 },
417 "bigvee" => Cmd::BigOp {
418 ch: '⋁',
419 integral: false,
420 },
421 "bigwedge" => Cmd::BigOp {
422 ch: '⋀',
423 integral: false,
424 },
425 "bigoplus" => Cmd::BigOp {
426 ch: '⨁',
427 integral: false,
428 },
429 "bigotimes" => Cmd::BigOp {
430 ch: '⨂',
431 integral: false,
432 },
433 "bigodot" => Cmd::BigOp {
434 ch: '⨀',
435 integral: false,
436 },
437 "biguplus" => Cmd::BigOp {
438 ch: '⨄',
439 integral: false,
440 },
441 "bigsqcup" => Cmd::BigOp {
442 ch: '⨆',
443 integral: false,
444 },
445 "sin" => Cmd::OpName {
447 rendered: "sin",
448 limits: false,
449 },
450 "cos" => Cmd::OpName {
451 rendered: "cos",
452 limits: false,
453 },
454 "tan" => Cmd::OpName {
455 rendered: "tan",
456 limits: false,
457 },
458 "cot" => Cmd::OpName {
459 rendered: "cot",
460 limits: false,
461 },
462 "sec" => Cmd::OpName {
463 rendered: "sec",
464 limits: false,
465 },
466 "csc" => Cmd::OpName {
467 rendered: "csc",
468 limits: false,
469 },
470 "arcsin" => Cmd::OpName {
471 rendered: "arcsin",
472 limits: false,
473 },
474 "arccos" => Cmd::OpName {
475 rendered: "arccos",
476 limits: false,
477 },
478 "arctan" => Cmd::OpName {
479 rendered: "arctan",
480 limits: false,
481 },
482 "sinh" => Cmd::OpName {
483 rendered: "sinh",
484 limits: false,
485 },
486 "cosh" => Cmd::OpName {
487 rendered: "cosh",
488 limits: false,
489 },
490 "tanh" => Cmd::OpName {
491 rendered: "tanh",
492 limits: false,
493 },
494 "coth" => Cmd::OpName {
495 rendered: "coth",
496 limits: false,
497 },
498 "exp" => Cmd::OpName {
499 rendered: "exp",
500 limits: false,
501 },
502 "log" => Cmd::OpName {
503 rendered: "log",
504 limits: false,
505 },
506 "lg" => Cmd::OpName {
507 rendered: "lg",
508 limits: false,
509 },
510 "ln" => Cmd::OpName {
511 rendered: "ln",
512 limits: false,
513 },
514 "arg" => Cmd::OpName {
515 rendered: "arg",
516 limits: false,
517 },
518 "deg" => Cmd::OpName {
519 rendered: "deg",
520 limits: false,
521 },
522 "dim" => Cmd::OpName {
523 rendered: "dim",
524 limits: false,
525 },
526 "hom" => Cmd::OpName {
527 rendered: "hom",
528 limits: false,
529 },
530 "ker" => Cmd::OpName {
531 rendered: "ker",
532 limits: false,
533 },
534 "mod" | "bmod" => Cmd::OpName {
537 rendered: "mod",
538 limits: false,
539 },
540 "lim" => Cmd::OpName {
541 rendered: "lim",
542 limits: true,
543 },
544 "limsup" => Cmd::OpName {
545 rendered: "lim sup",
546 limits: true,
547 },
548 "liminf" => Cmd::OpName {
549 rendered: "lim inf",
550 limits: true,
551 },
552 "max" => Cmd::OpName {
553 rendered: "max",
554 limits: true,
555 },
556 "min" => Cmd::OpName {
557 rendered: "min",
558 limits: true,
559 },
560 "sup" => Cmd::OpName {
561 rendered: "sup",
562 limits: true,
563 },
564 "inf" => Cmd::OpName {
565 rendered: "inf",
566 limits: true,
567 },
568 "det" => Cmd::OpName {
569 rendered: "det",
570 limits: true,
571 },
572 "gcd" => Cmd::OpName {
573 rendered: "gcd",
574 limits: true,
575 },
576 "Pr" => Cmd::OpName {
577 rendered: "Pr",
578 limits: true,
579 },
580 "operatorname" => Cmd::OperatorName,
581 "hat" => Cmd::Accent(AccentKind::Hat),
583 "check" => Cmd::Accent(AccentKind::Check),
584 "tilde" => Cmd::Accent(AccentKind::Tilde),
585 "acute" => Cmd::Accent(AccentKind::Acute),
586 "grave" => Cmd::Accent(AccentKind::Grave),
587 "dot" => Cmd::Accent(AccentKind::Dot),
588 "ddot" => Cmd::Accent(AccentKind::Ddot),
589 "dddot" => Cmd::Accent(AccentKind::Dddot),
590 "ddddot" => Cmd::Accent(AccentKind::Ddddot),
591 "breve" => Cmd::Accent(AccentKind::Breve),
592 "bar" => Cmd::Accent(AccentKind::Bar),
593 "vec" => Cmd::Accent(AccentKind::Vec),
594 "mathring" => Cmd::Accent(AccentKind::Ring),
595 "widehat" => Cmd::Accent(AccentKind::WideHat),
596 "widetilde" => Cmd::Accent(AccentKind::WideTilde),
597 "overline" => Cmd::Accent(AccentKind::OverLine),
598 "overbrace" => Cmd::Accent(AccentKind::OverBrace),
599 "underbrace" => Cmd::Accent(AccentKind::UnderBrace),
600 "overrightarrow" => Cmd::Accent(AccentKind::OverRightArrow),
601 "overleftarrow" => Cmd::Accent(AccentKind::OverLeftArrow),
602 "underline" => Cmd::Accent(AccentKind::UnderLine),
605 "frac" => Cmd::Frac(FracSpec {
607 bar: true,
608 delims: None,
609 forced_style: None,
610 }),
611 "dfrac" => Cmd::Frac(FracSpec {
612 bar: true,
613 delims: None,
614 forced_style: Some(Style::Display),
615 }),
616 "tfrac" => Cmd::Frac(FracSpec {
617 bar: true,
618 delims: None,
619 forced_style: Some(Style::Text),
620 }),
621 "binom" => Cmd::Frac(FracSpec {
622 bar: false,
623 delims: Some(('(', ')')),
624 forced_style: None,
625 }),
626 "over" => Cmd::OverInfix(FracSpec {
627 bar: true,
628 delims: None,
629 forced_style: None,
630 }),
631 "choose" => Cmd::OverInfix(FracSpec {
632 bar: false,
633 delims: Some(('(', ')')),
634 forced_style: None,
635 }),
636 "sqrt" => Cmd::Radical,
637 "text" => Cmd::Text,
639 "textbf" => Cmd::TextStyled(TextStyle::Bold),
640 "emph" => Cmd::TextStyled(TextStyle::Emph),
641 "mathbb" => Cmd::Alphabet(MathFont::Blackboard),
642 "mathds" => Cmd::Alphabet(MathFont::Blackboard),
645 "mathcal" => Cmd::Alphabet(MathFont::Calligraphic),
646 "mathrm" => Cmd::Alphabet(MathFont::Roman),
647 "mathbf" => Cmd::Alphabet(MathFont::Bold),
648 "boldsymbol" => Cmd::Alphabet(MathFont::BoldItalic),
649 "mathsf" => Cmd::Alphabet(MathFont::SansSerif),
650 "mathtt" => Cmd::Alphabet(MathFont::Typewriter),
651 "mathit" => Cmd::Alphabet(MathFont::Italic),
652 "displaystyle" => Cmd::StyleSwitch(Style::Display),
654 "textstyle" => Cmd::StyleSwitch(Style::Text),
655 "scriptstyle" => Cmd::StyleSwitch(Style::Script),
656 "scriptscriptstyle" => Cmd::StyleSwitch(Style::ScriptScript),
657 "tiny" => Cmd::SizeChange(0.5),
661 "footnotesize" => Cmd::SizeChange(0.8),
662 "small" => Cmd::SizeChange(0.9),
663 "large" => Cmd::SizeChange(1.2),
664 "Large" => Cmd::SizeChange(1.44),
665 "huge" => Cmd::SizeChange(2.074),
666 "quad" => Cmd::Spacing(SpaceKind::Quad),
667 "qquad" => Cmd::Spacing(SpaceKind::Qquad),
668 "phantom" => Cmd::Phantom(PhantomKind::Full),
669 "hphantom" => Cmd::Phantom(PhantomKind::Horizontal),
670 "vphantom" => Cmd::Phantom(PhantomKind::Vertical),
671 "stackrel" => Cmd::Stack(StackKind::Stackrel),
672 "overset" => Cmd::Stack(StackKind::Overset),
673 "underset" => Cmd::Stack(StackKind::Underset),
674 "substack" => Cmd::Substack,
675 "color" => Cmd::Color,
676 "big" => Cmd::SizedDelim {
678 size: DelimSize::Big,
679 class: None,
680 },
681 "Big" => Cmd::SizedDelim {
682 size: DelimSize::BBig,
683 class: None,
684 },
685 "bigg" => Cmd::SizedDelim {
686 size: DelimSize::Bigg,
687 class: None,
688 },
689 "Bigg" => Cmd::SizedDelim {
690 size: DelimSize::BBigg,
691 class: None,
692 },
693 "bigl" => Cmd::SizedDelim {
694 size: DelimSize::Big,
695 class: Some(AtomClass::Open),
696 },
697 "Bigl" => Cmd::SizedDelim {
698 size: DelimSize::BBig,
699 class: Some(AtomClass::Open),
700 },
701 "biggl" => Cmd::SizedDelim {
702 size: DelimSize::Bigg,
703 class: Some(AtomClass::Open),
704 },
705 "Biggl" => Cmd::SizedDelim {
706 size: DelimSize::BBigg,
707 class: Some(AtomClass::Open),
708 },
709 "bigr" => Cmd::SizedDelim {
710 size: DelimSize::Big,
711 class: Some(AtomClass::Close),
712 },
713 "Bigr" => Cmd::SizedDelim {
714 size: DelimSize::BBig,
715 class: Some(AtomClass::Close),
716 },
717 "biggr" => Cmd::SizedDelim {
718 size: DelimSize::Bigg,
719 class: Some(AtomClass::Close),
720 },
721 "Biggr" => Cmd::SizedDelim {
722 size: DelimSize::BBigg,
723 class: Some(AtomClass::Close),
724 },
725 "bigm" => Cmd::SizedDelim {
726 size: DelimSize::Big,
727 class: Some(AtomClass::Rel),
728 },
729 "Bigm" => Cmd::SizedDelim {
730 size: DelimSize::BBig,
731 class: Some(AtomClass::Rel),
732 },
733 "biggm" => Cmd::SizedDelim {
734 size: DelimSize::Bigg,
735 class: Some(AtomClass::Rel),
736 },
737 "Biggm" => Cmd::SizedDelim {
738 size: DelimSize::BBigg,
739 class: Some(AtomClass::Rel),
740 },
741 "left" => Cmd::Left,
743 "right" => Cmd::Right,
744 "limits" => Cmd::Limits,
745 "nolimits" => Cmd::NoLimits,
746 "begin" => Cmd::Begin,
747 "end" => Cmd::End,
748 "centering" => Cmd::AlignDecl(LineAlign::Center),
751 "doublespacing" => Cmd::LineSpacing(1.667),
753 "dx" => Cmd::UnsupportedT2,
756 _ => return None,
757 })
758}
759
760#[inline(always)]
765pub(crate) fn ding_char(code: u32) -> Option<char> {
766 match code {
767 51 => Some('✓'),
768 _ => None,
769 }
770}
771
772#[inline(always)]
774pub(crate) fn lookup_env(name: &str) -> Option<EnvDef> {
775 match name {
776 "array" => Some(EnvDef { has_spec: true }),
777 "align" | "align*" | "aligned" | "cases" | "matrix" | "pmatrix" | "bmatrix" | "Bmatrix"
778 | "vmatrix" | "Vmatrix" | "smallmatrix" | "substack" => Some(EnvDef { has_spec: false }),
779 _ => None,
780 }
781}
782
783#[inline(always)]
787pub(crate) fn line_align_env(name: &str) -> Option<LineAlign> {
788 match name {
789 "flushleft" => Some(LineAlign::Left),
790 "center" => Some(LineAlign::Center),
791 "flushright" => Some(LineAlign::Right),
792 _ => None,
793 }
794}
795
796#[inline(always)]
800pub(crate) fn control_symbol_is_t2(ch: char) -> bool {
801 let _ = ch;
802 false
803}
804
805pub(crate) fn compose_text_accent(accent: char, base: char) -> Option<char> {
810 Some(match (accent, base) {
811 ('\'', 'a') => 'á',
812 ('\'', 'e') => 'é',
813 ('\'', 'i') => 'í',
814 ('\'', 'o') => 'ó',
815 ('\'', 'u') => 'ú',
816 ('\'', 'y') => 'ý',
817 ('\'', 'A') => 'Á',
818 ('\'', 'E') => 'É',
819 ('\'', 'I') => 'Í',
820 ('\'', 'O') => 'Ó',
821 ('\'', 'U') => 'Ú',
822 ('"', 'a') => 'ä',
823 ('"', 'e') => 'ë',
824 ('"', 'i') => 'ï',
825 ('"', 'o') => 'ö',
826 ('"', 'u') => 'ü',
827 ('"', 'y') => 'ÿ',
828 ('"', 'A') => 'Ä',
829 ('"', 'E') => 'Ë',
830 ('"', 'I') => 'Ï',
831 ('"', 'O') => 'Ö',
832 ('"', 'U') => 'Ü',
833 _ => return None,
834 })
835}
836
837pub(crate) fn char_is_delim(ch: char) -> bool {
840 matches!(ch, '(' | ')' | '[' | ']' | '|' | '/' | '<' | '>' | '.')
841}
842
843#[must_use]
849pub fn construct_status(construct: &str) -> ConstructStatus {
850 match construct {
853 "script:sup" | "script:sub" | "prime" | "tie" | "alignment-tab" | "math-island" => {
854 return ConstructStatus::Supported;
855 }
856 _ => {}
857 }
858 if construct.starts_with("char:U+") {
859 return ConstructStatus::Supported;
860 }
861 if let Some(env) = construct.strip_prefix("env:") {
862 if lookup_env(env).is_some() || line_align_env(env).is_some() {
863 return ConstructStatus::Supported;
864 }
865 return ConstructStatus::Unknown;
866 }
867 if let Some(name) = construct.strip_prefix('\\') {
868 let mut chars = name.chars();
870 if let (Some(c), None) = (chars.next(), chars.next()) {
871 if !c.is_ascii_alphabetic() {
872 if control_symbol_is_t2(c) {
873 return ConstructStatus::UnsupportedT2;
874 }
875 return if matches!(
879 c,
880 '\\' | ','
881 | ':'
882 | ';'
883 | '!'
884 | ' '
885 | '{'
886 | '}'
887 | '%'
888 | '$'
889 | '&'
890 | '#'
891 | '_'
892 | '|'
893 | '\''
894 | '"'
895 ) {
896 ConstructStatus::Supported
897 } else {
898 ConstructStatus::Unknown
899 };
900 }
901 }
902 if matches!(name, "newcommand" | "renewcommand") {
905 return ConstructStatus::Supported;
906 }
907 return match lookup(name) {
908 Some(Cmd::UnsupportedT2) => ConstructStatus::UnsupportedT2,
909 Some(_) => ConstructStatus::Supported,
910 None => ConstructStatus::Unknown,
911 };
912 }
913 ConstructStatus::Unknown
914}
915
916#[cfg(test)]
917mod tests {
918 use super::*;
919
920 #[test]
921 fn t1_headliners_are_supported() {
922 for c in [
923 r"\frac",
924 r"\over",
925 r"\text",
926 r"\pi",
927 r"\cdot",
928 r"\left",
929 r"\right",
930 r"\sqrt",
931 r"\sum",
932 r"\mathds",
933 r"\minus",
934 r"\checkmark",
935 r"\textbf",
936 r"\emph",
937 r"\big",
938 r"\substack",
939 r"\,",
940 r"\\",
941 r"\{",
942 "env:array",
943 "env:substack",
944 "env:align*",
945 "env:cases",
946 "env:aligned",
947 "script:sup",
948 "math-island",
949 "prime",
950 "alignment-tab",
951 "tie",
952 ] {
953 assert_eq!(
954 construct_status(c),
955 ConstructStatus::Supported,
956 "expected {c} supported"
957 );
958 }
959 }
960
961 #[test]
962 fn t2_vocabulary_is_tiered() {
963 assert_eq!(
966 construct_status(r"\dx"),
967 ConstructStatus::UnsupportedT2,
968 r"expected \dx tier-2 (the last pending construct)"
969 );
970 for c in [
971 r"\nmid",
972 r"\oiint",
973 r"\i",
974 r"\j",
975 r"\circlearrowright",
976 r"\dddot",
977 r"\ddddot",
978 r"\xrightarrow",
979 r"\xmapsto",
980 r"\doublespacing",
981 r"\'",
982 "\\\"",
983 ] {
984 assert_eq!(
985 construct_status(c),
986 ConstructStatus::Supported,
987 "expected {c} graduated"
988 );
989 }
990 }
991
992 #[test]
993 fn astronomy_and_misc_symbols_are_supported() {
994 for c in [
997 r"\female",
998 r"\male",
999 r"\mars",
1000 r"\earth",
1001 r"\copyright",
1002 r"\ding",
1003 ] {
1004 assert_eq!(
1005 construct_status(c),
1006 ConstructStatus::Supported,
1007 "expected {c} supported"
1008 );
1009 }
1010 }
1011
1012 #[test]
1013 fn line_alignment_constructs_are_supported() {
1014 for c in [
1015 r"\centering",
1016 "env:flushleft",
1017 "env:center",
1018 "env:flushright",
1019 ] {
1020 assert_eq!(
1021 construct_status(c),
1022 ConstructStatus::Supported,
1023 "expected {c} supported"
1024 );
1025 }
1026 }
1027
1028 #[test]
1029 fn size_declarations_are_supported() {
1030 for c in [
1031 r"\tiny",
1032 r"\footnotesize",
1033 r"\small",
1034 r"\large",
1035 r"\Large",
1036 r"\huge",
1037 ] {
1038 assert_eq!(
1039 construct_status(c),
1040 ConstructStatus::Supported,
1041 "expected {c} supported"
1042 );
1043 }
1044 }
1045
1046 #[test]
1047 fn unknown_vocabulary_is_unknown() {
1048 assert_eq!(construct_status(r"\notacommand"), ConstructStatus::Unknown);
1049 assert_eq!(construct_status("env:mystery"), ConstructStatus::Unknown);
1050 }
1051
1052 #[test]
1053 fn char_constructs_are_parse_supported() {
1054 assert_eq!(construct_status("char:U+00F6"), ConstructStatus::Supported);
1055 }
1056}