1use 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 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(" "))
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 '.' | ',' | ';' => 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 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 "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 "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 "varepsilon" => ordinary('ε'),
249 "vartheta" => ordinary('ϑ'),
250 "varkappa" => ordinary('ϰ'),
251 "varrho" => ordinary('ϱ'),
252 "varsigma" => ordinary('ς'),
253 "varpi" => ordinary('ϖ'),
254 "varphi" => ordinary('φ'),
255 "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 "aleph" => ordinary('ℵ'),
270 "beth" => ordinary('ℶ'),
271 "gimel" => ordinary('ℷ'),
272 "daleth" => ordinary('ℸ'),
273 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "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 "prime" => ordinary('′'),
665 "dprime" => ordinary('″'),
666 "trprime" => ordinary('‴'),
667 "qprime" => ordinary('⁗'),
668 "backprime" => ordinary('‵'),
669 "backdprime" => ordinary('‶'),
670 "backtrprime" => ordinary('‷'),
671
672 "," | "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(" ")),
704 "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 "!" | "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 "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 "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 "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 "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 "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 "frac" => {
1218 return self.fraction_like(None, None, None, None);
1219 }
1220 "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 "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 "#" | "%" | "&" | "$" | "_" => 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 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 c if c.trim_start().is_empty() => E::Content(C::Text(" ")),
1820
1821 "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 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 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 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 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