Skip to main content

retex_engine/
scan.rs

1//! Scanning numbers, dimensions, glue, keywords and token lists
2//! (*TeX: The Program* §402–§486, e-TeX §1490ff, LuaTeX extensions).
3
4use std::rc::Rc;
5
6use retex_arith::{self as arith, MAX_DIMEN, UNITY};
7
8use crate::Engine;
9use crate::cmd::Cmd;
10use crate::eqtb::Loc;
11use crate::glue::{self, Glue};
12use crate::hash::NO_CS;
13use crate::input::{ScannerStatus, TokKind};
14use crate::primitives_gen as pg;
15use crate::token::{Token, TokenList};
16
17/// The level of a scanned value (`cur_val_level`).
18#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
19pub enum ValLevel {
20    /// An integer.
21    #[default]
22    Int = 0,
23    /// A dimension.
24    Dimen = 1,
25    /// Glue.
26    Glue = 2,
27    /// Muglue.
28    Mu = 3,
29    /// A font identifier.
30    Ident = 4,
31    /// A token list.
32    Tok = 5,
33}
34
35/// Offset of register numbers in the modifier of register-like
36/// commands defined by `\countdef` etc. (smaller modifiers are parameters).
37pub const REG_OFFSET: u32 = 0x1_0000;
38
39/// `register` modifiers.
40#[allow(missing_docs)]
41pub mod reg {
42    pub const COUNT: u32 = 0;
43    pub const ATTRIBUTE: u32 = 1;
44    pub const DIMEN: u32 = 2;
45    pub const SKIP: u32 = 3;
46    pub const MUSKIP: u32 = 4;
47}
48
49/// `last_item` modifiers (LuaTeX numbering).
50#[allow(missing_docs)]
51pub mod last_item {
52    pub const LAST_PENALTY: u32 = 0;
53    pub const LAST_KERN: u32 = 2;
54    pub const LAST_SKIP: u32 = 3;
55    pub const LAST_NODE_TYPE: u32 = 4;
56    pub const INPUT_LINE_NO: u32 = 5;
57    pub const BADNESS: u32 = 6;
58    pub const LAST_SAVED_BOX_RESOURCE_INDEX: u32 = 7;
59    pub const LAST_SAVED_IMAGE_RESOURCE_INDEX: u32 = 8;
60    pub const LAST_SAVED_IMAGE_RESOURCE_PAGES: u32 = 9;
61    pub const LAST_X_POS: u32 = 10;
62    pub const LAST_Y_POS: u32 = 11;
63    pub const RANDOM_SEED: u32 = 12;
64    pub const LUATEX_VERSION: u32 = 13;
65    pub const ETEX_MINOR_VERSION: u32 = 14;
66    pub const ETEX_VERSION: u32 = 15;
67    pub const CURRENT_GROUP_LEVEL: u32 = 16;
68    pub const CURRENT_GROUP_TYPE: u32 = 17;
69    pub const CURRENT_IF_LEVEL: u32 = 18;
70    pub const CURRENT_IF_TYPE: u32 = 19;
71    pub const CURRENT_IF_BRANCH: u32 = 20;
72    pub const GLUE_STRETCH_ORDER: u32 = 21;
73    pub const GLUE_SHRINK_ORDER: u32 = 22;
74    pub const ETEX_GLUE_STRETCH_ORDER: u32 = 23;
75    pub const ETEX_GLUE_SHRINK_ORDER: u32 = 24;
76    pub const FONT_CHAR_WD: u32 = 25;
77    pub const FONT_CHAR_HT: u32 = 26;
78    pub const FONT_CHAR_DP: u32 = 27;
79    pub const FONT_CHAR_IC: u32 = 28;
80    pub const PAR_SHAPE_LENGTH: u32 = 29;
81    pub const PAR_SHAPE_INDENT: u32 = 30;
82    pub const PAR_SHAPE_DIMEN: u32 = 31;
83    pub const GLUE_STRETCH: u32 = 32;
84    pub const GLUE_SHRINK: u32 = 33;
85    pub const MU_TO_GLUE: u32 = 34;
86    pub const GLUE_TO_MU: u32 = 35;
87    pub const NUM_EXPR: u32 = 36;
88    pub const DIM_EXPR: u32 = 38;
89    pub const GLUE_EXPR: u32 = 39;
90    pub const MU_EXPR: u32 = 40;
91}
92
93/// The LuaTeX version reported by `\luatexversion`.
94pub const LUATEX_VERSION: i32 = 122;
95/// `\luatexrevision`.
96pub const LUATEX_REVISION: &str = "0";
97/// `\eTeXversion`.
98pub const ETEX_VERSION: i32 = 2;
99/// `\eTeXminorversion`.
100pub const ETEX_MINOR_VERSION: i32 = 6;
101/// `\eTeXrevision`.
102pub const ETEX_REVISION: &str = ".6";
103
104impl Engine {
105    // ----- small helpers ---------------------------------------------------
106
107    /// Gets the next non-blank non-call token (TeX §406).
108    pub fn get_nonblank_noncall(&mut self) {
109        loop {
110            self.get_x_token();
111            if self.cur_cmd != Cmd::Spacer {
112                break;
113            }
114        }
115    }
116
117    /// Gets the next non-blank non-relax non-call token (§404).
118    pub fn get_nonblank_nonrelax_noncall(&mut self) {
119        loop {
120            self.get_x_token();
121            if self.cur_cmd != Cmd::Spacer && self.cur_cmd != Cmd::Relax {
122                break;
123            }
124        }
125    }
126
127    /// Scans an optional space (§443).
128    pub fn scan_optional_space(&mut self) {
129        self.get_x_token();
130        if self.cur_cmd != Cmd::Spacer {
131            self.back_input();
132        }
133    }
134
135    /// Scans a left brace, inserting one if missing (`scan_left_brace`).
136    pub fn scan_left_brace(&mut self) {
137        self.get_nonblank_nonrelax_noncall();
138        if self.cur_cmd != Cmd::LeftBrace {
139            self.print_err("Missing { inserted");
140            self.help(&[
141                "A left brace was mandatory here, so I've put one in.",
142                "You might want to delete and/or insert some corrections",
143                "so that I will find a matching right brace soon.",
144                // LuaTeX drops TeX82's parentheses around this line.
145                "If you're confused by all this, try typing `I}' now.",
146            ]);
147            self.back_error();
148            self.cur_tok = Token::LEFT_BRACE;
149            self.cur_cmd = Cmd::LeftBrace;
150            self.cur_chr = '{' as u32;
151            self.input.align_state += 1;
152        }
153    }
154
155    /// Scans an optional `=` (`scan_optional_equals`).
156    pub fn scan_optional_equals(&mut self) {
157        self.get_nonblank_noncall();
158        if self.cur_tok != Token::other('=' as u32) {
159            self.back_input();
160        }
161    }
162
163    /// Looks for a keyword (`scan_keyword`); letters match either case.
164    pub fn scan_keyword(&mut self, s: &str) -> bool {
165        // Keywords are short: the matched tokens stay on the stack.
166        let mut matched = [Token(0); 32];
167        let bytes = s.as_bytes();
168        debug_assert!(bytes.len() <= matched.len());
169        let mut k = 0;
170        while k < bytes.len() {
171            self.get_x_token();
172            let c = u32::from(bytes[k]);
173            let upper = if bytes[k].is_ascii_lowercase() {
174                c - 32
175            } else {
176                c
177            };
178            if self.cur_cs == NO_CS && (self.cur_chr == c || self.cur_chr == upper) {
179                matched[k] = self.cur_tok;
180                k += 1;
181            } else if self.cur_cmd != Cmd::Spacer || k > 0 {
182                self.back_input();
183                if k > 0 {
184                    self.back_list(Rc::from(&matched[..k]));
185                }
186                return false;
187            }
188        }
189        true
190    }
191
192    /// Pushes a token list to be read again (`back_list`).
193    pub fn back_list(&mut self, list: TokenList) {
194        self.begin_token_list(list, TokKind::BackedUp);
195    }
196
197    /// Reports an inappropriate mu/non-mu mix (`mu_error`).
198    pub fn mu_error(&mut self) {
199        self.print_err("Incompatible glue units");
200        self.help(&["I'm going to assume that 1mu=1pt when they're mixed."]);
201        self.error();
202    }
203
204    // ----- integers ---------------------------------------------------------
205
206    /// Scans a signed integer into `cur_val` (`scan_int`).
207    #[inline]
208    pub fn scan_int(&mut self) {
209        self.get_nonblank_noncall();
210        // Fast path: an internal quantity without a sign.
211        if self.cur_cmd.is_internal() {
212            self.scan_something_internal(ValLevel::Int, false);
213        } else {
214            self.scan_int_from_first();
215        }
216    }
217
218    /// [`scan_int`](Self::scan_int) once the first non-blank token has been
219    /// read.
220    #[inline(never)]
221    fn scan_int_from_first(&mut self) {
222        let mut ok_so_far = true;
223        // Get the next non-blank non-sign token.
224        let mut negative = false;
225        let mut first = true;
226        loop {
227            if !first {
228                self.get_nonblank_noncall();
229            }
230            first = false;
231            if self.cur_tok == Token::other('-' as u32) {
232                negative = !negative;
233                self.cur_tok = Token::other('+' as u32);
234            }
235            if self.cur_tok != Token::other('+' as u32) {
236                break;
237            }
238        }
239        if self.cur_tok == Token::other('`' as u32) {
240            // An alphabetic constant.
241            self.get_token();
242            if self.cur_tok.0 < crate::token::CS_TOKEN_FLAG {
243                self.cur_val = self.cur_chr as i32;
244                if self.cur_cmd <= Cmd::RightBrace {
245                    if self.cur_cmd == Cmd::RightBrace {
246                        self.input.align_state += 1;
247                    } else {
248                        self.input.align_state -= 1;
249                    }
250                }
251            } else {
252                let cs = self.cur_tok.cs_id();
253                self.cur_val = match self.hash.kind(cs) {
254                    crate::hash::CsKind::Active(c) | crate::hash::CsKind::Single(c) => *c as i32,
255                    _ => 0x11_0000,
256                };
257            }
258            if self.cur_val > 0x10_FFFF {
259                self.print_err("Improper alphabetic constant");
260                self.help(&[
261                    "A one-character control sequence belongs after a ` mark.",
262                    "So I'm essentially inserting \\0 here.",
263                ]);
264                self.cur_val = '0' as i32;
265                self.back_error();
266            } else {
267                self.scan_optional_space();
268            }
269        } else if self.cur_cmd.is_internal() {
270            self.scan_something_internal(ValLevel::Int, false);
271        } else if self.cur_cmd == Cmd::MathStyle {
272            // LuaTeX: the style commands are numbers (0-7).
273            self.cur_val = self.cur_chr as i32;
274        } else {
275            // A numeric constant.
276            let mut radix: i64 = 10;
277            let mut m: i64 = 214_748_364;
278            if self.cur_tok == Token::other('\'' as u32) {
279                radix = 8;
280                m = 0o2000000000;
281                self.get_x_token();
282            } else if self.cur_tok == Token::other('"' as u32) {
283                radix = 16;
284                m = 0o1000000000;
285                self.get_x_token();
286            }
287            let mut vacuous = true;
288            let mut val: i64 = 0;
289            loop {
290                let t = self.cur_tok;
291                let d: i64 = if !t.is_cs()
292                    && t.cmd() == Cmd::OtherChar
293                    && (0x30..=0x39).contains(&t.chr())
294                    && i64::from(t.chr() - 0x30) < radix
295                {
296                    i64::from(t.chr() - 0x30)
297                } else if radix == 16
298                    && !t.is_cs()
299                    && (t.cmd() == Cmd::Letter || t.cmd() == Cmd::OtherChar)
300                    && (0x41..=0x46).contains(&t.chr())
301                {
302                    i64::from(t.chr() - 0x41 + 10)
303                } else {
304                    break;
305                };
306                vacuous = false;
307                if val >= m && (val > m || d > 7 || radix != 10) {
308                    if ok_so_far {
309                        self.print_err("Number too big");
310                        self.help(&[
311                            "I can only go up to 2147483647='17777777777=\"7FFFFFFF,",
312                            "so I'm using that number instead of yours.",
313                        ]);
314                        self.error();
315                        val = i64::from(arith::INFINITY);
316                        ok_so_far = false;
317                    }
318                } else {
319                    val = val * radix + d;
320                }
321                self.get_x_token();
322            }
323            self.cur_val = val as i32;
324            if vacuous {
325                self.missing_number();
326            } else if self.cur_cmd != Cmd::Spacer {
327                self.back_input();
328            }
329        }
330        if negative {
331            self.cur_val = self.cur_val.wrapping_neg();
332        }
333    }
334
335    /// Reports a missing number (TeX's "Express astonishment").
336    fn missing_number(&mut self) {
337        self.print_err("Missing number, treated as zero");
338        self.help(&[
339            "A number should have been here; I inserted `0'.",
340            "(If you can't figure out why I needed to see a number,",
341            "look up `weird error' in the index to The TeXbook.)",
342        ]);
343        self.back_error();
344    }
345
346    fn scan_limited(&mut self, max: i32, what: &str, help: &'static str) {
347        self.scan_int();
348        if self.cur_val < 0 || self.cur_val > max {
349            self.print_err(&format!("Bad {what} ("));
350            self.printer.print_int(self.cur_val.into());
351            self.printer.print_char(')' as u32);
352            self.help(&[help, "I changed this one to zero."]);
353            self.int_error_zero();
354        }
355    }
356
357    fn int_error_zero(&mut self) {
358        self.error();
359        self.cur_val = 0;
360    }
361
362    /// Scans a register number 0–65535 (LuaTeX's `scan_register_num`).
363    pub fn scan_register_num(&mut self) {
364        self.scan_limited(
365            65535,
366            "register code",
367            "A register code must be between 0 and 65535.",
368        );
369    }
370
371    /// Scans a character code 0–0x10FFFF (`scan_char_num`).
372    pub fn scan_char_num(&mut self) {
373        self.scan_limited(
374            0x10_FFFF,
375            "character code",
376            "A character code must be between 0 and 1114111.",
377        );
378    }
379
380    /// Scans a stream number for `\read`, `\openin`, `\ifeof` (0–15).
381    pub fn scan_four_bit_int(&mut self) {
382        self.scan_limited(
383            15,
384            "number",
385            "Since I expected to read a number between 0 and 15,",
386        );
387    }
388
389    /// Scans a stream number for `\ifeof` and `\read`; LuaTeX accepts any
390    /// integer here and treats out-of-range streams as closed.
391    pub fn scan_four_bit_int_or_more(&mut self) {
392        self.scan_int();
393    }
394
395    /// Scans a math character for `\mathchardef`, `\mathchar` and
396    /// `\mathaccent` (`scan_fifteen_bit_int`) and returns it as a
397    /// `math_given` value; `cur_val` is the number `\the` reports.
398    ///
399    /// Observed in LuaTeX 1.22: numbers above `"8000` (math active) are read as packed
400    /// math codes (class·2²¹ + family·2²⁴ + character, what `\mathcode`
401    /// reports for `\Umathcode`s), so `\mathchardef\x\mathcode`c` works;
402    /// a character beyond U+10FFFF is an "Invalid math code during >
403    /// 0x8000 mathcode fallback", a negative number a "Bad mathchar".
404    pub fn scan_math_given(&mut self) -> u32 {
405        self.scan_int();
406        let v = self.cur_val;
407        let g = if v < 0 {
408            self.print_err("Bad mathchar (");
409            self.printer.print_int(v.into());
410            self.printer.print_char(')' as u32);
411            0
412        } else if v <= 0x8000 {
413            return v as u32;
414        } else if crate::codes::math_char(v.into()) > 0x10FFFF {
415            self.print_err("Invalid math code during > 0x8000 mathcode fallback");
416            0
417        } else {
418            v as u32 | crate::codes::MATH_GIVEN_PACKED
419        };
420        if g == 0 {
421            self.help(&["I'm going to use 0 instead of that illegal code value."]);
422            self.error();
423        }
424        self.cur_val = crate::codes::math_given_report(g);
425        g
426    }
427
428    /// Scans a 27-bit delimiter code (`scan_twenty_seven_bit_int`).
429    pub fn scan_twenty_seven_bit_int(&mut self) {
430        self.scan_int();
431        if self.cur_val < 0 || self.cur_val > 0o777777777 {
432            self.print_err("Bad delimiter code (");
433            self.printer.print_int(self.cur_val.into());
434            self.printer.print_char(')' as u32);
435            self.help(&[
436                "A numeric delimiter code must be between 0 and 2^{27}-1.",
437                "I changed this one to zero.",
438            ]);
439            self.int_error_zero();
440        }
441    }
442
443    /// Scans a math family number 0–255 (LuaTeX).
444    pub fn scan_math_family(&mut self) {
445        self.scan_limited(255, "number", "A family number must be between 0 and 255.");
446    }
447
448    /// Scans a category code 0–15.
449    pub fn scan_category_code(&mut self) {
450        self.scan_limited(
451            15,
452            "category code",
453            "A category code must be between 0 and 15.",
454        );
455    }
456
457    // ----- dimensions --------------------------------------------------------
458
459    /// Scans a normal dimension (`scan_normal_dimen`).
460    pub fn scan_normal_dimen(&mut self) {
461        self.scan_dimen(false, false, false);
462    }
463
464    /// Scans a dimension (`scan_dimen`, §448). `mu`: units must be `mu`;
465    /// `inf`: infinite units allowed; `shortcut`: `cur_val` already holds
466    /// the integer part. Sets `cur_val`, and `cur_order` via the returned
467    /// order.
468    pub fn scan_dimen(&mut self, mu: bool, inf: bool, shortcut: bool) -> glue::GlueOrder {
469        let mut f: i32 = 0;
470        let mut arith_error = false;
471        let mut cur_order = glue::NORMAL;
472        let mut negative = false;
473        'attach_sign: {
474            if !shortcut {
475                negative = false;
476                loop {
477                    self.get_nonblank_noncall();
478                    if self.cur_tok == Token::other('-' as u32) {
479                        negative = !negative;
480                        self.cur_tok = Token::other('+' as u32);
481                    }
482                    if self.cur_tok != Token::other('+' as u32) {
483                        break;
484                    }
485                }
486                if self.cur_cmd.is_internal() {
487                    if mu {
488                        self.scan_something_internal(ValLevel::Mu, false);
489                        if self.cur_val_level >= ValLevel::Glue {
490                            self.cur_val = self.cur_glue.width;
491                        }
492                        if self.cur_val_level == ValLevel::Mu {
493                            break 'attach_sign;
494                        }
495                        if self.cur_val_level != ValLevel::Int {
496                            self.mu_error();
497                        }
498                    } else {
499                        self.scan_something_internal(ValLevel::Dimen, false);
500                        if self.cur_val_level == ValLevel::Dimen {
501                            break 'attach_sign;
502                        }
503                    }
504                } else {
505                    self.back_input();
506                    if self.cur_tok == Token::other(',' as u32) {
507                        self.cur_tok = Token::other('.' as u32);
508                    }
509                    let mut radix10 = true;
510                    if self.cur_tok != Token::other('.' as u32) {
511                        self.scan_int_radix(&mut radix10);
512                    } else {
513                        self.cur_val = 0;
514                    }
515                    if self.cur_tok == Token::other(',' as u32) {
516                        self.cur_tok = Token::other('.' as u32);
517                    }
518                    if radix10 && self.cur_tok == Token::other('.' as u32) {
519                        // Scan decimal fraction.
520                        let mut digits: Vec<u8> = Vec::new();
521                        self.get_token(); // the point token is being re-scanned
522                        loop {
523                            self.get_x_token();
524                            let t = self.cur_tok;
525                            if t.is_cs()
526                                || t.cmd() != Cmd::OtherChar
527                                || !(0x30..=0x39).contains(&t.chr())
528                            {
529                                break;
530                            }
531                            if digits.len() < 17 {
532                                digits.push((t.chr() - 0x30) as u8);
533                            }
534                        }
535                        f = arith::round_decimals(&digits);
536                        if self.cur_cmd != Cmd::Spacer {
537                            self.back_input();
538                        }
539                    }
540                }
541            }
542            if self.cur_val < 0 {
543                negative = !negative;
544                self.cur_val = self.cur_val.wrapping_neg();
545            }
546            // Scan units.
547            'attach_fraction: {
548                'done: {
549                    if inf && self.scan_keyword("fi") {
550                        cur_order = glue::FI;
551                        while self.scan_keyword("l") {
552                            if cur_order == glue::FILLL {
553                                break;
554                            }
555                            cur_order += 1;
556                        }
557                        break 'attach_fraction;
558                    }
559                    // Units that are internal dimensions.
560                    let save_cur_val = self.cur_val;
561                    self.get_nonblank_noncall();
562                    let v: i32;
563                    if !self.cur_cmd.is_internal() {
564                        self.back_input();
565                        if mu {
566                            // Scan for mu units.
567                            if !self.scan_keyword("mu") {
568                                self.print_err("Illegal unit of measure (");
569                                self.printer.print_str("mu inserted)");
570                                self.help(&[
571                                    "The unit of measurement in math glue must be mu.",
572                                    "To recover gracefully from this error, it's best to",
573                                    "delete the erroneous units; e.g., type `2' to delete",
574                                    "two letters. (See Chapter 27 of The TeXbook.)",
575                                ]);
576                                self.error();
577                            }
578                            break 'attach_fraction;
579                        }
580                        if self.scan_keyword("em") {
581                            v = self.quad(self.eqtb.cur_font.val);
582                            self.scan_optional_space();
583                        } else if self.scan_keyword("ex") {
584                            v = self.x_height(self.eqtb.cur_font.val);
585                            self.scan_optional_space();
586                        } else if self.scan_keyword("px") {
587                            v = self.eqtb.dimen(pg::assign_dimen::PXDIMEN);
588                            self.scan_optional_space();
589                        } else {
590                            // Not an internal unit.
591                            if self.scan_keyword("true") {
592                                self.prepare_mag();
593                                let mag = self.eqtb.int(pg::assign_int::MAG);
594                                if mag != 1000 {
595                                    match arith::xn_over_d(self.cur_val, 1000, mag) {
596                                        Ok(r) => {
597                                            self.cur_val = r.quotient;
598                                            f = ((1000 * i64::from(f)
599                                                + 0o200000 * i64::from(r.remainder))
600                                                / i64::from(mag))
601                                                as i32;
602                                        }
603                                        Err(_) => arith_error = true,
604                                    }
605                                    self.cur_val += f / 0o200000;
606                                    f %= 0o200000;
607                                }
608                            }
609                            if self.scan_keyword("pt") {
610                                break 'attach_fraction;
611                            }
612                            let (num, denom): (i32, i32) = if self.scan_keyword("in") {
613                                (7227, 100)
614                            } else if self.scan_keyword("pc") {
615                                (12, 1)
616                            } else if self.scan_keyword("cm") {
617                                (7227, 254)
618                            } else if self.scan_keyword("mm") {
619                                (7227, 2540)
620                            } else if self.scan_keyword("bp") {
621                                (7227, 7200)
622                            } else if self.scan_keyword("dd") {
623                                (1238, 1157)
624                            } else if self.scan_keyword("cc") {
625                                (14856, 1157)
626                            } else if self.scan_keyword("nd") {
627                                (685, 642)
628                            } else if self.scan_keyword("nc") {
629                                (1370, 107)
630                            } else if self.scan_keyword("sp") {
631                                break 'done;
632                            } else {
633                                self.print_err("Illegal unit of measure (");
634                                self.printer.print_str("pt inserted)");
635                                self.help(&[
636                                    "Dimensions can be in units of em, ex, in, pt, pc,",
637                                    "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!",
638                                    "I'll assume that you meant to say pt, for printer's points.",
639                                    "To recover gracefully from this error, it's best to",
640                                    "delete the erroneous units; e.g., type `2' to delete",
641                                    "two letters. (See Chapter 27 of The TeXbook.)",
642                                ]);
643                                self.error();
644                                break 'attach_fraction;
645                            };
646                            match arith::xn_over_d(self.cur_val, num, denom) {
647                                Ok(r) => {
648                                    self.cur_val = r.quotient;
649                                    f = ((i64::from(num) * i64::from(f)
650                                        + 0o200000 * i64::from(r.remainder))
651                                        / i64::from(denom))
652                                        as i32;
653                                }
654                                Err(_) => arith_error = true,
655                            }
656                            self.cur_val = self.cur_val.wrapping_add(f / 0o200000);
657                            f %= 0o200000;
658                            break 'attach_fraction;
659                        }
660                    } else {
661                        if mu {
662                            self.scan_something_internal(ValLevel::Mu, false);
663                            if self.cur_val_level >= ValLevel::Glue {
664                                self.cur_val = self.cur_glue.width;
665                            }
666                            if self.cur_val_level != ValLevel::Mu {
667                                self.mu_error();
668                            }
669                        } else {
670                            self.scan_something_internal(ValLevel::Dimen, false);
671                        }
672                        v = self.cur_val;
673                    }
674                    // found:
675                    let frac = arith::xn_over_d(v, f, 0o200000)
676                        .map(|r| r.quotient)
677                        .unwrap_or_else(|_| {
678                            arith_error = true;
679                            0
680                        });
681                    self.cur_val = arith::nx_plus_y(save_cur_val, v, frac).unwrap_or_else(|_| {
682                        arith_error = true;
683                        0
684                    });
685                    break 'attach_sign;
686                }
687                // done: (sp units)
688                self.scan_optional_space();
689                break 'attach_sign;
690            }
691            // attach_fraction:
692            if self.cur_val >= 0o40000 {
693                arith_error = true;
694            } else {
695                self.cur_val = self.cur_val * UNITY + f;
696            }
697            self.scan_optional_space();
698        }
699        // attach_sign:
700        if arith_error || self.cur_val.wrapping_abs() >= 0o10000000000 || self.cur_val == i32::MIN {
701            self.print_err("Dimension too large");
702            self.help(&[
703                "I can't work with sizes bigger than about 19 feet.",
704                "Continue and I'll use the largest value I can.",
705            ]);
706            self.error();
707            self.cur_val = MAX_DIMEN;
708        }
709        if negative {
710            self.cur_val = -self.cur_val;
711        }
712        cur_order
713    }
714
715    /// `scan_int` that also reports whether the radix was 10 (needed by
716    /// `scan_dimen` to decide on a decimal fraction).
717    fn scan_int_radix(&mut self, radix10: &mut bool) {
718        // Peek at the first significant token to learn the radix.
719        self.get_nonblank_noncall();
720        let t = self.cur_tok;
721        *radix10 = !(t == Token::other('\'' as u32)
722            || t == Token::other('"' as u32)
723            || t == Token::other('`' as u32)
724            || self.cur_cmd.is_internal());
725        self.back_input();
726        self.scan_int();
727    }
728
729    /// Freezes the magnification (`prepare_mag`).
730    pub fn prepare_mag(&mut self) {
731        let mag = self.eqtb.int(pg::assign_int::MAG);
732        if self.mag_set > 0 && mag != self.mag_set {
733            self.print_err("Incompatible magnification (");
734            self.printer.print_int(mag.into());
735            self.printer.print_str(");");
736            self.printer
737                .print_nl(" the previous value will be retained");
738            self.help(&[
739                "I can handle only one magnification ratio per job. So I've",
740                "reverted to the magnification you used earlier on this run.",
741            ]);
742            self.int_error(self.mag_set);
743            self.eqtb.int_par[pg::assign_int::MAG as usize].val = self.mag_set;
744        }
745        let mag = self.eqtb.int(pg::assign_int::MAG);
746        if mag <= 0 || mag > 32768 {
747            self.print_err("Illegal magnification has been changed to 1000");
748            self.help(&["The magnification ratio must be between 1 and 32768."]);
749            self.int_error(mag);
750            self.eqtb.int_par[pg::assign_int::MAG as usize].val = 1000;
751        }
752        self.mag_set = self.eqtb.int(pg::assign_int::MAG);
753    }
754
755    /// Reports an error with an integer in parentheses (`int_error`).
756    pub fn int_error(&mut self, n: i32) {
757        self.printer.print_str(" (");
758        self.printer.print_int(n.into());
759        self.printer.print_char(')' as u32);
760        self.error();
761    }
762
763    // ----- glue --------------------------------------------------------------
764
765    /// Scans glue (`scan_glue`); `level` is [`ValLevel::Glue`] or
766    /// [`ValLevel::Mu`]. Sets `cur_glue`.
767    pub fn scan_glue(&mut self, level: ValLevel) {
768        let mu = level == ValLevel::Mu;
769        let mut negative = false;
770        loop {
771            self.get_nonblank_noncall();
772            if self.cur_tok == Token::other('-' as u32) {
773                negative = !negative;
774                self.cur_tok = Token::other('+' as u32);
775            }
776            if self.cur_tok != Token::other('+' as u32) {
777                break;
778            }
779        }
780        if self.cur_cmd.is_internal() {
781            self.scan_something_internal(level, negative);
782            if self.cur_val_level >= ValLevel::Glue {
783                if self.cur_val_level != level {
784                    self.mu_error();
785                }
786                return;
787            }
788            if self.cur_val_level == ValLevel::Int {
789                self.scan_dimen(mu, false, true);
790            } else if level == ValLevel::Mu {
791                self.mu_error();
792            }
793        } else {
794            self.back_input();
795            self.scan_dimen(mu, false, false);
796            if negative {
797                self.cur_val = -self.cur_val;
798            }
799        }
800        let mut q = Glue::fixed(self.cur_val);
801        if self.scan_keyword("plus") {
802            let o = self.scan_dimen(mu, true, false);
803            q.stretch = self.cur_val;
804            q.stretch_order = o;
805        }
806        if self.scan_keyword("minus") {
807            let o = self.scan_dimen(mu, true, false);
808            q.shrink = self.cur_val;
809            q.shrink_order = o;
810        }
811        self.cur_glue = q;
812        self.cur_val_level = level;
813    }
814
815    // ----- internal quantities -----------------------------------------------
816
817    /// Sets the scanned result.
818    fn scanned(&mut self, v: i32, level: ValLevel) {
819        self.cur_val = v;
820        self.cur_val_level = level;
821    }
822
823    fn scanned_glue(&mut self, g: Glue, level: ValLevel) {
824        self.cur_glue = g;
825        self.cur_val = g.width;
826        self.cur_val_level = level;
827    }
828
829    /// Fetches an internal quantity (`scan_something_internal`, §413).
830    pub fn scan_something_internal(&mut self, level: ValLevel, negative: bool) {
831        let m = self.cur_chr;
832        match self.cur_cmd {
833            Cmd::DefCharCode => {
834                self.scan_char_num();
835                let c = self.cur_val as u32;
836                use pg::def_char_code as dc;
837                let v = match m {
838                    dc::CATCODE => i64::from(self.eqtb.catcode(c)),
839                    dc::LCCODE => self.eqtb.lc.get(c),
840                    dc::UCCODE => self.eqtb.uc.get(c),
841                    dc::SFCODE => self.eqtb.sf.get(c),
842                    _ => i64::from(crate::codes::math_code_report(self.eqtb.math.get(c))),
843                };
844                self.scanned(v as i32, ValLevel::Int);
845            }
846            Cmd::DefDelCode | Cmd::ExtdefDelCode => {
847                self.scan_char_num();
848                let v = self.eqtb.del.get(self.cur_val as u32);
849                self.scanned(crate::codes::delcode_report(v, self.cur_cmd), ValLevel::Int);
850            }
851            Cmd::ExtdefMathCode => {
852                self.scan_char_num();
853                let v = self.eqtb.math.get(self.cur_val as u32);
854                self.scanned(crate::codes::math_code_report(v), ValLevel::Int);
855            }
856            Cmd::ToksRegister | Cmd::AssignToks | Cmd::DefFamily | Cmd::SetFont | Cmd::DefFont => {
857                if level != ValLevel::Tok {
858                    self.missing_number();
859                    self.scanned(0, ValLevel::Dimen);
860                } else if self.cur_cmd <= Cmd::AssignToks {
861                    let list = if self.cur_cmd == Cmd::ToksRegister {
862                        if m == 0 {
863                            self.scan_register_num();
864                            self.eqtb.toks[self.cur_val as usize].val.clone()
865                        } else {
866                            self.eqtb.toks[(m - REG_OFFSET) as usize].val.clone()
867                        }
868                    } else if m >= REG_OFFSET {
869                        self.eqtb.toks[(m - REG_OFFSET) as usize].val.clone()
870                    } else {
871                        self.eqtb.toks_param(m).clone()
872                    };
873                    self.cur_toks = Some(list);
874                    self.cur_val_level = ValLevel::Tok;
875                } else {
876                    self.back_input();
877                    self.scan_font_ident();
878                    self.cur_val_level = ValLevel::Ident;
879                }
880            }
881            Cmd::AssignInt => {
882                let v = if m >= REG_OFFSET {
883                    self.eqtb.count[(m - REG_OFFSET) as usize].val
884                } else {
885                    self.eqtb.int(m)
886                };
887                self.scanned(v, ValLevel::Int);
888            }
889            Cmd::AssignAttr => {
890                let v = self.eqtb.attr[(m - REG_OFFSET.min(m)) as usize].val;
891                self.scanned(v, ValLevel::Int);
892            }
893            Cmd::AssignDir | Cmd::AssignDirection => {
894                let v = self.eqtb.int(pg::assign_int::COUNT.min(m + DIR_PAR_BASE));
895                let _ = v;
896                self.scanned(self.dir_param(m), ValLevel::Int);
897            }
898            Cmd::AssignDimen => {
899                let v = if m >= REG_OFFSET {
900                    self.eqtb.dimen[(m - REG_OFFSET) as usize].val
901                } else {
902                    self.eqtb.dimen(m)
903                };
904                self.scanned(v, ValLevel::Dimen);
905            }
906            Cmd::AssignGlue => {
907                let g = if m >= REG_OFFSET {
908                    self.eqtb.skip[(m - REG_OFFSET) as usize].val
909                } else {
910                    self.eqtb.glue(m)
911                };
912                self.scanned_glue(g, ValLevel::Glue);
913            }
914            Cmd::AssignMuGlue => {
915                let g = if m >= REG_OFFSET {
916                    self.eqtb.muskip[(m - REG_OFFSET) as usize].val
917                } else {
918                    self.eqtb.glue(m + crate::eqtb::MU_GLUE_OFFSET)
919                };
920                self.scanned_glue(g, ValLevel::Mu);
921            }
922            Cmd::SetAux => {
923                let v = self.fetch_aux(m);
924                if let Some((v, l)) = v {
925                    self.scanned(v, l);
926                } else {
927                    self.scanned(
928                        0,
929                        if m == SET_AUX_PREV_DEPTH {
930                            ValLevel::Dimen
931                        } else {
932                            ValLevel::Int
933                        },
934                    );
935                }
936            }
937            Cmd::SetPrevGraf => {
938                let v = self.fetch_prev_graf();
939                self.scanned(v, ValLevel::Int);
940            }
941            Cmd::SetPageInt => {
942                let v = match m {
943                    0 => self.dead_cycles,
944                    1 => self.insert_penalties,
945                    _ => self.err.interaction as i32,
946                };
947                self.scanned(v, ValLevel::Int);
948            }
949            Cmd::SetPageDimen => {
950                let v = self.page_so_far_value(m);
951                self.scanned(v, ValLevel::Dimen);
952            }
953            Cmd::SetTexShape => {
954                let v = self
955                    .eqtb
956                    .par_shape
957                    .val
958                    .as_ref()
959                    .map_or(0, |s| (s.len() / 2) as i32);
960                self.scanned(v, ValLevel::Int);
961            }
962            Cmd::SetEtexShape => {
963                self.scan_int();
964                let n = self.cur_val;
965                let v = match &self.eqtb.etex_shape[m as usize].val {
966                    None => 0,
967                    Some(s) if n < 0 => 0,
968                    Some(s) => {
969                        let len = s.len() as i32;
970                        if n == 0 {
971                            len
972                        } else {
973                            s[(n.min(len) - 1) as usize]
974                        }
975                    }
976                };
977                self.scanned(v, ValLevel::Int);
978            }
979            Cmd::SetBoxDimen => {
980                self.scan_register_num();
981                let p = self.eqtb.boxes[self.cur_val as usize].val;
982                let v = if p.is_null() {
983                    0
984                } else {
985                    let b = self.nodes.box_data(p);
986                    match (m, b) {
987                        (2, Some(b)) => b.width,
988                        (3, Some(b)) => b.depth,
989                        (4, Some(b)) => b.height,
990                        _ => 0,
991                    }
992                };
993                self.scanned(v, ValLevel::Dimen);
994            }
995            Cmd::CharGiven | Cmd::XmathGiven => {
996                self.scanned(self.cur_chr as i32, ValLevel::Int);
997            }
998            Cmd::MathGiven => {
999                self.scanned(crate::codes::math_given_report(self.cur_chr), ValLevel::Int);
1000            }
1001            Cmd::AssignFontDimen => {
1002                let v = self.find_font_dimen(false);
1003                self.scanned(v, ValLevel::Dimen);
1004            }
1005            Cmd::AssignFontInt => {
1006                self.scan_font_ident();
1007                let f = self.cur_val as u32;
1008                let v = self.font_int_param(f, m);
1009                self.scanned(v, ValLevel::Int);
1010            }
1011            Cmd::Register => {
1012                self.scan_register_num();
1013                let n = self.cur_val as usize;
1014                match m {
1015                    reg::COUNT => self.scanned(self.eqtb.count[n].val, ValLevel::Int),
1016                    reg::ATTRIBUTE => self.scanned(self.eqtb.attr[n].val, ValLevel::Int),
1017                    reg::DIMEN => self.scanned(self.eqtb.dimen[n].val, ValLevel::Dimen),
1018                    reg::SKIP => {
1019                        let g = self.eqtb.skip[n].val;
1020                        self.scanned_glue(g, ValLevel::Glue);
1021                    }
1022                    _ => {
1023                        let g = self.eqtb.muskip[n].val;
1024                        self.scanned_glue(g, ValLevel::Mu);
1025                    }
1026                }
1027            }
1028            Cmd::SetMathParam => {
1029                // After a missing style LuaTeX reads an unset parameter.
1030                let found = self.scan_math_style();
1031                let style = self.cur_val as u32;
1032                let v = self
1033                    .eqtb
1034                    .math_param
1035                    .get(&(m * 8 + style))
1036                    .filter(|_| found)
1037                    .map_or(crate::codes::UNDEFINED_MATH_PARAM, |s| s.val);
1038                if crate::codes::math_param_is_glue(m) {
1039                    // An unset spacing shows TeX's default for the pair.
1040                    let g = if found && v == crate::codes::UNDEFINED_MATH_PARAM {
1041                        let (l, r) = ((m - 49) / 8, (m - 49) % 8);
1042                        self.math_spacing(l, r, style)
1043                            .map_or(crate::glue::Glue::ZERO, |(g, _)| g)
1044                    } else {
1045                        self.math_param_glue(v)
1046                    };
1047                    self.scanned_glue(g, ValLevel::Mu);
1048                } else if m == crate::mathparams::mp::RADICAL_DEGREE_RAISE {
1049                    // A percentage.
1050                    self.scanned(v, ValLevel::Int);
1051                } else {
1052                    self.scanned(v, ValLevel::Dimen);
1053                }
1054            }
1055            Cmd::LastItem => self.fetch_last_item(m, level),
1056            _ => {
1057                self.print_err("You can't use `");
1058                self.print_cmd_chr(self.cur_cmd, self.cur_chr);
1059                self.printer.print_str("' after ");
1060                self.print_esc_str("the");
1061                self.help(&["I'm forgetting what you said and using zero instead."]);
1062                self.error();
1063                if level != ValLevel::Tok {
1064                    self.scanned(0, ValLevel::Dimen);
1065                } else {
1066                    self.scanned(0, ValLevel::Int);
1067                }
1068            }
1069        }
1070        // Convert to a lower level.
1071        while self.cur_val_level > level {
1072            if self.cur_val_level == ValLevel::Glue {
1073                self.cur_val = self.cur_glue.width;
1074            } else if self.cur_val_level == ValLevel::Mu {
1075                self.mu_error();
1076            }
1077            self.cur_val_level = match self.cur_val_level {
1078                ValLevel::Tok => ValLevel::Ident,
1079                ValLevel::Ident => ValLevel::Mu,
1080                ValLevel::Mu => ValLevel::Glue,
1081                ValLevel::Glue => ValLevel::Dimen,
1082                _ => ValLevel::Int,
1083            };
1084            if self.cur_val_level == ValLevel::Glue || self.cur_val_level == ValLevel::Mu {
1085                // Coming from Ident/Tok (not possible in practice).
1086            }
1087        }
1088        if negative {
1089            if self.cur_val_level >= ValLevel::Glue {
1090                let g = &mut self.cur_glue;
1091                g.width = -g.width;
1092                g.stretch = -g.stretch;
1093                g.shrink = -g.shrink;
1094                self.cur_val = g.width;
1095            } else {
1096                self.cur_val = self.cur_val.wrapping_neg();
1097            }
1098        }
1099    }
1100
1101    /// Fetches a `last_item` quantity.
1102    fn fetch_last_item(&mut self, m: u32, level: ValLevel) {
1103        use last_item as li;
1104        match m {
1105            li::LAST_PENALTY | li::LAST_KERN | li::LAST_SKIP | li::LAST_NODE_TYPE => {
1106                let (v, g, l) = self.fetch_last_node_item(m);
1107                if l >= ValLevel::Glue {
1108                    self.scanned_glue(g, l);
1109                } else {
1110                    self.scanned(v, l);
1111                }
1112            }
1113            li::INPUT_LINE_NO => self.scanned(self.input.line(), ValLevel::Int),
1114            li::BADNESS => self.scanned(self.last_badness, ValLevel::Int),
1115            li::LUATEX_VERSION => self.scanned(LUATEX_VERSION, ValLevel::Int),
1116            li::ETEX_VERSION => self.scanned(ETEX_VERSION, ValLevel::Int),
1117            li::ETEX_MINOR_VERSION => self.scanned(ETEX_MINOR_VERSION, ValLevel::Int),
1118            li::CURRENT_GROUP_LEVEL => {
1119                self.scanned(i32::from(self.save.cur_level) - 1, ValLevel::Int)
1120            }
1121            li::CURRENT_GROUP_TYPE => self.scanned(self.save.cur_group as i32, ValLevel::Int),
1122            li::CURRENT_IF_LEVEL => self.scanned(self.cond.stack.len() as i32, ValLevel::Int),
1123            li::CURRENT_IF_TYPE => {
1124                let v = if self.cond.stack.is_empty() {
1125                    0
1126                } else if self.cond.cur_if < crate::cond::UNLESS_CODE {
1127                    self.cond.cur_if as i32 + 1
1128                } else {
1129                    -((self.cond.cur_if - crate::cond::UNLESS_CODE) as i32 + 1)
1130                };
1131                self.scanned(v, ValLevel::Int);
1132            }
1133            li::CURRENT_IF_BRANCH => {
1134                let v = match self.cond.if_limit {
1135                    crate::cond::OR_CODE | crate::cond::ELSE_CODE => 1,
1136                    crate::cond::FI_CODE => -1,
1137                    _ => 0,
1138                };
1139                self.scanned(v, ValLevel::Int);
1140            }
1141            li::RANDOM_SEED => self.scanned(self.random_seed, ValLevel::Int),
1142            li::LAST_X_POS => self.scanned(self.last_x_pos, ValLevel::Int),
1143            li::LAST_Y_POS => self.scanned(self.last_y_pos, ValLevel::Int),
1144            li::LAST_SAVED_BOX_RESOURCE_INDEX
1145            | li::LAST_SAVED_IMAGE_RESOURCE_INDEX
1146            | li::LAST_SAVED_IMAGE_RESOURCE_PAGES => {
1147                self.scanned(self.last_saved_resource(m), ValLevel::Int)
1148            }
1149            li::GLUE_STRETCH_ORDER
1150            | li::GLUE_SHRINK_ORDER
1151            | li::ETEX_GLUE_STRETCH_ORDER
1152            | li::ETEX_GLUE_SHRINK_ORDER => {
1153                self.scan_normal_glue();
1154                let g = self.cur_glue;
1155                let o = if m == li::GLUE_STRETCH_ORDER || m == li::ETEX_GLUE_STRETCH_ORDER {
1156                    g.stretch_order
1157                } else {
1158                    g.shrink_order
1159                };
1160                // e-TeX reports fil=1…filll=3, LuaTeX's own variant counts
1161                // the extra `fi` order.
1162                let v = if m == li::ETEX_GLUE_STRETCH_ORDER || m == li::ETEX_GLUE_SHRINK_ORDER {
1163                    i32::from(o)
1164                } else {
1165                    i32::from(o.saturating_sub(1)).max(0)
1166                };
1167                self.scanned(v, ValLevel::Int);
1168            }
1169            li::FONT_CHAR_WD | li::FONT_CHAR_HT | li::FONT_CHAR_DP | li::FONT_CHAR_IC => {
1170                self.scan_font_ident();
1171                let f = self.cur_val as u32;
1172                self.scan_char_num();
1173                let v = self.font_char_dimen(f, self.cur_val as u32, m - li::FONT_CHAR_WD);
1174                self.scanned(v, ValLevel::Dimen);
1175            }
1176            li::PAR_SHAPE_LENGTH | li::PAR_SHAPE_INDENT | li::PAR_SHAPE_DIMEN => {
1177                let q = m - li::PAR_SHAPE_LENGTH;
1178                self.scan_int();
1179                let v = match &self.eqtb.par_shape.val {
1180                    None => 0,
1181                    Some(s) if self.cur_val <= 0 => {
1182                        let _ = s;
1183                        0
1184                    }
1185                    Some(s) => {
1186                        let n = (s.len() / 2) as i32;
1187                        let mut cv = self.cur_val;
1188                        let mut q = q;
1189                        if q == 2 {
1190                            q = (cv % 2) as u32;
1191                            cv = (cv + q as i32) / 2;
1192                        }
1193                        if cv > n {
1194                            cv = n;
1195                        }
1196                        // indent is at 2*(k-1), length at 2*(k-1)+1
1197                        let idx = 2 * (cv - 1) as usize + if q == 0 { 1 } else { 0 };
1198                        s[idx]
1199                    }
1200                };
1201                self.scanned(v, ValLevel::Dimen);
1202            }
1203            li::GLUE_STRETCH | li::GLUE_SHRINK => {
1204                self.scan_normal_glue();
1205                let g = self.cur_glue;
1206                self.scanned(
1207                    if m == li::GLUE_STRETCH {
1208                        g.stretch
1209                    } else {
1210                        g.shrink
1211                    },
1212                    ValLevel::Dimen,
1213                );
1214            }
1215            li::MU_TO_GLUE => {
1216                self.scan_mu_glue();
1217                let g = self.cur_glue;
1218                self.scanned_glue(g, ValLevel::Glue);
1219            }
1220            li::GLUE_TO_MU => {
1221                self.scan_normal_glue();
1222                let g = self.cur_glue;
1223                self.scanned_glue(g, ValLevel::Mu);
1224            }
1225            li::NUM_EXPR | li::DIM_EXPR | li::GLUE_EXPR | li::MU_EXPR => {
1226                let l = match m {
1227                    li::NUM_EXPR => ValLevel::Int,
1228                    li::DIM_EXPR => ValLevel::Dimen,
1229                    li::GLUE_EXPR => ValLevel::Glue,
1230                    _ => ValLevel::Mu,
1231                };
1232                self.scan_expr(l);
1233            }
1234            _ => {
1235                let _ = level;
1236                self.scanned(0, ValLevel::Int);
1237            }
1238        }
1239    }
1240
1241    /// Scans glue (`scan_normal_glue`).
1242    pub fn scan_normal_glue(&mut self) {
1243        self.scan_glue(ValLevel::Glue);
1244    }
1245
1246    /// Scans muglue (`scan_mu_glue`).
1247    pub fn scan_mu_glue(&mut self) {
1248        self.scan_glue(ValLevel::Mu);
1249    }
1250
1251    /// Scans the math style of a `\Umath…` parameter: a style command
1252    /// (`\displaystyle`, `\crampedtextstyle`, …), whose number (0–7) is
1253    /// left in `cur_val`. Anything else is put back and reported, and
1254    /// `\displaystyle` is used (as LuaTeX 1.22 does; numbers are not
1255    /// accepted). Returns whether a style was found.
1256    pub fn scan_math_style(&mut self) -> bool {
1257        self.get_nonblank_noncall();
1258        if self.cur_cmd == Cmd::MathStyle {
1259            self.cur_val = self.cur_chr as i32;
1260            return true;
1261        }
1262        self.print_err("Missing math style, treated as ");
1263        self.print_esc_str("displaystyle");
1264        self.help(&["A style should have been here; I inserted `\\displaystyle'."]);
1265        self.back_error();
1266        self.cur_val = 0;
1267        false
1268    }
1269
1270    // ----- e-TeX expressions -------------------------------------------------
1271
1272    /// Evaluates an e-TeX expression (`scan_expr`). Sets `cur_val` (or
1273    /// `cur_glue`) and `cur_val_level`.
1274    pub fn scan_expr(&mut self, level: ValLevel) {
1275        let mut arith_err = false;
1276        let r = self.scan_expr_level(level, &mut arith_err, 0);
1277        if arith_err {
1278            self.print_err("Arithmetic overflow");
1279            self.help(&[
1280                "I can't evaluate this expression,",
1281                "since the result is out of range.",
1282            ]);
1283            self.error();
1284            match level {
1285                ValLevel::Glue | ValLevel::Mu => {
1286                    self.cur_glue = Glue::ZERO;
1287                    self.cur_val = 0;
1288                }
1289                _ => self.cur_val = 0,
1290            }
1291        } else {
1292            match level {
1293                ValLevel::Glue | ValLevel::Mu => {
1294                    self.cur_glue = r;
1295                    self.cur_val = r.width;
1296                }
1297                _ => self.cur_val = r.width,
1298            }
1299        }
1300        self.cur_val_level = level;
1301    }
1302
1303    /// Recursive-descent evaluator for e-TeX expressions. Values are
1304    /// carried as [`Glue`]; integers and dimensions use only `width`.
1305    fn scan_expr_level(&mut self, l: ValLevel, err: &mut bool, depth: u32) -> Glue {
1306        // expr ::= term { (+|-) term } ; term ::= factor { (*|/) number }
1307        let max = |l: ValLevel| {
1308            if l == ValLevel::Int {
1309                arith::INFINITY
1310            } else {
1311                MAX_DIMEN
1312            }
1313        };
1314        let mut e = Glue::ZERO;
1315        let mut first = true;
1316        let mut negative_next = false;
1317        loop {
1318            // A term.
1319            let mut t = self.scan_expr_factor(l, err, depth);
1320            // Multiplicative operators.
1321            loop {
1322                self.get_nonblank_noncall();
1323                if self.cur_tok == Token::other('*' as u32) {
1324                    self.scan_int_factor(err, depth);
1325                    let f = self.cur_val;
1326                    // Look for a following '/' to combine into scaling.
1327                    self.get_nonblank_noncall();
1328                    if self.cur_tok == Token::other('/' as u32) {
1329                        self.scan_int_factor(err, depth);
1330                        let d = self.cur_val;
1331                        t = self.expr_scale(t, f, d, l, err);
1332                    } else {
1333                        self.back_input();
1334                        t = self.expr_mult(t, f, l, err);
1335                    }
1336                } else if self.cur_tok == Token::other('/' as u32) {
1337                    self.scan_int_factor(err, depth);
1338                    let d = self.cur_val;
1339                    t = self.expr_div(t, d, l, err);
1340                } else {
1341                    self.back_input();
1342                    break;
1343                }
1344            }
1345            if first {
1346                e = t;
1347                first = false;
1348            } else {
1349                e = self.expr_add(e, t, l, negative_next, max(l), err);
1350            }
1351            self.get_nonblank_noncall();
1352            if self.cur_tok == Token::other('+' as u32) {
1353                negative_next = false;
1354            } else if self.cur_tok == Token::other('-' as u32) {
1355                negative_next = true;
1356            } else {
1357                // End of expression: an optional \relax is absorbed.
1358                if self.cur_cmd != Cmd::Relax {
1359                    self.back_input();
1360                }
1361                return e;
1362            }
1363        }
1364    }
1365
1366    fn scan_expr_factor(&mut self, l: ValLevel, err: &mut bool, depth: u32) -> Glue {
1367        self.get_nonblank_noncall();
1368        if self.cur_tok == Token::other('(' as u32) {
1369            let v = self.scan_expr_paren(l, err, depth);
1370            return v;
1371        }
1372        self.back_input();
1373        match l {
1374            ValLevel::Int => {
1375                self.scan_int();
1376                Glue::fixed(self.cur_val)
1377            }
1378            ValLevel::Dimen => {
1379                self.scan_normal_dimen();
1380                Glue::fixed(self.cur_val)
1381            }
1382            ValLevel::Glue => {
1383                self.scan_normal_glue();
1384                self.cur_glue
1385            }
1386            _ => {
1387                self.scan_mu_glue();
1388                self.cur_glue
1389            }
1390        }
1391    }
1392
1393    fn scan_expr_paren(&mut self, l: ValLevel, err: &mut bool, depth: u32) -> Glue {
1394        let v = self.scan_expr_level(l, err, depth + 1);
1395        // scan_expr_level stops at a token it backed up; expect ')'.
1396        self.get_nonblank_noncall();
1397        if self.cur_tok != Token::other(')' as u32) {
1398            self.print_err("Missing ) inserted for expression");
1399            self.help(&["I was expecting to see `+', `-', `*', `/', or `)'. Didn't."]);
1400            self.back_error();
1401        }
1402        v
1403    }
1404
1405    fn scan_int_factor(&mut self, err: &mut bool, depth: u32) {
1406        self.get_nonblank_noncall();
1407        if self.cur_tok == Token::other('(' as u32) {
1408            let v = self.scan_expr_paren(ValLevel::Int, err, depth);
1409            self.cur_val = v.width;
1410        } else {
1411            self.back_input();
1412            self.scan_int();
1413        }
1414    }
1415
1416    fn expr_add(
1417        &mut self,
1418        a: Glue,
1419        b: Glue,
1420        l: ValLevel,
1421        neg: bool,
1422        max: i32,
1423        err: &mut bool,
1424    ) -> Glue {
1425        let mut add = |x: i32, y: i32| match arith::add_or_sub(x, y, max, neg) {
1426            Ok(v) => v,
1427            Err(_) => {
1428                *err = true;
1429                0
1430            }
1431        };
1432        if l <= ValLevel::Dimen {
1433            return Glue::fixed(add(a.width, b.width));
1434        }
1435        let mut e = a;
1436        e.width = add(a.width, b.width);
1437        if e.stretch_order == b.stretch_order {
1438            e.stretch = add(e.stretch, b.stretch);
1439        } else if e.stretch_order < b.stretch_order && b.stretch != 0 {
1440            e.stretch = if neg { -b.stretch } else { b.stretch };
1441            e.stretch_order = b.stretch_order;
1442        }
1443        if e.shrink_order == b.shrink_order {
1444            e.shrink = add(e.shrink, b.shrink);
1445        } else if e.shrink_order < b.shrink_order && b.shrink != 0 {
1446            e.shrink = if neg { -b.shrink } else { b.shrink };
1447            e.shrink_order = b.shrink_order;
1448        }
1449        normalize_glue(e)
1450    }
1451
1452    fn expr_mult(&mut self, t: Glue, f: i32, l: ValLevel, err: &mut bool) -> Glue {
1453        let mut m = |x: i32| {
1454            let r = if l == ValLevel::Int {
1455                arith::mult_integers(x, f)
1456            } else {
1457                arith::nx_plus_y(x, f, 0)
1458            };
1459            r.unwrap_or_else(|_| {
1460                *err = true;
1461                0
1462            })
1463        };
1464        if l <= ValLevel::Dimen {
1465            return Glue::fixed(m(t.width));
1466        }
1467        let mut g = t;
1468        g.width = m(g.width);
1469        g.stretch = m(g.stretch);
1470        g.shrink = m(g.shrink);
1471        normalize_glue(g)
1472    }
1473
1474    fn expr_div(&mut self, t: Glue, f: i32, l: ValLevel, err: &mut bool) -> Glue {
1475        let mut d = |x: i32| {
1476            arith::quotient(x, f).unwrap_or_else(|_| {
1477                *err = true;
1478                0
1479            })
1480        };
1481        if l <= ValLevel::Dimen {
1482            return Glue::fixed(d(t.width));
1483        }
1484        let mut g = t;
1485        g.width = d(g.width);
1486        g.stretch = d(g.stretch);
1487        g.shrink = d(g.shrink);
1488        normalize_glue(g)
1489    }
1490
1491    fn expr_scale(&mut self, t: Glue, n: i32, f: i32, l: ValLevel, err: &mut bool) -> Glue {
1492        let max = if l == ValLevel::Int {
1493            arith::INFINITY
1494        } else {
1495            MAX_DIMEN
1496        };
1497        let mut s = |x: i32| {
1498            arith::fract(x, n, f, max).unwrap_or_else(|_| {
1499                *err = true;
1500                0
1501            })
1502        };
1503        if l <= ValLevel::Dimen {
1504            return Glue::fixed(s(t.width));
1505        }
1506        let mut g = t;
1507        g.width = s(g.width);
1508        g.stretch = s(g.stretch);
1509        g.shrink = s(g.shrink);
1510        normalize_glue(g)
1511    }
1512
1513    // ----- token lists -------------------------------------------------------
1514
1515    /// Part of [`scan_toks`](Self::scan_toks): appends to `out` the tokens
1516    /// of the current token list that `scan_toks` would copy unchanged
1517    /// (characters other than `#`, `&` and parameters; braces, counted in
1518    /// `unbalance`; control sequences that are not expandable, or protected
1519    /// macros, when expanding; any but outer ones when not), and stops at
1520    /// the first other token, which the general path handles. Returns the
1521    /// closing brace if the list ended the body.
1522    fn copy_plain_tokens(
1523        &mut self,
1524        out: &mut Vec<Token>,
1525        unbalance: &mut i32,
1526        xpand: bool,
1527    ) -> Option<Token> {
1528        let Some(crate::input::Level::Tokens(t)) = self.input.stack.last_mut() else {
1529            return None;
1530        };
1531        while let Some(&tok) = t.list.get(t.loc) {
1532            if tok.is_cs() {
1533                let m = self.eqtb.meaning(tok.cs_id());
1534                let plain = if m.cmd <= Cmd::MAX_COMMAND {
1535                    !(Cmd::TabMark..=Cmd::CarRet).contains(&m.cmd)
1536                } else if m.cmd.is_outer_or_special() {
1537                    false
1538                } else if xpand {
1539                    m.cmd >= Cmd::Call && m.mac.as_ref().is_some_and(|m| m.protected)
1540                } else {
1541                    true
1542                };
1543                if !plain {
1544                    return None;
1545                }
1546            } else {
1547                match tok.cmd() {
1548                    Cmd::LeftBrace => {
1549                        self.input.align_state += 1;
1550                        *unbalance += 1;
1551                    }
1552                    Cmd::RightBrace => {
1553                        self.input.align_state -= 1;
1554                        *unbalance -= 1;
1555                        if *unbalance == 0 {
1556                            t.loc += 1;
1557                            return Some(tok);
1558                        }
1559                    }
1560                    Cmd::TabMark | Cmd::CarRet | Cmd::MacParam => return None,
1561                    _ => {}
1562                }
1563            }
1564            out.push(tok);
1565            t.loc += 1;
1566        }
1567        None
1568    }
1569
1570    /// Scans a balanced text (`scan_toks`). For macro definitions
1571    /// (`macro_def`), the parameter text is scanned first and the result
1572    /// contains match tokens, `END_MATCH` and the body. With `xpand`, the
1573    /// text is expanded as in `\edef`. Returns the list and whether the
1574    /// definition text used `#` correctly.
1575    pub fn scan_toks(&mut self, macro_def: bool, xpand: bool) -> Vec<Token> {
1576        if macro_def {
1577            self.input.scanner_status = ScannerStatus::Defining;
1578        } else {
1579            self.input.scanner_status = ScannerStatus::Absorbing;
1580        }
1581        self.input.warning_index = self.cur_cs;
1582        let mut out: Vec<Token> = Vec::new();
1583        let mut unbalance: i32;
1584        let mut t: u32 = 0x30; // zero_token: next parameter number
1585        let mut hash_brace = Token(0);
1586        'found: {
1587            if macro_def {
1588                // Scan and build the parameter part; `#{` ends it early
1589                // (§476) and the body follows.
1590                'params: loop {
1591                    self.get_token();
1592                    if self.cur_tok.0 < Token::RIGHT_BRACE.0 & !0x1F_FFFF | 0x1F_FFFF
1593                        && !self.cur_tok.is_cs()
1594                        && self.cur_cmd <= Cmd::RightBrace
1595                    {
1596                        break; // left or right brace
1597                    }
1598                    if self.cur_cmd == Cmd::MacParam && !self.cur_tok.is_cs() {
1599                        // A parameter, or #{.
1600                        let s = Token::match_token(self.cur_chr);
1601                        self.get_token();
1602                        if self.cur_cmd == Cmd::LeftBrace && !self.cur_tok.is_cs() {
1603                            hash_brace = self.cur_tok;
1604                            out.push(self.cur_tok);
1605                            break 'params;
1606                        }
1607                        if t == 0x39 {
1608                            self.print_err("You already have nine parameters");
1609                            self.help(&["I'm going to ignore the # sign you just used."]);
1610                            self.error();
1611                        } else {
1612                            t += 1;
1613                            if self.cur_tok != Token::other(t) {
1614                                self.print_err("Parameters must be numbered consecutively");
1615                                self.help(&[
1616                                    "I've inserted the digit you should have used after the #.",
1617                                    "Type `1' to delete what you did use.",
1618                                ]);
1619                                self.back_error();
1620                            }
1621                            self.cur_tok = s;
1622                        }
1623                    }
1624                    out.push(self.cur_tok);
1625                }
1626                out.push(Token::END_MATCH);
1627                if hash_brace.0 == 0 && self.cur_cmd == Cmd::RightBrace {
1628                    self.print_err("Missing { inserted");
1629                    self.input.align_state += 1;
1630                    self.help(&[
1631                        "Where was the left brace? You said something like `\\def\\a}',",
1632                        "which I'm going to interpret as `\\def\\a{}'.",
1633                    ]);
1634                    self.error();
1635                    break 'found;
1636                }
1637            } else {
1638                self.scan_left_brace();
1639            }
1640            // Scan and build the body.
1641            unbalance = 1;
1642            loop {
1643                // Fast path: copy the plain tokens of the current token list
1644                // directly (most of the body of an \edef that re-expands a
1645                // long macro).
1646                if let Some(end_brace) = self.copy_plain_tokens(&mut out, &mut unbalance, xpand) {
1647                    self.cur_tok = end_brace;
1648                    self.cur_cmd = Cmd::RightBrace;
1649                    self.cur_chr = end_brace.chr();
1650                    self.cur_cs = NO_CS;
1651                    break 'found;
1652                }
1653                if xpand {
1654                    // Expand the next part of the input.
1655                    loop {
1656                        self.get_next();
1657                        if self.cur_cmd >= Cmd::Call
1658                            && self
1659                                .eqtb
1660                                .meaning(self.cur_cs)
1661                                .mac
1662                                .as_ref()
1663                                .is_some_and(|m| m.protected)
1664                        {
1665                            self.cur_cmd = Cmd::Relax;
1666                            self.cur_chr = crate::input::NO_EXPAND_FLAG;
1667                            break;
1668                        }
1669                        if self.cur_cmd <= Cmd::MAX_COMMAND {
1670                            break;
1671                        }
1672                        if self.cur_cmd != Cmd::The {
1673                            self.expand();
1674                        } else {
1675                            let list = self.the_toks();
1676                            out.extend_from_slice(&list);
1677                        }
1678                    }
1679                    self.x_token();
1680                } else {
1681                    self.get_token();
1682                }
1683                if self.cur_tok.is_cs()
1684                    || self.cur_cmd > Cmd::MacParam
1685                    || self.cur_cmd < Cmd::LeftBrace
1686                {
1687                    // not a brace or parameter
1688                } else if self.cur_cmd < Cmd::RightBrace {
1689                    unbalance += 1;
1690                } else if self.cur_cmd == Cmd::RightBrace {
1691                    unbalance -= 1;
1692                    if unbalance == 0 {
1693                        break 'found;
1694                    }
1695                } else if self.cur_cmd == Cmd::MacParam && macro_def {
1696                    // Look for parameter number or ##.
1697                    let s = self.cur_tok;
1698                    if xpand {
1699                        self.get_x_token();
1700                    } else {
1701                        self.get_token();
1702                    }
1703                    if self.cur_cmd != Cmd::MacParam || self.cur_tok.is_cs() {
1704                        let c = self.cur_tok;
1705                        if c.is_cs() || c.cmd() != Cmd::OtherChar || c.chr() <= 0x30 || c.chr() > t
1706                        {
1707                            self.print_err("Illegal parameter number in definition of ");
1708                            self.sprint_cs(self.input.warning_index);
1709                            self.help(&[
1710                                "You meant to type ## instead of #, right?",
1711                                "Or maybe a } was forgotten somewhere earlier, and things",
1712                                "are all screwed up? I'm going to assume that you meant ##.",
1713                            ]);
1714                            self.back_error();
1715                            self.cur_tok = s;
1716                        } else {
1717                            self.cur_tok = Token::out_param(c.chr() - 0x30);
1718                        }
1719                    }
1720                }
1721                out.push(self.cur_tok);
1722            }
1723        }
1724        self.input.scanner_status = ScannerStatus::Normal;
1725        if hash_brace.0 != 0 {
1726            out.push(hash_brace);
1727        }
1728        out
1729    }
1730}
1731
1732/// Offset used to map direction parameter modifiers (unused placeholder).
1733const DIR_PAR_BASE: u32 = 0;
1734
1735/// `set_aux` modifier for `\prevdepth`.
1736pub const SET_AUX_PREV_DEPTH: u32 = 1;
1737/// `set_aux` modifier for `\spacefactor`.
1738pub const SET_AUX_SPACE_FACTOR: u32 = 134;
1739
1740/// Sets an infinite component's order to normal when the component is
1741/// zero (`normalize_glue`).
1742pub fn normalize_glue(mut g: Glue) -> Glue {
1743    if g.stretch == 0 {
1744        g.stretch_order = glue::NORMAL;
1745    }
1746    if g.shrink == 0 {
1747        g.shrink_order = glue::NORMAL;
1748    }
1749    g
1750}
1751
1752/// Avoids an unused-import warning when `Loc` is not needed here.
1753#[allow(dead_code)]
1754fn _loc(_: Loc) {}