1fn task(acts: &[(&str, Option<&str>)], next: Option<&str>, revisit: bool, cont: bool) -> Task {
8 Task {
9 actions: acts
10 .iter()
11 .map(|(t, o)| ActionRef {
12 type_: (*t).to_string(),
13 option: o.map(|s| s.to_string()),
14 })
15 .collect(),
16 next_state: next.map(|s| s.to_string()),
17 revisit,
18 to_continue: cont,
19 }
20}
21
22fn raw(patterns: &'static str, states: &'static str, task: Task) -> RawEntry {
23 RawEntry {
24 patterns,
25 states,
26 task,
27 }
28}
29
30fn transitions_for(machine: &str) -> &'static HashMap<String, Vec<Transition>> {
33 match machine {
34 "ce" => &CE,
35 "a" => &A,
36 "o" => &O,
37 "text" => &TEXT,
38 "pq" => &PQ,
39 "bd" => &BD,
40 "oxidation" => &OXIDATION,
41 "tex-math" => &TEX_MATH,
42 "tex-math tight" => &TEX_MATH_TIGHT,
43 "9,9" => &NUM99,
44 "pu" => &PU,
45 "pu-2" => &PU2,
46 "pu-9,9" => &PU99,
47 _ => &CE,
48 }
49}
50
51static CE: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
56 build_transitions(&[
57 raw("empty", "*", task(&[("output", None)], None, false, false)),
58 raw(
59 "else",
60 "0|1|2",
61 task(&[("beginsWithBond=false", None)], None, true, true),
62 ),
63 raw(
64 "oxidation$",
65 "0",
66 task(&[("oxidation-output", None)], None, false, false),
67 ),
68 raw(
69 "CMT",
70 "r",
71 task(&[("rdt=", None)], Some("rt"), false, false),
72 ),
73 raw(
74 "CMT",
75 "rd",
76 task(&[("rqt=", None)], Some("rdt"), false, false),
77 ),
78 raw(
79 "arrowUpDown",
80 "0|1|2|as",
81 task(&[("sb=false", None), ("output", None), ("operator", None)], Some("1"), false, false),
82 ),
83 raw(
84 "uprightEntities",
85 "0|1|2",
86 task(&[("o=", None), ("output", None)], Some("1"), false, false),
87 ),
88 raw("orbital", "0|1|2|3", task(&[("o=", None)], Some("o"), false, false)),
89 raw("->", "0|1|2|3", task(&[("r=", None)], Some("r"), false, false)),
90 raw(
91 "->",
92 "a|as",
93 task(&[("output", None), ("r=", None)], Some("r"), false, false),
94 ),
95 raw(
96 "->",
97 "*",
98 task(&[("output", None), ("r=", None)], Some("r"), false, false),
99 ),
100 raw("+", "o", task(&[("d= kv", None)], Some("d"), false, false)),
101 raw("+", "d|D", task(&[("d=", None)], Some("d"), false, false)),
102 raw("+", "q", task(&[("d=", None)], Some("qd"), false, false)),
103 raw("+", "qd|qD", task(&[("d=", None)], Some("qd"), false, false)),
104 raw(
105 "+",
106 "dq",
107 task(&[("output", None), ("d=", None)], Some("d"), false, false),
108 ),
109 raw(
110 "+",
111 "3",
112 task(&[("sb=false", None), ("output", None), ("operator", None)], Some("0"), false, false),
113 ),
114 raw("amount", "0|2", task(&[("a=", None)], Some("a"), false, false)),
115 raw(
116 "pm-operator",
117 "0|1|2|a|as",
118 task(&[("sb=false", None), ("output", None), ("operator", Some("\\pm"))], Some("0"), false, false),
119 ),
120 raw(
121 "operator",
122 "0|1|2|a|as",
123 task(&[("sb=false", None), ("output", None), ("operator", None)], Some("0"), false, false),
124 ),
125 raw(
126 "-$",
127 "o|q",
128 task(&[("charge or bond", None), ("output", None)], Some("qd"), false, false),
129 ),
130 raw("-$", "d", task(&[("d=", None)], Some("d"), false, false)),
131 raw(
132 "-$",
133 "D",
134 task(&[("output", None), ("bond", Some("-"))], Some("3"), false, false),
135 ),
136 raw("-$", "q", task(&[("d=", None)], Some("qd"), false, false)),
137 raw("-$", "qd", task(&[("d=", None)], Some("qd"), false, false)),
138 raw(
139 "-$",
140 "qD|dq",
141 task(&[("output", None), ("bond", Some("-"))], Some("3"), false, false),
142 ),
143 raw(
144 "-9",
145 "3|o",
146 task(&[("output", None), ("insert", Some("hyphen"))], Some("3"), false, false),
147 ),
148 raw(
149 "- orbital overlap",
150 "o",
151 task(&[("output", None), ("insert", Some("hyphen"))], Some("2"), false, false),
152 ),
153 raw(
154 "- orbital overlap",
155 "d",
156 task(&[("output", None), ("insert", Some("hyphen"))], Some("2"), false, false),
157 ),
158 raw(
159 "-",
160 "0|1|2",
161 task(&[("output", Some("1")), ("beginsWithBond=true", None), ("bond", Some("-"))], Some("3"), false, false),
162 ),
163 raw("-", "3", task(&[("bond", Some("-"))], None, false, false)),
164 raw(
165 "-",
166 "a",
167 task(&[("output", None), ("insert", Some("hyphen"))], Some("2"), false, false),
168 ),
169 raw(
170 "-",
171 "as",
172 task(&[("output", Some("2")), ("bond", Some("-"))], Some("3"), false, false),
173 ),
174 raw("-", "b", task(&[("b=", None)], None, false, false)),
175 raw(
176 "-",
177 "o",
178 task(&[("- after o/d", Some("false"))], Some("2"), false, false),
179 ),
180 raw(
181 "-",
182 "q",
183 task(&[("- after o/d", Some("false"))], Some("2"), false, false),
184 ),
185 raw(
186 "-",
187 "d|qd|dq",
188 task(&[("- after o/d", Some("true"))], Some("2"), false, false),
189 ),
190 raw(
191 "-",
192 "D|qD|p",
193 task(&[("output", None), ("bond", Some("-"))], Some("3"), false, false),
194 ),
195 raw("amount2", "1|3", task(&[("a=", None)], Some("a"), false, false)),
196 raw(
197 "letters",
198 "0|1|2|3|a|as|b|p|bp|o",
199 task(&[("o=", None)], Some("o"), false, false),
200 ),
201 raw(
202 "letters",
203 "q|dq",
204 task(&[("output", None), ("o=", None)], Some("o"), false, false),
205 ),
206 raw(
207 "letters",
208 "d|D|qd|qD",
209 task(&[("o after d", None)], Some("o"), false, false),
210 ),
211 raw("digits", "o", task(&[("q=", None)], Some("q"), false, false)),
212 raw("digits", "d|D", task(&[("q=", None)], Some("dq"), false, false)),
213 raw(
214 "digits",
215 "q",
216 task(&[("output", None), ("o=", None)], Some("o"), false, false),
217 ),
218 raw("digits", "a", task(&[("o=", None)], Some("o"), false, false)),
219 raw("space A", "b|p|bp", task(&[], None, false, false)),
220 raw("space", "a", task(&[], Some("as"), false, false)),
221 raw("space", "0", task(&[("sb=false", None)], None, false, false)),
222 raw("space", "1|2", task(&[("sb=true", None)], None, false, false)),
223 raw(
224 "space",
225 "r|rt|rd|rdt|rdq",
226 task(&[("output", None)], Some("0"), false, false),
227 ),
228 raw(
229 "space",
230 "*",
231 task(&[("output", None), ("sb=true", None)], Some("1"), false, false),
232 ),
233 raw(
234 "1st-level escape",
235 "1|2",
236 task(&[("output", None), ("insert+p1", Some("1st-level escape"))], None, false, false),
237 ),
238 raw(
239 "1st-level escape",
240 "*",
241 task(&[("output", None), ("insert+p1", Some("1st-level escape"))], Some("0"), false, false),
242 ),
243 raw("[(...)]", "r|rt", task(&[("rd=", None)], Some("rd"), false, false)),
244 raw(
245 "[(...)]",
246 "rd|rdt",
247 task(&[("rq=", None)], Some("rdq"), false, false),
248 ),
249 raw(
250 "...",
251 "o|d|D|dq|qd|qD",
252 task(&[("output", None), ("bond", Some("..."))], Some("3"), false, false),
253 ),
254 raw(
255 "...",
256 "*",
257 task(&[("output", Some("1")), ("insert", Some("ellipsis"))], Some("1"), false, false),
258 ),
259 raw(
260 ". __* ",
261 "*",
262 task(&[("output", None), ("insert", Some("addition compound"))], Some("1"), false, false),
263 ),
264 raw(
265 "state of aggregation $",
266 "*",
267 task(&[("output", None), ("state of aggregation", None)], Some("1"), false, false),
268 ),
269 raw(
270 "{[(",
271 "a|as|o",
272 task(&[("o=", None), ("output", None), ("parenthesisLevel++", None)], Some("2"), false, false),
273 ),
274 raw(
275 "{[(",
276 "0|1|2|3",
277 task(&[("o=", None), ("output", None), ("parenthesisLevel++", None)], Some("2"), false, false),
278 ),
279 raw(
280 "{[(",
281 "*",
282 task(&[("output", None), ("o=", None), ("output", None), ("parenthesisLevel++", None)], Some("2"), false, false),
283 ),
284 raw(
285 ")]}",
286 "0|1|2|3|b|p|bp|o",
287 task(&[("o=", None), ("parenthesisLevel--", None)], Some("o"), false, false),
288 ),
289 raw(
290 ")]}",
291 "a|as|d|D|q|qd|qD|dq",
292 task(&[("output", None), ("o=", None), ("parenthesisLevel--", None)], Some("o"), false, false),
293 ),
294 raw(
295 ", ",
296 "*",
297 task(&[("output", None), ("comma", None)], Some("0"), false, false),
298 ),
299 raw("^_", "*", task(&[], None, false, false)),
300 raw(
301 "^{(...)}|^($...$)",
302 "0|1|2|as",
303 task(&[("b=", None)], Some("b"), false, false),
304 ),
305 raw(
306 "^{(...)}|^($...$)",
307 "p",
308 task(&[("b=", None)], Some("bp"), false, false),
309 ),
310 raw(
311 "^{(...)}|^($...$)",
312 "3|o",
313 task(&[("d= kv", None)], Some("D"), false, false),
314 ),
315 raw(
316 "^{(...)}|^($...$)",
317 "q",
318 task(&[("d=", None)], Some("qD"), false, false),
319 ),
320 raw(
321 "^{(...)}|^($...$)",
322 "d|D|qd|qD|dq",
323 task(&[("output", None), ("d=", None)], Some("D"), false, false),
324 ),
325 raw(
326 "^a|^\\x{}{}|^\\x{}|^\\x|'",
327 "0|1|2|as",
328 task(&[("b=", None)], Some("b"), false, false),
329 ),
330 raw(
331 "^a|^\\x{}{}|^\\x{}|^\\x|'",
332 "p",
333 task(&[("b=", None)], Some("bp"), false, false),
334 ),
335 raw(
336 "^a|^\\x{}{}|^\\x{}|^\\x|'",
337 "3|o",
338 task(&[("d= kv", None)], Some("d"), false, false),
339 ),
340 raw(
341 "^a|^\\x{}{}|^\\x{}|^\\x|'",
342 "q",
343 task(&[("d=", None)], Some("qd"), false, false),
344 ),
345 raw(
346 "^a|^\\x{}{}|^\\x{}|^\\x|'",
347 "d|qd|D|qD",
348 task(&[("d=", None)], None, false, false),
349 ),
350 raw(
351 "^a|^\\x{}{}|^\\x{}|^\\x|'",
352 "dq",
353 task(&[("output", None), ("d=", None)], Some("d"), false, false),
354 ),
355 raw(
356 "_{(state of aggregation)}$",
357 "d|D|q|qd|qD|dq",
358 task(&[("output", None), ("q=", None)], Some("q"), false, false),
359 ),
360 raw(
361 "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x",
362 "0|1|2|as",
363 task(&[("p=", None)], Some("p"), false, false),
364 ),
365 raw(
366 "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x",
367 "b",
368 task(&[("p=", None)], Some("bp"), false, false),
369 ),
370 raw(
371 "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x",
372 "3|o",
373 task(&[("q=", None)], Some("q"), false, false),
374 ),
375 raw(
376 "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x",
377 "d|D",
378 task(&[("q=", None)], Some("dq"), false, false),
379 ),
380 raw(
381 "_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x",
382 "q|qd|qD|dq",
383 task(&[("output", None), ("q=", None)], Some("q"), false, false),
384 ),
385 raw(
386 "=<>",
387 "0|1|2|3|a|as|o|q|d|D|qd|qD|dq",
388 task(&[("output", Some("2")), ("bond", None)], Some("3"), false, false),
389 ),
390 raw(
391 "#",
392 "0|1|2|3|a|as|o",
393 task(&[("output", Some("2")), ("bond", Some("#"))], Some("3"), false, false),
394 ),
395 raw(
396 "{}^",
397 "*",
398 task(&[("output", Some("1")), ("insert", Some("tinySkip"))], Some("1"), false, false),
399 ),
400 raw("{}", "*", task(&[("output", Some("1"))], Some("1"), false, false)),
401 raw(
402 "{...}",
403 "0|1|2|3|a|as|b|p|bp",
404 task(&[("o=", None)], Some("o"), false, false),
405 ),
406 raw(
407 "{...}",
408 "o|d|D|q|qd|qD|dq",
409 task(&[("output", None), ("o=", None)], Some("o"), false, false),
410 ),
411 raw("$...$", "a", task(&[("a=", None)], None, false, false)),
412 raw(
413 "$...$",
414 "0|1|2|3|as|b|p|bp|o",
415 task(&[("o=", None)], Some("o"), false, false),
416 ),
417 raw("$...$", "as|o", task(&[("o=", None)], None, false, false)),
418 raw(
419 "$...$",
420 "q|d|D|qd|qD|dq",
421 task(&[("output", None), ("o=", None)], Some("o"), false, false),
422 ),
423 raw(
424 "\\bond{(...)}",
425 "*",
426 task(&[("output", Some("2")), ("bond", None)], Some("3"), false, false),
427 ),
428 raw(
429 "\\frac{(...)}",
430 "*",
431 task(&[("output", Some("1")), ("frac-output", None)], Some("3"), false, false),
432 ),
433 raw(
434 "\\overset{(...)}",
435 "*",
436 task(&[("output", Some("2")), ("overset-output", None)], Some("3"), false, false),
437 ),
438 raw(
439 "\\underset{(...)}",
440 "*",
441 task(&[("output", Some("2")), ("underset-output", None)], Some("3"), false, false),
442 ),
443 raw(
444 "\\underbrace{(...)}",
445 "*",
446 task(&[("output", Some("2")), ("underbrace-output", None)], Some("3"), false, false),
447 ),
448 raw(
449 "\\color{(...)}{(...)}",
450 "*",
451 task(&[("output", Some("2")), ("color-output", None)], Some("3"), false, false),
452 ),
453 raw(
454 "\\color{(...)}",
455 "*",
456 task(&[("output", Some("2")), ("color0-output", None)], None, false, false),
457 ),
458 raw(
459 "\\ce{(...)}",
460 "*",
461 task(&[("output", Some("2")), ("ce", None)], Some("3"), false, false),
462 ),
463 raw(
464 "\\,",
465 "*",
466 task(&[("output", Some("1")), ("copy", None)], Some("1"), false, false),
467 ),
468 raw(
469 "\\pu{(...)}",
470 "*",
471 task(&[("output", None), ("write", Some("{")), ("pu", None), ("write", Some("}"))], Some("3"), false, false),
472 ),
473 raw(
474 "\\x{}{}|\\x{}|\\x",
475 "0|1|2|3|a|as|b|p|bp|o|c0",
476 task(&[("o=", None), ("output", None)], Some("3"), false, false),
477 ),
478 raw(
479 "\\x{}{}|\\x{}|\\x",
480 "*",
481 task(&[("output", None), ("o=", None), ("output", None)], Some("3"), false, false),
482 ),
483 raw(
484 "others",
485 "*",
486 task(&[("output", Some("1")), ("copy", None)], Some("3"), false, false),
487 ),
488 raw(
489 "else2",
490 "a",
491 task(&[("a to o", None)], Some("o"), true, false),
492 ),
493 raw(
494 "else2",
495 "as",
496 task(&[("output", None), ("sb=true", None)], Some("1"), true, false),
497 ),
498 raw(
499 "else2",
500 "r|rt|rd|rdt|rdq",
501 task(&[("output", None)], Some("0"), true, false),
502 ),
503 raw(
504 "else2",
505 "*",
506 task(&[("output", None), ("copy", None)], Some("3"), true, false),
507 ),
508 ])
509});
510
511static A: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
516 build_transitions(&[
517 raw("empty", "*", task(&[], None, false, false)),
518 raw("1/2$", "0", task(&[("1/2", None)], None, false, false)),
519 raw("else", "0", task(&[], Some("1"), true, false)),
520 raw(
521 "${(...)}$__$(...)$",
522 "*",
523 task(&[("tex-math tight", None)], Some("1"), false, false),
524 ),
525 raw(
526 ",",
527 "*",
528 task(&[("insert", Some("commaDecimal"))], None, false, false),
529 ),
530 raw("else2", "*", task(&[("copy", None)], None, false, false)),
531 ])
532});
533
534static O: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
535 build_transitions(&[
536 raw("empty", "*", task(&[], None, false, false)),
537 raw("1/2$", "0", task(&[("1/2", None)], None, false, false)),
538 raw("else", "0", task(&[], Some("1"), true, false)),
539 raw("letters", "*", task(&[("rm", None)], None, false, false)),
540 raw(
541 "\\ca",
542 "*",
543 task(&[("insert", Some("circa"))], None, false, false),
544 ),
545 raw(
546 "\\pu{(...)}",
547 "*",
548 task(&[("write", Some("{")), ("pu", None), ("write", Some("}"))], None, false, false),
549 ),
550 raw("\\x{}{}|\\x{}|\\x", "*", task(&[("copy", None)], None, false, false)),
551 raw(
552 "${(...)}$__$(...)$",
553 "*",
554 task(&[("tex-math", None)], None, false, false),
555 ),
556 raw(
557 "{(...)}",
558 "*",
559 task(&[("write", Some("{")), ("text", None), ("write", Some("}"))], None, false, false),
560 ),
561 raw("else2", "*", task(&[("copy", None)], None, false, false)),
562 ])
563});
564
565static TEXT: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
566 build_transitions(&[
567 raw("empty", "*", task(&[("output", None)], None, false, false)),
568 raw("{...}", "*", task(&[("text=", None)], None, false, false)),
569 raw(
570 "${(...)}$__$(...)$",
571 "*",
572 task(&[("tex-math", None)], None, false, false),
573 ),
574 raw(
575 "\\greek",
576 "*",
577 task(&[("output", None), ("rm", None)], None, false, false),
578 ),
579 raw(
580 "\\pu{(...)}",
581 "*",
582 task(&[("output", None), ("write", Some("{")), ("pu", None), ("write", Some("}"))], None, false, false),
583 ),
584 raw(
585 "\\,|\\x{}{}|\\x{}|\\x",
586 "*",
587 task(&[("output", None), ("copy", None)], None, false, false),
588 ),
589 raw("else", "*", task(&[("text=", None)], None, false, false)),
590 ])
591});
592
593static PQ: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
594 build_transitions(&[
595 raw("empty", "*", task(&[], None, false, false)),
596 raw(
597 "state of aggregation $",
598 "*",
599 task(&[("state of aggregation", None)], None, false, false),
600 ),
601 raw("i$", "0", task(&[], Some("!f"), true, false)),
602 raw("(KV letters),", "0", task(&[("rm", None)], Some("0"), false, false)),
603 raw("formula$", "0", task(&[], Some("f"), true, false)),
604 raw("1/2$", "0", task(&[("1/2", None)], None, false, false)),
605 raw("else", "0", task(&[], Some("!f"), true, false)),
606 raw(
607 "${(...)}$__$(...)$",
608 "*",
609 task(&[("tex-math", None)], None, false, false),
610 ),
611 raw("{(...)}", "*", task(&[("text", None)], None, false, false)),
612 raw("a-z", "f", task(&[("tex-math", None)], None, false, false)),
613 raw("letters", "*", task(&[("rm", None)], None, false, false)),
614 raw("-9.,9", "*", task(&[("9,9", None)], None, false, false)),
615 raw(
616 ",",
617 "*",
618 task(&[("insert+p1", Some("comma enumeration S"))], None, false, false),
619 ),
620 raw(
621 "\\color{(...)}{(...)}",
622 "*",
623 task(&[("color-output", None)], None, false, false),
624 ),
625 raw(
626 "\\color{(...)}",
627 "*",
628 task(&[("color0-output", None)], None, false, false),
629 ),
630 raw("\\ce{(...)}", "*", task(&[("ce", None)], None, false, false)),
631 raw(
632 "\\pu{(...)}",
633 "*",
634 task(&[("write", Some("{")), ("pu", None), ("write", Some("}"))], None, false, false),
635 ),
636 raw("\\,|\\x{}{}|\\x{}|\\x", "*", task(&[("copy", None)], None, false, false)),
637 raw("else2", "*", task(&[("copy", None)], None, false, false)),
638 ])
639});
640
641static BD: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
642 build_transitions(&[
643 raw("empty", "*", task(&[], None, false, false)),
644 raw("x$", "0", task(&[], Some("!f"), true, false)),
645 raw("formula$", "0", task(&[], Some("f"), true, false)),
646 raw("else", "0", task(&[], Some("!f"), true, false)),
647 raw("-9.,9 no missing 0", "*", task(&[("9,9", None)], None, false, false)),
648 raw(
649 ".",
650 "*",
651 task(&[("insert", Some("electron dot"))], None, false, false),
652 ),
653 raw("a-z", "f", task(&[("tex-math", None)], None, false, false)),
654 raw("x", "*", task(&[("insert", Some("KV x"))], None, false, false)),
655 raw("letters", "*", task(&[("rm", None)], None, false, false)),
656 raw("'", "*", task(&[("insert", Some("prime"))], None, false, false)),
657 raw(
658 "${(...)}$__$(...)$",
659 "*",
660 task(&[("tex-math", None)], None, false, false),
661 ),
662 raw("{(...)}", "*", task(&[("text", None)], None, false, false)),
663 raw(
664 "\\color{(...)}{(...)}",
665 "*",
666 task(&[("color-output", None)], None, false, false),
667 ),
668 raw(
669 "\\color{(...)}",
670 "*",
671 task(&[("color0-output", None)], None, false, false),
672 ),
673 raw("\\ce{(...)}", "*", task(&[("ce", None)], None, false, false)),
674 raw(
675 "\\pu{(...)}",
676 "*",
677 task(&[("write", Some("{")), ("pu", None), ("write", Some("}"))], None, false, false),
678 ),
679 raw("\\,|\\x{}{}|\\x{}|\\x", "*", task(&[("copy", None)], None, false, false)),
680 raw("else2", "*", task(&[("copy", None)], None, false, false)),
681 ])
682});
683
684static OXIDATION: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
685 build_transitions(&[
686 raw("empty", "*", task(&[("roman-numeral", None)], None, false, false)),
687 raw(
688 "pm-operator",
689 "*",
690 task(&[("o=+p1", Some("\\pm"))], None, false, false),
691 ),
692 raw("else", "*", task(&[("o=", None)], None, false, false)),
693 ])
694});
695
696static TEX_MATH: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
697 build_transitions(&[
698 raw("empty", "*", task(&[("output", None)], None, false, false)),
699 raw(
700 "\\ce{(...)}",
701 "*",
702 task(&[("output", None), ("ce", None)], None, false, false),
703 ),
704 raw(
705 "\\pu{(...)}",
706 "*",
707 task(&[("output", None), ("write", Some("{")), ("pu", None), ("write", Some("}"))], None, false, false),
708 ),
709 raw("{...}|\\,|\\x{}{}|\\x{}|\\x", "*", task(&[("o=", None)], None, false, false)),
710 raw("else", "*", task(&[("o=", None)], None, false, false)),
711 ])
712});
713
714static TEX_MATH_TIGHT: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
715 build_transitions(&[
716 raw("empty", "*", task(&[("output", None)], None, false, false)),
717 raw(
718 "\\ce{(...)}",
719 "*",
720 task(&[("output", None), ("ce", None)], None, false, false),
721 ),
722 raw(
723 "\\pu{(...)}",
724 "*",
725 task(&[("output", None), ("write", Some("{")), ("pu", None), ("write", Some("}"))], None, false, false),
726 ),
727 raw("{...}|\\,|\\x{}{}|\\x{}|\\x", "*", task(&[("o=", None)], None, false, false)),
728 raw("-|+", "*", task(&[("tight operator", None)], None, false, false)),
729 raw("else", "*", task(&[("o=", None)], None, false, false)),
730 ])
731});
732
733static NUM99: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
734 build_transitions(&[
735 raw("empty", "*", task(&[], None, false, false)),
736 raw(",", "*", task(&[("comma", None)], None, false, false)),
737 raw("else", "*", task(&[("copy", None)], None, false, false)),
738 ])
739});
740
741static PU: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
746 build_transitions(&[
747 raw("empty", "*", task(&[("output", None)], None, false, false)),
748 raw(
749 "space$",
750 "*",
751 task(&[("output", None), ("space", None)], None, false, false),
752 ),
753 raw("{[(|)]}", "0|a", task(&[("copy", None)], None, false, false)),
754 raw(
755 "(-)(9)^(-9)",
756 "0",
757 task(&[("number^", None)], Some("a"), false, false),
758 ),
759 raw(
760 "(-)(9.,9)(e)(99)",
761 "0",
762 task(&[("enumber", None)], Some("a"), false, false),
763 ),
764 raw("space", "0|a", task(&[], None, false, false)),
765 raw(
766 "pm-operator",
767 "0|a",
768 task(&[("operator", Some("\\pm"))], Some("0"), false, false),
769 ),
770 raw("operator", "0|a", task(&[("copy", None)], Some("0"), false, false)),
771 raw("//", "d", task(&[("o=", None)], Some("/"), false, false)),
772 raw("/", "d", task(&[("o=", None)], Some("/"), false, false)),
773 raw(
774 "{...}|else",
775 "0|d",
776 task(&[("d=", None)], Some("d"), false, false),
777 ),
778 raw(
779 "{...}|else",
780 "a",
781 task(&[("space", None), ("d=", None)], Some("d"), false, false),
782 ),
783 raw(
784 "{...}|else",
785 "/|q",
786 task(&[("q=", None)], Some("q"), false, false),
787 ),
788 ])
789});
790
791static PU2: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
792 build_transitions(&[
793 raw("empty", "*", task(&[("output", None)], None, false, false)),
794 raw(
795 "*",
796 "*",
797 task(&[("output", None), ("cdot", None)], Some("0"), false, false),
798 ),
799 raw("\\x", "*", task(&[("rm=", None)], None, false, false)),
800 raw(
801 "space",
802 "*",
803 task(&[("output", None), ("space", None)], Some("0"), false, false),
804 ),
805 raw(
806 "^{(...)}|^(-1)",
807 "1",
808 task(&[("^(-1)", None)], None, false, false),
809 ),
810 raw("-9.,9", "0", task(&[("rm=", None)], Some("0"), false, false)),
811 raw(
812 "-9.,9",
813 "1",
814 task(&[("^(-1)", None)], Some("0"), false, false),
815 ),
816 raw("{...}|else", "*", task(&[("rm=", None)], Some("1"), false, false)),
817 ])
818});
819
820static PU99: LazyLock<HashMap<String, Vec<Transition>>> = LazyLock::new(|| {
821 build_transitions(&[
822 raw("empty", "0", task(&[("output-0", None)], None, false, false)),
823 raw("empty", "o", task(&[("output-o", None)], None, false, false)),
824 raw(
825 ",",
826 "0",
827 task(&[("output-0", None), ("comma", None)], Some("o"), false, false),
828 ),
829 raw(
830 ".",
831 "0",
832 task(&[("output-0", None), ("copy", None)], Some("o"), false, false),
833 ),
834 raw("else", "*", task(&[("text=", None)], None, false, false)),
835 ])
836});
837
838fn machine_action(
843 machine: &str,
844 buf: &mut Buffer,
845 m: &MVal,
846 opt: &Option<String>,
847 type_: &str,
848) -> Option<Out> {
849 match machine {
850 "ce" => ce_action(buf, m, opt, type_),
851 "text" => text_action(buf, type_),
852 "pq" => pq_action(buf, m, type_),
853 "bd" => bd_action(buf, m, type_),
854 "oxidation" => oxidation_action(buf, type_),
855 "tex-math" | "tex-math tight" => texmath_action(machine, buf, m, type_),
856 "9,9" => Some(num99_action(type_)),
857 "pu" => pu_action(buf, m, opt, type_),
858 "pu-2" => pu2_action(buf, m, type_),
859 "pu-9,9" => pu99_action(buf, type_),
860 _ => None,
861 }
862}
863
864fn gof(opt_str: &Option<String>, m: &str) -> Field {
865 Field::Nodes(go(opt_str.as_deref().unwrap_or(""), m))
866}
867
868fn arrow_sub(buf_field: &Option<String>, rdt: &Option<String>, ce_m: &str) -> Field {
869 match rdt.as_deref() {
870 Some("M") => Field::Nodes(go(buf_field.as_deref().unwrap_or(""), "tex-math")),
871 Some("T") => Field::Nodes(vec![Parsed::N(NodeData {
872 type_: "text".into(),
873 p1: Some(Field::Str(buf_field.clone().unwrap_or_default())),
874 ..Default::default()
875 })]),
876 _ => Field::Nodes(go(buf_field.as_deref().unwrap_or(""), ce_m)),
877 }
878}
879
880fn ce_action(buf: &mut Buffer, m: &MVal, opt: &Option<String>, type_: &str) -> Option<Out> {
881 let out = match type_ {
882 "o after d" => {
883 let mut ret: Vec<Parsed> = Vec::new();
884 let d_is_int = buf
885 .d
886 .as_ref()
887 .map(|d| re!("^[1-9][0-9]*$").is_match(d))
888 .unwrap_or(false);
889 if d_is_int {
890 let tmp = buf.d.take();
891 concat(&mut ret, ce_action(buf, m, &None, "output").unwrap_or(Out::None));
892 ret.push(Parsed::N(NodeData {
893 type_: "tinySkip".into(),
894 ..Default::default()
895 }));
896 buf.b = tmp;
897 } else {
898 concat(&mut ret, ce_action(buf, m, &None, "output").unwrap_or(Out::None));
899 }
900 append_field(&mut buf.o, m);
901 Out::Many(ret)
902 }
903 "d= kv" => {
904 buf.d = Some(mval_str(m));
905 buf.d_type = Some("kv".into());
906 Out::None
907 }
908 "charge or bond" => {
909 if buf.begins_with_bond {
910 let mut ret: Vec<Parsed> = Vec::new();
911 concat(&mut ret, ce_action(buf, m, &None, "output").unwrap_or(Out::None));
912 ret.push(Parsed::N(NodeData {
913 type_: "bond".into(),
914 kind_: Some("-".into()),
915 ..Default::default()
916 }));
917 Out::Many(ret)
918 } else {
919 buf.d = Some(mval_str(m));
920 Out::None
921 }
922 }
923 "- after o/d" => {
924 let is_after_d = opt.as_deref() == Some("true");
925 let o_str = buf.o.clone().unwrap_or_default();
926 let c1 = match_pattern("orbital", &o_str);
927 let c2 = match_pattern("one lowercase greek letter $", &o_str);
928 let c3 = match_pattern("one lowercase latin letter $", &o_str);
929 let c4 = match_pattern("$one lowercase latin letter$ $", &o_str);
930 let m_str = mval_str(m);
931 let hyphen_follows = m_str == "-"
932 && ((c1.as_ref().map(|x| x.remainder.is_empty()).unwrap_or(false))
933 || c2.is_some()
934 || c3.is_some()
935 || c4.is_some());
936 if hyphen_follows
937 && buf.a.is_none()
938 && buf.b.is_none()
939 && buf.p.is_none()
940 && buf.d.is_none()
941 && buf.q.is_none()
942 && c1.is_none()
943 && c3.is_some()
944 {
945 let ov = buf.o.take().unwrap_or_default();
946 buf.o = Some(format!("${}$", ov));
947 }
948 let mut ret: Vec<Parsed> = Vec::new();
949 if hyphen_follows {
950 concat(&mut ret, ce_action(buf, m, &None, "output").unwrap_or(Out::None));
951 ret.push(Parsed::N(NodeData {
952 type_: "hyphen".into(),
953 ..Default::default()
954 }));
955 } else {
956 let d_str = buf.d.clone().unwrap_or_default();
957 let c1d = match_pattern("digits", &d_str);
958 if is_after_d && c1d.as_ref().map(|x| x.remainder.is_empty()).unwrap_or(false) {
959 append_field(&mut buf.d, m);
960 concat(&mut ret, ce_action(buf, m, &None, "output").unwrap_or(Out::None));
961 } else {
962 concat(&mut ret, ce_action(buf, m, &None, "output").unwrap_or(Out::None));
963 ret.push(Parsed::N(NodeData {
964 type_: "bond".into(),
965 kind_: Some("-".into()),
966 ..Default::default()
967 }));
968 }
969 }
970 Out::Many(ret)
971 }
972 "a to o" => {
973 buf.o = buf.a.take();
974 Out::None
975 }
976 "sb=true" => {
977 buf.sb = true;
978 Out::None
979 }
980 "sb=false" => {
981 buf.sb = false;
982 Out::None
983 }
984 "beginsWithBond=true" => {
985 buf.begins_with_bond = true;
986 Out::None
987 }
988 "beginsWithBond=false" => {
989 buf.begins_with_bond = false;
990 Out::None
991 }
992 "parenthesisLevel++" => {
993 buf.parenthesis_level += 1;
994 Out::None
995 }
996 "parenthesisLevel--" => {
997 buf.parenthesis_level -= 1;
998 Out::None
999 }
1000 "state of aggregation" => Out::One(Parsed::N(NodeData {
1001 type_: "state of aggregation".into(),
1002 p1: Some(Field::Nodes(go(&mval_str(m), "o"))),
1003 ..Default::default()
1004 })),
1005 "comma" => {
1006 let raw = mval_str(m);
1007 let a = raw.trim_end();
1008 let with_space = a != raw;
1009 let kind = if with_space && buf.parenthesis_level == 0 {
1010 "comma enumeration L"
1011 } else {
1012 "comma enumeration M"
1013 };
1014 Out::One(Parsed::N(NodeData {
1015 type_: kind.into(),
1016 p1: Some(Field::Str(a.to_string())),
1017 ..Default::default()
1018 }))
1019 }
1020 "output" => {
1021 let entity_follows = match opt.as_deref() {
1022 Some("1") => 1,
1023 Some("2") => 2,
1024 _ => 0,
1025 };
1026 let ret: Vec<Parsed> = if buf.r.is_none() {
1027 let mut ret = Vec::new();
1028 let empty = buf.a.is_none()
1029 && buf.b.is_none()
1030 && buf.p.is_none()
1031 && buf.o.is_none()
1032 && buf.q.is_none()
1033 && buf.d.is_none()
1034 && entity_follows == 0;
1035 if !empty {
1036 if buf.sb {
1037 ret.push(Parsed::N(NodeData {
1038 type_: "entitySkip".into(),
1039 ..Default::default()
1040 }));
1041 }
1042 let o_none = buf.o.is_none();
1043 let q_none = buf.q.is_none();
1044 let d_none = buf.d.is_none();
1045 let b_none = buf.b.is_none();
1046 let p_none = buf.p.is_none();
1047 if o_none && q_none && d_none && b_none && p_none && entity_follows != 2 {
1048 buf.o = buf.a.take();
1049 } else if o_none && q_none && d_none && (buf.b.is_some() || buf.p.is_some()) {
1050 buf.o = buf.a.take();
1051 buf.d = buf.b.take();
1052 buf.q = buf.p.take();
1053 } else if buf.o.is_some()
1054 && buf.d_type.as_deref() == Some("kv")
1055 && match_pattern("d-oxidation$", &buf.d.clone().unwrap_or_default()).is_some()
1056 {
1057 buf.d_type = Some("oxidation".into());
1058 } else if buf.o.is_some() && buf.d_type.as_deref() == Some("kv") && buf.q.is_none() {
1059 buf.d_type = None;
1060 }
1061 let d_machine = if buf.d_type.as_deref() == Some("oxidation") {
1062 "oxidation"
1063 } else {
1064 "bd"
1065 };
1066 ret.push(Parsed::N(NodeData {
1067 type_: "chemfive".into(),
1068 a: Some(gof(&buf.a.clone(), "a")),
1069 b: Some(gof(&buf.b.clone(), "bd")),
1070 p: Some(gof(&buf.p.clone(), "pq")),
1071 o: Some(gof(&buf.o.clone(), "o")),
1072 q: Some(gof(&buf.q.clone(), "pq")),
1073 d: Some(gof(&buf.d.clone(), d_machine)),
1074 d_type: buf.d_type.clone(),
1075 ..Default::default()
1076 }));
1077 }
1078 ret
1079 } else {
1080 let rd = arrow_sub(&buf.rd.clone(), &buf.rdt.clone(), "ce");
1081 let rq = arrow_sub(&buf.rq.clone(), &buf.rqt.clone(), "ce");
1082 vec![Parsed::N(NodeData {
1083 type_: "arrow".into(),
1084 r: buf.r.clone(),
1085 rd: Some(rd),
1086 rq: Some(rq),
1087 ..Default::default()
1088 })]
1089 };
1090 buf.clear(true);
1091 Out::Many(ret)
1092 }
1093 "oxidation-output" => {
1094 let mut ret: Vec<Parsed> = vec![Parsed::S("{".into())];
1095 ret.extend(go(&mval_str(m), "oxidation"));
1096 ret.push(Parsed::S("}".into()));
1097 Out::Many(ret)
1098 }
1099 "frac-output" | "overset-output" | "underset-output" | "underbrace-output" => {
1100 let (node_type, p1m, p2m) = match type_ {
1101 "frac-output" => ("frac-ce", 0usize, 1usize),
1102 "overset-output" => ("overset", 0, 1),
1103 "underset-output" => ("underset", 0, 1),
1104 _ => ("underbrace", 0, 1),
1105 };
1106 let (g1, g2) = mval_pair(m, p1m, p2m);
1107 Out::One(Parsed::N(NodeData {
1108 type_: node_type.into(),
1109 p1: Some(Field::Nodes(go(&g1, "ce"))),
1110 p2: Some(Field::Nodes(go(&g2, "ce"))),
1111 ..Default::default()
1112 }))
1113 }
1114 "color-output" => {
1115 let (g1, g2) = mval_pair(m, 0, 1);
1116 Out::One(Parsed::N(NodeData {
1117 type_: "color".into(),
1118 color1: Some(g1),
1119 color2: Some(Field::Nodes(go(&g2, "ce"))),
1120 ..Default::default()
1121 }))
1122 }
1123 "r=" => {
1124 buf.r = Some(mval_str(m));
1125 Out::None
1126 }
1127 "rdt=" => {
1128 buf.rdt = Some(mval_str(m));
1129 Out::None
1130 }
1131 "rd=" => {
1132 buf.rd = Some(mval_str(m));
1133 Out::None
1134 }
1135 "rqt=" => {
1136 buf.rqt = Some(mval_str(m));
1137 Out::None
1138 }
1139 "rq=" => {
1140 buf.rq = Some(mval_str(m));
1141 Out::None
1142 }
1143 "operator" => Out::One(Parsed::N(NodeData {
1144 type_: "operator".into(),
1145 kind_: Some(opt.clone().unwrap_or_else(|| mval_str(m))),
1146 ..Default::default()
1147 })),
1148 _ => return None,
1149 };
1150 Some(out)
1151}
1152
1153fn mval_pair(m: &MVal, i: usize, j: usize) -> (String, String) {
1154 match m {
1155 MVal::V(v) => (
1156 v.get(i).cloned().unwrap_or_default(),
1157 v.get(j).cloned().unwrap_or_default(),
1158 ),
1159 MVal::S(s) => (s.clone(), String::new()),
1160 }
1161}
1162
1163fn text_action(buf: &mut Buffer, type_: &str) -> Option<Out> {
1164 if type_ == "output" {
1165 let ret = buf.text_.take().map(|t| {
1166 Out::One(Parsed::N(NodeData {
1167 type_: "text".into(),
1168 p1: Some(Field::Str(t)),
1169 ..Default::default()
1170 }))
1171 });
1172 buf.clear(false);
1173 Some(ret.unwrap_or(Out::None))
1174 } else {
1175 None
1176 }
1177}
1178
1179fn pq_action(_buf: &mut Buffer, m: &MVal, type_: &str) -> Option<Out> {
1180 match type_ {
1181 "state of aggregation" => Some(Out::One(Parsed::N(NodeData {
1182 type_: "state of aggregation subscript".into(),
1183 p1: Some(Field::Nodes(go(&mval_str(m), "o"))),
1184 ..Default::default()
1185 }))),
1186 "color-output" => {
1187 let (g1, g2) = mval_pair(m, 0, 1);
1188 Some(Out::One(Parsed::N(NodeData {
1189 type_: "color".into(),
1190 color1: Some(g1),
1191 color2: Some(Field::Nodes(go(&g2, "pq"))),
1192 ..Default::default()
1193 })))
1194 }
1195 _ => None,
1196 }
1197}
1198
1199fn bd_action(_buf: &mut Buffer, m: &MVal, type_: &str) -> Option<Out> {
1200 if type_ == "color-output" {
1201 let (g1, g2) = mval_pair(m, 0, 1);
1202 Some(Out::One(Parsed::N(NodeData {
1203 type_: "color".into(),
1204 color1: Some(g1),
1205 color2: Some(Field::Nodes(go(&g2, "bd"))),
1206 ..Default::default()
1207 })))
1208 } else {
1209 None
1210 }
1211}
1212
1213fn oxidation_action(buf: &mut Buffer, type_: &str) -> Option<Out> {
1214 if type_ == "roman-numeral" {
1215 Some(Out::One(Parsed::N(NodeData {
1216 type_: "roman numeral".into(),
1217 p1: Some(Field::Str(buf.o.clone().unwrap_or_default())),
1218 ..Default::default()
1219 })))
1220 } else {
1221 None
1222 }
1223}
1224
1225fn texmath_action(machine: &str, buf: &mut Buffer, m: &MVal, type_: &str) -> Option<Out> {
1226 match type_ {
1227 "tight operator" => {
1228 append_str(&mut buf.o, &format!("{{{}}}", mval_str(m)));
1229 Some(Out::None)
1230 }
1231 "output" => {
1232 let ret = buf.o.take().map(|o| {
1233 Out::One(Parsed::N(NodeData {
1234 type_: "tex-math".into(),
1235 p1: Some(Field::Str(o)),
1236 ..Default::default()
1237 }))
1238 });
1239 buf.clear(false);
1240 let _ = machine;
1241 Some(ret.unwrap_or(Out::None))
1242 }
1243 _ => None,
1244 }
1245}
1246
1247fn num99_action(type_: &str) -> Out {
1248 if type_ == "comma" {
1249 Out::One(Parsed::N(NodeData {
1250 type_: "commaDecimal".into(),
1251 ..Default::default()
1252 }))
1253 } else {
1254 Out::None
1255 }
1256}
1257
1258fn pu_action(buf: &mut Buffer, m: &MVal, opt: &Option<String>, type_: &str) -> Option<Out> {
1259 let out = match type_ {
1260 "enumber" => {
1261 let v = match m {
1262 MVal::V(v) => v.clone(),
1263 MVal::S(s) => vec![s.clone()],
1264 };
1265 let mut ret: Vec<Parsed> = Vec::new();
1266 let g0 = v.first().map(|s| s.as_str()).unwrap_or("");
1267 if g0 == "+-" || g0 == "+/-" {
1268 ret.push(Parsed::S("\\pm ".into()));
1269 } else if !g0.is_empty() {
1270 ret.push(Parsed::S(g0.to_string()));
1271 }
1272 let g1 = v.get(1).cloned().unwrap_or_default();
1273 if !g1.is_empty() {
1274 ret.extend(go(&g1, "pu-9,9"));
1275 let g2 = v.get(2).cloned().unwrap_or_default();
1276 if !g2.is_empty() {
1277 if g2.contains(',') || g2.contains('.') {
1278 ret.extend(go(&g2, "pu-9,9"));
1279 } else {
1280 ret.push(Parsed::S(g2));
1281 }
1282 }
1283 let g3 = v.get(3).map(|s| s.as_str()).unwrap_or("");
1284 let g4 = v.get(4).map(|s| s.as_str()).unwrap_or("");
1285 if !g3.is_empty() || !g4.is_empty() {
1286 if g3 == "e" || g4 == "*" {
1287 ret.push(Parsed::N(NodeData {
1288 type_: "cdot".into(),
1289 ..Default::default()
1290 }));
1291 } else {
1292 ret.push(Parsed::N(NodeData {
1293 type_: "times".into(),
1294 ..Default::default()
1295 }));
1296 }
1297 }
1298 }
1299 let g5 = v.get(5).cloned().unwrap_or_default();
1300 if !g5.is_empty() {
1301 ret.push(Parsed::S(format!("10^{{{}}}", g5)));
1302 }
1303 Out::Many(ret)
1304 }
1305 "number^" => {
1306 let v = match m {
1307 MVal::V(v) => v.clone(),
1308 MVal::S(s) => vec![s.clone()],
1309 };
1310 let mut ret: Vec<Parsed> = Vec::new();
1311 let g0 = v.first().map(|s| s.as_str()).unwrap_or("");
1312 if g0 == "+-" || g0 == "+/-" {
1313 ret.push(Parsed::S("\\pm ".into()));
1314 } else if !g0.is_empty() {
1315 ret.push(Parsed::S(g0.to_string()));
1316 }
1317 let g1 = v.get(1).cloned().unwrap_or_default();
1318 ret.extend(go(&g1, "pu-9,9"));
1319 let g2 = v.get(2).cloned().unwrap_or_default();
1320 ret.push(Parsed::S(format!("^{{{}}}", g2)));
1321 Out::Many(ret)
1322 }
1323 "operator" => Out::One(Parsed::N(NodeData {
1324 type_: "operator".into(),
1325 kind_: Some(opt.clone().unwrap_or_else(|| mval_str(m))),
1326 ..Default::default()
1327 })),
1328 "space" => Out::One(Parsed::N(NodeData {
1329 type_: "pu-space-1".into(),
1330 ..Default::default()
1331 })),
1332 "output" => {
1333 if let Some(d) = &buf.d {
1335 if let Some(md) = match_pattern("{(...)}", d) {
1336 if md.remainder.is_empty() {
1337 buf.d = Some(mval_str(&md.m));
1338 }
1339 }
1340 }
1341 if let Some(q) = &buf.q {
1342 if let Some(mq) = match_pattern("{(...)}", q) {
1343 if mq.remainder.is_empty() {
1344 buf.q = Some(mval_str(&mq.m));
1345 }
1346 }
1347 }
1348 fn rep_c_f(s: &str) -> String {
1349 s.replace("\u{00B0}C", "{}^{\\circ}C")
1350 .replace("^oC", "{}^{\\circ}C")
1351 .replace("^{o}C", "{}^{\\circ}C")
1352 .replace("\u{00B0}F", "{}^{\\circ}F")
1353 .replace("^oF", "{}^{\\circ}F")
1354 .replace("^{o}F", "{}^{\\circ}F")
1355 }
1356 if let Some(d) = buf.d.as_mut() {
1357 *d = rep_c_f(d);
1358 }
1359 let ret: Vec<Parsed> = if let Some(q) = buf.q.as_mut() {
1360 *q = rep_c_f(q);
1361 let bd = go(&buf.d.clone().unwrap_or_default(), "pu");
1362 let bq = go(&buf.q.clone().unwrap_or_default(), "pu");
1363 if buf.o.as_deref() == Some("//") {
1364 vec![Parsed::N(NodeData {
1365 type_: "pu-frac".into(),
1366 p1: Some(Field::Nodes(bd)),
1367 p2: Some(Field::Nodes(bq)),
1368 ..Default::default()
1369 })]
1370 } else {
1371 let mut ret = bd;
1372 if ret.len() > 1 || bq.len() > 1 {
1373 ret.push(Parsed::N(NodeData {
1374 type_: " / ".into(),
1375 ..Default::default()
1376 }));
1377 } else {
1378 ret.push(Parsed::N(NodeData {
1379 type_: "/".into(),
1380 ..Default::default()
1381 }));
1382 }
1383 ret.extend(bq);
1384 ret
1385 }
1386 } else {
1387 go(&buf.d.clone().unwrap_or_default(), "pu-2")
1388 };
1389 buf.clear(false);
1390 Out::Many(ret)
1391 }
1392 _ => return None,
1393 };
1394 Some(out)
1395}
1396
1397fn pu2_action(buf: &mut Buffer, m: &MVal, type_: &str) -> Option<Out> {
1398 let out = match type_ {
1399 "cdot" => Out::One(Parsed::N(NodeData {
1400 type_: "tight cdot".into(),
1401 ..Default::default()
1402 })),
1403 "^(-1)" => {
1404 if let Some(rm) = buf.rm.as_mut() {
1405 rm.push_str(&format!("^{{{}}}", mval_str(m)));
1406 }
1407 Out::None
1408 }
1409 "space" => Out::One(Parsed::N(NodeData {
1410 type_: "pu-space-2".into(),
1411 ..Default::default()
1412 })),
1413 "output" => {
1414 let ret: Vec<Parsed> = if let Some(rm) = &buf.rm {
1415 if let Some(mrm) = match_pattern("{(...)}", rm) {
1416 if mrm.remainder.is_empty() {
1417 go(&mval_str(&mrm.m), "pu")
1418 } else {
1419 vec![Parsed::N(NodeData {
1420 type_: "rm".into(),
1421 p1: Some(Field::Str(rm.clone())),
1422 ..Default::default()
1423 })]
1424 }
1425 } else {
1426 vec![Parsed::N(NodeData {
1427 type_: "rm".into(),
1428 p1: Some(Field::Str(rm.clone())),
1429 ..Default::default()
1430 })]
1431 }
1432 } else {
1433 Vec::new()
1434 };
1435 buf.clear(false);
1436 Out::Many(ret)
1437 }
1438 _ => return None,
1439 };
1440 Some(out)
1441}
1442
1443fn pu99_action(buf: &mut Buffer, type_: &str) -> Option<Out> {
1444 let out = match type_ {
1445 "comma" => Out::One(Parsed::N(NodeData {
1446 type_: "commaDecimal".into(),
1447 ..Default::default()
1448 })),
1449 "output-0" => {
1450 let text = buf.text_.clone().unwrap_or_default();
1451 let ret = thousands_split(&text, true);
1452 buf.clear(false);
1453 Out::Many(ret)
1454 }
1455 "output-o" => {
1456 let text = buf.text_.clone().unwrap_or_default();
1457 let ret = thousands_split(&text, false);
1458 buf.clear(false);
1459 Out::Many(ret)
1460 }
1461 _ => return None,
1462 };
1463 Some(out)
1464}
1465
1466fn thousands_split(text: &str, reverse: bool) -> Vec<Parsed> {
1468 let mut ret: Vec<Parsed> = Vec::new();
1469 if text.len() > 4 {
1470 if reverse {
1471 let mut a = text.len() % 3;
1472 if a == 0 {
1473 a = 3;
1474 }
1475 let mut i = text.len().saturating_sub(3);
1476 while i > 0 {
1477 ret.push(Parsed::S(text[i..i + 3].to_string()));
1478 ret.push(Parsed::N(NodeData {
1479 type_: "1000 separator".into(),
1480 ..Default::default()
1481 }));
1482 i = i.saturating_sub(3);
1483 }
1484 ret.push(Parsed::S(text[..a].to_string()));
1485 ret.reverse();
1486 } else {
1487 let a = text.len() - 3;
1488 let mut i = 0;
1489 while i < a {
1490 ret.push(Parsed::S(text[i..i + 3].to_string()));
1491 ret.push(Parsed::N(NodeData {
1492 type_: "1000 separator".into(),
1493 ..Default::default()
1494 }));
1495 i += 3;
1496 }
1497 ret.push(Parsed::S(text[i..].to_string()));
1498 }
1499 } else {
1500 ret.push(Parsed::S(text.to_string()));
1501 }
1502 ret
1503}