1use crate::commands::primitives::PRIMITIVES;
4use crate::commands::{
5 ActiveConditional, CharOrPrimitive, PrimitiveCommand, ResolvedToken, TeXCommand,
6};
7use crate::engine::gullet::Gullet;
8use crate::engine::mouth::Mouth;
9use crate::engine::state::State;
10use crate::engine::EngineAux;
11use crate::engine::{EngineReferences, EngineTypes};
12use crate::tex::catcodes::CommandCode;
13use crate::tex::characters::Character;
14use crate::tex::numerics::TeXDimen;
15use crate::tex::numerics::{
16 MuDim, MuSkip, MuStretchShrink, NumSet, Skip, StretchShrink, STRETCH_SHRINK_UNITS,
17};
18use crate::tex::tokens::control_sequences::{CSHandler, ResolvedCSName};
19use crate::tex::tokens::token_lists::TokenList;
20use crate::tex::tokens::{StandardToken, Token};
21use crate::utils::errors::{TeXError, TeXResult};
22use either::Either;
23
24pub fn read_arguments<ET: EngineTypes>(
27 engine: &mut EngineReferences<ET>,
28 args: &mut [Vec<ET::Token>; 9],
29 params: TokenList<ET::Token>,
30 long: bool,
31 token: &ET::Token,
32) -> TeXResult<(), ET> {
33 let mut i = 1usize;
34 let inner = ¶ms.0;
35 let mut next = &inner[0];
36 loop {
37 match next.is_argument_marker() {
38 Some(a) => match inner.get(i) {
39 Some(n) if n.is_argument_marker().is_some() => {
40 next = n;
41 i += 1;
42 read_argument(engine, &mut args[a as usize], long, token)?
43 }
44 None => {
45 return read_argument(engine, &mut args[a as usize], long, token);
46 }
47 Some(o) => {
48 let mut delim = vec![o.clone()];
49 i += 1;
50 while let Some(n) = inner.get(i) {
51 if n.is_argument_marker().is_some() {
52 break;
53 }
54 delim.push(n.clone());
55 i += 1;
56 }
57 read_delimited_argument(engine, &mut args[a as usize], &delim, long, token)?;
58 match inner.get(i) {
59 Some(n) => {
60 next = n;
61 i += 1
62 }
63 _ => return Ok(()),
64 }
65 }
66 },
67 _ => match engine.mouth.get_next(engine.aux, engine.state)? {
68 Some(o) if o == *next => match inner.get(i) {
69 Some(n) => {
70 next = n;
71 i += 1
72 }
73 _ => return Ok(()),
74 },
75 Some(o) => TeXError::wrong_definition(
76 engine.aux,
77 engine.state,
78 engine.mouth,
79 &o,
80 next,
81 token,
82 )?,
83 None => {
84 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, token)?
85 }
86 },
87 }
88 }
89}
90
91fn read_delimited_argument<ET: EngineTypes>(
92 engine: &mut EngineReferences<ET>,
93 arg: &mut Vec<ET::Token>,
94 delim: &[ET::Token],
95 long: bool,
96 token: &ET::Token,
97) -> TeXResult<(), ET> {
98 let par = engine.aux.memory.cs_interner().par();
99 let last = delim.last().unwrap();
100 let ends_with_bgroup = last.command_code() == CommandCode::BeginGroup;
101 let mut remove_braces: Option<Option<usize>> = None;
102 loop {
103 let t = engine.need_next(false, token)?;
104 match t.command_code() {
105 CommandCode::Primitive if t.is_primitive() == Some(PRIMITIVES.noexpand) => continue,
106 CommandCode::BeginGroup if !ends_with_bgroup => {
107 if arg.is_empty() {
108 remove_braces = Some(None)
109 }
110 arg.push(t);
111 let r = if long {
112 engine.read_until_endgroup(token, |_, _, t| {
113 arg.push(t);
114 Ok(())
115 })?
116 } else {
117 engine.read_until_endgroup(token, |a, s, t| {
118 if t.is_cs(&par) {
119 Err(TeXError::ParagraphEnded(
120 t.display(
121 a.memory.cs_interner(),
122 s.get_catcode_scheme(),
123 s.get_escape_char(),
124 )
125 .to_string(),
126 ))
127 } else {
128 arg.push(t);
129 Ok(())
130 }
131 })?
132 };
133 arg.push(r);
134 if let Some(ref mut n @ None) = remove_braces {
135 *n = Some(arg.len())
136 }
137 continue;
138 }
139 CommandCode::Escape if !long && t.is_cs(&par) => {
140 TeXError::paragraph_ended(engine.aux, engine.state, engine.mouth, token)?
141 }
142 _ => (),
143 }
144 if t == *last {
145 arg.push(t);
146 if arg.ends_with(delim) {
147 arg.truncate(arg.len() - delim.len());
148 if let Some(Some(n)) = remove_braces {
149 if arg.len() == n {
150 arg.pop();
151 arg.drain(..1).next();
152 }
153 }
154 if ends_with_bgroup {
155 if let Some(a) = engine.gullet.get_align_data() {
156 a.ingroups -= 1
157 }
158 }
159 return Ok(());
160 }
161 } else {
162 arg.push(t);
163 }
164 }
165}
166
167fn read_argument<ET: EngineTypes>(
168 engine: &mut EngineReferences<ET>,
169 arg: &mut Vec<ET::Token>,
170 long: bool,
171 token: &ET::Token,
172) -> TeXResult<(), ET> {
173 let eof = |a: &_, s: &_, m: &mut _| TeXError::file_end_while_use(a, s, m, token);
174 loop {
175 let t = match engine.mouth.get_next(engine.aux, engine.state)? {
176 Some(t) => t,
177 None => {
178 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, token)?;
179 continue;
180 }
181 };
182 match t.command_code() {
183 CommandCode::Primitive if t.is_primitive() == Some(PRIMITIVES.noexpand) => continue,
184 CommandCode::Space => continue,
185 CommandCode::BeginGroup => {
186 if long {
187 engine.mouth.read_until_endgroup(
188 engine.aux,
189 engine.state,
190 |_, t| {
191 arg.push(t);
192 Ok(())
193 },
194 eof,
195 )?;
196 } else {
197 let par = engine.aux.memory.cs_interner().par();
198 engine.mouth.read_until_endgroup(
199 engine.aux,
200 engine.state,
201 |a, t| {
202 if t.is_cs(&par) {
203 Err(TeXError::ParagraphEnded(
204 t.display(
205 a.memory.cs_interner(),
206 engine.state.get_catcode_scheme(),
207 engine.state.get_escape_char(),
208 )
209 .to_string(),
210 ))
211 } else {
212 arg.push(t);
213 Ok(())
214 }
215 },
216 eof,
217 )?;
218 }
219 return Ok(());
220 }
221 _ => (),
222 }
223 arg.push(t);
224 return Ok(());
225 }
226}
227
228pub fn expand_until_endgroup<
230 ET: EngineTypes,
231 Fn: FnMut(&mut EngineAux<ET>, &ET::State, ET::Token) -> TeXResult<(), ET>,
232>(
233 engine: &mut EngineReferences<ET>,
234 expand_protected: bool,
235 edef_like: bool,
236 token: &ET::Token,
237 mut cont: Fn,
238) -> TeXResult<(), ET> {
239 let mut ingroups = 0;
240 loop {
241 let t = engine.need_next(false, token)?;
242 match t.command_code() {
243 CommandCode::EndGroup => {
244 if ingroups == 0 {
245 return Ok(());
246 }
247 ingroups -= 1;
248 cont(engine.aux, engine.state, t)?;
249 continue;
250 }
251 CommandCode::BeginGroup => {
252 ingroups += 1;
253 cont(engine.aux, engine.state, t)?;
254 continue;
255 }
256 CommandCode::Primitive if t.is_primitive() == Some(PRIMITIVES.noexpand) => {
257 let next = engine.need_next(false, token)?;
258 cont(engine.aux, engine.state, next)?;
259 continue;
260 }
261 CommandCode::Escape | CommandCode::Active => match engine.resolve(&t) {
262 ResolvedToken::Tk { .. } => cont(engine.aux, engine.state, t)?,
263 ResolvedToken::Cmd(Some(TeXCommand::Macro(m)))
264 if m.protected && !expand_protected =>
265 {
266 cont(engine.aux, engine.state, t)?
267 }
268 ResolvedToken::Cmd(Some(TeXCommand::Macro(m))) => {
269 ET::Gullet::do_macro(engine, m.clone(), t)?
270 }
271 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
272 name,
273 cmd: PrimitiveCommand::Conditional(cond),
274 })) => ET::Gullet::do_conditional(engine, *name, t, *cond, false)?,
275 ResolvedToken::Cmd(Some(TeXCommand::Primitive { name, .. }))
276 if *name == PRIMITIVES.unexpanded =>
277 {
278 engine.expand_until_bgroup(false, &t)?;
279 engine.read_until_endgroup(token, |a, s, t| {
280 if edef_like && t.command_code() == CommandCode::Parameter {
281 cont(a, s, t.clone())?;
282 }
283 cont(a, s, t)
284 })?;
285 }
286 ResolvedToken::Cmd(Some(TeXCommand::Primitive { name, .. }))
287 if *name == PRIMITIVES.the =>
288 {
289 engine.do_the(|a, s, _, t| {
290 if t.command_code() == CommandCode::Parameter && edef_like {
291 cont(a, s, t.clone())?;
292 }
293 cont(a, s, t)
294 })?
295 }
296 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
297 name,
298 cmd: PrimitiveCommand::SimpleExpandable(e),
299 })) => ET::Gullet::do_simple_expandable(engine, *name, t, *e)?,
300 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
301 name,
302 cmd: PrimitiveCommand::Expandable(e),
303 })) => ET::Gullet::do_expandable(engine, *name, t, *e)?,
304 ResolvedToken::Cmd(_) => cont(engine.aux, engine.state, t)?,
305 },
306 _ => cont(engine.aux, engine.state, t)?,
307 }
308 }
309}
310
311pub(crate) fn case_loop<ET: EngineTypes>(
312 engine: &mut EngineReferences<ET>,
313 idx: usize,
314) -> TeXResult<(), ET> {
315 let (mut incond, cond) = {
316 let conds = engine.gullet.get_conditionals();
317 let ic = conds.len() - idx;
318 for _ in 0..ic {
319 conds.pop();
320 }
321 (
322 ic,
323 match conds.pop() {
324 Some(ActiveConditional::Case(c)) => c,
325 _ => unreachable!(),
326 },
327 )
328 };
329 let mut curr_int = <ET::Num as NumSet>::Int::default();
330 let one = <ET::Num as NumSet>::Int::from(1);
331 let mut conds = std::mem::take(engine.gullet.get_conditionals());
332 engine.iterate(
333 |_, state, t| {
334 if !t.is_cs_or_active() {
335 return Ok(None);
336 }
337 match ET::Gullet::char_or_primitive(state, &t) {
338 Some(CharOrPrimitive::Primitive(name))
339 if name == PRIMITIVES.r#else && incond == 0 =>
340 {
341 conds.push(ActiveConditional::Else(PRIMITIVES.ifcase));
342 Ok(Some(()))
343 }
344 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.fi => {
345 if incond == 0 {
346 Ok(Some(()))
347 } else {
348 incond -= 1;
349 Ok(None)
350 }
351 }
352 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.or && incond == 0 => {
353 curr_int = curr_int + one;
354 if cond == curr_int {
355 conds.push(ActiveConditional::Case(cond));
356 Ok(Some(()))
357 } else {
358 Ok(None)
359 }
360 }
361 Some(CharOrPrimitive::Primitive(name)) => {
362 if let Some(TeXCommand::Primitive {
363 cmd: PrimitiveCommand::Conditional(_),
364 ..
365 }) = state.primitives().get_id(name)
366 {
367 incond += 1
368 }
369 Ok(None)
370 }
371 _ => Ok(None),
372 }
373 },
374 |a, s, m| TeXError::incomplete_conditional(a, s, m, PRIMITIVES.ifcase),
375 )?;
376 *engine.gullet.get_conditionals() = conds;
377 Ok(())
378}
379
380pub fn false_loop<ET: EngineTypes>(
384 engine: &mut EngineReferences<ET>,
385 idx: usize,
386 allowelse: bool,
387 mut skipelse: bool,
388) -> TeXResult<(), ET> {
389 let (mut incond, cond) = {
390 let conds = engine.gullet.get_conditionals();
391 let ic = conds.len() - idx;
392 for _ in 0..ic {
393 conds.pop();
394 }
395 (ic, conds.pop().unwrap().name())
396 };
397 let mut conds = std::mem::take(engine.gullet.get_conditionals());
398 engine.iterate(
399 |_, state, t| {
400 if !t.is_cs_or_active() {
401 return Ok(None);
402 }
403 match ET::Gullet::char_or_primitive(state, &t) {
404 Some(CharOrPrimitive::Primitive(name))
405 if name == PRIMITIVES.r#else && incond == 0 =>
406 {
407 if allowelse {
408 conds.push(ActiveConditional::Else(cond));
409 Ok(Some(()))
410 } else if skipelse {
411 skipelse = false;
412 Ok(None)
413 } else {
414 Ok(None) }
416 }
417 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.fi => {
418 if incond == 0 {
419 Ok(Some(()))
420 } else {
421 incond -= 1;
422 Ok(None)
423 }
424 }
425 Some(CharOrPrimitive::Primitive(name)) => {
426 if let Some(TeXCommand::Primitive {
427 cmd: PrimitiveCommand::Conditional(_),
428 ..
429 }) = state.primitives().get_id(name)
430 {
431 incond += 1
432 }
433 Ok(None)
434 }
435 _ => Ok(None),
436 }
437 },
438 |a, s, m| TeXError::incomplete_conditional(a, s, m, cond),
439 )?;
440 *engine.gullet.get_conditionals() = conds;
441 Ok(())
442}
443
444pub fn read_string<ET: EngineTypes>(
446 engine: &mut EngineReferences<ET>,
447 skip_eq: bool,
448 ret: &mut String,
449 in_token: &ET::Token,
450) -> TeXResult<(), ET> {
451 use std::fmt::Write;
452 let mut quoted = false;
453 let mut braced = false;
454 let mut had_eq = !skip_eq;
455 let mut was_quoted = false;
456 let mut is_empty = true;
457 crate::expand_loop!(engine,token,
458 ResolvedToken::Tk {char,code,..} => match code {
459 CommandCode::Space if is_empty && !was_quoted && !braced => (),
460 CommandCode::BeginGroup if is_empty && !was_quoted && !quoted && !braced => braced = true,
461 CommandCode::Space if quoted || braced => ret.push(' '),
462 CommandCode::Space => return Ok(()),
463 CommandCode::Other if !had_eq && is_empty && matches!(char.try_into(),Ok(b'=')) => {
464 had_eq = true;
465 }
466 CommandCode::EndGroup if braced => return Ok(()),
467 _ => {
468 if matches!(char.try_into(),Ok(b'\"')) {
469 was_quoted = true;
470 is_empty = false;
471 quoted = !quoted;
472 } else {
473 is_empty = false;
474 char.display_fmt(ret);
475 }
476 }
477 }
478 ResolvedToken::Cmd(Some(TeXCommand::CharDef(c))) => {
479 c.display_fmt(ret);
480 is_empty = false;
481 }
482 ResolvedToken::Cmd(Some(TeXCommand::Char{char,..})) => {
483 is_empty = false;
484 char.display_fmt(ret)
485 }
486 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Relax,..})) if !quoted && !braced => return Ok(()),
487 ResolvedToken::Cmd(_) if !quoted && !braced => {
488 engine.mouth.requeue(token);
489 return Ok(())
490 }
491 _ => {
492 is_empty = false;
493 write!(ret,"{}",token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()))?;
494 }
495 );
496 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, in_token)
497}
498
499pub fn read_maybe_braced_string<ET: EngineTypes>(
501 engine: &mut EngineReferences<ET>,
502 skip_eq: bool,
503 ret: &mut String,
504 in_token: &ET::Token,
505) -> TeXResult<(), ET> {
506 use std::fmt::Write;
507 let mut quoted = false;
508 let mut had_eq = !skip_eq;
509 let mut was_quoted = false;
510 let mut is_empty = true;
511 let mut braced = false;
512 crate::expand_loop!(engine,token,
513 ResolvedToken::Tk {char,code,..} => match code {
514 CommandCode::Space if is_empty && !was_quoted && !braced => (),
515 CommandCode::Space if quoted || braced => ret.push(' '),
516 CommandCode::Space => return Ok(()),
517 CommandCode::BeginGroup if !braced && is_empty => {braced = true;is_empty = false}
518 CommandCode::EndGroup if braced => return Ok(()),
519 CommandCode::Other if !had_eq && is_empty && matches!(char.try_into(),Ok(b'=')) => {
520 had_eq = true;
521 }
522 _ => {
523 if matches!(char.try_into(),Ok(b'\"')) {
524 if quoted {return Ok(())}
525 was_quoted = true;
526 is_empty = false;
527 quoted = !quoted;
528 } else {
529 is_empty = false;
530 char.display_fmt(ret);
531 }
532 }
533 }
534 ResolvedToken::Cmd(Some(TeXCommand::CharDef(c))) => {
535 c.display_fmt(ret);
536 is_empty = false;
537 }
538 ResolvedToken::Cmd(Some(TeXCommand::Char{char,..})) => {
539 is_empty = false;
540 char.display_fmt(ret)
541 }
542 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Relax,..})) if !quoted && !braced => return Ok(()),
543 ResolvedToken::Cmd(_) if !quoted => {
544 engine.mouth.requeue(token);
545 return Ok(())
546 }
547 _ => {
548 is_empty = false;
549 write!(ret,"{}",token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()))?;
550 }
551 );
552 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, in_token)
553}
554
555fn is_ascii_digit(u: u8) -> bool {
556 (48..=57).contains(&u)
557}
558fn is_ascii_oct_digit(u: u8) -> bool {
559 (48..=55).contains(&u)
560}
561fn is_ascii_hex_digit(u: u8) -> bool {
562 is_ascii_digit(u) || (65..=70).contains(&u) || (97..=102).contains(&u)
563}
564
565pub struct NumContinuation<ET: EngineTypes> {
566 pub is_negative: bool,
567 pub next: Either<u8, (TeXCommand<ET>, ET::Token)>,
568}
569
570pub fn read_numeric<ET: EngineTypes>(
577 engine: &mut EngineReferences<ET>,
578 skip_eq: bool,
579 in_token: &ET::Token,
580) -> TeXResult<NumContinuation<ET>, ET> {
581 let mut is_negative = false;
582 let mut had_eq = !skip_eq;
583 crate::expand_loop!(token => if token.is_primitive() == Some(PRIMITIVES.noexpand) {continue};engine,
584 ResolvedToken::Tk {char,code,..} => match (char.try_into(),code) {
585 (_,CommandCode::Space) => (),
586 (Ok(b'='),CommandCode::Other) if !had_eq => {
587 had_eq = true;
588 }
589 (Ok(b'-'),CommandCode::Other) => {
590 is_negative = !is_negative;
591 }
592 (Ok(b'+'),CommandCode::Other) => (),
593 (Ok(b),CommandCode::Other) => return Ok(NumContinuation{is_negative,next:either::Left(b)}),
594 _ => return Err(TeXError::General(format!(
595 "Unexpected token when reading numeric:{}\nTODO: Better error message",
596 token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char())
597 )))
598 }
599 ResolvedToken::Cmd(Some(TeXCommand::Char {char,code})) => match ((*char).try_into(),code) {
600 (_,CommandCode::Space) => (),
601 (Ok(b'='),CommandCode::Other) if !had_eq => {
602 had_eq = true;
603 }
604 (Ok(b'-'),CommandCode::Other) => {
605 is_negative = !is_negative;
606 }
607 (Ok(b'+'),CommandCode::Other) => (),
608 (Ok(b),CommandCode::Other) => return Ok(NumContinuation{is_negative,next:either::Left(b)}),
609 _ => return Err(TeXError::General(format!(
610 "Unexpected token when reading numeric:{}\nTODO: Better error message",
611 token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char())
612 )))
613 }
614 ResolvedToken::Cmd(None) =>
615 TeXError::undefined(engine.aux,engine.state,engine.mouth,&token)?,
616 ResolvedToken::Cmd(Some(cmd)) => return Ok(NumContinuation{is_negative,next:either::Right((cmd.clone(),token))})
617 );
618 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &in_token)?;
619 Ok(NumContinuation {
620 is_negative,
621 next: either::Left(b'0'),
622 })
623}
624
625pub fn read_int<ET: EngineTypes>(
627 engine: &mut EngineReferences<ET>,
628 skip_eq: bool,
629 in_token: &ET::Token,
630) -> TeXResult<ET::Int, ET> {
631 let NumContinuation { is_negative, next } = read_numeric(engine, skip_eq, in_token)?;
632 match next {
633 Either::Left(b) => read_int_byte(engine, is_negative, b),
634 Either::Right((cmd, token)) => read_int_command(engine, is_negative, cmd, token),
635 }
636}
637
638pub fn read_int_byte<ET: EngineTypes>(
640 engine: &mut EngineReferences<ET>,
641 is_negative: bool,
642 b: u8,
643) -> TeXResult<ET::Int, ET> {
644 match b {
645 b'\"' => read_hex_int(engine, is_negative),
646 b'\'' => read_oct_int(engine, is_negative),
647 b'`' => read_int_char(engine, is_negative),
648 b if is_ascii_digit(b) => read_dec_int(engine, is_negative, b),
649 _ => {
650 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
651 Ok(ET::Int::default())
652 }
653 }
654}
655
656fn read_int_char<ET: EngineTypes>(
658 engine: &mut EngineReferences<ET>,
659 is_negative: bool,
660) -> TeXResult<ET::Int, ET> {
661 let next = match engine.mouth.get_next(engine.aux, engine.state)? {
662 Some(t) => t,
663 None => {
664 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
665 return Ok(ET::Int::default());
666 }
667 };
668 let ret = match next.to_enum() {
669 StandardToken::Character(c, _) => {
670 let i: u64 = c.into();
671 match <ET::Num as NumSet>::Int::try_from(i as i64) {
672 Ok(v) => {
673 if is_negative {
674 -v
675 } else {
676 v
677 }
678 }
679 _ => {
680 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
681 return Ok(ET::Int::default());
682 }
683 }
684 }
685 StandardToken::ControlSequence(cs) => {
686 let resolved = engine.aux.memory.cs_interner().resolve(&cs);
687 if resolved.len() == 1 {
688 let c: ET::Char = resolved.iter().next().unwrap();
689 if is_negative {
690 -<ET::Num as NumSet>::Int::from(c.into() as i32)
691 } else {
692 <ET::Num as NumSet>::Int::from(c.into() as i32)
693 }
694 } else {
695 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
696 return Ok(ET::Int::default());
697 }
698 }
699 _ => {
700 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
701 return Ok(ET::Int::default());
702 }
703 };
704 crate::expand_loop!(engine,token,
705 ResolvedToken::Tk{code,..} => {
706 if code != CommandCode::Space {
707 engine.requeue(token)?;
708 }
709 break
710 }
711 ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::Space,..})) => {
712 break
713 }
714 ResolvedToken::Cmd(_) => {
715 engine.requeue(token)?;
716 break
717 }
718 );
719 Ok(ret)
720}
721
722pub fn read_int_command<ET: EngineTypes>(
724 engine: &mut EngineReferences<ET>,
725 is_negative: bool,
726 cmd: TeXCommand<ET>,
727 token: ET::Token,
728) -> TeXResult<ET::Int, ET> {
729 match cmd {
730 TeXCommand::Primitive {
731 cmd: PrimitiveCommand::Int { read, .. },
732 ..
733 } => {
734 if is_negative {
735 Ok(-read(engine, token)?)
736 } else {
737 read(engine, token)
738 }
739 }
740 TeXCommand::Primitive {
741 name,
742 cmd: PrimitiveCommand::PrimitiveInt,
743 } => Ok(if is_negative {
744 -engine.state.get_primitive_int(name)
745 } else {
746 engine.state.get_primitive_int(name)
747 }),
748 TeXCommand::IntRegister(u) => {
749 let i = engine.state.get_int_register(u);
750 Ok(if is_negative { -i } else { i })
751 }
752 TeXCommand::CharDef(c) => {
753 let val: u64 = c.into();
754 match <ET::Num as NumSet>::Int::try_from(val as i64) {
755 Ok(val) => Ok(if is_negative { -val } else { val }),
756 _ => {
757 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
758 Ok(ET::Int::default())
759 }
760 }
761 }
762 TeXCommand::MathChar(u) => match <ET::Num as NumSet>::Int::try_from(u as i64) {
763 Ok(val) => Ok(if is_negative { -val } else { val }),
764 _ => {
765 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
766 Ok(ET::Int::default())
767 }
768 },
769 TeXCommand::DimRegister(u) => {
770 let base = ET::Num::dim_to_int(engine.state.get_dim_register(u));
771 Ok(if is_negative { -base } else { base })
772 }
773 TeXCommand::Primitive {
774 name,
775 cmd: PrimitiveCommand::PrimitiveDim,
776 } => {
777 let base = ET::Num::dim_to_int(engine.state.get_primitive_dim(name));
778 Ok(if is_negative { -base } else { base })
779 }
780 TeXCommand::Primitive {
781 cmd: PrimitiveCommand::Dim { read, .. },
782 ..
783 } => {
784 let base = ET::Num::dim_to_int((read)(engine, token)?);
785 Ok(if is_negative { -base } else { base })
786 }
787 TeXCommand::SkipRegister(u) => {
788 let base = ET::Num::dim_to_int(engine.state.get_skip_register(u).base);
789 Ok(if is_negative { -base } else { base })
790 }
791 TeXCommand::Primitive {
792 name,
793 cmd: PrimitiveCommand::PrimitiveSkip,
794 } => {
795 let base = ET::Num::dim_to_int(engine.state.get_primitive_skip(name).base);
796 Ok(if is_negative { -base } else { base })
797 }
798 TeXCommand::Primitive {
799 cmd: PrimitiveCommand::Skip { read, .. },
800 ..
801 } => {
802 let base = ET::Num::dim_to_int((read)(engine, token)?.base);
803 Ok(if is_negative { -base } else { base })
804 }
805 _ => {
806 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
807 Ok(ET::Int::default())
808 }
809 }
810}
811
812fn read_dec_int<ET: EngineTypes>(
813 engine: &mut EngineReferences<ET>,
814 is_negative: bool,
815 first: u8,
816) -> TeXResult<ET::Int, ET> {
817 let mut ret = (first - b'0') as i32;
818 crate::expand_loop!(engine,token,
819 ResolvedToken::Tk{char,code} => match (char.try_into(),code) {
820 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => {
821 ret = 10*ret + ((b - b'0') as i32);
822 }
823 (_,CommandCode::Space) => return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)}),
824 _ => {
825 engine.requeue(token)?;
826 return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)})
827 }
828 }
829 ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::Space,..})) => {
830 return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)})
831 }
832 ResolvedToken::Cmd(_) => {
833 engine.requeue(token)?;
834 return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)})
835 }
836 );
837 Ok(if is_negative {
838 -ET::Int::from(ret)
839 } else {
840 ET::Int::from(ret)
841 })
842}
843
844fn hex_to_num(b: u8) -> i64 {
845 match b {
846 b'0'..=b'9' => (b - b'0') as i64,
847 b'A'..=b'F' => (10 + b - b'A') as i64,
848 b'a'..=b'f' => (10 + b - b'a') as i64,
849 _ => unreachable!(),
850 }
851}
852
853fn read_hex_int<ET: EngineTypes>(
854 engine: &mut EngineReferences<ET>,
855 is_negative: bool,
856) -> TeXResult<ET::Int, ET> {
857 let mut ret = 0i64;
858 let mut empty = true;
859 crate::expand_loop!(engine,token,
860 ResolvedToken::Tk{char,code} => match (char.try_into(),code) {
861 (Ok(b),CommandCode::Other|CommandCode::Letter) if is_ascii_hex_digit(b) => {
862 ret = 16*ret + hex_to_num(b);
863 empty = false;
864 }
865 (_,CommandCode::Space) => {
866 let ret = if is_negative {-ret} else {ret};
867 match ret.try_into() {
868 Ok(v) => return Ok(v),
869 _ => return Err(TeXError::General(format!("Integer out of range: {}\nTODO: Better error message",ret)))
870 }
871 }
872 _ if !empty => {
873 engine.requeue(token)?;
874 let ret = if is_negative {-ret} else {ret};
875 match ret.try_into() {
876 Ok(v) => return Ok(v),
877 _ => return Err(TeXError::General(format!("Integer out of range: {}\nTODO: Better error message",ret)))
878 }
879 }
880 _ => {
881 engine.requeue(token)?;
882 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
883 return Ok(ET::Int::default())
884 }
885 }
886 ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::Space,..})) => {
887 let ret = if is_negative {-ret} else {ret};
888 match ret.try_into() {
889 Ok(v) => return Ok(v),
890 _ => return Err(TeXError::General(format!("Integer out of range: {}\nTODO: Better error message",ret)))
891 }
892 }
893 ResolvedToken::Cmd(_) if !empty => {
894 engine.mouth.requeue(token);
895 let ret = if is_negative {-ret} else {ret};
896 match ret.try_into() {
897 Ok(v) => return Ok(v),
898 _ => return Err(TeXError::General(format!("Integer out of range: {}\nTODO: Better error message",ret)))
899 }
900 }
901 _ => {
902 engine.requeue(token)?;
903 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
904 return Ok(ET::Int::default())
905 }
906 );
907 if empty {
908 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
909 Ok(ET::Int::default())
910 } else {
911 let ret = if is_negative { -ret } else { ret };
912 match ret.try_into() {
913 Ok(v) => Ok(v),
914 _ => Err(TeXError::General(format!(
915 "Integer out of range: {}\nTODO: Better error message",
916 ret
917 ))),
918 }
919 }
920}
921
922fn read_oct_int<ET: EngineTypes>(
923 engine: &mut EngineReferences<ET>,
924 is_negative: bool,
925) -> TeXResult<ET::Int, ET> {
926 let mut ret = 0i32;
927 let mut empty = true;
928 crate::expand_loop!(engine,token,
929 ResolvedToken::Tk{char,code} => match (char.try_into(),code) {
930 (Ok(b),CommandCode::Other) if is_ascii_oct_digit(b) => {
931 ret = 8*ret + ((b - b'0') as i32);
932 empty = false
933 }
934 (_,CommandCode::Space) if !empty => return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)}),
935 _ if !empty => {
936 engine.requeue(token)?;
937 return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)})
938 }
939 _ => {
940 engine.requeue(token)?;
941 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
942 return Ok(ET::Int::default())
943 }
944 }
945 ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::Space,..})) if !empty => {
946 return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)})
947 }
948 ResolvedToken::Cmd(_) if !empty => {
949 engine.mouth.requeue(token);
950 return Ok(if is_negative {- ET::Int::from(ret)} else {ET::Int::from(ret)})
951 }
952 _ => {
953 engine.requeue(token)?;
954 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
955 return Ok(ET::Int::default())
956 }
957 );
958 if empty {
959 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
960 Ok(ET::Int::default())
961 } else {
962 Ok(if is_negative {
963 -ET::Int::from(ret)
964 } else {
965 ET::Int::from(ret)
966 })
967 }
968}
969
970pub fn read_dim<ET: EngineTypes>(
972 engine: &mut EngineReferences<ET>,
973 skip_eq: bool,
974 in_token: &ET::Token,
975) -> TeXResult<ET::Dim, ET> {
976 let NumContinuation { is_negative, next } = read_numeric(engine, skip_eq, in_token)?;
977 match next {
978 Either::Left(b) => read_dim_byte(engine, is_negative, b),
979 Either::Right((cmd, token)) => read_dim_command(engine, is_negative, cmd, token),
980 }
981}
982
983pub fn read_dim_byte<ET: EngineTypes>(
985 engine: &mut EngineReferences<ET>,
986 is_negative: bool,
987 b: u8,
988) -> TeXResult<ET::Dim, ET> {
989 if b == b',' || b == b'.' {
990 read_dim_float(engine, is_negative, b'.')
991 } else if b == b'`' {
992 let i = read_int_char(engine, is_negative)?.into();
993 read_unit_or_dim(engine, i as f64)
994 } else if is_ascii_digit(b) {
995 read_dim_float(engine, is_negative, b)
996 } else {
997 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
998 read_unit_or_dim(engine, 0f64)
999 }
1000}
1001
1002pub fn read_dim_command<ET: EngineTypes>(
1006 engine: &mut EngineReferences<ET>,
1007 is_negative: bool,
1008 cmd: TeXCommand<ET>,
1009 token: ET::Token,
1010) -> TeXResult<ET::Dim, ET> {
1011 match cmd {
1012 TeXCommand::IntRegister(u) => {
1013 let i = engine.state.get_int_register(u);
1014 let i = if is_negative { -i } else { i };
1015 let i = i.into() as f64;
1016 read_unit_or_dim(engine, i)
1017 }
1018 TeXCommand::Primitive {
1019 name,
1020 cmd: PrimitiveCommand::PrimitiveInt,
1021 } => {
1022 let i = engine.state.get_primitive_int(name);
1023 let i = if is_negative { -i } else { i };
1024 let i = i.into() as f64;
1025 read_unit_or_dim(engine, i)
1026 }
1027 TeXCommand::Primitive {
1028 cmd: PrimitiveCommand::Int { read, .. },
1029 ..
1030 } => {
1031 let i = if is_negative {
1032 -read(engine, token)?
1033 } else {
1034 read(engine, token)?
1035 };
1036 let f = i.into() as f64;
1037 read_unit_or_dim(engine, f)
1038 }
1039 TeXCommand::CharDef(c) => {
1040 let val = if is_negative {
1041 -(c.into() as f64)
1042 } else {
1043 c.into() as f64
1044 };
1045 read_unit_or_dim(engine, val)
1046 }
1047 TeXCommand::MathChar(c) => {
1048 let val = if is_negative { -(c as f64) } else { c as f64 };
1049 read_unit_or_dim(engine, val)
1050 }
1051 TeXCommand::DimRegister(u) => {
1052 if is_negative {
1053 Ok(-engine.state.get_dim_register(u))
1054 } else {
1055 Ok(engine.state.get_dim_register(u))
1056 }
1057 }
1058 TeXCommand::Primitive {
1059 name,
1060 cmd: PrimitiveCommand::PrimitiveDim,
1061 } => {
1062 let val = engine.state.get_primitive_dim(name);
1063 if is_negative {
1064 Ok(-val)
1065 } else {
1066 Ok(val)
1067 }
1068 }
1069 TeXCommand::Primitive {
1070 cmd: PrimitiveCommand::Dim { read, .. },
1071 ..
1072 } => {
1073 let val = read(engine, token)?;
1074 if is_negative {
1075 Ok(-val)
1076 } else {
1077 Ok(val)
1078 }
1079 }
1080 TeXCommand::SkipRegister(u) => {
1081 let val = engine.state.get_skip_register(u).base;
1082 if is_negative {
1083 Ok(-val)
1084 } else {
1085 Ok(val)
1086 }
1087 }
1088 TeXCommand::Primitive {
1089 name,
1090 cmd: PrimitiveCommand::PrimitiveSkip,
1091 } => {
1092 let val = engine.state.get_primitive_skip(name).base;
1093 if is_negative {
1094 Ok(-val)
1095 } else {
1096 Ok(val)
1097 }
1098 }
1099 TeXCommand::Primitive {
1100 cmd: PrimitiveCommand::Skip { read, .. },
1101 ..
1102 } => {
1103 let val = read(engine, token)?.base;
1104 if is_negative {
1105 Ok(-val)
1106 } else {
1107 Ok(val)
1108 }
1109 }
1110 _ => {
1111 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1112 read_unit_or_dim(engine, 0f64)
1113 }
1114 }
1115}
1116
1117fn read_dim_float<ET: EngineTypes>(
1118 engine: &mut EngineReferences<ET>,
1119 is_negative: bool,
1120 first: u8,
1121) -> TeXResult<ET::Dim, ET> {
1122 let mut ret = 0f64;
1123 let mut in_decimal = first == b'.';
1124 let mut fac = 10f64;
1125 if !in_decimal {
1126 ret = (first - b'0') as f64;
1127 }
1128 crate::expand_loop!(engine,token,
1129 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1130 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => {
1131 if in_decimal {
1132 ret += (b - b'0') as f64 / fac;
1133 fac *= 10.0;
1134 } else {
1135 ret = 10.0*ret + ((b - b'0') as f64);
1136 }
1137 }
1138 (Ok(b','|b'.'),CommandCode::Other) => {
1139 if in_decimal {
1140 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1141 return read_unit_or_dim(engine,0f64)
1142 }
1143 in_decimal = true;
1144 }
1145 (_,CommandCode::Space) => {
1146 let f = if is_negative {-ret} else {ret};
1147 return read_unit_or_dim(engine,f)
1148 }
1149 (Ok(b),CommandCode::Other | CommandCode::Letter) => {
1150 let f = if is_negative {-ret} else {ret};
1151 return read_dim_unit(engine,f,Some((b,token)))
1152 }
1153 _ => {
1154 engine.requeue(token)?;
1155 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1156 return read_unit_or_dim(engine,0f64)
1157 }
1158 }
1159 ResolvedToken::Cmd(Some(c)) => {
1160 let f = if is_negative {-ret} else {ret};
1161 return read_unit_cmd(engine,f,c.clone(),token)
1162 }
1163 _ => {
1164 engine.requeue(token)?;
1165 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1166 return read_unit_or_dim(engine,0f64)
1167 }
1168 );
1169 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1170 read_unit_or_dim(engine, 0f64)
1171}
1172
1173fn read_unit_or_dim<ET: EngineTypes>(
1174 engine: &mut EngineReferences<ET>,
1175 float: f64,
1176) -> TeXResult<ET::Dim, ET> {
1177 crate::expand_loop!(engine,token,
1178 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1179 (Ok(b),CommandCode::Other | CommandCode::Letter) => {
1180 return read_dim_unit(engine,float,Some((b,token)))
1181 }
1182 (_,CommandCode::Space) => (),
1183 _ => {
1184 engine.requeue(token)?;
1185 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1186 return Ok(ET::Dim::from_float(engine,float,b"pt"))
1187 }
1188 }
1189 ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::Space,..})) => (),
1190 ResolvedToken::Cmd(Some(TeXCommand::Char{char,code:CommandCode::Other | CommandCode::Letter})) => match (*char).try_into() {
1191 Ok(b) => return read_dim_unit(engine,float,Some((b,token))),
1192 _ => {
1193 engine.requeue(token)?;
1194 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1195 return Ok(ET::Dim::from_float(engine,float,b"pt"))
1196 }
1197 }
1198 ResolvedToken::Cmd(Some(cmd)) => return read_unit_cmd(engine,float,cmd.clone(),token),
1199 ResolvedToken::Cmd(None) =>
1200 TeXError::undefined(engine.aux,engine.state,engine.mouth,&token)?,
1201 );
1202 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1203 Ok(ET::Dim::from_float(engine, float, b"pt"))
1204}
1205
1206fn read_unit_cmd<ET: EngineTypes>(
1207 engine: &mut EngineReferences<ET>,
1208 float: f64,
1209 cmd: TeXCommand<ET>,
1210 token: ET::Token,
1211) -> TeXResult<ET::Dim, ET> {
1212 match cmd {
1213 TeXCommand::IntRegister(u) => {
1214 let base = ET::Dim::from_sp(engine.state.get_int_register(u).into() as i32);
1215 Ok(base.scale_float(float))
1216 }
1217 TeXCommand::Primitive {
1218 name,
1219 cmd: PrimitiveCommand::PrimitiveInt,
1220 } => {
1221 let base = ET::Dim::from_sp(engine.state.get_primitive_int(name).into() as i32);
1222 Ok(base.scale_float(float))
1223 }
1224 TeXCommand::Primitive {
1225 cmd: PrimitiveCommand::Int { read, .. },
1226 ..
1227 } => {
1228 let base = ET::Dim::from_sp(read(engine, token)?.into() as i32);
1229 Ok(base.scale_float(float))
1230 }
1231 TeXCommand::DimRegister(u) => {
1232 let base = engine.state.get_dim_register(u);
1233 Ok(base.scale_float(float))
1234 }
1235 TeXCommand::Primitive {
1236 name,
1237 cmd: PrimitiveCommand::PrimitiveDim,
1238 } => {
1239 let base = engine.state.get_primitive_dim(name);
1240 Ok(base.scale_float(float))
1241 }
1242 TeXCommand::Primitive {
1243 cmd: PrimitiveCommand::Dim { read, .. },
1244 ..
1245 } => {
1246 let base = read(engine, token)?;
1247 Ok(base.scale_float(float))
1248 }
1249 TeXCommand::SkipRegister(u) => {
1250 let base = engine.state.get_skip_register(u).base;
1251 Ok(base.scale_float(float))
1252 }
1253 TeXCommand::Primitive {
1254 name,
1255 cmd: PrimitiveCommand::PrimitiveSkip,
1256 } => {
1257 let base = engine.state.get_primitive_skip(name).base;
1258 Ok(base.scale_float(float))
1259 }
1260 TeXCommand::Primitive {
1261 cmd: PrimitiveCommand::Skip { read, .. },
1262 ..
1263 } => {
1264 let base = read(engine, token)?.base;
1265 Ok(base.scale_float(float))
1266 }
1267 _ => {
1268 engine.requeue(token)?;
1269 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1270 Ok(ET::Dim::from_float(engine, float, b"pt"))
1271 }
1272 }
1273}
1274
1275fn read_dim_unit<ET: EngineTypes>(
1276 engine: &mut EngineReferences<ET>,
1277 mut float: f64,
1278 mut first: Option<(u8, ET::Token)>,
1279) -> TeXResult<ET::Dim, ET> {
1280 let is_true = match &first {
1281 Some((b't' | b'T', _)) => read_keyword(engine, b"true", std::mem::take(&mut first))?,
1282 Some(_) => false,
1283 None => read_keyword(engine, b"true", None)?,
1284 };
1285 if is_true {
1286 let mag = engine.state.get_primitive_int(PRIMITIVES.mag);
1287 float *= Into::<i64>::into(mag) as f64 / 1000.0;
1288 }
1289 let units = ET::Dim::UNITS;
1290 match read_keywords(engine, units, first)? {
1291 Some(d) => Ok(<ET::Num as NumSet>::Dim::from_float(engine, float, d)),
1292 _ => {
1293 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1294 Ok(ET::Dim::from_float(engine, float, b"pt"))
1295 }
1296 }
1297}
1298
1299pub fn read_skip<ET: EngineTypes>(
1301 engine: &mut EngineReferences<ET>,
1302 skip_eq: bool,
1303 in_token: &ET::Token,
1304) -> TeXResult<Skip<ET::Dim>, ET> {
1305 let NumContinuation { is_negative, next } = read_numeric(engine, skip_eq, in_token)?;
1306 match next {
1307 Either::Left(b) => read_skip_byte(engine, is_negative, b),
1308 Either::Right((cmd, token)) => read_skip_command(engine, is_negative, cmd, token),
1309 }
1310}
1311
1312pub fn read_skip_byte<ET: EngineTypes>(
1314 engine: &mut EngineReferences<ET>,
1315 is_negative: bool,
1316 b: u8,
1317) -> TeXResult<Skip<ET::Dim>, ET> {
1318 if b == b',' || b == b'.' {
1319 read_skip_dim(engine, is_negative, b'.')
1320 } else if is_ascii_digit(b) {
1321 read_skip_dim(engine, is_negative, b)
1322 } else {
1323 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1324 read_skip_dim(engine, false, b)
1325 }
1326}
1327
1328pub fn read_skip_command<ET: EngineTypes>(
1331 engine: &mut EngineReferences<ET>,
1332 is_negative: bool,
1333 cmd: TeXCommand<ET>,
1334 token: ET::Token,
1335) -> TeXResult<Skip<ET::Dim>, ET> {
1336 match cmd {
1337 TeXCommand::IntRegister(u) => {
1338 let base = engine.state.get_int_register(u);
1339 let base = if is_negative { -base } else { base };
1340 let base = read_unit_or_dim(engine, base.into() as f64)?;
1341 read_skip_ii(engine, base)
1342 }
1343 TeXCommand::Primitive {
1344 name,
1345 cmd: PrimitiveCommand::PrimitiveInt,
1346 } => {
1347 let base = engine.state.get_primitive_int(name);
1348 let base = if is_negative { -base } else { base };
1349 let base = read_unit_or_dim(engine, base.into() as f64)?;
1350 read_skip_ii(engine, base)
1351 }
1352 TeXCommand::Primitive {
1353 cmd: PrimitiveCommand::Int { read, .. },
1354 ..
1355 } => {
1356 let base = read(engine, token)?;
1357 let base = if is_negative { -base } else { base };
1358 let base = read_unit_or_dim(engine, base.into() as f64)?;
1359 read_skip_ii(engine, base)
1360 }
1361 TeXCommand::CharDef(c) => {
1362 let val: u64 = c.into();
1363 let base = read_unit_or_dim(engine, val as f64)?;
1364 read_skip_ii(engine, base)
1365 }
1366 TeXCommand::MathChar(u) => {
1367 let base = read_unit_or_dim(engine, u as f64)?;
1368 read_skip_ii(engine, base)
1369 }
1370 TeXCommand::DimRegister(u) => {
1371 let base = engine.state.get_dim_register(u);
1372 let base = if is_negative { -base } else { base };
1373 read_skip_ii(engine, base)
1374 }
1375 TeXCommand::Primitive {
1376 name,
1377 cmd: PrimitiveCommand::PrimitiveDim,
1378 } => {
1379 let base = engine.state.get_primitive_dim(name);
1380 let base = if is_negative { -base } else { base };
1381 read_skip_ii(engine, base)
1382 }
1383 TeXCommand::Primitive {
1384 cmd: PrimitiveCommand::Dim { read, .. },
1385 ..
1386 } => {
1387 let base = read(engine, token)?;
1388 let base = if is_negative { -base } else { base };
1389 read_skip_ii(engine, base)
1390 }
1391 TeXCommand::SkipRegister(u) => {
1392 let base = engine.state.get_skip_register(u);
1393 Ok(if is_negative { -base } else { base })
1394 }
1395 TeXCommand::Primitive {
1396 name,
1397 cmd: PrimitiveCommand::PrimitiveSkip,
1398 } => {
1399 let val = engine.state.get_primitive_skip(name);
1400 Ok(if is_negative { -val } else { val })
1401 }
1402 TeXCommand::Primitive {
1403 cmd: PrimitiveCommand::Skip { read, .. },
1404 ..
1405 } => {
1406 let val = read(engine, token)?;
1407 Ok(if is_negative { -val } else { val })
1408 }
1409 _ => {
1410 engine.requeue(token)?;
1411 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1412 let base = read_unit_or_dim(engine, 0f64)?;
1413 read_skip_ii(engine, base)
1414 }
1415 }
1416}
1417
1418const PLUS: &[u8] = b"plus";
1419const MINUS: &[u8] = b"minus";
1420
1421fn read_skip_dim<ET: EngineTypes>(
1422 engine: &mut EngineReferences<ET>,
1423 is_negative: bool,
1424 first: u8,
1425) -> TeXResult<Skip<ET::Dim>, ET> {
1426 let base = read_dim_float(engine, is_negative, first)?;
1427 read_skip_ii(engine, base)
1428}
1429
1430fn read_skip_ii<ET: EngineTypes>(
1431 engine: &mut EngineReferences<ET>,
1432 base: <ET::Num as NumSet>::Dim,
1433) -> TeXResult<Skip<ET::Dim>, ET> {
1434 match read_keywords(engine, &[PLUS, MINUS], None)? {
1435 Some(b) if b == PLUS => {
1436 let stretch = read_stretch(engine)?;
1437 if read_keyword(engine, MINUS, None)? {
1438 Ok(Skip::new(base, Some(stretch), Some(read_stretch(engine)?)))
1439 } else {
1440 Ok(Skip::new(base, Some(stretch), None))
1441 }
1442 }
1443 Some(b) if b == MINUS => {
1444 let shrink = read_stretch(engine)?;
1445 if read_keyword(engine, PLUS, None)? {
1446 Ok(Skip::new(base, Some(read_stretch(engine)?), Some(shrink)))
1447 } else {
1448 Ok(Skip::new(base, None, Some(shrink)))
1449 }
1450 }
1451 _ => Ok(Skip::new(base, None, None)),
1452 }
1453}
1454
1455fn read_stretch<ET: EngineTypes>(
1456 engine: &mut EngineReferences<ET>,
1457) -> TeXResult<StretchShrink<ET::Dim>, ET> {
1458 let mut is_negative = false;
1459 crate::expand_loop!(engine,token,
1460 ResolvedToken::Tk {char,code } => match (char.try_into(),code) {
1461 (_,CommandCode::Space) => (),
1462 (Ok(b'-'),CommandCode::Other) => {
1463 is_negative = !is_negative;
1464 }
1465 (Ok(b'+'),CommandCode::Other) => (),
1466 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => return read_stretch_float(engine,is_negative,b),
1467 (Ok(b','|b'.'),CommandCode::Other) => return read_stretch_float(engine,is_negative,b'.'),
1468 _ => {
1469 engine.requeue(token)?;
1470 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1471 return read_stretch_unit(engine,0f64,None)
1472 }
1473 }
1474 ResolvedToken::Cmd(cmd) => match cmd {
1475 Some(TeXCommand::DimRegister(u)) => {
1476 let base = engine.state.get_dim_register(*u);
1477 return Ok(StretchShrink::Dim(if is_negative {-base} else {base}))
1478 }
1479 Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveDim}) => {
1480 let base = engine.state.get_primitive_dim(*name);
1481 return Ok(StretchShrink::Dim(if is_negative {-base} else {base}))
1482 }
1483 Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Dim{read,..},..}) => {
1484 let base = read(engine,token)?;
1485 return Ok(StretchShrink::Dim(if is_negative {-base} else {base}))
1486 }
1487 Some(TeXCommand::IntRegister(u)) => {
1488 let base = engine.state.get_int_register(*u).into() as f64;
1489 return read_stretch_unit(engine,if is_negative {-base} else {base},None)
1490 }
1491 Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveInt}) => {
1492 let base = engine.state.get_primitive_int(*name).into() as f64;
1493 return read_stretch_unit(engine,if is_negative {-base} else {base},None)
1494 }
1495 Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Int{read,..},..}) => {
1496 let base = read(engine,token)?.into() as f64;
1497 return read_stretch_unit(engine,if is_negative {-base} else {base},None)
1498 }
1499 Some(TeXCommand::CharDef(c)) => {
1500 let base = Into::<u64>::into(*c) as f64;
1501 return read_stretch_unit(engine,if is_negative {-base} else {base},None)
1502 }
1503 _ => {
1504 engine.requeue(token)?;
1505 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1506 return read_stretch_unit(engine,0f64,None)
1507 }
1508 }
1509 );
1510 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1511 read_stretch_unit(engine, 0f64, None)
1512}
1513fn read_stretch_float<ET: EngineTypes>(
1514 engine: &mut EngineReferences<ET>,
1515 is_negative: bool,
1516 first: u8,
1517) -> TeXResult<StretchShrink<ET::Dim>, ET> {
1518 let mut ret = 0f64;
1519 let mut in_decimal = first == b'.';
1520 let mut scale = 10f64;
1521 if !in_decimal {
1522 ret = (first - b'0') as f64;
1523 }
1524 crate::expand_loop!(engine,token,
1525 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1526 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => {
1527 if in_decimal {
1528 ret += (b - b'0') as f64 / scale;
1529 scale *= 10.0;
1530 } else {
1531 ret = 10.0*ret + ((b - b'0') as f64);
1532 }
1533 }
1534 (Ok(b','|b'.'),CommandCode::Other) => {
1535 if in_decimal {
1536 engine.requeue(token)?;
1537 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1538 return Ok(StretchShrink::Dim(ET::Dim::from_float(engine,ret,b"pt")))
1539 }
1540 in_decimal = true;
1541 }
1542 (_,CommandCode::Space) => {
1543 let f = if is_negative {-ret} else {ret};
1544 return read_stretch_unit(engine,f,None)
1545 }
1546 (Ok(b),CommandCode::Other | CommandCode::Letter) => {
1547 let f = if is_negative {-ret} else {ret};
1548 return read_stretch_unit(engine,f,Some((b,token)))
1549 }
1550 _ => {
1551 engine.requeue(token)?;
1552 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1553 return Ok(StretchShrink::Dim(ET::Dim::from_float(engine,ret,b"pt")))
1554 }
1555 }
1556 ResolvedToken::Cmd(Some(TeXCommand::Primitive {name,cmd:PrimitiveCommand::PrimitiveDim})) => {
1557 let base = engine.state.get_primitive_dim(*name);
1558 let scale = if is_negative {-ret} else {ret};
1559 return Ok(StretchShrink::Dim(base.scale_float(scale)))
1560 }
1561 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Dim{read,..},..})) => {
1562 let base = read(engine,token)?;
1563 let scale = if is_negative {-ret} else {ret};
1564 return Ok(StretchShrink::Dim(base.scale_float(scale)))
1565 }
1566 ResolvedToken::Cmd(Some(TeXCommand::DimRegister(u))) => {
1567 let base = engine.state.get_dim_register(*u);
1568 let scale = if is_negative {-ret} else {ret};
1569 return Ok(StretchShrink::Dim(base.scale_float(scale)))
1570 }
1571 ResolvedToken::Cmd(Some(TeXCommand::Primitive {name,cmd:PrimitiveCommand::PrimitiveSkip})) => {
1572 let base = engine.state.get_primitive_skip(*name).base;
1573 let scale = if is_negative {-ret} else {ret};
1574 return Ok(StretchShrink::Dim(base.scale_float(scale)))
1575 }
1576 ResolvedToken::Cmd(Some(TeXCommand::SkipRegister(u))) => {
1577 let base = engine.state.get_skip_register(*u).base;
1578 let scale = if is_negative {-ret} else {ret};
1579 return Ok(StretchShrink::Dim(base.scale_float(scale)))
1580 }
1581 _ => {
1582 engine.requeue(token)?;
1583 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1584 return Ok(StretchShrink::Dim(ET::Dim::from_float(engine,ret,b"pt")))
1585 }
1586 );
1587 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1588 Ok(StretchShrink::Dim(ET::Dim::from_float(engine, ret, b"pt")))
1589}
1590fn read_stretch_unit<ET: EngineTypes>(
1591 engine: &mut EngineReferences<ET>,
1592 mut float: f64,
1593 mut first: Option<(u8, ET::Token)>,
1594) -> TeXResult<StretchShrink<ET::Dim>, ET> {
1595 let is_true = match &first {
1596 Some((b't' | b'T', _)) => read_keyword(engine, b"true", std::mem::take(&mut first))?,
1597 Some(_) => false,
1598 None => read_keyword(engine, b"true", None)?,
1599 };
1600 if is_true {
1601 let mag = engine.state.get_primitive_int(PRIMITIVES.mag);
1602 float *= Into::<i64>::into(mag) as f64 / 1000.0;
1603 }
1604 match read_keywords(engine, STRETCH_SHRINK_UNITS, first)? {
1605 Some(d) => Ok(StretchShrink::from_float(engine, float, d)),
1606 _ => {
1607 let ret = read_unit_or_dim(engine, float)?;
1608 Ok(StretchShrink::Dim(ret))
1609 }
1610 }
1611}
1612
1613pub fn read_muskip<ET: EngineTypes>(
1615 engine: &mut EngineReferences<ET>,
1616 skip_eq: bool,
1617 in_token: &ET::Token,
1618) -> TeXResult<MuSkip<ET::MuDim>, ET> {
1619 let NumContinuation { is_negative, next } = read_numeric(engine, skip_eq, in_token)?;
1620 match next {
1621 Either::Left(b) => read_muskip_byte(engine, is_negative, b, |d, e| read_muskip_ii(e, d)),
1622 Either::Right((cmd, token)) => read_muskip_command(
1623 engine,
1624 is_negative,
1625 cmd,
1626 token,
1627 |d, e| read_muskip_ii(e, d),
1628 Ok,
1629 ),
1630 }
1631}
1632
1633pub fn read_mudim<ET: EngineTypes>(
1635 engine: &mut EngineReferences<ET>,
1636 skip_eq: bool,
1637 in_token: &ET::Token,
1638) -> TeXResult<ET::MuDim, ET> {
1639 let NumContinuation { is_negative, next } = read_numeric(engine, skip_eq, in_token)?;
1640 match next {
1641 Either::Left(b) => read_muskip_byte(engine, is_negative, b, |d, _| Ok(d)),
1642 Either::Right((cmd, token)) => read_muskip_command(
1643 engine,
1644 is_negative,
1645 cmd,
1646 token,
1647 |d, _| Ok(d),
1648 |s| Ok(s.base),
1649 ),
1650 }
1651}
1652
1653pub fn read_muskip_byte<R, ET: EngineTypes>(
1655 engine: &mut EngineReferences<ET>,
1656 is_negative: bool,
1657 b: u8,
1658 kern: fn(ET::MuDim, &mut EngineReferences<ET>) -> TeXResult<R, ET>,
1659) -> TeXResult<R, ET> {
1660 if b == b',' || b == b'.' {
1661 read_muskip_dim(engine, is_negative, b'.', kern)
1662 } else if is_ascii_digit(b) {
1663 read_muskip_dim(engine, is_negative, b, kern)
1664 } else {
1665 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1666 read_muskip_dim(engine, false, b, kern)
1667 }
1668}
1669
1670pub fn read_muskip_command<R, ET: EngineTypes>(
1672 engine: &mut EngineReferences<ET>,
1673 is_negative: bool,
1674 cmd: TeXCommand<ET>,
1675 token: ET::Token,
1676 kern: fn(ET::MuDim, &mut EngineReferences<ET>) -> TeXResult<R, ET>,
1677 skip: fn(MuSkip<ET::MuDim>) -> TeXResult<R, ET>,
1678) -> TeXResult<R, ET> {
1679 match cmd {
1680 TeXCommand::MuSkipRegister(u) => {
1681 let base = engine.state.get_muskip_register(u);
1682 let base = if is_negative { -base } else { base };
1683 skip(base)
1684 }
1685 TeXCommand::Primitive {
1686 name,
1687 cmd: PrimitiveCommand::PrimitiveMuSkip,
1688 } => {
1689 let base = engine.state.get_primitive_muskip(name);
1690 let base = if is_negative { -base } else { base };
1691 skip(base)
1692 }
1693 TeXCommand::Primitive {
1694 cmd: PrimitiveCommand::MuSkip { read, .. },
1695 ..
1696 } => {
1697 let base = read(engine, token)?;
1698 let base = if is_negative { -base } else { base };
1699 skip(base)
1700 }
1701 TeXCommand::CharDef(c) => {
1702 let base = c.into() as i64;
1703 let base = (if is_negative { -base } else { base }) as f64;
1704 let base = read_mudim_unit(engine, base, None)?;
1705 kern(base, engine)
1706 }
1707 TeXCommand::IntRegister(u) => {
1708 let base = engine.state.get_int_register(u);
1709 let base = (if is_negative { -base } else { base }).into() as f64;
1710 let base = read_mudim_unit(engine, base, None)?;
1711 kern(base, engine)
1712 }
1713 _ => {
1714 engine.requeue(token)?;
1715 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1716 let base = read_mudim_unit(engine, 0f64, None)?;
1717 kern(base, engine)
1718 }
1719 }
1720}
1721
1722fn read_muskip_dim<R, ET: EngineTypes>(
1723 engine: &mut EngineReferences<ET>,
1724 is_negative: bool,
1725 first: u8,
1726 kern: fn(ET::MuDim, &mut EngineReferences<ET>) -> TeXResult<R, ET>,
1727) -> TeXResult<R, ET> {
1728 let base = read_mudim_float(engine, is_negative, first)?;
1729 kern(base, engine)
1730}
1731
1732fn read_mudim_float<ET: EngineTypes>(
1733 engine: &mut EngineReferences<ET>,
1734 is_negative: bool,
1735 first: u8,
1736) -> TeXResult<ET::MuDim, ET> {
1737 let mut ret = 0f64;
1738 let mut in_decimal = first == b'.';
1739 let mut fac = 10f64;
1740 if !in_decimal {
1741 ret = (first - b'0') as f64;
1742 }
1743 crate::expand_loop!(engine,token,
1744 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1745 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => {
1746 if in_decimal {
1747 ret += (b - b'0') as f64 / fac;
1748 fac *= 10.0;
1749 } else {
1750 ret = 10.0*ret + ((b - b'0') as f64);
1751 }
1752 }
1753 (Ok(b','|b'.'),CommandCode::Other) => {
1754 if in_decimal {
1755 engine.requeue(token)?;
1756 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1757 return Ok(ET::MuDim::from_float(engine,ret,b"mu"))
1758 }
1759 in_decimal = true;
1760 }
1761 (_,CommandCode::Space) => {
1762 let f = if is_negative {-ret} else {ret};
1763 return read_mudim_unit(engine,f,None)
1764 }
1765 (Ok(b),CommandCode::Other | CommandCode::Letter) => {
1766 let f = if is_negative {-ret} else {ret};
1767 return read_mudim_unit(engine,f,Some((b,token)))
1768 }
1769 _ => {
1770 engine.requeue(token)?;
1771 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1772 return Ok(ET::MuDim::from_float(engine,ret,b"mu"))
1773 }
1774 }
1775 _ => {
1776 engine.requeue(token)?;
1777 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1778 return Ok(ET::MuDim::from_float(engine,ret,b"mu"))
1779 }
1780 );
1781 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1782 Ok(ET::MuDim::from_float(engine, ret, b"mu"))
1783}
1784
1785fn read_mudim_unit<ET: EngineTypes>(
1786 engine: &mut EngineReferences<ET>,
1787 float: f64,
1788 first: Option<(u8, ET::Token)>,
1789) -> TeXResult<ET::MuDim, ET> {
1790 let units = ET::MuDim::UNITS;
1791 match read_keywords(engine, units, first)? {
1792 Some(d) => Ok(ET::MuDim::from_float(engine, float, d)),
1793 _ => {
1794 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1795 Ok(ET::MuDim::from_float(engine, float, b"mu"))
1796 }
1797 }
1798}
1799
1800pub(crate) fn read_muskip_ii<ET: EngineTypes>(
1801 engine: &mut EngineReferences<ET>,
1802 base: ET::MuDim,
1803) -> TeXResult<MuSkip<ET::MuDim>, ET> {
1804 match read_keywords(engine, &[PLUS, MINUS], None)? {
1805 Some(b) if b == PLUS => {
1806 let stretch = read_mustretch(engine)?;
1807 Ok(if read_keyword(engine, MINUS, None)? {
1808 MuSkip::new(base, Some(stretch), Some(read_mustretch(engine)?))
1809 } else {
1810 MuSkip::new(base, Some(stretch), None)
1811 })
1812 }
1813 Some(b) if b == MINUS => {
1814 let shrink = read_mustretch(engine)?;
1815 Ok(if read_keyword(engine, PLUS, None)? {
1816 MuSkip::new(base, Some(read_mustretch(engine)?), Some(shrink))
1817 } else {
1818 MuSkip::new(base, None, Some(shrink))
1819 })
1820 }
1821 _ => Ok(MuSkip::new(base, None, None)),
1822 }
1823}
1824
1825fn read_mustretch<ET: EngineTypes>(
1826 engine: &mut EngineReferences<ET>,
1827) -> TeXResult<MuStretchShrink<ET::MuDim>, ET> {
1828 let mut is_negative = false;
1829 crate::expand_loop!(engine,token,
1830 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1831 (_,CommandCode::Space) => (),
1832 (Ok(b'-'),CommandCode::Other) => {
1833 is_negative = !is_negative;
1834 }
1835 (Ok(b'+'),CommandCode::Other) => (),
1836 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => return read_mustretch_float(engine,is_negative,b),
1837 (Ok(b','|b'.'),CommandCode::Other) => return read_mustretch_float(engine,is_negative,b'.'),
1838 _ => {
1839 engine.requeue(token)?;
1840 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1841 return read_mustretch_unit(engine,0f64,None)
1842 }
1843 }
1844 _ => {
1845 engine.requeue(token)?;
1846 TeXError::missing_number(engine.aux,engine.state,engine.mouth)?;
1847 return read_mustretch_unit(engine,0f64,None)
1848 }
1849 );
1850 TeXError::missing_number(engine.aux, engine.state, engine.mouth)?;
1851 Ok(MuStretchShrink::Mu(ET::MuDim::default()))
1852}
1853fn read_mustretch_float<ET: EngineTypes>(
1854 engine: &mut EngineReferences<ET>,
1855 is_negative: bool,
1856 first: u8,
1857) -> TeXResult<MuStretchShrink<ET::MuDim>, ET> {
1858 let mut ret = 0f64;
1859 let mut in_decimal = first == b'.';
1860 let mut fac = 10f64;
1861 if !in_decimal {
1862 ret = (first - b'0') as f64;
1863 }
1864 crate::expand_loop!(engine,token,
1865 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1866 (Ok(b),CommandCode::Other) if is_ascii_digit(b) => {
1867 if in_decimal {
1868 ret += (b - b'0') as f64 / fac;
1869 fac *= 10.0;
1870 } else {
1871 ret = 10.0*ret + ((b - b'0') as f64);
1872 }
1873 }
1874 (Ok(b','|b'.'),CommandCode::Other) => {
1875 if in_decimal {
1876 engine.requeue(token)?;
1877 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1878 return Ok(MuStretchShrink::Mu(ET::MuDim::from_float(engine,ret,b"mu")))
1879 }
1880 in_decimal = true;
1881 }
1882 (_,CommandCode::Space) => {
1883 let f = if is_negative {-ret} else {ret};
1884 return read_mustretch_unit(engine,f,None)
1885 }
1886 (Ok(b),CommandCode::Other | CommandCode::Letter) => {
1887 let f = if is_negative {-ret} else {ret};
1888 return read_mustretch_unit(engine,f,Some((b,token)))
1889 }
1890 _ => {
1891 engine.requeue(token)?;
1892 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1893 return Ok(MuStretchShrink::Mu(ET::MuDim::from_float(engine,ret,b"mu")))
1894 }
1895 }
1896 _ => {
1897 engine.requeue(token)?;
1898 TeXError::missing_unit(engine.aux,engine.state,engine.mouth)?;
1899 return Ok(MuStretchShrink::Mu(ET::MuDim::from_float(engine,ret,b"mu")))
1900 }
1901 );
1902 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1903 Ok(MuStretchShrink::Mu(ET::MuDim::from_float(
1904 engine, ret, b"mu",
1905 )))
1906}
1907fn read_mustretch_unit<ET: EngineTypes>(
1908 engine: &mut EngineReferences<ET>,
1909 float: f64,
1910 first: Option<(u8, ET::Token)>,
1911) -> TeXResult<MuStretchShrink<ET::MuDim>, ET> {
1912 match read_keywords(engine, STRETCH_SHRINK_UNITS, first)? {
1913 Some(d) => Ok(MuStretchShrink::from_float(engine, float, d)),
1914 _ => match read_keywords(engine, ET::MuDim::UNITS, None)? {
1915 Some(d) => Ok(MuStretchShrink::from_float(engine, float, d)),
1916 _ => {
1917 TeXError::missing_unit(engine.aux, engine.state, engine.mouth)?;
1918 Ok(MuStretchShrink::Mu(ET::MuDim::from_float(
1919 engine, float, b"mu",
1920 )))
1921 }
1922 },
1923 }
1924}
1925
1926pub fn read_keyword<ET: EngineTypes>(
1928 engine: &mut EngineReferences<ET>,
1929 kw: &[u8],
1930 first: Option<(u8, ET::Token)>,
1931) -> TeXResult<bool, ET> {
1932 let mut ret = arrayvec::ArrayVec::<_, 20>::new(); let mut read = arrayvec::ArrayVec::<_, 20>::new(); if let Some((b, t)) = first {
1935 ret.push(b.to_ascii_lowercase());
1936 read.push(t);
1937 }
1938 crate::expand_loop!(engine,token,
1939 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1940 (_,CommandCode::Space) if ret.is_empty() => (),
1941 (Ok(b),_) => {
1942 ret.push(b.to_ascii_lowercase());
1943 read.push(token);
1944 if !kw.starts_with(&ret) {
1945 for t in read.into_iter().rev() {engine.requeue(t)?}
1946 return Ok(false)
1947 }
1948 if kw.len() == ret.len() {
1949 return Ok(true)
1950 }
1951 }
1952 _ => {
1953 read.push(token);
1954 for t in read.into_iter().rev() {engine.requeue(t)?}
1955 return Ok(false)
1956 }
1957 }
1958 ResolvedToken::Cmd(_) => {
1959 read.push(token);
1960 for t in read.into_iter().rev() {engine.requeue(t)?}
1961 return Ok(false)
1962 }
1963 );
1964 if kw.len() != ret.len() || !kw.starts_with(&ret) {
1965 for t in read.into_iter().rev() {
1966 engine.requeue(t)?
1967 }
1968 Ok(false)
1969 } else {
1970 Ok(true)
1971 }
1972}
1973
1974pub fn read_keywords<'a, ET: EngineTypes>(
1976 engine: &mut EngineReferences<ET>,
1977 kws: &[&'a [u8]],
1978 first: Option<(u8, ET::Token)>,
1979) -> TeXResult<Option<&'a [u8]>, ET> {
1980 let mut ret = arrayvec::ArrayVec::<_, 20>::new(); let mut read = arrayvec::ArrayVec::<_, 20>::new(); if let Some((b, t)) = first {
1983 ret.push(b.to_ascii_lowercase());
1984 read.push(t);
1985 }
1986 crate::expand_loop!(engine,token,
1987 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
1988 (_,CommandCode::Space) if ret.is_empty() => (),
1989 (Ok(b),_) => {
1990 ret.push(b.to_ascii_lowercase());
1991 let curr = kws.iter().filter(|k| k.to_ascii_lowercase().starts_with(&ret)).collect::<Vec<_>>();
1992 if curr.is_empty() {
1993 ret.pop();
1994 match kws.iter().find(|e| e.eq_ignore_ascii_case(ret.as_slice())) {
1995 Some(w) => {
1996 engine.requeue(token)?;
1997 return Ok(Some(w))
1998 }
1999 None => {
2000 engine.requeue(token)?;
2001 for t in read.into_iter().rev() {engine.requeue(t)?}
2002 return Ok(None)
2003 }
2004 }
2005 }
2006 read.push(token);
2007 if curr.len() == 1 && curr[0].len() == ret.len() {
2008 return Ok(Some(curr[0]))
2009 }
2010 }
2011 _ => {
2012 let curr = kws.iter().filter(|k| k.to_ascii_lowercase().starts_with(&ret)).collect::<Vec<_>>();
2013 match curr.iter().find(|b| b.eq_ignore_ascii_case(ret.as_slice())) {
2014 Some(b) => {
2015 engine.requeue(token)?;
2016 return Ok(Some(**b))
2017 }
2018 _ => {
2019 engine.requeue(token)?;
2020 for t in read.into_iter().rev() {engine.requeue(t)?}
2021 return Ok(None)
2022 }
2023 }
2024 }
2025 }
2026 ResolvedToken::Cmd(_) => {
2027 let curr = kws.iter().filter(|k| k.to_ascii_lowercase().starts_with(&ret)).collect::<Vec<_>>();
2028 match curr.iter().find(|b| ***b == ret.as_slice()) {
2029 Some(b) => {
2030 engine.mouth.requeue(token);
2031 return Ok(Some(**b))
2032 }
2033 _ => {
2034 engine.mouth.requeue(token);
2035 for t in read.into_iter().rev() {engine.requeue(t)?}
2036 return Ok(None)
2037 }
2038 }
2039 }
2040 );
2041 match kws.iter().find(|b| b.eq_ignore_ascii_case(ret.as_slice())) {
2042 Some(b) => Ok(Some(*b)),
2043 _ => {
2044 for t in read.into_iter().rev() {
2045 engine.requeue(t)?
2046 }
2047 Ok(None)
2048 }
2049 }
2050}
2051
2052pub fn read_chars<ET: EngineTypes>(
2054 engine: &mut EngineReferences<ET>,
2055 kws: &[u8],
2056) -> TeXResult<Either<u8, Option<ET::Token>>, ET> {
2057 crate::expand_loop!(engine,token,
2058 ResolvedToken::Tk {char,code} => match (char.try_into(),code) {
2059 (_,CommandCode::Space) => (),
2060 (Ok(b),_) if kws.contains(&b) => {
2061 return Ok(Either::Left(b))
2062 }
2063 _ => {
2064 return Ok(Either::Right(Some(token)))
2065 }
2066 }
2067 ResolvedToken::Cmd(_) => {
2068 return Ok(Either::Right(Some(token)))
2069 }
2070 );
2071 Ok(Either::Right(None))
2072}
2073
2074pub enum CSOrActiveChar<T: Token> {
2076 Active(T::Char),
2077 Name(T::CS),
2078}
2079impl<ET: EngineTypes> EngineReferences<'_, ET> {
2080 pub fn read_control_sequence(
2083 &mut self,
2084 in_token: &ET::Token,
2085 ) -> TeXResult<CSOrActiveChar<ET::Token>, ET> {
2086 loop {
2087 let token = self.need_next(false, in_token)?;
2088 match self.resolve(&token) {
2089 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
2090 name,
2091 cmd: PrimitiveCommand::Expandable(f),
2092 })) => ET::Gullet::do_expandable(self, *name, token, *f)?,
2093 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
2094 name,
2095 cmd: PrimitiveCommand::SimpleExpandable(f),
2096 })) => ET::Gullet::do_simple_expandable(self, *name, token, *f)?,
2097 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
2098 name,
2099 cmd: PrimitiveCommand::Conditional(f),
2100 })) => ET::Gullet::do_conditional(self, *name, token, *f, false)?,
2101 ResolvedToken::Cmd(_) => {
2102 let ret = match token.to_enum() {
2103 StandardToken::Character(c, CommandCode::Active) => {
2104 CSOrActiveChar::Active(c)
2105 }
2106 StandardToken::ControlSequence(cs) => CSOrActiveChar::Name(cs),
2107 _ => unreachable!(),
2108 };
2109 self.set_command(
2110 &ret,
2111 Some(TeXCommand::Primitive {
2112 name: PRIMITIVES.relax,
2113 cmd: PrimitiveCommand::Relax,
2114 }),
2115 false,
2116 );
2117 return Ok(ret);
2118 }
2119 ResolvedToken::Tk {
2120 code: CommandCode::Space,
2121 ..
2122 } => (),
2123 _ => {
2124 return Err(TeXError::General(
2125 "Control sequence expected\n TODO: Better error message".to_string(),
2126 ))
2127 }
2128 }
2129 }
2130 }
2131}