1use std::rc::Rc;
5
6use retex_arith as arith;
7
8use crate::Engine;
9use crate::cmd::Cmd;
10use crate::eqtb::{CodeKind, Loc, MU_GLUE_OFFSET, Meaning, Val};
11use crate::expand::RELAX_CHR;
12use crate::glue::Glue;
13use crate::hash::{CsId, NO_CS};
14use crate::primitives_gen as pg;
15use crate::scan::{REG_OFFSET, ValLevel, reg};
16use crate::token::{Macro, Token, TokenList};
17
18pub mod prefix {
20 pub const LONG: u32 = 1;
22 pub const OUTER: u32 = 2;
24 pub const GLOBAL: u32 = 4;
26 pub const PROTECTED: u32 = 8;
28}
29
30#[allow(missing_docs)]
32pub mod shorthand {
33 pub const CHAR_DEF: u32 = 0;
34 pub const MATH_CHAR_DEF: u32 = 1;
35 pub const UMATH_CHAR_DEF: u32 = 2;
36 pub const COUNT_DEF: u32 = 3;
37 pub const ATTRIBUTE_DEF: u32 = 4;
38 pub const DIMEN_DEF: u32 = 5;
39 pub const SKIP_DEF: u32 = 6;
40 pub const MU_SKIP_DEF: u32 = 7;
41 pub const TOKS_DEF: u32 = 8;
42 pub const UMATH_CHAR_NUM_DEF: u32 = 9;
43}
44
45impl Engine {
46 pub fn get_r_token(&mut self) {
48 loop {
49 loop {
50 self.get_token();
51 if self.cur_tok != Token::SPACE {
52 break;
53 }
54 }
55 let frozen = self.cur_cs != NO_CS
56 && matches!(self.hash.kind(self.cur_cs), crate::hash::CsKind::Frozen);
57 if self.cur_cs == NO_CS || frozen {
58 self.print_err("Missing control sequence inserted");
59 self.help(&[
60 "Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",
61 "I've inserted an inaccessible control sequence so that your",
62 "definition will be completed without mixing me up too badly.",
63 "You can recover graciously from this error, if you're",
64 "careful; see exercise 27.2 in The TeXbook.",
65 ]);
66 if self.cur_cs == NO_CS {
67 self.back_input();
68 }
69 self.cur_tok = Token::cs(self.cs.frozen_protection);
70 self.ins_error();
71 continue;
72 }
73 return;
74 }
75 }
76
77 fn define_cs(&mut self, global: bool, p: CsId, meaning: Meaning) {
79 self.define(global, Loc::Cs(p), Val::Meaning(meaning));
80 }
81
82 pub fn prefixed_command(&mut self) {
84 if self.cur_cmd != Cmd::Prefix && self.eqtb.int(pg::assign_int::GLOBALDEFS) == 0 {
87 match self.cur_cmd {
88 Cmd::Let => self.let_command(false),
89 Cmd::DefCharCode => self.def_char_code(false),
90 Cmd::Register | Cmd::Advance | Cmd::Multiply | Cmd::Divide => {
91 self.do_register_command(false)
92 }
93 Cmd::AssignInt => self.assign_int_command(false),
94 _ => return self.prefixed_command_general(),
95 }
96 if self.after_token.0 != 0 {
97 self.cur_tok = self.after_token;
98 self.back_input();
99 self.after_token = Token(0);
100 }
101 return;
102 }
103 self.prefixed_command_general();
104 }
105
106 fn assign_int_command(&mut self, global: bool) {
108 let p = self.cur_chr;
109 self.scan_optional_equals();
110 self.scan_int();
111 let loc = if p >= REG_OFFSET {
112 Loc::Count(p - REG_OFFSET)
113 } else {
114 Loc::IntPar(p)
115 };
116 self.assign_int_param(global, loc, self.cur_val);
117 if (p == pg::assign_int::LOCALINTERLINEPENALTY || p == pg::assign_int::LOCALBROKENPENALTY)
120 && self.mode == crate::maincontrol::Mode::Horizontal
121 {
122 let lp = self.new_local_par();
123 self.tail_append(lp);
124 }
125 }
126
127 #[inline(never)]
129 fn prefixed_command_general(&mut self) {
130 let mut a: u32 = 0;
131 while self.cur_cmd == Cmd::Prefix {
132 if a & self.cur_chr == 0 {
133 a += self.cur_chr;
134 }
135 self.get_nonblank_nonrelax_noncall();
136 if self.cur_cmd <= Cmd::MAX_NON_PREFIXED {
137 self.print_err("You can't use a prefix with `");
138 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
139 self.printer.print_char('\'' as u32);
140 self.help(&[
141 "I'll pretend you didn't say \\long or \\outer or \\global or",
142 "\\protected.",
143 ]);
144 self.back_error();
145 return;
146 }
147 if self.eqtb.int(pg::assign_int::TRACINGCOMMANDS) > 2 {
148 self.show_cur_cmd_chr();
149 }
150 }
151 if self.cur_cmd != Cmd::Def
153 && self.cur_cmd != Cmd::DefLuaCall
154 && (a & (prefix::LONG | prefix::OUTER | prefix::PROTECTED)) != 0
155 && self.eqtb.int(pg::assign_int::SUPPRESSLONGERROR) <= 0
156 {
157 self.print_err("You can't use `");
158 self.print_esc_str("long");
159 self.printer.print_str("' or `");
160 self.print_esc_str("outer");
161 self.help(&["I'll pretend you didn't say \\long or \\outer or \\protected here."]);
162 self.printer.print_str("' or `");
163 self.print_esc_str("protected");
164 self.printer.print_str("' with `");
165 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
166 self.printer.print_char('\'' as u32);
167 self.error();
168 }
169 let global_defs = self.eqtb.int(pg::assign_int::GLOBALDEFS);
171 if global_defs != 0 {
172 if global_defs < 0 {
173 a &= !prefix::GLOBAL;
174 } else {
175 a |= prefix::GLOBAL;
176 }
177 }
178 let global = a & prefix::GLOBAL != 0;
179 match self.cur_cmd {
180 Cmd::SetFont => {
181 let f = self.cur_chr;
182 self.define(global, Loc::CurFont, Val::Int(i64::from(f)));
183 }
184 Cmd::Def => {
185 let mut a = a;
186 if self.cur_chr & 1 != 0 && !global && global_defs >= 0 {
187 a |= prefix::GLOBAL;
188 }
189 let e = self.cur_chr >= 2;
190 self.get_r_token();
191 let p = self.cur_cs;
192 self.def_ref_cs = p;
193 let toks = self.scan_toks(true, e);
194 self.def_ref_cs = NO_CS;
195 let cmd = Cmd::from_u8(Cmd::Call as u8 + (a & 3) as u8);
196 let mac = Macro::new(a & prefix::PROTECTED != 0, Rc::from(toks));
197 self.define_cs(
198 a & prefix::GLOBAL != 0,
199 p,
200 Meaning {
201 cmd,
202 chr: 0,
203 mac: Some(Rc::new(mac)),
204 },
205 );
206 }
207 Cmd::Let => self.let_command(global),
208 Cmd::ShorthandDef => self.shorthand_def(global),
209 Cmd::ReadToCs => {
210 let j = self.cur_chr;
211 self.scan_int();
212 let n = self.cur_val;
213 if !self.scan_keyword("to") {
214 self.print_err("Missing `to' inserted");
215 self.help(&[
216 "You should have said `\\read<number> to \\cs'.",
217 "I'm going to look for the \\cs now.",
218 ]);
219 self.error();
220 }
221 self.get_r_token();
222 let p = self.cur_cs;
223 let toks = self.read_toks(n, p, j);
224 self.define_cs(
225 global,
226 p,
227 Meaning {
228 cmd: Cmd::Call,
229 chr: 0,
230 mac: Some(Rc::new(Macro::new(false, Rc::from(toks)))),
231 },
232 );
233 }
234 Cmd::ToksRegister | Cmd::AssignToks => self.assign_toks(global),
235 Cmd::Combinetoks => self.combine_toks(),
236 Cmd::AssignInt => self.assign_int_command(global),
237 Cmd::AssignAttr => {
238 let p = self.cur_chr - REG_OFFSET.min(self.cur_chr);
239 self.scan_optional_equals();
240 self.scan_int();
241 self.define(global, Loc::Attr(p), Val::Int(i64::from(self.cur_val)));
242 }
243 Cmd::AssignDir | Cmd::AssignDirection => {
244 let p = self.cur_chr;
245 let by_name = self.cur_cmd == Cmd::AssignDir;
246 self.scan_optional_equals();
247 let d = if by_name {
248 self.scan_direction_spec()
249 } else {
250 self.scan_int();
251 self.cur_val
252 };
253 let prev = self.dir_param(p);
254 self.assign_dir_param(global, p, d);
255 if p == crate::primitives_gen::assign_dir::TEXTDIR {
256 self.text_dir_assigned(d, prev);
257 }
258 }
259 Cmd::AssignDimen => {
260 let p = self.cur_chr;
261 self.scan_optional_equals();
262 self.scan_normal_dimen();
263 let loc = if p >= REG_OFFSET {
264 Loc::Dimen(p - REG_OFFSET)
265 } else {
266 Loc::DimenPar(p)
267 };
268 self.define(global, loc, Val::Int(i64::from(self.cur_val)));
269 }
270 Cmd::AssignGlue | Cmd::AssignMuGlue => {
271 let p = self.cur_chr;
272 let mu = self.cur_cmd == Cmd::AssignMuGlue;
273 self.scan_optional_equals();
274 self.scan_glue(if mu { ValLevel::Mu } else { ValLevel::Glue });
275 let g = self.cur_glue;
276 let loc = match (mu, p >= REG_OFFSET) {
277 (false, true) => Loc::Skip(p - REG_OFFSET),
278 (true, true) => Loc::MuSkip(p - REG_OFFSET),
279 (false, false) => Loc::GluePar(p),
280 (true, false) => Loc::GluePar(p + MU_GLUE_OFFSET),
281 };
282 self.define(global, loc, Val::Glue(g));
283 }
284 Cmd::DefCharCode => self.def_char_code(global),
285 Cmd::DefDelCode => {
286 self.scan_char_num();
287 let c = self.cur_val as u32;
288 self.scan_optional_equals();
289 self.scan_int();
290 let v = self.cur_val;
291 if v > 0o77777777 {
292 self.invalid_code(v, 0o77777777, false);
293 }
294 let code = crate::codes::delcode_from_classic(self.cur_val);
295 self.define(global, Loc::Code(CodeKind::Del, c), Val::Int(code));
296 }
297 Cmd::ExtdefMathCode => {
298 let num = self.cur_chr != pg::extdef_math_code::UMATHCODE;
299 self.scan_char_num();
300 let c = self.cur_val as u32;
301 self.scan_optional_equals();
302 let code = if num {
303 self.scan_int();
304 i64::from(self.cur_val)
305 } else {
306 self.scan_umath_triple()
307 };
308 self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
309 }
310 Cmd::ExtdefDelCode => {
311 let num = self.cur_chr != pg::extdef_del_code::UDELCODE;
312 self.scan_char_num();
313 let c = self.cur_val as u32;
314 self.scan_optional_equals();
315 let code = if num {
316 self.scan_int();
317 i64::from(self.cur_val)
318 } else {
319 self.scan_math_family();
320 let fam = self.cur_val as i64;
321 self.scan_char_num();
322 (fam << 21) + i64::from(self.cur_val)
323 };
324 self.define(global, Loc::Code(CodeKind::Del, c), Val::Int(code));
325 }
326 Cmd::DefFamily => {
327 let size = self.cur_chr;
328 self.scan_math_family();
329 let fam = self.cur_val as u32;
330 self.scan_optional_equals();
331 self.scan_font_ident();
332 let f = self.cur_val;
333 self.define(
334 global,
335 Loc::MathFont(size * crate::eqtb::NUM_FAMILIES as u32 + fam),
336 Val::Int(i64::from(f)),
337 );
338 self.set_font_math_params(global, size, fam, f as u32);
339 }
340 Cmd::SetMathParam => {
341 let p = self.cur_chr;
342 let _ = self.scan_math_style();
343 let style = self.cur_val as u32;
344 self.scan_optional_equals();
345 let v = if crate::codes::math_param_is_glue(p) {
346 self.scan_glue(ValLevel::Mu);
347 self.store_math_param_glue(self.cur_glue)
348 } else if p == crate::mathparams::mp::RADICAL_DEGREE_RAISE {
349 self.scan_int();
350 self.cur_val
351 } else {
352 self.scan_normal_dimen();
353 self.cur_val
354 };
355 self.define(
356 global,
357 Loc::MathParam(p * 8 + style),
358 Val::Int(i64::from(v)),
359 );
360 }
361 Cmd::Register | Cmd::Advance | Cmd::Multiply | Cmd::Divide => {
362 self.do_register_command(global)
363 }
364 Cmd::SetBox => {
365 self.scan_register_num();
366 let n = self.cur_val as u32;
367 let ctx = if global {
368 crate::maincontrol::BoxContext::GlobalSetBox(n)
369 } else {
370 crate::maincontrol::BoxContext::SetBox(n)
371 };
372 self.scan_optional_equals();
373 if self.err.set_box_allowed {
374 self.scan_box(ctx);
375 } else {
376 self.print_err("Improper ");
377 self.print_esc_str("setbox");
378 self.help(&[
379 "Sorry, \\setbox is not allowed after \\halign in a display,",
380 "or between \\accent and an accented character.",
381 ]);
382 self.error();
383 }
384 }
385 Cmd::SetAux => self.alter_aux(),
386 Cmd::SetPrevGraf => self.alter_prev_graf(),
387 Cmd::SetPageDimen => self.alter_page_so_far(),
388 Cmd::SetPageInt => self.alter_integer(),
389 Cmd::SetBoxDimen => self.alter_box_dimen(),
390 Cmd::SetTexShape => {
391 self.scan_optional_equals();
392 self.scan_int();
393 let n = self.cur_val;
394 let shape = if n <= 0 {
395 None
396 } else {
397 let mut v = Vec::with_capacity(2 * n as usize);
398 for _ in 0..n {
399 self.scan_normal_dimen();
400 v.push(self.cur_val);
401 self.scan_normal_dimen();
402 v.push(self.cur_val);
403 }
404 Some(Rc::from(v))
405 };
406 self.define(global, Loc::ParShape, Val::Shape(shape));
407 }
408 Cmd::SetEtexShape => {
409 let q = self.cur_chr;
410 self.scan_optional_equals();
411 self.scan_int();
412 let n = self.cur_val;
413 let shape = if n <= 0 {
414 None
415 } else {
416 let mut v = Vec::with_capacity(n as usize);
417 for _ in 0..n {
418 self.scan_int();
419 v.push(self.cur_val);
420 }
421 Some(Rc::from(v))
422 };
423 self.define(global, Loc::EtexShape(q), Val::Shape(shape));
424 }
425 Cmd::HyphData => self.hyph_data(global),
426 Cmd::AssignFontDimen => {
427 self.find_font_dimen(true);
428 let loc = self.font_dimen_loc;
429 self.scan_optional_equals();
430 self.scan_normal_dimen();
431 self.set_font_dimen(loc, self.cur_val);
432 }
433 Cmd::AssignFontInt => {
434 let n = self.cur_chr;
435 self.scan_font_ident();
436 let f = self.cur_val as u32;
437 if (2..=5).contains(&n) {
438 self.scan_char_num();
440 let c = self.cur_val as u32;
441 self.scan_optional_equals();
442 self.scan_int();
443 self.set_font_char_code(f, n, c, self.cur_val);
444 } else {
445 self.scan_optional_equals();
446 self.scan_int();
447 self.set_font_int_param(f, n, self.cur_val);
448 }
449 }
450 Cmd::DefFont => self.new_font(global),
451 Cmd::LetterspaceFont => self.letterspace_font(global),
452 Cmd::CopyFont => self.copy_font(global),
453 Cmd::SetFontId => {
454 self.scan_int();
455 let f = self.cur_val;
456 if self.font_exists(f) {
457 self.define(global, Loc::CurFont, Val::Int(i64::from(f)));
458 }
459 }
460 Cmd::SetInteraction => self.new_interaction(),
461 Cmd::DefLuaCall => self.def_lua_call(global, a & prefix::PROTECTED != 0),
462 Cmd::AssignBoxDir | Cmd::AssignBoxDirection => {
463 self.scan_register_num();
464 let n = self.cur_val as u32;
465 self.scan_optional_equals();
466 let d = if self.cur_cmd == Cmd::AssignBoxDir {
467 self.scan_direction_spec()
468 } else {
469 self.scan_int();
470 self.cur_val
471 };
472 self.set_box_dir(n, d);
473 }
474 _ => self.confusion("prefix"),
475 }
476 if self.after_token.0 != 0 {
478 self.cur_tok = self.after_token;
479 self.back_input();
480 self.after_token = Token(0);
481 }
482 }
483
484 fn assign_int_param(&mut self, global: bool, loc: Loc, v: i32) {
485 self.define(global, loc, Val::Int(i64::from(v)));
486 if let Loc::IntPar(i) = loc {
487 self.int_param_changed(i);
488 }
489 }
490
491 pub fn int_param_changed(&mut self, i: u32) {
493 if i == pg::assign_int::NEWLINECHAR {
494 self.printer.new_line_char = self.eqtb.int(i);
495 } else if i == pg::assign_int::SYNCTEX {
496 self.synctex_changed();
497 }
498 }
499
500 fn let_command(&mut self, global: bool) {
502 let n = self.cur_chr;
503 let mut global = global;
504 if n == 3 {
505 self.scan_int();
507 let c = self.cur_val;
508 if !(1..=0x10_FFFF).contains(&c) {
509 self.print_err("invalid number for \\letcharcode");
510 self.help(&[]);
511 self.error();
512 return;
513 }
514 let p = self.hash.active(c as u32);
515 self.scan_optional_equals();
516 self.get_token();
517 let m = self.token_meaning();
518 self.define_cs(global, p, m);
519 return;
520 }
521 if n == 0 {
522 global = true; }
524 self.get_r_token();
525 let p = self.cur_cs;
526 if n <= 1 {
527 loop {
528 self.get_token();
529 if self.cur_cmd != Cmd::Spacer {
530 break;
531 }
532 }
533 if self.cur_tok == Token::other('=' as u32) {
534 self.get_token();
535 if self.cur_cmd == Cmd::Spacer {
536 self.get_token();
537 }
538 }
539 } else {
540 self.get_token();
541 let q = self.cur_tok;
542 self.get_token();
543 self.back_input();
544 self.cur_tok = q;
545 self.back_input(); }
547 let m = self.token_meaning();
548 self.define_cs(global, p, m);
549 }
550
551 fn token_meaning(&self) -> Meaning {
553 if self.cur_cs != NO_CS {
554 let mut m = self.eqtb.meaning(self.cur_cs).clone();
555 if m.cmd != self.cur_cmd {
558 m = Meaning::prim(self.cur_cmd, self.cur_chr);
559 }
560 m
561 } else {
562 Meaning::prim(self.cur_cmd, self.cur_chr)
563 }
564 }
565
566 fn shorthand_def(&mut self, global: bool) {
568 let n = self.cur_chr;
569 self.get_r_token();
570 let p = self.cur_cs;
571 self.define_cs(global, p, Meaning::prim(Cmd::Relax, RELAX_CHR));
572 self.scan_optional_equals();
573 let m = match n {
574 shorthand::CHAR_DEF => {
575 self.scan_char_num();
576 Meaning::prim(Cmd::CharGiven, self.cur_val as u32)
577 }
578 shorthand::MATH_CHAR_DEF => {
579 let g = self.scan_math_given();
581 Meaning::prim(Cmd::MathGiven, g)
582 }
583 shorthand::UMATH_CHAR_DEF => {
584 let code = self.scan_umath_triple();
585 Meaning::prim(Cmd::XmathGiven, code as u32)
586 }
587 shorthand::UMATH_CHAR_NUM_DEF => {
588 self.scan_int();
589 Meaning::prim(Cmd::XmathGiven, self.cur_val as u32)
590 }
591 _ => {
592 self.scan_register_num();
593 let r = self.cur_val as u32 + REG_OFFSET;
594 match n {
595 shorthand::COUNT_DEF => Meaning::prim(Cmd::AssignInt, r),
596 shorthand::ATTRIBUTE_DEF => Meaning::prim(Cmd::AssignAttr, r),
597 shorthand::DIMEN_DEF => Meaning::prim(Cmd::AssignDimen, r),
598 shorthand::SKIP_DEF => Meaning::prim(Cmd::AssignGlue, r),
599 shorthand::MU_SKIP_DEF => Meaning::prim(Cmd::AssignMuGlue, r),
600 _ => Meaning::prim(Cmd::AssignToks, r),
601 }
602 }
603 };
604 self.define_cs(global, p, m);
605 }
606
607 pub(crate) fn scan_umath_triple(&mut self) -> i64 {
609 self.scan_int();
610 let class = self.cur_val;
611 self.scan_math_family();
612 let fam = self.cur_val;
613 self.scan_char_num();
614 let chr = self.cur_val;
615 if !(0..=7).contains(&class) {
616 self.print_err("Invalid math code");
617 self.help(&["I'm going to use 0 instead of that illegal code value."]);
618 self.error();
619 return 0;
620 }
621 crate::codes::math_code(class as u32, fam as u32, chr as u32)
622 }
623
624 fn assign_toks(&mut self, global: bool) {
626 let q = self.cur_cs;
627 let loc = if self.cur_cmd == Cmd::ToksRegister {
628 self.scan_register_num();
629 Loc::Toks(self.cur_val as u32)
630 } else if self.cur_chr >= REG_OFFSET {
631 Loc::Toks(self.cur_chr - REG_OFFSET)
632 } else {
633 Loc::ToksPar(self.cur_chr)
634 };
635 self.scan_optional_equals();
636 self.get_nonblank_nonrelax_noncall();
637 if self.cur_cmd != Cmd::LeftBrace {
638 let src = if self.cur_cmd == Cmd::ToksRegister {
640 self.scan_register_num();
641 Some(self.eqtb.toks[self.cur_val as usize].val.clone())
642 } else if self.cur_cmd == Cmd::AssignToks {
643 Some(if self.cur_chr >= REG_OFFSET {
644 self.eqtb.toks[(self.cur_chr - REG_OFFSET) as usize]
645 .val
646 .clone()
647 } else {
648 self.eqtb.toks_param(self.cur_chr).clone()
649 })
650 } else {
651 None
652 };
653 if let Some(list) = src {
654 self.define(global, loc, Val::Toks(list));
655 return;
656 }
657 }
658 self.back_input();
659 self.cur_cs = q;
660 self.def_ref_cs = q;
661 let mut toks = self.scan_toks(false, false);
662 self.def_ref_cs = NO_CS;
663 if !toks.is_empty() && loc == Loc::ToksPar(pg::assign_toks::OUTPUT) {
664 toks.insert(0, Token::LEFT_BRACE);
665 toks.push(Token::RIGHT_BRACE);
666 }
667 let list: TokenList = if toks.is_empty() {
668 crate::token::empty_list()
669 } else {
670 Rc::from(toks)
671 };
672 self.define(global, loc, Val::Toks(list));
673 }
674
675 fn combine_toks(&mut self) {
677 let code = self.cur_chr;
678 let global = code >= 4;
679 let expand = code % 2 == 1;
680 let append = code % 4 < 2;
681 self.get_nonblank_nonrelax_noncall();
684 let loc = if self.cur_cmd == Cmd::AssignToks {
685 if self.cur_chr >= REG_OFFSET {
686 Loc::Toks(self.cur_chr - REG_OFFSET)
687 } else {
688 Loc::ToksPar(self.cur_chr)
689 }
690 } else if self.cur_cmd == Cmd::ToksRegister {
691 self.scan_register_num();
692 Loc::Toks(self.cur_val as u32)
693 } else {
694 self.back_input();
695 self.scan_register_num();
696 Loc::Toks(self.cur_val as u32)
697 };
698 let new = if expand {
699 self.scan_toks_expanded()
700 } else {
701 self.scan_general_text().to_vec()
702 };
703 let old = match self.eqtb.get(loc) {
704 Val::Toks(t) => t,
705 _ => crate::token::empty_list(),
706 };
707 let mut v: Vec<Token> = Vec::with_capacity(old.len() + new.len());
708 if append {
709 v.extend_from_slice(&old);
710 v.extend_from_slice(&new);
711 } else {
712 v.extend_from_slice(&new);
713 v.extend_from_slice(&old);
714 }
715 self.define(global, loc, Val::Toks(Rc::from(v)));
716 }
717
718 fn invalid_code(&mut self, v: i32, n: i32, range: bool) {
719 self.print_err("Invalid code (");
720 self.printer.print_int(v.into());
721 if range {
722 self.printer.print_str("), should be in the range 0..");
723 } else {
724 self.printer.print_str("), should be at most ");
725 }
726 self.printer.print_int(n.into());
727 self.help(&["I'm going to use 0 instead of that illegal code value."]);
728 self.error();
729 self.cur_val = 0;
730 }
731
732 fn def_char_code(&mut self, global: bool) {
734 use pg::def_char_code as dc;
735 let which = self.cur_chr;
736 let n: i32 = match which {
737 dc::CATCODE => 15,
738 dc::MATHCODE => 0x8000,
739 dc::SFCODE => 0o77777,
740 _ => 0x10_FFFF,
741 };
742 self.scan_char_num();
743 let c = self.cur_val as u32;
744 self.scan_optional_equals();
745 self.scan_int();
746 if self.cur_val < 0 || self.cur_val > n {
747 self.invalid_code(self.cur_val, n, true);
748 }
749 let v = self.cur_val;
750 match which {
751 dc::CATCODE => {
752 let table = self.eqtb.cur_cat_table();
753 self.define(global, Loc::Cat { table, ch: c }, Val::Int(i64::from(v)));
754 }
755 dc::LCCODE => self.define(global, Loc::Code(CodeKind::Lc, c), Val::Int(v.into())),
756 dc::UCCODE => self.define(global, Loc::Code(CodeKind::Uc, c), Val::Int(v.into())),
757 dc::SFCODE => self.define(global, Loc::Code(CodeKind::Sf, c), Val::Int(v.into())),
758 _ => {
759 let code = if v == 0x8000 {
760 crate::codes::MATH_ACTIVE
761 } else {
762 let v = v as u32;
763 crate::codes::math_code(v >> 12, (v >> 8) & 0xF, v & 0xFF)
764 };
765 self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
766 }
767 }
768 }
769
770 fn do_register_command(&mut self, global: bool) {
772 let q = self.cur_cmd;
773 let (loc, p): (Loc, ValLevel) = 'found: {
775 if q != Cmd::Register {
776 self.get_x_token();
777 let r = self.cur_chr;
778 let l = match self.cur_cmd {
779 Cmd::AssignInt => Some((
780 if r >= REG_OFFSET {
781 Loc::Count(r - REG_OFFSET)
782 } else {
783 Loc::IntPar(r)
784 },
785 ValLevel::Int,
786 )),
787 Cmd::AssignAttr => Some((Loc::Attr(r - REG_OFFSET.min(r)), ValLevel::Int)),
788 Cmd::AssignDimen => Some((
789 if r >= REG_OFFSET {
790 Loc::Dimen(r - REG_OFFSET)
791 } else {
792 Loc::DimenPar(r)
793 },
794 ValLevel::Dimen,
795 )),
796 Cmd::AssignGlue => Some((
797 if r >= REG_OFFSET {
798 Loc::Skip(r - REG_OFFSET)
799 } else {
800 Loc::GluePar(r)
801 },
802 ValLevel::Glue,
803 )),
804 Cmd::AssignMuGlue => Some((
805 if r >= REG_OFFSET {
806 Loc::MuSkip(r - REG_OFFSET)
807 } else {
808 Loc::GluePar(r + MU_GLUE_OFFSET)
809 },
810 ValLevel::Mu,
811 )),
812 _ => None,
813 };
814 if let Some(x) = l {
815 break 'found x;
816 }
817 if self.cur_cmd != Cmd::Register {
818 self.print_err("You can't use `");
819 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
820 self.printer.print_str("' after ");
821 self.print_cmd_chr(q, 0);
822 self.help(&["I'm forgetting what you said and not changing anything."]);
823 self.error();
824 return;
825 }
826 }
827 let kind = self.cur_chr;
828 self.scan_register_num();
829 let n = self.cur_val as u32;
830 match kind {
831 reg::COUNT => (Loc::Count(n), ValLevel::Int),
832 reg::ATTRIBUTE => (Loc::Attr(n), ValLevel::Int),
833 reg::DIMEN => (Loc::Dimen(n), ValLevel::Dimen),
834 reg::SKIP => (Loc::Skip(n), ValLevel::Glue),
835 _ => (Loc::MuSkip(n), ValLevel::Mu),
836 }
837 };
838 if q == Cmd::Register {
839 self.scan_optional_equals();
840 } else {
841 let _ = self.scan_keyword("by");
842 }
843 let mut arith_error = false;
844 let old = self.eqtb.get(loc);
845 let result: Val = if q < Cmd::Multiply {
846 if p < ValLevel::Glue {
847 if p == ValLevel::Int {
848 self.scan_int();
849 } else {
850 self.scan_normal_dimen();
851 }
852 let mut v = self.cur_val;
853 if q == Cmd::Advance
854 && let Val::Int(o) = old
855 {
856 v = v.wrapping_add(o as i32);
857 }
858 Val::Int(i64::from(v))
859 } else {
860 self.scan_glue(p);
861 let mut g = self.cur_glue;
862 if q == Cmd::Advance
863 && let Val::Glue(r) = old
864 {
865 g = add_glue(g, r);
866 }
867 Val::Glue(g)
868 }
869 } else {
870 self.scan_int();
871 let f = self.cur_val;
872 let op = |x: i32, err: &mut bool| -> i32 {
873 let r = if q == Cmd::Multiply {
874 if p == ValLevel::Int {
875 arith::mult_integers(x, f)
876 } else {
877 arith::nx_plus_y(x, f, 0)
878 }
879 } else {
880 arith::x_over_n(x, f).map(|d| d.quotient)
881 };
882 r.unwrap_or_else(|_| {
883 *err = true;
884 0
885 })
886 };
887 match old {
888 Val::Int(x) => Val::Int(i64::from(op(x as i32, &mut arith_error))),
889 Val::Glue(g) => {
890 let mut n = g;
891 n.width = op(g.width, &mut arith_error);
892 n.stretch = op(g.stretch, &mut arith_error);
893 n.shrink = op(g.shrink, &mut arith_error);
894 Val::Glue(n)
895 }
896 other => other,
897 }
898 };
899 if arith_error {
900 self.print_err("Arithmetic overflow");
901 self.help(&[
902 "I can't carry out that multiplication or division,",
903 "since the result is out of range.",
904 ]);
905 self.error();
906 return;
907 }
908 self.define(global, loc, result);
909 }
910
911 pub fn new_interaction(&mut self) {
913 self.printer.print_ln();
914 self.err.interaction = crate::error::Interaction::from_i32(self.cur_chr as i32);
915 self.printer.selector = match (self.err.interaction, self.printer.log_opened()) {
916 (crate::error::Interaction::Batch, true) => crate::print::Selector::LogOnly,
917 (crate::error::Interaction::Batch, false) => crate::print::Selector::NoPrint,
918 (_, true) => crate::print::Selector::TermAndLog,
919 (_, false) => crate::print::Selector::TermOnly,
920 };
921 }
922
923 fn alter_integer(&mut self) {
926 let c = self.cur_chr;
927 self.scan_optional_equals();
928 self.scan_int();
929 match c {
930 0 => self.dead_cycles = self.cur_val,
931 1 => self.insert_penalties = self.cur_val,
932 _ => {
933 if (0..=3).contains(&self.cur_val) {
934 self.cur_chr = self.cur_val as u32;
935 self.new_interaction();
936 } else {
937 self.print_err("Bad interaction mode (");
938 self.printer.print_int(self.cur_val.into());
939 self.printer.print_char(')' as u32);
940 self.help(&[
941 "Modes are 0=batch, 1=nonstop, 2=scroll, and",
942 "3=errorstop. Proceed, and I'll ignore this case.",
943 ]);
944 self.int_error(self.cur_val);
945 }
946 }
947 }
948 }
949}
950
951pub fn add_glue(mut q: Glue, r: Glue) -> Glue {
953 q.width = q.width.wrapping_add(r.width);
954 if q.stretch == 0 {
955 q.stretch_order = crate::glue::NORMAL;
956 }
957 if q.stretch_order == r.stretch_order {
958 q.stretch = q.stretch.wrapping_add(r.stretch);
959 } else if q.stretch_order < r.stretch_order && r.stretch != 0 {
960 q.stretch = r.stretch;
961 q.stretch_order = r.stretch_order;
962 }
963 if q.shrink == 0 {
964 q.shrink_order = crate::glue::NORMAL;
965 }
966 if q.shrink_order == r.shrink_order {
967 q.shrink = q.shrink.wrapping_add(r.shrink);
968 } else if q.shrink_order < r.shrink_order && r.shrink != 0 {
969 q.shrink = r.shrink;
970 q.shrink_order = r.shrink_order;
971 }
972 q
973}