pulldown_latex/parser/
primitives.rs

1//! A module that implements the behavior of every primitive of the supported LaTeX syntax. This
2//! includes every primitive macro and active character.
3
4use core::panic;
5
6use crate::event::{
7    ArrayColumn as AC, ColorChange as CC, ColorTarget as CT, ColumnAlignment, Content as C,
8    DelimiterSize, DelimiterType, Dimension, DimensionUnit, EnvironmentFlow, Event as E, Font,
9    Grouping as G, GroupingKind, Line, MatrixType, RelationContent, ScriptPosition as SP,
10    ScriptType as ST, StateChange as SC, Style as S, Visual as V,
11};
12
13use super::{
14    lex,
15    tables::{
16        char_delimiter_map, control_sequence_delimiter_map, is_binary, is_relation, token_to_delim,
17    },
18    AlignmentCount, Argument, CharToken, ErrorKind, InnerParser, InnerResult, Instruction as I,
19    Token,
20};
21
22impl<'b, 'store> InnerParser<'b, 'store> {
23    /// Handle a character token, returning a corresponding event.
24    ///
25    /// This function specially treats numbers as `mi`.
26    ///
27    /// ## Panics
28    /// - This function will panic if the `\` or `%` character is given
29    pub(super) fn handle_char_token(&mut self, token: CharToken<'store>) -> InnerResult<()> {
30        let instruction = I::Event(match token.into() {
31            '\\' => panic!("(internal error: please report) the `\\` character should never be observed as a token"),
32            '%' => panic!("(internal error: please report) the `%` character should never be observed as a token"),
33            '_' => {
34                if self.state.handling_argument {
35                    return Err(ErrorKind::ScriptAsArgument)
36                }
37                self.buffer.extend([
38                    I::Event(E::Begin(G::Normal)),
39                ]);
40                self.content = token.as_str();
41                E::End
42            }
43            '^' => {
44                if self.state.handling_argument {
45                    return Err(ErrorKind::ScriptAsArgument)
46                }
47                self.buffer.extend([
48                    I::Event(E::Begin(G::Normal)),
49                ]);
50                self.content = token.as_str();
51                E::End
52            }
53            '$' => return Err(ErrorKind::MathShift),
54            '#' => return Err(ErrorKind::HashSign),
55            '&' if self
56                    .state
57                    .allowed_alignment_count
58                    .as_deref()
59                    .is_some_and(AlignmentCount::can_increment) && !self.state.handling_argument => {
60                       self
61                           .state
62                           .allowed_alignment_count
63                           .as_mut()
64                           .expect("we have checked that `allowed_alignment_count` is Some")
65                           .increment();
66                        E::EnvironmentFlow(EnvironmentFlow::Alignment)
67                    },
68            '&' => return Err(ErrorKind::Alignment),
69            '{' => {
70                let str = &mut self.content;
71                let group = lex::group_content(str, GroupingKind::Normal)?;
72                self.buffer.extend([
73                    I::Event(E::Begin(G::Normal)),
74                    I::SubGroup { content: group, allowed_alignment_count: None },
75                    I::Event(E::End)
76                ]);
77                return Ok(())
78            },
79            '}' => {
80                return Err(ErrorKind::UnbalancedGroup(None))
81            },
82
83            '~' => {
84                E::Content(C::Text("&nbsp;"))
85            },
86
87            '0'..='9' => {
88                let content = token.as_str();
89                let mut len = content
90                    .chars()
91                    .skip(1)
92                    .take_while(|&c| matches!(c, '.' | ',' | '0'..='9'))
93                    .count()
94                    + 1;
95                if matches!(content.as_bytes()[len - 1], b'.' | b',') {
96                    len -= 1;
97                }
98                let (number, rest) = content.split_at(len);
99                self.content = rest;
100                self.buffer
101                    .push(I::Event(E::Content(C::Number(number))));
102                return Ok(())
103            }
104            // Punctuation
105            '.' | ',' | ';' => E::Content(C::Punctuation(token.into())),
106            '\'' => ordinary('′'),
107            '-' => binary('−'),
108            '*' => binary('∗'),
109            c if is_binary(c) => binary(c),
110            c if is_relation(c) => relation(c),
111            c if char_delimiter_map(c).is_some() => {
112                let (content, ty) = char_delimiter_map(c).unwrap();
113                if ty == DelimiterType::Fence {
114                    ordinary(content)
115                } else {
116                    E::Content(C::Delimiter {
117                        content,
118                        size: None,
119                        ty,
120                    })
121                }
122            }
123            c => ordinary(c),
124        });
125        self.buffer.push(instruction);
126        Ok(())
127    }
128
129    /// Handle a supported control sequence, pushing instructions to the provided stack.
130    pub(super) fn handle_primitive(&mut self, control_sequence: &'store str) -> InnerResult<()> {
131        let event = match control_sequence {
132            "arccos" | "cos" | "csc" | "exp" | "ker" | "sinh" | "arcsin" | "cosh" | "deg"
133            | "lg" | "ln" | "arctan" | "cot" | "det" | "hom" | "log" | "sec" | "tan" | "arg"
134            | "coth" | "dim" | "sin" | "tanh" | "sgn" => E::Content(C::Function(control_sequence)),
135            "lim" | "Pr" | "sup" | "max" | "inf" | "gcd" | "min" => {
136                self.state.allow_script_modifiers = true;
137                self.state.script_position = SP::Movable;
138                E::Content(C::Function(control_sequence))
139            }
140            "liminf" => {
141                self.state.allow_script_modifiers = true;
142                self.state.script_position = SP::Movable;
143                E::Content(C::Function("lim inf"))
144            }
145            "limsup" => {
146                self.state.allow_script_modifiers = true;
147                self.state.script_position = SP::Movable;
148                E::Content(C::Function("lim sup"))
149            }
150
151            "operatorname" => {
152                self.state.allow_script_modifiers = true;
153                let argument = lex::argument(&mut self.content)?;
154                match argument {
155                    Argument::Token(Token::ControlSequence(_)) => {
156                        return Err(ErrorKind::ControlSequenceAsArgument)
157                    }
158                    Argument::Token(Token::Character(char_)) => {
159                        E::Content(C::Function(char_.as_str()))
160                    }
161                    Argument::Group(content) => E::Content(C::Function(content)),
162                }
163            }
164            "bmod" => E::Content(C::Function("mod")),
165            "pmod" => {
166                let argument = lex::argument(&mut self.content)?;
167                self.buffer.extend([
168                    I::Event(E::Space {
169                        width: Some(Dimension::new(1., DimensionUnit::Em)),
170                        height: None,
171                    }),
172                    I::Event(E::Begin(G::Normal)),
173                    I::Event(E::Content(C::Delimiter {
174                        content: '(',
175                        size: None,
176                        ty: DelimiterType::Open,
177                    })),
178                    I::Event(E::Content(C::Function("mod"))),
179                ]);
180                self.handle_argument(argument)?;
181                self.buffer.extend([
182                    I::Event(E::End),
183                    I::Event(E::Content(C::Delimiter {
184                        content: ')',
185                        size: None,
186                        ty: DelimiterType::Close,
187                    })),
188                ]);
189                return Ok(());
190            }
191
192            // TODO: Operators with '*', for operatorname* and friends
193
194            /////////////////////////
195            // Non-Latin Alphabets //
196            /////////////////////////
197            // Lowercase Greek letters
198            "alpha" => ordinary('α'),
199            "beta" => ordinary('β'),
200            "gamma" => ordinary('γ'),
201            "delta" => ordinary('δ'),
202            "epsilon" => ordinary('ϵ'),
203            "zeta" => ordinary('ζ'),
204            "eta" => ordinary('η'),
205            "theta" => ordinary('θ'),
206            "iota" => ordinary('ι'),
207            "kappa" => ordinary('κ'),
208            "lambda" => ordinary('λ'),
209            "mu" => ordinary('µ'),
210            "nu" => ordinary('ν'),
211            "xi" => ordinary('ξ'),
212            "pi" => ordinary('π'),
213            "rho" => ordinary('ρ'),
214            "sigma" => ordinary('σ'),
215            "tau" => ordinary('τ'),
216            "upsilon" => ordinary('υ'),
217            "phi" => ordinary('ϕ'),
218            "chi" => ordinary('χ'),
219            "psi" => ordinary('ψ'),
220            "omega" => ordinary('ω'),
221            "omicron" => ordinary('ο'),
222            // Uppercase Greek letters
223            "Alpha" => ordinary('Α'),
224            "Beta" => ordinary('Β'),
225            "Gamma" => ordinary('Γ'),
226            "Delta" => ordinary('Δ'),
227            "Epsilon" => ordinary('Ε'),
228            "Zeta" => ordinary('Ζ'),
229            "Eta" => ordinary('Η'),
230            "Theta" => ordinary('Θ'),
231            "Iota" => ordinary('Ι'),
232            "Kappa" => ordinary('Κ'),
233            "Lambda" => ordinary('Λ'),
234            "Mu" => ordinary('Μ'),
235            "Nu" => ordinary('Ν'),
236            "Xi" => ordinary('Ξ'),
237            "Pi" => ordinary('Π'),
238            "Rho" => ordinary('Ρ'),
239            "Sigma" => ordinary('Σ'),
240            "Tau" => ordinary('Τ'),
241            "Upsilon" => ordinary('Υ'),
242            "Phi" => ordinary('Φ'),
243            "Chi" => ordinary('Χ'),
244            "Psi" => ordinary('Ψ'),
245            "Omega" => ordinary('Ω'),
246            "Omicron" => ordinary('Ο'),
247            // Lowercase Greek Variants
248            "varepsilon" => ordinary('ε'),
249            "vartheta" => ordinary('ϑ'),
250            "varkappa" => ordinary('ϰ'),
251            "varrho" => ordinary('ϱ'),
252            "varsigma" => ordinary('ς'),
253            "varpi" => ordinary('ϖ'),
254            "varphi" => ordinary('φ'),
255            // Uppercase Greek Variants
256            "varGamma" => ordinary('𝛤'),
257            "varDelta" => ordinary('𝛥'),
258            "varTheta" => ordinary('𝛩'),
259            "varLambda" => ordinary('𝛬'),
260            "varXi" => ordinary('𝛯'),
261            "varPi" => ordinary('𝛱'),
262            "varSigma" => ordinary('𝛴'),
263            "varUpsilon" => ordinary('𝛶'),
264            "varPhi" => ordinary('𝛷'),
265            "varPsi" => ordinary('𝛹'),
266            "varOmega" => ordinary('𝛺'),
267
268            // Hebrew letters
269            "aleph" => ordinary('ℵ'),
270            "beth" => ordinary('ℶ'),
271            "gimel" => ordinary('ℷ'),
272            "daleth" => ordinary('ℸ'),
273            // Other symbols
274            "digamma" => ordinary('ϝ'),
275            "eth" => ordinary('ð'),
276            "ell" => ordinary('ℓ'),
277            "nabla" => ordinary('∇'),
278            "partial" => ordinary('∂'),
279            "Finv" => ordinary('Ⅎ'),
280            "Game" => ordinary('ℷ'),
281            "hbar" | "hslash" => ordinary('ℏ'),
282            "imath" => ordinary('ı'),
283            "jmath" => ordinary('ȷ'),
284            "Im" => ordinary('ℑ'),
285            "Re" => ordinary('ℜ'),
286            "wp" => ordinary('℘'),
287            "Bbbk" => ordinary('𝕜'),
288            "Angstrom" => ordinary('Å'),
289            "backepsilon" => ordinary('϶'),
290
291            ///////////////////////////
292            // Symbols & Punctuation //
293            ///////////////////////////
294            "dots" => {
295                if self.content.trim_start().starts_with(['.', ',']) {
296                    ordinary('…')
297                } else {
298                    ordinary('⋯')
299                }
300            }
301            "ldots" | "dotso" | "dotsc" => ordinary('…'),
302            "cdots" | "dotsi" | "dotsm" | "dotsb" | "idotsin" => ordinary('⋯'),
303            "ddots" => ordinary('⋱'),
304            "iddots" => ordinary('⋰'),
305            "vdots" => ordinary('⋮'),
306            "mathellipsis" => ordinary('…'),
307            "infty" => ordinary('∞'),
308            "checkmark" => ordinary('✓'),
309            "ballotx" => ordinary('✗'),
310            "dagger" | "dag" => ordinary('†'),
311            "ddagger" | "ddag" => ordinary('‡'),
312            "angle" => ordinary('∠'),
313            "measuredangle" => ordinary('∡'),
314            "lq" => ordinary('‘'),
315            "Box" => ordinary('□'),
316            "sphericalangle" => ordinary('∢'),
317            "square" => ordinary('□'),
318            "top" => ordinary('⊤'),
319            "rq" => ordinary('′'),
320            "blacksquare" => ordinary('■'),
321            "bot" => ordinary('⊥'),
322            "triangledown" => ordinary('▽'),
323            "Bot" => ordinary('⫫'),
324            "triangleleft" => ordinary('◃'),
325            "triangleright" => ordinary('▹'),
326            "cent" => ordinary('¢'),
327            "colon" | "ratio" | "vcentcolon" => ordinary(':'),
328            "bigtriangledown" => ordinary('▽'),
329            "pounds" | "mathsterling" => ordinary('£'),
330            "bigtriangleup" => ordinary('△'),
331            "blacktriangle" => ordinary('▲'),
332            "blacktriangledown" => ordinary('▼'),
333            "yen" => ordinary('¥'),
334            "blacktriangleleft" => ordinary('◀'),
335            "euro" => ordinary('€'),
336            "blacktriangleright" => ordinary('▶'),
337            "Diamond" => ordinary('◊'),
338            "degree" => ordinary('°'),
339            "lozenge" => ordinary('◊'),
340            "blacklozenge" => ordinary('⧫'),
341            "mho" => ordinary('℧'),
342            "bigstar" => ordinary('★'),
343            "diagdown" => ordinary('╲'),
344            "maltese" => ordinary('✠'),
345            "diagup" => ordinary('╱'),
346            "P" => ordinary('¶'),
347            "clubsuit" => ordinary('♣'),
348            "varclubsuit" => ordinary('♧'),
349            "S" => ordinary('§'),
350            "diamondsuit" => ordinary('♢'),
351            "vardiamondsuit" => ordinary('♦'),
352            "copyright" => ordinary('©'),
353            "heartsuit" => ordinary('♡'),
354            "varheartsuit" => ordinary('♥'),
355            "circledR" => ordinary('®'),
356            "spadesuit" => ordinary('♠'),
357            "varspadesuit" => ordinary('♤'),
358            "circledS" => ordinary('Ⓢ'),
359            "female" => ordinary('♀'),
360            "male" => ordinary('♂'),
361            "astrosun" => ordinary('☉'),
362            "sun" => ordinary('☼'),
363            "leftmoon" => ordinary('☾'),
364            "rightmoon" => ordinary('☽'),
365            "smiley" => ordinary('☺'),
366            "Earth" => ordinary('⊕'),
367            "flat" => ordinary('♭'),
368            "standardstate" => ordinary('⦵'),
369            "natural" => ordinary('♮'),
370            "sharp" => ordinary('♯'),
371            "permil" => ordinary('‰'),
372            "QED" => ordinary('∎'),
373            "lightning" => ordinary('↯'),
374            "diameter" => ordinary('⌀'),
375            "leftouterjoin" => ordinary('⟕'),
376            "rightouterjoin" => ordinary('⟖'),
377            "concavediamond" => ordinary('⟡'),
378            "concavediamondtickleft" => ordinary('⟢'),
379            "concavediamondtickright" => ordinary('⟣'),
380            "fullouterjoin" => ordinary('⟗'),
381            "triangle" | "vartriangle" => ordinary('△'),
382            "whitesquaretickleft" => ordinary('⟤'),
383            "whitesquaretickright" => ordinary('⟥'),
384
385            ////////////////////////
386            // Font state changes //
387            ////////////////////////
388            // LaTeX native absolute font changes (old behavior a.k.a NFSS 1)
389            "bf" => self.font_change(Font::Bold),
390            "cal" => self.font_change(Font::Script),
391            "it" => self.font_change(Font::Italic),
392            "rm" => self.font_change(Font::UpRight),
393            "sf" => self.font_change(Font::SansSerif),
394            "tt" => self.font_change(Font::Monospace),
395            // amsfonts font changes (old behavior a.k.a NFSS 1)
396            // unicode-math font changes (old behavior a.k.a NFSS 1)
397            // changes, as described in https://mirror.csclub.uwaterloo.ca/CTAN/macros/unicodetex/latex/unicode-math/unicode-math.pdf
398            // (section. 3.1)
399            "mathbf" | "symbf" | "mathbfup" | "symbfup" | "boldsymbol" => {
400                return self.font_group(Some(Font::Bold))
401            }
402            "mathcal" | "symcal" | "mathup" | "symup" => {
403                return self.font_group(Some(Font::Script))
404            }
405            "mathit" | "symit" => return self.font_group(Some(Font::Italic)),
406            "mathrm" | "symrm" => return self.font_group(Some(Font::UpRight)),
407            "mathsf" | "symsf" | "mathsfup" | "symsfup" => {
408                return self.font_group(Some(Font::SansSerif))
409            }
410            "mathtt" | "symtt" => return self.font_group(Some(Font::Monospace)),
411            "mathbb" | "symbb" => return self.font_group(Some(Font::DoubleStruck)),
412            "mathfrak" | "symfrak" => return self.font_group(Some(Font::Fraktur)),
413            "mathbfcal" | "symbfcal" => return self.font_group(Some(Font::BoldScript)),
414            "mathsfit" | "symsfit" => return self.font_group(Some(Font::SansSerifItalic)),
415            "mathbfit" | "symbfit" => return self.font_group(Some(Font::BoldItalic)),
416            "mathbffrak" | "symbffrak" => return self.font_group(Some(Font::BoldFraktur)),
417            "mathbfsfup" | "symbfsfup" => return self.font_group(Some(Font::BoldSansSerif)),
418            "mathbfsfit" | "symbfsfit" => return self.font_group(Some(Font::SansSerifBoldItalic)),
419            "mathnormal" | "symnormal" => return self.font_group(None),
420
421            ////////////////////////
422            // Style state change //
423            ////////////////////////
424            "displaystyle" => self.style_change(S::Display),
425            "textstyle" => self.style_change(S::Text),
426            "scriptstyle" => self.style_change(S::Script),
427            "scriptscriptstyle" => self.style_change(S::ScriptScript),
428
429            ////////////////////////
430            // Color state change //
431            ////////////////////////
432            "color" => {
433                let Argument::Group(color) = lex::argument(&mut self.content)? else {
434                    return Err(ErrorKind::Argument);
435                };
436                self.state.skip_scripts = true;
437
438                let color = lex::color(color).ok_or(ErrorKind::UnknownColor)?;
439                E::StateChange(SC::Color(CC {
440                    color,
441                    target: CT::Text,
442                }))
443            }
444            "textcolor" => {
445                let str = &mut self.content;
446                let Argument::Group(color) = lex::argument(str)? else {
447                    return Err(ErrorKind::Argument);
448                };
449
450                let color = lex::color(color).ok_or(ErrorKind::UnknownColor)?;
451                let modified = lex::argument(str)?;
452
453                self.buffer.extend([
454                    I::Event(E::Begin(G::Normal)),
455                    I::Event(E::StateChange(SC::Color(CC {
456                        color,
457                        target: CT::Text,
458                    }))),
459                ]);
460                self.handle_argument(modified)?;
461                E::End
462            }
463            "colorbox" => {
464                let Argument::Group(color) = lex::argument(&mut self.content)? else {
465                    return Err(ErrorKind::Argument);
466                };
467
468                let color = lex::color(color).ok_or(ErrorKind::UnknownColor)?;
469                self.buffer.extend([
470                    I::Event(E::Begin(G::Normal)),
471                    I::Event(E::StateChange(SC::Color(CC {
472                        color,
473                        target: CT::Background,
474                    }))),
475                ]);
476                self.text_argument()?;
477                E::End
478            }
479            "fcolorbox" => {
480                let str = &mut self.content;
481                let Argument::Group(frame_color) = lex::argument(str)? else {
482                    return Err(ErrorKind::Argument);
483                };
484                let Argument::Group(background_color) = lex::argument(str)? else {
485                    return Err(ErrorKind::Argument);
486                };
487
488                let frame_color = lex::color(frame_color).ok_or(ErrorKind::UnknownColor)?;
489                let background_color =
490                    lex::color(background_color).ok_or(ErrorKind::UnknownColor)?;
491                self.buffer.extend([
492                    I::Event(E::Begin(G::Normal)),
493                    I::Event(E::StateChange(SC::Color(CC {
494                        color: frame_color,
495                        target: CT::Border,
496                    }))),
497                    I::Event(E::StateChange(SC::Color(CC {
498                        color: background_color,
499                        target: CT::Background,
500                    }))),
501                ]);
502                self.text_argument()?;
503                E::End
504            }
505
506            ///////////////////////////////
507            // Delimiters size modifiers //
508            ///////////////////////////////
509            // Sizes taken from `texzilla`
510            // Big left and right seem to not care about which delimiter is used. i.e., \bigl) and \bigr) are the same.
511            "big" | "bigl" | "bigr" | "bigm" => return self.sized_delim(DelimiterSize::Big),
512            "Big" | "Bigl" | "Bigr" | "Bigm" => return self.sized_delim(DelimiterSize::BIG),
513            "bigg" | "biggl" | "biggr" | "biggm" => return self.sized_delim(DelimiterSize::Bigg),
514            "Bigg" | "Biggl" | "Biggr" | "Biggm" => return self.sized_delim(DelimiterSize::BIGG),
515
516            "left" => {
517                let curr_str = &mut self.content;
518                let opening = if let Some(rest) = curr_str.strip_prefix('.') {
519                    *curr_str = rest;
520                    None
521                } else {
522                    Some(lex::delimiter(curr_str)?.0)
523                };
524
525                let curr_str = &mut self.content;
526                let group_content = lex::group_content(curr_str, GroupingKind::LeftRight)?;
527                let closing = if let Some(rest) = curr_str.strip_prefix('.') {
528                    *curr_str = rest;
529                    None
530                } else {
531                    Some(lex::delimiter(curr_str)?.0)
532                };
533
534                self.buffer.extend([
535                    I::Event(E::Begin(G::LeftRight(opening, closing))),
536                    I::SubGroup {
537                        content: group_content,
538                        allowed_alignment_count: None,
539                    },
540                    I::Event(E::End),
541                ]);
542
543                return Ok(());
544            }
545            // TODO: Check the conditions for this op. Does it need to be
546            // within a left-right group?
547            "middle" => {
548                let delimiter = lex::delimiter(&mut self.content)?;
549                E::Content(C::Delimiter {
550                    content: delimiter.0,
551                    size: Some(DelimiterSize::Big),
552                    ty: DelimiterType::Fence,
553                })
554            }
555            "right" => {
556                return Err(ErrorKind::UnbalancedGroup(None));
557            }
558
559            ///////////////////
560            // Big Operators //
561            ///////////////////
562            // NOTE: All of the following operators allow limit modifiers.
563            // The following operators have above and below limits by default.
564            "sum" => self.large_op('∑', true),
565            "prod" => self.large_op('∏', true),
566            "coprod" => self.large_op('∐', true),
567            "bigvee" => self.large_op('⋁', true),
568            "bigwedge" => self.large_op('⋀', true),
569            "bigcup" => self.large_op('⋃', true),
570            "bigcap" => self.large_op('⋂', true),
571            "biguplus" => self.large_op('⨄', true),
572            "bigoplus" => self.large_op('⨁', true),
573            "bigotimes" => self.large_op('⨂', true),
574            "bigodot" => self.large_op('⨀', true),
575            "bigsqcup" => self.large_op('⨆', true),
576            "bigsqcap" => self.large_op('⨅', true),
577            "bigtimes" => self.large_op('⨉', true),
578            "intop" => self.large_op('∫', true),
579            // The following operators do not have above and below limits by default.
580            "int" => self.large_op('∫', false),
581            "iint" => self.large_op('∬', false),
582            "iiint" => self.large_op('∭', false),
583            "smallint" => {
584                self.state.allow_script_modifiers = true;
585                E::Content(C::LargeOp {
586                    content: '∫',
587                    small: true,
588                })
589            }
590            "iiiint" => self.large_op('⨌', false),
591            "intcap" => self.large_op('⨙', false),
592            "intcup" => self.large_op('⨚', false),
593            "oint" => self.large_op('∮', false),
594            "varointclockwise" => self.large_op('∲', false),
595            "intclockwise" => self.large_op('∱', false),
596            "oiint" => self.large_op('∯', false),
597            "pointint" => self.large_op('⨕', false),
598            "rppolint" => self.large_op('⨒', false),
599            "scpolint" => self.large_op('⨓', false),
600            "oiiint" => self.large_op('∰', false),
601            "intlarhk" => self.large_op('⨗', false),
602            "sqint" => self.large_op('⨖', false),
603            "intx" => self.large_op('⨘', false),
604            "intbar" => self.large_op('⨍', false),
605            "intBar" => self.large_op('⨎', false),
606            "fint" => self.large_op('⨏', false),
607
608            /////////////
609            // Accents //
610            /////////////
611            "acute" => return self.accent('´', false),
612            "bar" | "overline" => return self.accent('‾', false),
613            "underbar" | "underline" => return self.underscript('_'),
614            "breve" => return self.accent('˘', false),
615            "check" => return self.accent('ˇ', false),
616            "dot" => return self.accent('˙', false),
617            "ddot" => return self.accent('¨', false),
618            "grave" => return self.accent('`', false),
619            "hat" => return self.accent('^', false),
620            "tilde" => return self.accent('~', false),
621            "vec" => return self.accent('→', false),
622            "mathring" => return self.accent('˚', false),
623
624            // Arrows
625            "overleftarrow" => return self.accent('←', true),
626            "underleftarrow" => return self.underscript('←'),
627            "overrightarrow" => return self.accent('→', true),
628            "Overrightarrow" => return self.accent('⇒', true),
629            "underrightarrow" => return self.underscript('→'),
630            "overleftrightarrow" => return self.accent('↔', true),
631            "underleftrightarrow" => return self.underscript('↔'),
632            "overleftharpoon" => return self.accent('↼', true),
633            "overrightharpoon" => return self.accent('⇀', true),
634
635            // Wide ops
636            "widecheck" => return self.accent('ˇ', true),
637            "widehat" => return self.accent('^', true),
638            "widetilde" => return self.accent('~', true),
639            "wideparen" | "overparen" => return self.accent('⏜', true),
640
641            // Groups
642            "overgroup" => {
643                self.state.script_position = SP::AboveBelow;
644                return self.accent('⏠', true);
645            }
646            "undergroup" => {
647                self.state.script_position = SP::AboveBelow;
648                return self.underscript('⏡');
649            }
650            "overbrace" => {
651                self.state.script_position = SP::AboveBelow;
652                return self.accent('⏞', true);
653            }
654            "underbrace" => {
655                self.state.script_position = SP::AboveBelow;
656                return self.underscript('⏟');
657            }
658            "underparen" => {
659                self.state.script_position = SP::AboveBelow;
660                return self.underscript('⏝');
661            }
662
663            // Primes
664            "prime" => ordinary('′'),
665            "dprime" => ordinary('″'),
666            "trprime" => ordinary('‴'),
667            "qprime" => ordinary('⁗'),
668            "backprime" => ordinary('‵'),
669            "backdprime" => ordinary('‶'),
670            "backtrprime" => ordinary('‷'),
671
672            /////////////
673            // Spacing //
674            /////////////
675            "," | "thinspace" => E::Space {
676                width: Some(Dimension::new(3. / 18., DimensionUnit::Em)),
677                height: None,
678            },
679            ">" | ":" | "medspace" => E::Space {
680                width: Some(Dimension::new(4. / 18., DimensionUnit::Em)),
681                height: None,
682            },
683            ";" | "thickspace" => E::Space {
684                width: Some(Dimension::new(5. / 18., DimensionUnit::Em)),
685                height: None,
686            },
687            "enspace" => E::Space {
688                width: Some(Dimension::new(0.5, DimensionUnit::Em)),
689                height: None,
690            },
691            "quad" => E::Space {
692                width: Some(Dimension::new(1., DimensionUnit::Em)),
693                height: None,
694            },
695            "qquad" => E::Space {
696                width: Some(Dimension::new(2., DimensionUnit::Em)),
697                height: None,
698            },
699            "mathstrut" => E::Space {
700                width: None,
701                height: Some(Dimension::new(0.7, DimensionUnit::Em)),
702            },
703            "~" | "nobreakspace" => E::Content(C::Text("&nbsp;")),
704            // Variable spacing
705            "kern" => {
706                let dimension = lex::dimension(&mut self.content)?;
707                E::Space {
708                    width: Some(dimension),
709                    height: None,
710                }
711            }
712            "hskip" => {
713                let glue = lex::glue(&mut self.content)?;
714                E::Space {
715                    width: Some(glue.0),
716                    height: None,
717                }
718            }
719            "mkern" => {
720                let dimension = lex::dimension(&mut self.content)?;
721                if dimension.unit == DimensionUnit::Mu {
722                    E::Space {
723                        width: Some(dimension),
724                        height: None,
725                    }
726                } else {
727                    return Err(ErrorKind::MathUnit);
728                }
729            }
730            "mskip" => {
731                let glue = lex::glue(&mut self.content)?;
732                if glue.0.unit == DimensionUnit::Mu
733                    && glue
734                        .1
735                        .map_or(true, |Dimension { unit, .. }| unit == DimensionUnit::Mu)
736                    && glue
737                        .2
738                        .map_or(true, |Dimension { unit, .. }| unit == DimensionUnit::Mu)
739                {
740                    E::Space {
741                        width: Some(glue.0),
742                        height: None,
743                    }
744                } else {
745                    return Err(ErrorKind::MathUnit);
746                }
747            }
748            "hspace" => {
749                let Argument::Group(mut argument) = lex::argument(&mut self.content)? else {
750                    return Err(ErrorKind::DimensionArgument);
751                };
752                let glue = lex::glue(&mut argument)?;
753                E::Space {
754                    width: Some(glue.0),
755                    height: None,
756                }
757            }
758            // Negative spacing
759            "!" | "negthinspace" => E::Space {
760                width: Some(Dimension::new(-3. / 18., DimensionUnit::Em)),
761                height: None,
762            },
763            "negmedspace" => E::Space {
764                width: Some(Dimension::new(-4. / 18., DimensionUnit::Em)),
765                height: None,
766            },
767            "negthickspace" => E::Space {
768                width: Some(Dimension::new(-5. / 18., DimensionUnit::Em)),
769                height: None,
770            },
771
772            ////////////////////////
773            // Logic & Set Theory //
774            ////////////////////////
775            "forall" => ordinary('∀'),
776            "exists" => ordinary('∃'),
777            "complement" => ordinary('∁'),
778            "nexists" => ordinary('∄'),
779            "neg" | "lnot" => ordinary('¬'),
780
781            "therefore" => relation('∴'),
782            "because" => relation('∵'),
783            "subset" => relation('⊂'),
784            "supset" => relation('⊃'),
785            "strictif" => relation('⥽'),
786            "strictfi" => relation('⥼'),
787            "mapsto" => relation('↦'),
788            "implies" => relation('⟹'),
789            "mid" => relation('∣'),
790            "to" => relation('→'),
791            "impliedby" => relation('⟸'),
792            "in" | "isin" => relation('∈'),
793            "ni" => relation('∋'),
794            "gets" => relation('←'),
795            "iff" => relation('⟺'),
796            "notni" => relation('∌'),
797
798            "land" => binary('∧'),
799
800            "emptyset" => ordinary('∅'),
801            "varnothing" => ordinary('⌀'),
802
803            //////////////////////
804            // Binary Operators //
805            //////////////////////
806            "ldotp" => binary('.'),
807            "cdotp" => binary('·'),
808            "cdot" => binary('⋅'),
809            "centerdot" => binary('·'),
810            "circ" => binary('∘'),
811            "bullet" => binary('∙'),
812            "circledast" => binary('⊛'),
813            "circledcirc" => binary('⊚'),
814            "circleddash" => binary('⊝'),
815            "bigcirc" => binary('◯'),
816            "leftthreetimes" => binary('⋋'),
817            "rhd" => binary('⊳'),
818            "lhd" => binary('⊲'),
819            "rightthreetimes" => binary('⋌'),
820            "rtimes" => binary('⋊'),
821            "ltimes" => binary('⋉'),
822            "leftmodels" => binary('⊨'),
823            "amalg" => binary('⨿'),
824            "ast" => binary('*'),
825            "asymp" => binary('≍'),
826            "And" | "with" => binary('&'),
827            "lor" => binary('∨'),
828            "setminus" => binary('∖'),
829            "Cup" => binary('⋓'),
830            "cup" => binary('∪'),
831            "sqcup" => binary('⊔'),
832            "sqcap" => binary('⊓'),
833            "lessdot" => binary('⋖'),
834            "smallsetminus" => E::Content(C::BinaryOp {
835                content: '∖',
836                small: false,
837            }),
838            "barwedge" => binary('⌅'),
839            "curlyvee" => binary('⋎'),
840            "curlywedge" => binary('⋏'),
841            "sslash" => binary('⫽'),
842            "div" => binary('÷'),
843            "mp" => binary('∓'),
844            "times" => binary('×'),
845            "boxdot" => binary('⊡'),
846            "divideontimes" => binary('⋇'),
847            "odot" => binary('⊙'),
848            "unlhd" => binary('⊴'),
849            "boxminus" => binary('⊟'),
850            "dotplus" => binary('∔'),
851            "ominus" => binary('⊖'),
852            "unrhd" => binary('⊵'),
853            "boxplus" => binary('⊞'),
854            "doublebarwedge" => binary('⩞'),
855            "oplus" => binary('⊕'),
856            "uplus" => binary('⊎'),
857            "boxtimes" => binary('⊠'),
858            "doublecap" => binary('⋒'),
859            "otimes" => binary('⊗'),
860            "vee" => binary('∨'),
861            "veebar" => binary('⊻'),
862            "Cap" => binary('⋒'),
863            "parr" => binary('⅋'),
864            "wedge" => binary('∧'),
865            "cap" => binary('∩'),
866            "gtrdot" => binary('⋗'),
867            "pm" => binary('±'),
868            "intercal" => binary('⊺'),
869            "wr" => binary('≀'),
870            "circledvert" => binary('⦶'),
871            "blackhourglass" => binary('⧗'),
872            "circlehbar" => binary('⦵'),
873            "operp" => binary('⦹'),
874            "boxast" => binary('⧆'),
875            "boxbox" => binary('⧈'),
876            "oslash" => binary('⊘'),
877            "boxcircle" => binary('⧇'),
878            "diamond" => binary('⋄'),
879            "Otimes" => binary('⨷'),
880            "hourglass" => binary('⧖'),
881            "otimeshat" => binary('⨶'),
882            "triangletimes" => binary('⨻'),
883            "lozengeminus" => binary('⟠'),
884            "star" => binary('⋆'),
885            "obar" => binary('⌽'),
886            "obslash" => binary('⦸'),
887            "triangleminus" => binary('⨺'),
888            "odiv" => binary('⨸'),
889            "triangleplus" => binary('⨹'),
890            "circledequal" => binary('⊜'),
891            "ogreaterthan" => binary('⧁'),
892            "circledparallel" => binary('⦷'),
893            "olessthan" => binary('⧀'),
894
895            ///////////////
896            // Relations //
897            ///////////////
898            "eqcirc" => relation('≖'),
899            "lessgtr" => relation('≶'),
900            "smile" | "sincoh" => relation('⌣'),
901            "eqcolon" | "minuscolon" => relation('∹'),
902            "lesssim" => relation('≲'),
903            "sqsubset" => relation('⊏'),
904            "ll" => relation('≪'),
905            "sqsubseteq" => relation('⊑'),
906            "eqqcolon" => relation('≕'),
907            "lll" => relation('⋘'),
908            "sqsupset" => relation('⊐'),
909            "llless" => relation('⋘'),
910            "sqsupseteq" => relation('⊒'),
911            "approx" => relation('≈'),
912            "eqdef" => relation('≝'),
913            "lt" => relation('<'),
914            "stareq" => relation('≛'),
915            "approxeq" => relation('≊'),
916            "eqsim" => relation('≂'),
917            "measeq" => relation('≞'),
918            "Subset" => relation('⋐'),
919            "arceq" => relation('≘'),
920            "eqslantgtr" => relation('⪖'),
921            "eqslantless" => relation('⪕'),
922            "models" => relation('⊨'),
923            "subseteq" => relation('⊆'),
924            "backcong" => relation('≌'),
925            "equiv" => relation('≡'),
926            "multimap" => relation('⊸'),
927            "subseteqq" => relation('⫅'),
928            "fallingdotseq" => relation('≒'),
929            "multimapboth" => relation('⧟'),
930            "succ" => relation('≻'),
931            "backsim" => relation('∽'),
932            "frown" => relation('⌢'),
933            "multimapinv" => relation('⟜'),
934            "succapprox" => relation('⪸'),
935            "backsimeq" => relation('⋍'),
936            "ge" => relation('≥'),
937            "origof" => relation('⊶'),
938            "succcurlyeq" => relation('≽'),
939            "between" => relation('≬'),
940            "geq" => relation('≥'),
941            "owns" => relation('∋'),
942            "succeq" => relation('⪰'),
943            "bumpeq" => relation('≏'),
944            "geqq" => relation('≧'),
945            "parallel" => relation('∥'),
946            "succsim" => relation('≿'),
947            "Bumpeq" => relation('≎'),
948            "geqslant" => relation('⩾'),
949            "perp" => relation('⟂'),
950            "Supset" => relation('⋑'),
951            "circeq" => relation('≗'),
952            "gg" => relation('≫'),
953            "Perp" => relation('⫫'),
954            "coh" => relation('⌢'),
955            "ggg" => relation('⋙'),
956            "pitchfork" => relation('⋔'),
957            "supseteq" => relation('⊇'),
958            "gggtr" => relation('⋙'),
959            "prec" => relation('≺'),
960            "supseteqq" => relation('⫆'),
961            "gt" => relation('>'),
962            "precapprox" => relation('⪷'),
963            "thickapprox" => relation('≈'),
964            "gtrapprox" => relation('⪆'),
965            "preccurlyeq" => relation('≼'),
966            "thicksim" => relation('∼'),
967            "gtreqless" => relation('⋛'),
968            "preceq" => relation('⪯'),
969            "trianglelefteq" => relation('⊴'),
970            "coloneqq" | "colonequals" => relation('≔'),
971            "gtreqqless" => relation('⪌'),
972            "precsim" => relation('≾'),
973            "triangleq" => relation('≜'),
974            "Coloneqq" | "coloncolonequals" => relation('⩴'),
975            "gtrless" => relation('≷'),
976            "propto" => relation('∝'),
977            "trianglerighteq" => relation('⊵'),
978            "gtrsim" => relation('≳'),
979            "questeq" => relation('≟'),
980            "varpropto" => relation('∝'),
981            "imageof" => relation('⊷'),
982            "cong" => relation('≅'),
983            "risingdotseq" => relation('≓'),
984            "vartriangleleft" => relation('⊲'),
985            "curlyeqprec" => relation('⋞'),
986            "scoh" => relation('⌢'),
987            "vartriangleright" => relation('⊳'),
988            "curlyeqsucc" => relation('⋟'),
989            "le" => relation('≤'),
990            "shortmid" => E::Content(C::Relation {
991                content: RelationContent::single_char('∣'),
992                small: true,
993            }),
994            "shortparallel" => E::Content(C::Relation {
995                content: RelationContent::single_char('∥'),
996                small: true,
997            }),
998            "vdash" => relation('⊢'),
999            "dashv" => relation('⊣'),
1000            "leq" => relation('≤'),
1001            "vDash" => relation('⊨'),
1002            "dblcolon" | "coloncolon" => relation('∷'),
1003            "leqq" => relation('≦'),
1004            "sim" => relation('∼'),
1005            "Vdash" => relation('⊩'),
1006            "doteq" => relation('≐'),
1007            "leqslant" => relation('⩽'),
1008            "simeq" => relation('≃'),
1009            "Dash" => relation('⊫'),
1010            "Doteq" => relation('≑'),
1011            "lessapprox" => relation('⪅'),
1012            "Vvdash" => relation('⊪'),
1013            "doteqdot" => relation('≑'),
1014            "lesseqgtr" => relation('⋚'),
1015            "smallfrown" => relation('⌢'),
1016            "veeeq" => relation('≚'),
1017            "eqeq" => relation('⩵'),
1018            "lesseqqgtr" => relation('⪋'),
1019            "smallsmile" => E::Content(C::Relation {
1020                content: RelationContent::single_char('⌣'),
1021                small: true,
1022            }),
1023            "wedgeq" => relation('≙'),
1024            "bowtie" | "Join" => relation('⋈'),
1025            // Negated relations
1026            "gnapprox" => relation('⪊'),
1027            "ngeqslant" => relation('≱'),
1028            "nsubset" => relation('⊄'),
1029            "nVdash" => relation('⊮'),
1030            "gneq" => relation('⪈'),
1031            "ngtr" => relation('≯'),
1032            "nsubseteq" => relation('⊈'),
1033            "precnapprox" => relation('⪹'),
1034            "gneqq" => relation('≩'),
1035            "nleq" => relation('≰'),
1036            "nsubseteqq" => relation('⊈'),
1037            "precneqq" => relation('⪵'),
1038            "gnsim" => relation('⋧'),
1039            "nleqq" => relation('≰'),
1040            "nsucc" => relation('⊁'),
1041            "precnsim" => relation('⋨'),
1042            "nleqslant" => relation('≰'),
1043            "nsucceq" => relation('⋡'),
1044            "subsetneq" => relation('⊊'),
1045            "lnapprox" => relation('⪉'),
1046            "nless" => relation('≮'),
1047            "nsupset" => relation('⊅'),
1048            "subsetneqq" => relation('⫋'),
1049            "lneq" => relation('⪇'),
1050            "nmid" => relation('∤'),
1051            "nsupseteq" => relation('⊉'),
1052            "succnapprox" => relation('⪺'),
1053            "lneqq" => relation('≨'),
1054            "notin" => relation('∉'),
1055            "nsupseteqq" => relation('⊉'),
1056            "succneqq" => relation('⪶'),
1057            "lnsim" => relation('⋦'),
1058            "ntriangleleft" => relation('⋪'),
1059            "succnsim" => relation('⋩'),
1060            "nparallel" => relation('∦'),
1061            "ntrianglelefteq" => relation('⋬'),
1062            "supsetneq" => relation('⊋'),
1063            "ncong" => relation('≆'),
1064            "nprec" => relation('⊀'),
1065            "ntriangleright" => relation('⋫'),
1066            "supsetneqq" => relation('⫌'),
1067            "ne" => relation('≠'),
1068            "npreceq" => relation('⋠'),
1069            "ntrianglerighteq" => relation('⋭'),
1070            "neq" => relation('≠'),
1071            "nshortmid" => E::Content(C::Relation {
1072                content: RelationContent::single_char('∤'),
1073                small: true,
1074            }),
1075            "nvdash" => relation('⊬'),
1076            "ngeq" => relation('≱'),
1077            "nshortparallel" => E::Content(C::Relation {
1078                content: RelationContent::single_char('∦'),
1079                small: true,
1080            }),
1081            "nvDash" => relation('⊭'),
1082            "ngeqq" => relation('≱'),
1083            "nsim" => relation('≁'),
1084            "nVDash" => relation('⊯'),
1085            "varsupsetneqq" => multirelation('⫌', '\u{fe00}'),
1086            "varsubsetneqq" => multirelation('⫋', '\u{fe00}'),
1087            "varsubsetneq" => multirelation('⊊', '\u{fe00}'),
1088            "varsupsetneq" => multirelation('⊋', '\u{fe00}'),
1089            "gvertneqq" => multirelation('≩', '\u{fe00}'),
1090            "lvertneqq" => multirelation('≨', '\u{fe00}'),
1091            "Eqcolon" | "minuscoloncolon" => multirelation('−', '∷'),
1092            "Eqqcolon" => multirelation('=', '∷'),
1093            "approxcolon" => multirelation('≈', ':'),
1094            "colonapprox" => multirelation(':', '≈'),
1095            "approxcoloncolon" => multirelation('≈', '∷'),
1096            "Colonapprox" | "coloncolonapprox" => multirelation('∷', '≈'),
1097            "coloneq" | "colonminus" => multirelation(':', '−'),
1098            "Coloneq" | "coloncolonminus" => multirelation('∷', '−'),
1099            "colonsim" => multirelation(':', '∼'),
1100            "Colonsim" | "coloncolonsim" => multirelation('∷', '∼'),
1101
1102            ////////////
1103            // Arrows //
1104            ////////////
1105            "circlearrowleft" => relation('↺'),
1106            "Leftrightarrow" => relation('⇔'),
1107            "restriction" => relation('↾'),
1108            "circlearrowright" => relation('↻'),
1109            "leftrightarrows" => relation('⇆'),
1110            "rightarrow" => relation('→'),
1111            "curvearrowleft" => relation('↶'),
1112            "leftrightharpoons" => relation('⇋'),
1113            "Rightarrow" => relation('⇒'),
1114            "curvearrowright" => relation('↷'),
1115            "leftrightsquigarrow" => relation('↭'),
1116            "rightarrowtail" => relation('↣'),
1117            "dashleftarrow" => relation('⇠'),
1118            "Lleftarrow" => relation('⇚'),
1119            "rightharpoondown" => relation('⇁'),
1120            "dashrightarrow" => relation('⇢'),
1121            "longleftarrow" => relation('⟵'),
1122            "rightharpoonup" => relation('⇀'),
1123            "downarrow" => relation('↓'),
1124            "Longleftarrow" => relation('⟸'),
1125            "rightleftarrows" => relation('⇄'),
1126            "Downarrow" => relation('⇓'),
1127            "longleftrightarrow" => relation('⟷'),
1128            "rightleftharpoons" => relation('⇌'),
1129            "downdownarrows" => relation('⇊'),
1130            "Longleftrightarrow" => relation('⟺'),
1131            "rightrightarrows" => relation('⇉'),
1132            "downharpoonleft" => relation('⇃'),
1133            "longmapsto" => relation('⟼'),
1134            "rightsquigarrow" => relation('⇝'),
1135            "downharpoonright" => relation('⇂'),
1136            "longrightarrow" => relation('⟶'),
1137            "Rrightarrow" => relation('⇛'),
1138            "Longrightarrow" => relation('⟹'),
1139            "Rsh" => relation('↱'),
1140            "hookleftarrow" => relation('↩'),
1141            "looparrowleft" => relation('↫'),
1142            "searrow" => relation('↘'),
1143            "hookrightarrow" => relation('↪'),
1144            "looparrowright" => relation('↬'),
1145            "swarrow" => relation('↙'),
1146            "Lsh" => relation('↰'),
1147            "mapsfrom" => relation('↤'),
1148            "twoheadleftarrow" => relation('↞'),
1149            "twoheadrightarrow" => relation('↠'),
1150            "leadsto" => relation('⇝'),
1151            "nearrow" => relation('↗'),
1152            "uparrow" => relation('↑'),
1153            "leftarrow" => relation('←'),
1154            "nleftarrow" => relation('↚'),
1155            "Uparrow" => relation('⇑'),
1156            "Leftarrow" => relation('⇐'),
1157            "nLeftarrow" => relation('⇍'),
1158            "updownarrow" => relation('↕'),
1159            "leftarrowtail" => relation('↢'),
1160            "nleftrightarrow" => relation('↮'),
1161            "Updownarrow" => relation('⇕'),
1162            "leftharpoondown" => relation('↽'),
1163            "nLeftrightarrow" => relation('⇎'),
1164            "upharpoonleft" => relation('↿'),
1165            "leftharpoonup" => relation('↼'),
1166            "nrightarrow" => relation('↛'),
1167            "upharpoonright" => relation('↾'),
1168            "leftleftarrows" => relation('⇇'),
1169            "nRightarrow" => relation('⇏'),
1170            "upuparrows" => relation('⇈'),
1171            "leftrightarrow" => relation('↔'),
1172            "nwarrow" => relation('↖'),
1173            "xleftarrow" => {
1174                let below = lex::optional_argument(&mut self.content);
1175                let above = lex::argument(&mut self.content)?;
1176                self.buffer.extend([
1177                    I::Event(E::Script {
1178                        ty: if below.is_some() {
1179                            ST::SubSuperscript
1180                        } else {
1181                            ST::Superscript
1182                        },
1183                        position: SP::AboveBelow,
1184                    }),
1185                    I::Event(relation('←')),
1186                ]);
1187                if let Some(below) = below {
1188                    self.handle_argument(Argument::Group(below))?;
1189                }
1190                self.handle_argument(above)?;
1191                return Ok(());
1192            }
1193            "xrightarrow" => {
1194                let below = lex::optional_argument(&mut self.content);
1195                let above = lex::argument(&mut self.content)?;
1196                self.buffer.extend([
1197                    I::Event(E::Script {
1198                        ty: if below.is_some() {
1199                            ST::SubSuperscript
1200                        } else {
1201                            ST::Superscript
1202                        },
1203                        position: SP::AboveBelow,
1204                    }),
1205                    I::Event(relation('→')),
1206                ]);
1207                if let Some(below) = below {
1208                    self.handle_argument(Argument::Group(below))?;
1209                }
1210                self.handle_argument(above)?;
1211                return Ok(());
1212            }
1213
1214            ///////////////
1215            // Fractions //
1216            ///////////////
1217            "frac" => {
1218                return self.fraction_like(None, None, None, None);
1219            }
1220            // TODO: better errors for this
1221            "genfrac" => {
1222                let str = &mut self.content;
1223                let ldelim_argument = lex::argument(str)?;
1224                let ldelim = match ldelim_argument {
1225                    Argument::Token(token) => {
1226                        Some(token_to_delim(token).ok_or(ErrorKind::Delimiter)?)
1227                    }
1228                    Argument::Group(group) => {
1229                        if group.is_empty() {
1230                            None
1231                        } else {
1232                            return Err(ErrorKind::Delimiter);
1233                        }
1234                    }
1235                };
1236                let rdelim_argument = lex::argument(str)?;
1237                let rdelim = match rdelim_argument {
1238                    Argument::Token(token) => {
1239                        Some(token_to_delim(token).ok_or(ErrorKind::Delimiter)?)
1240                    }
1241                    Argument::Group(group) => {
1242                        if group.is_empty() {
1243                            None
1244                        } else {
1245                            return Err(ErrorKind::Delimiter);
1246                        }
1247                    }
1248                };
1249                let bar_size_argument = lex::argument(str)?;
1250                let bar_size = match bar_size_argument {
1251                    Argument::Token(_) => return Err(ErrorKind::DimensionArgument),
1252                    Argument::Group("") => None,
1253                    Argument::Group(mut group) => lex::dimension(&mut group).and_then(|dim| {
1254                        if group.is_empty() {
1255                            Ok(Some(dim))
1256                        } else {
1257                            Err(ErrorKind::DimensionArgument)
1258                        }
1259                    })?,
1260                };
1261                let display_style_argument = lex::argument(str)?;
1262                let display_style = match display_style_argument {
1263                    Argument::Token(t) => match t {
1264                        Token::ControlSequence(_) => return Err(ErrorKind::Argument),
1265                        Token::Character(c) => Some(match c.into() {
1266                            '0' => S::Display,
1267                            '1' => S::Text,
1268                            '2' => S::Script,
1269                            '3' => S::ScriptScript,
1270                            _ => return Err(ErrorKind::Argument),
1271                        }),
1272                    },
1273                    Argument::Group(group) => match group {
1274                        "0" => Some(S::Display),
1275                        "1" => Some(S::Text),
1276                        "2" => Some(S::Script),
1277                        "3" => Some(S::ScriptScript),
1278                        "" => None,
1279                        _ => return Err(ErrorKind::Argument),
1280                    },
1281                };
1282
1283                self.fraction_like(
1284                    ldelim.map(|d| d.0),
1285                    rdelim.map(|d| d.0),
1286                    bar_size,
1287                    display_style,
1288                )?;
1289
1290                return Ok(());
1291            }
1292            "cfrac" | "dfrac" => {
1293                self.fraction_like(None, None, None, Some(S::Display))?;
1294                return Ok(());
1295            }
1296            "tfrac" => {
1297                self.fraction_like(None, None, None, Some(S::Text))?;
1298                return Ok(());
1299            }
1300            "binom" => {
1301                self.fraction_like(
1302                    Some('('),
1303                    Some(')'),
1304                    Some(Dimension::new(0., DimensionUnit::Em)),
1305                    None,
1306                )?;
1307                return Ok(());
1308            }
1309            "dbinom" => {
1310                self.fraction_like(
1311                    Some('('),
1312                    Some(')'),
1313                    Some(Dimension::new(0., DimensionUnit::Em)),
1314                    Some(S::Display),
1315                )?;
1316                return Ok(());
1317            }
1318            "tbinom" => {
1319                self.fraction_like(
1320                    Some('('),
1321                    Some(')'),
1322                    Some(Dimension::new(0., DimensionUnit::Em)),
1323                    Some(S::Text),
1324                )?;
1325                return Ok(());
1326            }
1327            "overset" | "stackrel" => {
1328                self.buffer.push(I::Event(E::Script {
1329                    ty: ST::Superscript,
1330                    position: SP::AboveBelow,
1331                }));
1332                let before_over_index = self.buffer.len();
1333                let over = lex::argument(&mut self.content)?;
1334                self.handle_argument(over)?;
1335                let over_events = self.buffer.split_off(before_over_index);
1336                let base = lex::argument(&mut self.content)?;
1337                self.handle_argument(base)?;
1338                self.buffer.extend(over_events);
1339                return Ok(());
1340            }
1341            "underset" => {
1342                self.buffer.push(I::Event(E::Script {
1343                    ty: ST::Subscript,
1344                    position: SP::AboveBelow,
1345                }));
1346                let before_under_index = self.buffer.len();
1347                let under = lex::argument(&mut self.content)?;
1348                self.handle_argument(under)?;
1349                let under_events = self.buffer.split_off(before_under_index);
1350                let base = lex::argument(&mut self.content)?;
1351                self.handle_argument(base)?;
1352                self.buffer.extend(under_events);
1353                return Ok(());
1354            }
1355
1356            //////////////
1357            // Radicals //
1358            //////////////
1359            "sqrt" => {
1360                if let Some(index) = lex::optional_argument(&mut self.content) {
1361                    self.buffer.push(I::Event(E::Visual(V::Root)));
1362                    let arg = lex::argument(&mut self.content)?;
1363                    self.handle_argument(arg)?;
1364                    self.buffer.push(I::SubGroup {
1365                        content: index,
1366                        allowed_alignment_count: None,
1367                    });
1368                } else {
1369                    self.buffer.push(I::Event(E::Visual(V::SquareRoot)));
1370                    let arg = lex::argument(&mut self.content)?;
1371                    self.handle_argument(arg)?;
1372                }
1373                return Ok(());
1374            }
1375            "surd" => {
1376                self.buffer.extend([
1377                    I::Event(E::Visual(V::SquareRoot)),
1378                    I::Event(E::Space {
1379                        width: Some(Dimension::new(0., DimensionUnit::Em)),
1380                        height: Some(Dimension::new(0.7, DimensionUnit::Em)),
1381                    }),
1382                ]);
1383                return Ok(());
1384            }
1385
1386            "backslash" => ordinary('\\'),
1387
1388            ///////////////////
1389            // Miscellaneous //
1390            ///////////////////
1391            "#" | "%" | "&" | "$" | "_" => ordinary(
1392                control_sequence
1393                    .chars()
1394                    .next()
1395                    .expect("the control sequence contains one of the matched characters"),
1396            ),
1397            "|" => ordinary('∥'),
1398            "text" => return self.text_argument(),
1399            "not" | "cancel" => {
1400                self.buffer.push(I::Event(E::Visual(V::Negation)));
1401                let argument = lex::argument(&mut self.content)?;
1402                self.handle_argument(argument)?;
1403                return Ok(());
1404            }
1405            "char" => {
1406                let number = lex::unsigned_integer(&mut self.content)?;
1407                if number > 255 {
1408                    return Err(ErrorKind::InvalidCharNumber);
1409                }
1410                E::Content(C::Ordinary {
1411                    content: char::from_u32(number as u32)
1412                        .expect("the number is a valid char since it is less than 256"),
1413                    stretchy: false,
1414                })
1415            }
1416            "relax" => {
1417                return if self.state.handling_argument {
1418                    Err(ErrorKind::Relax)
1419                } else {
1420                    Ok(())
1421                }
1422            }
1423
1424            "begingroup" => {
1425                let group = lex::group_content(&mut self.content, GroupingKind::BeginEnd)?;
1426                self.buffer.extend([
1427                    I::Event(E::Begin(G::Normal)),
1428                    I::SubGroup {
1429                        content: group,
1430                        allowed_alignment_count: None,
1431                    },
1432                    I::Event(E::End),
1433                ]);
1434                return Ok(());
1435            }
1436            "endgroup" => return Err(ErrorKind::UnbalancedGroup(None)),
1437
1438            "begin" => {
1439                let Argument::Group(argument) = lex::argument(&mut self.content)? else {
1440                    return Err(ErrorKind::Argument);
1441                };
1442
1443                let mut style = None;
1444                let mut wrap: Option<(char, char)> = None;
1445
1446                let (environment, align_count, grouping_kind) = match argument {
1447                    "array" => {
1448                        let (grouping, count) = self.array_environment()?;
1449                        (grouping, count, GroupingKind::Array { display: false })
1450                    }
1451                    "darray" => {
1452                        style = Some(S::Display);
1453                        let (grouping, count) = self.array_environment()?;
1454                        (grouping, count, GroupingKind::Array { display: true })
1455                    }
1456                    "matrix" => (
1457                        G::Matrix {
1458                            alignment: ColumnAlignment::Center,
1459                        },
1460                        u16::MAX,
1461                        GroupingKind::Matrix {
1462                            ty: MatrixType::Normal,
1463                            column_spec: false,
1464                        },
1465                    ),
1466                    "matrix*" => (
1467                        G::Matrix {
1468                            alignment: self
1469                                .optional_alignment()?
1470                                .unwrap_or(ColumnAlignment::Center),
1471                        },
1472                        u16::MAX,
1473                        GroupingKind::Matrix {
1474                            ty: MatrixType::Normal,
1475                            column_spec: true,
1476                        },
1477                    ),
1478                    "smallmatrix" => {
1479                        style = Some(S::Text);
1480                        (
1481                            G::Matrix {
1482                                alignment: ColumnAlignment::Center,
1483                            },
1484                            u16::MAX,
1485                            GroupingKind::Matrix {
1486                                ty: MatrixType::Small,
1487                                column_spec: false,
1488                            },
1489                        )
1490                    }
1491                    "pmatrix" => {
1492                        wrap = Some(('(', ')'));
1493                        (
1494                            G::Matrix {
1495                                alignment: ColumnAlignment::Center,
1496                            },
1497                            u16::MAX,
1498                            GroupingKind::Matrix {
1499                                ty: MatrixType::Parens,
1500                                column_spec: false,
1501                            },
1502                        )
1503                    }
1504                    "pmatrix*" => {
1505                        wrap = Some(('(', ')'));
1506                        (
1507                            G::Matrix {
1508                                alignment: self
1509                                    .optional_alignment()?
1510                                    .unwrap_or(ColumnAlignment::Center),
1511                            },
1512                            u16::MAX,
1513                            GroupingKind::Matrix {
1514                                ty: MatrixType::Parens,
1515                                column_spec: true,
1516                            },
1517                        )
1518                    }
1519                    "bmatrix" => {
1520                        wrap = Some(('[', ']'));
1521                        (
1522                            G::Matrix {
1523                                alignment: ColumnAlignment::Center,
1524                            },
1525                            u16::MAX,
1526                            GroupingKind::Matrix {
1527                                ty: MatrixType::Brackets,
1528                                column_spec: false,
1529                            },
1530                        )
1531                    }
1532                    "bmatrix*" => {
1533                        wrap = Some(('[', ']'));
1534                        (
1535                            G::Matrix {
1536                                alignment: self
1537                                    .optional_alignment()?
1538                                    .unwrap_or(ColumnAlignment::Center),
1539                            },
1540                            u16::MAX,
1541                            GroupingKind::Matrix {
1542                                ty: MatrixType::Brackets,
1543                                column_spec: true,
1544                            },
1545                        )
1546                    }
1547                    "vmatrix" => {
1548                        wrap = Some(('|', '|'));
1549                        (
1550                            G::Matrix {
1551                                alignment: ColumnAlignment::Center,
1552                            },
1553                            u16::MAX,
1554                            GroupingKind::Matrix {
1555                                ty: MatrixType::Vertical,
1556                                column_spec: false,
1557                            },
1558                        )
1559                    }
1560                    "vmatrix*" => {
1561                        wrap = Some(('|', '|'));
1562                        (
1563                            G::Matrix {
1564                                alignment: self
1565                                    .optional_alignment()?
1566                                    .unwrap_or(ColumnAlignment::Center),
1567                            },
1568                            u16::MAX,
1569                            GroupingKind::Matrix {
1570                                ty: MatrixType::Vertical,
1571                                column_spec: true,
1572                            },
1573                        )
1574                    }
1575                    "Vmatrix" => {
1576                        wrap = Some(('‖', '‖'));
1577                        (
1578                            G::Matrix {
1579                                alignment: ColumnAlignment::Center,
1580                            },
1581                            u16::MAX,
1582                            GroupingKind::Matrix {
1583                                ty: MatrixType::DoubleVertical,
1584                                column_spec: false,
1585                            },
1586                        )
1587                    }
1588                    "Vmatrix*" => {
1589                        wrap = Some(('‖', '‖'));
1590                        (
1591                            G::Matrix {
1592                                alignment: self
1593                                    .optional_alignment()?
1594                                    .unwrap_or(ColumnAlignment::Center),
1595                            },
1596                            u16::MAX,
1597                            GroupingKind::Matrix {
1598                                ty: MatrixType::DoubleVertical,
1599                                column_spec: true,
1600                            },
1601                        )
1602                    }
1603                    "Bmatrix" => {
1604                        wrap = Some(('{', '}'));
1605                        (
1606                            G::Matrix {
1607                                alignment: ColumnAlignment::Center,
1608                            },
1609                            u16::MAX,
1610                            GroupingKind::Matrix {
1611                                ty: MatrixType::Braces,
1612                                column_spec: false,
1613                            },
1614                        )
1615                    }
1616                    "Bmatrix*" => {
1617                        wrap = Some(('{', '}'));
1618                        (
1619                            G::Matrix {
1620                                alignment: self
1621                                    .optional_alignment()?
1622                                    .unwrap_or(ColumnAlignment::Center),
1623                            },
1624                            u16::MAX,
1625                            GroupingKind::Matrix {
1626                                ty: MatrixType::Braces,
1627                                column_spec: true,
1628                            },
1629                        )
1630                    }
1631                    "cases" => (
1632                        G::Cases { left: true },
1633                        1,
1634                        GroupingKind::Cases {
1635                            left: true,
1636                            display: false,
1637                        },
1638                    ),
1639                    "dcases" => {
1640                        style = Some(S::Display);
1641                        (
1642                            G::Cases { left: true },
1643                            1,
1644                            GroupingKind::Cases {
1645                                left: true,
1646                                display: true,
1647                            },
1648                        )
1649                    }
1650                    "rcases" => (
1651                        G::Cases { left: false },
1652                        1,
1653                        GroupingKind::Cases {
1654                            left: false,
1655                            display: false,
1656                        },
1657                    ),
1658                    "drcases" => {
1659                        style = Some(S::Display);
1660                        (
1661                            G::Cases { left: false },
1662                            1,
1663                            GroupingKind::Cases {
1664                                left: false,
1665                                display: true,
1666                            },
1667                        )
1668                    }
1669                    "equation" => (
1670                        G::Equation { eq_numbers: true },
1671                        0,
1672                        GroupingKind::Equation { eq_numbers: true },
1673                    ),
1674                    "equation*" => (
1675                        G::Equation { eq_numbers: false },
1676                        0,
1677                        GroupingKind::Equation { eq_numbers: false },
1678                    ),
1679                    "align" => (
1680                        G::Align { eq_numbers: true },
1681                        u16::MAX,
1682                        GroupingKind::Align { eq_numbers: true },
1683                    ),
1684                    "align*" => (
1685                        G::Align { eq_numbers: false },
1686                        u16::MAX,
1687                        GroupingKind::Align { eq_numbers: false },
1688                    ),
1689                    "aligned" => (G::Aligned, u16::MAX, GroupingKind::Aligned),
1690                    "gather" => (
1691                        G::Gather { eq_numbers: true },
1692                        0,
1693                        GroupingKind::Gather { eq_numbers: true },
1694                    ),
1695                    "gather*" => (
1696                        G::Gather { eq_numbers: false },
1697                        0,
1698                        GroupingKind::Gather { eq_numbers: false },
1699                    ),
1700                    "gathered" => (G::Gathered, 0, GroupingKind::Gathered),
1701                    "alignat" => {
1702                        let pairs = match lex::argument(&mut self.content)? {
1703                            Argument::Group(mut content) => lex::unsigned_integer(&mut content),
1704                            _ => Err(ErrorKind::Argument),
1705                        }? as u16;
1706                        (
1707                            G::Alignat {
1708                                pairs,
1709                                eq_numbers: true,
1710                            },
1711                            (pairs * 2).saturating_sub(1),
1712                            GroupingKind::Alignat { eq_numbers: true },
1713                        )
1714                    }
1715                    "alignat*" => {
1716                        let pairs = match lex::argument(&mut self.content)? {
1717                            Argument::Group(mut content) => lex::unsigned_integer(&mut content),
1718                            _ => Err(ErrorKind::Argument),
1719                        }? as u16;
1720                        (
1721                            G::Alignat {
1722                                pairs,
1723                                eq_numbers: false,
1724                            },
1725                            (pairs * 2).saturating_sub(1),
1726                            GroupingKind::Alignat { eq_numbers: false },
1727                        )
1728                    }
1729                    "alignedat" => {
1730                        let pairs = match lex::argument(&mut self.content)? {
1731                            Argument::Group(mut content) => lex::unsigned_integer(&mut content),
1732                            _ => Err(ErrorKind::Argument),
1733                        }? as u16;
1734                        (
1735                            G::Alignedat { pairs },
1736                            (pairs * 2).saturating_sub(1),
1737                            GroupingKind::Alignedat,
1738                        )
1739                    }
1740                    "subarray" => {
1741                        let alignment = match lex::argument(&mut self.content)? {
1742                            Argument::Group("l") => ColumnAlignment::Left,
1743                            Argument::Group("c") => ColumnAlignment::Center,
1744                            Argument::Group("r") => ColumnAlignment::Right,
1745                            _ => return Err(ErrorKind::Argument),
1746                        };
1747                        (G::SubArray { alignment }, 0, GroupingKind::SubArray)
1748                    }
1749                    "multline" => (G::Multline, 0, GroupingKind::Multline),
1750                    "split" => (G::Split, 1, GroupingKind::Split),
1751                    _ => return Err(ErrorKind::Environment),
1752                };
1753
1754                let wrap_used = if let Some((left, right)) = wrap {
1755                    self.buffer
1756                        .push(I::Event(E::Begin(G::LeftRight(Some(left), Some(right)))));
1757                    true
1758                } else {
1759                    false
1760                };
1761
1762                let horizontal_lines = lex::horizontal_lines(&mut self.content);
1763                let content = lex::group_content(&mut self.content, grouping_kind)?;
1764                self.buffer.push(I::Event(E::Begin(environment)));
1765                if let Some(style) = style {
1766                    self.buffer.push(I::Event(E::StateChange(SC::Style(style))));
1767                }
1768                if horizontal_lines.len() > 0 {
1769                    self.buffer
1770                        .push(I::Event(E::EnvironmentFlow(EnvironmentFlow::StartLines {
1771                            lines: horizontal_lines,
1772                        })));
1773                }
1774                self.buffer.extend([
1775                    I::SubGroup {
1776                        content,
1777                        allowed_alignment_count: Some(AlignmentCount::new(align_count)),
1778                    },
1779                    I::Event(E::End),
1780                ]);
1781
1782                if wrap_used {
1783                    self.buffer.push(I::Event(E::End));
1784                }
1785                return Ok(());
1786            }
1787            "end" => return Err(ErrorKind::UnbalancedGroup(None)),
1788            "\\" | "cr"
1789                if self.state.allowed_alignment_count.is_some()
1790                    && !self.state.handling_argument =>
1791            {
1792                self.state.allowed_alignment_count.as_mut().unwrap().reset();
1793                let additional_space =
1794                    if let Some(mut arg) = lex::optional_argument(&mut self.content) {
1795                        Some(lex::dimension(&mut arg)?)
1796                    } else {
1797                        None
1798                    };
1799
1800                let horizontal_lines = lex::horizontal_lines(&mut self.content);
1801                E::EnvironmentFlow(EnvironmentFlow::NewLine {
1802                    spacing: additional_space,
1803                    horizontal_lines,
1804                })
1805            }
1806            "\\" | "cr" => return Err(ErrorKind::NewLine),
1807
1808            // Delimiters
1809            cs if control_sequence_delimiter_map(cs).is_some() => {
1810                let (content, ty) = control_sequence_delimiter_map(cs).unwrap();
1811                E::Content(C::Delimiter {
1812                    content,
1813                    size: None,
1814                    ty,
1815                })
1816            }
1817
1818            // Spacing
1819            c if c.trim_start().is_empty() => E::Content(C::Text("&nbsp;")),
1820
1821            // Macros
1822            "def" => {
1823                let (cs, parameter_text, replacement_text) = lex::definition(&mut self.content)?;
1824                self.state.skip_scripts = true;
1825                return self
1826                    .macro_context
1827                    .define(cs, parameter_text, replacement_text);
1828            }
1829            "let" => {
1830                let (cs, token) = lex::let_assignment(&mut self.content)?;
1831                self.state.skip_scripts = true;
1832                self.macro_context.assign(cs, token);
1833                return Ok(());
1834            }
1835            "futurelet" => {
1836                let (cs, token, rest) = lex::futurelet_assignment(&mut self.content)?;
1837                self.state.skip_scripts = true;
1838                self.macro_context.assign(cs, token);
1839                self.content = rest;
1840
1841                return Ok(());
1842            }
1843            "newcommand" => return self.new_command(Some(false)),
1844            "renewcommand" => return self.new_command(Some(true)),
1845            "providecommand" => return self.new_command(None),
1846            _ => return Err(ErrorKind::UnknownPrimitive),
1847        };
1848        self.buffer.push(I::Event(event));
1849        Ok(())
1850    }
1851
1852    /// Return a delimiter with the given size from the next character in the parser.
1853    fn sized_delim(&mut self, size: DelimiterSize) -> InnerResult<()> {
1854        let current = &mut self.content;
1855        let (content, ty) = lex::delimiter(current)?;
1856        self.buffer.push(I::Event(E::Content(C::Delimiter {
1857            content,
1858            size: Some(size),
1859            ty,
1860        })));
1861        Ok(())
1862    }
1863
1864    /// Override the `font_state` for the argument to the command.
1865    fn font_group(&mut self, font: Option<Font>) -> InnerResult<()> {
1866        let argument = lex::argument(&mut self.content)?;
1867        self.buffer.extend([
1868            I::Event(E::Begin(G::Normal)),
1869            I::Event(E::StateChange(SC::Font(font))),
1870        ]);
1871        match argument {
1872            Argument::Token(token) => {
1873                match token {
1874                    Token::ControlSequence(cs) => self.handle_primitive(cs)?,
1875                    Token::Character(c) => self.handle_char_token(c)?,
1876                };
1877            }
1878            Argument::Group(group) => {
1879                self.buffer.push(I::SubGroup {
1880                    content: group,
1881                    allowed_alignment_count: None,
1882                });
1883            }
1884        };
1885        self.buffer.push(I::Event(E::End));
1886        Ok(())
1887    }
1888
1889    /// Accent commands. parse the argument, and overset the accent.
1890    fn accent(&mut self, accent: char, stretchy: bool) -> InnerResult<()> {
1891        let argument = lex::argument(&mut self.content)?;
1892        self.buffer.push(I::Event(E::Script {
1893            ty: ST::Superscript,
1894            position: SP::AboveBelow,
1895        }));
1896        self.handle_argument(argument)?;
1897        self.buffer.push(I::Event(E::Content(C::Ordinary {
1898            content: accent,
1899            stretchy,
1900        })));
1901        Ok(())
1902    }
1903
1904    /// Underscript commands. parse the argument, and underset the accent.
1905    fn underscript(&mut self, content: char) -> InnerResult<()> {
1906        let argument = lex::argument(&mut self.content)?;
1907        self.buffer.push(I::Event(E::Script {
1908            ty: ST::Subscript,
1909            position: SP::AboveBelow,
1910        }));
1911        self.handle_argument(argument)?;
1912        self.buffer.push(I::Event(E::Content(C::Ordinary {
1913            content,
1914            stretchy: true,
1915        })));
1916
1917        Ok(())
1918    }
1919
1920    fn large_op(&mut self, op: char, movable: bool) -> E<'store> {
1921        self.state.allow_script_modifiers = true;
1922        self.state.script_position = if movable { SP::Movable } else { SP::Right };
1923        E::Content(C::LargeOp {
1924            content: op,
1925            small: false,
1926        })
1927    }
1928
1929    fn font_change(&mut self, font: Font) -> E<'store> {
1930        self.state.skip_scripts = true;
1931        E::StateChange(SC::Font(Some(font)))
1932    }
1933
1934    fn style_change(&mut self, style: S) -> E<'store> {
1935        self.state.skip_scripts = true;
1936        E::StateChange(SC::Style(style))
1937    }
1938
1939    fn text_argument(&mut self) -> InnerResult<()> {
1940        let argument = lex::argument(&mut self.content)?;
1941        self.buffer
1942            .push(I::Event(E::Content(C::Text(match argument {
1943                Argument::Token(Token::Character(c)) => c.as_str(),
1944                Argument::Group(inner) => inner,
1945                _ => return Err(ErrorKind::ControlSequenceAsArgument),
1946            }))));
1947        Ok(())
1948    }
1949
1950    fn fraction_like(
1951        &mut self,
1952        open: Option<char>,
1953        close: Option<char>,
1954        bar_size: Option<Dimension>,
1955        style: Option<S>,
1956    ) -> InnerResult<()> {
1957        let open_close_group = open.is_some() || close.is_some();
1958        if open_close_group {
1959            self.buffer
1960                .push(I::Event(E::Begin(G::LeftRight(open, close))));
1961        }
1962        if let Some(style) = style {
1963            if !open_close_group {
1964                self.buffer.push(I::Event(E::Begin(G::Normal)));
1965            }
1966            self.buffer.push(I::Event(E::StateChange(SC::Style(style))));
1967        };
1968
1969        self.buffer.push(I::Event(E::Visual(V::Fraction(bar_size))));
1970        let numerator = lex::argument(&mut self.content)?;
1971        self.handle_argument(numerator)?;
1972        let denominator = lex::argument(&mut self.content)?;
1973        self.handle_argument(denominator)?;
1974        if open_close_group || style.is_some() {
1975            self.buffer.push(I::Event(E::End));
1976        }
1977
1978        Ok(())
1979    }
1980
1981    fn array_environment(&mut self) -> InnerResult<(G, u16)> {
1982        let Argument::Group(array_columns_str) = lex::argument(&mut self.content)? else {
1983            return Err(ErrorKind::Argument);
1984        };
1985
1986        let mut column_count: u16 = 0;
1987        let mut contains_column = false;
1988        let array_columns = array_columns_str
1989            .chars()
1990            .map(|c| {
1991                column_count += 1;
1992                Ok(match c {
1993                    'c' => {
1994                        contains_column = true;
1995                        AC::Column(ColumnAlignment::Center)
1996                    }
1997                    'l' => {
1998                        contains_column = true;
1999                        AC::Column(ColumnAlignment::Left)
2000                    }
2001                    'r' => {
2002                        contains_column = true;
2003                        AC::Column(ColumnAlignment::Right)
2004                    }
2005                    '|' => {
2006                        column_count -= 1;
2007                        AC::Separator(Line::Solid)
2008                    }
2009                    ':' => {
2010                        column_count -= 1;
2011                        AC::Separator(Line::Dashed)
2012                    }
2013                    _ => return Err(ErrorKind::Argument),
2014                })
2015            })
2016            .collect::<Result<_, _>>()?;
2017
2018        if !contains_column {
2019            return Err(ErrorKind::ArrayNoColumns);
2020        }
2021
2022        Ok((G::Array(array_columns), column_count.saturating_sub(1)))
2023    }
2024
2025    fn optional_alignment(&mut self) -> InnerResult<Option<ColumnAlignment>> {
2026        let alignment = lex::optional_argument(&mut self.content);
2027        Ok(match alignment {
2028            Some("c") => Some(ColumnAlignment::Center),
2029            Some("l") => Some(ColumnAlignment::Left),
2030            Some("r") => Some(ColumnAlignment::Right),
2031            None => None,
2032            _ => return Err(ErrorKind::Argument),
2033        })
2034    }
2035
2036    fn new_command(&mut self, should_already_exist: Option<bool>) -> InnerResult<()> {
2037        let mut group = lex::brace_argument(&mut self.content)?;
2038        let cs = lex::control_sequence(&mut group)?;
2039
2040        if should_already_exist.is_some_and(|sae| sae != self.macro_context.contains(cs)) {
2041            return Err(if should_already_exist.unwrap() {
2042                ErrorKind::MacroNotDefined
2043            } else {
2044                ErrorKind::MacroAlreadyDefined
2045            });
2046        }
2047
2048        let arg_count = (lex::optional_argument(&mut self.content).ok_or(ErrorKind::Argument)?)
2049            .parse::<u8>()
2050            .map_err(|_| ErrorKind::Number)?;
2051        let first_arg_default = lex::optional_argument(&mut self.content);
2052        if arg_count > 9 && arg_count >= first_arg_default.is_some() as u8 {
2053            return Err(ErrorKind::TooManyParams);
2054        }
2055
2056        let replacement_text = lex::brace_argument(&mut self.content)?;
2057
2058        if self.macro_context.contains(cs) && should_already_exist.is_none() {
2059            return Ok(());
2060        }
2061        self.macro_context
2062            .insert_command(cs, arg_count, first_arg_default, replacement_text)?;
2063        Ok(())
2064    }
2065}
2066
2067#[inline]
2068fn ordinary(ident: char) -> E<'static> {
2069    E::Content(C::Ordinary {
2070        content: ident,
2071        stretchy: false,
2072    })
2073}
2074
2075#[inline]
2076fn relation(rel: char) -> E<'static> {
2077    E::Content(C::Relation {
2078        content: RelationContent::single_char(rel),
2079        small: false,
2080    })
2081}
2082
2083fn multirelation(first: char, second: char) -> E<'static> {
2084    E::Content(C::Relation {
2085        content: RelationContent::double_char(first, second),
2086        small: false,
2087    })
2088}
2089
2090#[inline]
2091fn binary(op: char) -> E<'static> {
2092    E::Content(C::BinaryOp {
2093        content: op,
2094        small: false,
2095    })
2096}
2097
2098// TODO implementations:
2099// - `raise`, `lower`
2100// - `hbox`, `mbox`?
2101// - `vcenter`
2102// - `rule`
2103// - `math_` atoms
2104// - `mathchoice` (TeXbook p. 151)
2105
2106// Unimplemented primitives:
2107// `sl` (slanted) font: https://tug.org/texinfohtml/latex2e.html#index-_005csl
2108// `bbit` (double-struck italic) font
2109// `symliteral` wtf is this? (in unicode-math)
2110// `sc` (small caps) font: https://tug.org/texinfohtml/latex2e.html#index-_005csc