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 {
197 protected: a & prefix::PROTECTED != 0,
198 tokens: Rc::from(toks),
199 };
200 self.define_cs(
201 a & prefix::GLOBAL != 0,
202 p,
203 Meaning {
204 cmd,
205 chr: 0,
206 mac: Some(Rc::new(mac)),
207 },
208 );
209 }
210 Cmd::Let => self.let_command(global),
211 Cmd::ShorthandDef => self.shorthand_def(global),
212 Cmd::ReadToCs => {
213 let j = self.cur_chr;
214 self.scan_int();
215 let n = self.cur_val;
216 if !self.scan_keyword("to") {
217 self.print_err("Missing `to' inserted");
218 self.help(&[
219 "You should have said `\\read<number> to \\cs'.",
220 "I'm going to look for the \\cs now.",
221 ]);
222 self.error();
223 }
224 self.get_r_token();
225 let p = self.cur_cs;
226 let toks = self.read_toks(n, p, j);
227 self.define_cs(
228 global,
229 p,
230 Meaning {
231 cmd: Cmd::Call,
232 chr: 0,
233 mac: Some(Rc::new(Macro {
234 protected: false,
235 tokens: Rc::from(toks),
236 })),
237 },
238 );
239 }
240 Cmd::ToksRegister | Cmd::AssignToks => self.assign_toks(global),
241 Cmd::Combinetoks => self.combine_toks(),
242 Cmd::AssignInt => self.assign_int_command(global),
243 Cmd::AssignAttr => {
244 let p = self.cur_chr - REG_OFFSET.min(self.cur_chr);
245 self.scan_optional_equals();
246 self.scan_int();
247 self.define(global, Loc::Attr(p), Val::Int(i64::from(self.cur_val)));
248 }
249 Cmd::AssignDir | Cmd::AssignDirection => {
250 let p = self.cur_chr;
251 let by_name = self.cur_cmd == Cmd::AssignDir;
252 self.scan_optional_equals();
253 let d = if by_name {
254 self.scan_direction_spec()
255 } else {
256 self.scan_int();
257 self.cur_val
258 };
259 self.assign_dir_param(global, p, d);
260 if p == crate::primitives_gen::assign_dir::TEXTDIR {
261 self.text_dir_assigned(d);
262 }
263 }
264 Cmd::AssignDimen => {
265 let p = self.cur_chr;
266 self.scan_optional_equals();
267 self.scan_normal_dimen();
268 let loc = if p >= REG_OFFSET {
269 Loc::Dimen(p - REG_OFFSET)
270 } else {
271 Loc::DimenPar(p)
272 };
273 self.define(global, loc, Val::Int(i64::from(self.cur_val)));
274 }
275 Cmd::AssignGlue | Cmd::AssignMuGlue => {
276 let p = self.cur_chr;
277 let mu = self.cur_cmd == Cmd::AssignMuGlue;
278 self.scan_optional_equals();
279 self.scan_glue(if mu { ValLevel::Mu } else { ValLevel::Glue });
280 let g = self.cur_glue;
281 let loc = match (mu, p >= REG_OFFSET) {
282 (false, true) => Loc::Skip(p - REG_OFFSET),
283 (true, true) => Loc::MuSkip(p - REG_OFFSET),
284 (false, false) => Loc::GluePar(p),
285 (true, false) => Loc::GluePar(p + MU_GLUE_OFFSET),
286 };
287 self.define(global, loc, Val::Glue(g));
288 }
289 Cmd::DefCharCode => self.def_char_code(global),
290 Cmd::DefDelCode => {
291 self.scan_char_num();
292 let c = self.cur_val as u32;
293 self.scan_optional_equals();
294 self.scan_int();
295 let v = self.cur_val;
296 if v > 0o77777777 {
297 self.invalid_code(v, 0o77777777, false);
298 }
299 let code = crate::codes::delcode_from_classic(self.cur_val);
300 self.define(global, Loc::Code(CodeKind::Del, c), Val::Int(code));
301 }
302 Cmd::ExtdefMathCode => {
303 let num = self.cur_chr != pg::extdef_math_code::UMATHCODE;
304 self.scan_char_num();
305 let c = self.cur_val as u32;
306 self.scan_optional_equals();
307 let code = if num {
308 self.scan_int();
309 i64::from(self.cur_val)
310 } else {
311 self.scan_umath_triple()
312 };
313 self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
314 }
315 Cmd::ExtdefDelCode => {
316 let num = self.cur_chr != pg::extdef_del_code::UDELCODE;
317 self.scan_char_num();
318 let c = self.cur_val as u32;
319 self.scan_optional_equals();
320 let code = if num {
321 self.scan_int();
322 i64::from(self.cur_val)
323 } else {
324 self.scan_math_family();
325 let fam = self.cur_val as i64;
326 self.scan_char_num();
327 (fam << 21) + i64::from(self.cur_val)
328 };
329 self.define(global, Loc::Code(CodeKind::Del, c), Val::Int(code));
330 }
331 Cmd::DefFamily => {
332 let size = self.cur_chr;
333 self.scan_math_family();
334 let fam = self.cur_val as u32;
335 self.scan_optional_equals();
336 self.scan_font_ident();
337 let f = self.cur_val;
338 self.define(
339 global,
340 Loc::MathFont(size * crate::eqtb::NUM_FAMILIES as u32 + fam),
341 Val::Int(i64::from(f)),
342 );
343 self.set_font_math_params(global, size, fam, f as u32);
344 }
345 Cmd::SetMathParam => {
346 let p = self.cur_chr;
347 let _ = self.scan_math_style();
348 let style = self.cur_val as u32;
349 self.scan_optional_equals();
350 let v = if crate::codes::math_param_is_glue(p) {
351 self.scan_glue(ValLevel::Mu);
352 self.store_math_param_glue(self.cur_glue)
353 } else if p == crate::mathparams::mp::RADICAL_DEGREE_RAISE {
354 self.scan_int();
355 self.cur_val
356 } else {
357 self.scan_normal_dimen();
358 self.cur_val
359 };
360 self.define(
361 global,
362 Loc::MathParam(p * 8 + style),
363 Val::Int(i64::from(v)),
364 );
365 }
366 Cmd::Register | Cmd::Advance | Cmd::Multiply | Cmd::Divide => {
367 self.do_register_command(global)
368 }
369 Cmd::SetBox => {
370 self.scan_register_num();
371 let n = self.cur_val as u32;
372 let ctx = if global {
373 crate::maincontrol::BoxContext::GlobalSetBox(n)
374 } else {
375 crate::maincontrol::BoxContext::SetBox(n)
376 };
377 self.scan_optional_equals();
378 if self.err.set_box_allowed {
379 self.scan_box(ctx);
380 } else {
381 self.print_err("Improper ");
382 self.print_esc_str("setbox");
383 self.help(&[
384 "Sorry, \\setbox is not allowed after \\halign in a display,",
385 "or between \\accent and an accented character.",
386 ]);
387 self.error();
388 }
389 }
390 Cmd::SetAux => self.alter_aux(),
391 Cmd::SetPrevGraf => self.alter_prev_graf(),
392 Cmd::SetPageDimen => self.alter_page_so_far(),
393 Cmd::SetPageInt => self.alter_integer(),
394 Cmd::SetBoxDimen => self.alter_box_dimen(),
395 Cmd::SetTexShape => {
396 self.scan_optional_equals();
397 self.scan_int();
398 let n = self.cur_val;
399 let shape = if n <= 0 {
400 None
401 } else {
402 let mut v = Vec::with_capacity(2 * n as usize);
403 for _ in 0..n {
404 self.scan_normal_dimen();
405 v.push(self.cur_val);
406 self.scan_normal_dimen();
407 v.push(self.cur_val);
408 }
409 Some(Rc::from(v))
410 };
411 self.define(global, Loc::ParShape, Val::Shape(shape));
412 }
413 Cmd::SetEtexShape => {
414 let q = self.cur_chr;
415 self.scan_optional_equals();
416 self.scan_int();
417 let n = self.cur_val;
418 let shape = if n <= 0 {
419 None
420 } else {
421 let mut v = Vec::with_capacity(n as usize);
422 for _ in 0..n {
423 self.scan_int();
424 v.push(self.cur_val);
425 }
426 Some(Rc::from(v))
427 };
428 self.define(global, Loc::EtexShape(q), Val::Shape(shape));
429 }
430 Cmd::HyphData => self.hyph_data(global),
431 Cmd::AssignFontDimen => {
432 self.find_font_dimen(true);
433 let loc = self.font_dimen_loc;
434 self.scan_optional_equals();
435 self.scan_normal_dimen();
436 self.set_font_dimen(loc, self.cur_val);
437 }
438 Cmd::AssignFontInt => {
439 let n = self.cur_chr;
440 self.scan_font_ident();
441 let f = self.cur_val as u32;
442 if (2..=5).contains(&n) {
443 self.scan_char_num();
445 let c = self.cur_val as u32;
446 self.scan_optional_equals();
447 self.scan_int();
448 self.set_font_char_code(f, n, c, self.cur_val);
449 } else {
450 self.scan_optional_equals();
451 self.scan_int();
452 self.set_font_int_param(f, n, self.cur_val);
453 }
454 }
455 Cmd::DefFont => self.new_font(global),
456 Cmd::LetterspaceFont => self.letterspace_font(global),
457 Cmd::CopyFont => self.copy_font(global),
458 Cmd::SetFontId => {
459 self.scan_int();
460 let f = self.cur_val;
461 if self.font_exists(f) {
462 self.define(global, Loc::CurFont, Val::Int(i64::from(f)));
463 }
464 }
465 Cmd::SetInteraction => self.new_interaction(),
466 Cmd::DefLuaCall => self.def_lua_call(global, a & prefix::PROTECTED != 0),
467 Cmd::AssignBoxDir | Cmd::AssignBoxDirection => {
468 self.scan_register_num();
469 let n = self.cur_val as u32;
470 self.scan_optional_equals();
471 let d = if self.cur_cmd == Cmd::AssignBoxDir {
472 self.scan_direction_spec()
473 } else {
474 self.scan_int();
475 self.cur_val
476 };
477 self.set_box_dir(n, d);
478 }
479 _ => self.confusion("prefix"),
480 }
481 if self.after_token.0 != 0 {
483 self.cur_tok = self.after_token;
484 self.back_input();
485 self.after_token = Token(0);
486 }
487 }
488
489 fn assign_int_param(&mut self, global: bool, loc: Loc, v: i32) {
490 self.define(global, loc, Val::Int(i64::from(v)));
491 if let Loc::IntPar(i) = loc {
492 self.int_param_changed(i);
493 }
494 }
495
496 pub fn int_param_changed(&mut self, i: u32) {
498 if i == pg::assign_int::NEWLINECHAR {
499 self.printer.new_line_char = self.eqtb.int(i);
500 } else if i == pg::assign_int::SYNCTEX {
501 self.synctex_changed();
502 }
503 }
504
505 fn let_command(&mut self, global: bool) {
507 let n = self.cur_chr;
508 let mut global = global;
509 if n == 3 {
510 self.scan_int();
512 let c = self.cur_val;
513 if !(1..=0x10_FFFF).contains(&c) {
514 self.print_err("invalid number for \\letcharcode");
515 self.help(&[]);
516 self.error();
517 return;
518 }
519 let p = self.hash.active(c as u32);
520 self.scan_optional_equals();
521 self.get_token();
522 let m = self.token_meaning();
523 self.define_cs(global, p, m);
524 return;
525 }
526 if n == 0 {
527 global = true; }
529 self.get_r_token();
530 let p = self.cur_cs;
531 if n <= 1 {
532 loop {
533 self.get_token();
534 if self.cur_cmd != Cmd::Spacer {
535 break;
536 }
537 }
538 if self.cur_tok == Token::other('=' as u32) {
539 self.get_token();
540 if self.cur_cmd == Cmd::Spacer {
541 self.get_token();
542 }
543 }
544 } else {
545 self.get_token();
546 let q = self.cur_tok;
547 self.get_token();
548 self.back_input();
549 self.cur_tok = q;
550 self.back_input(); }
552 let m = self.token_meaning();
553 self.define_cs(global, p, m);
554 }
555
556 fn token_meaning(&self) -> Meaning {
558 if self.cur_cs != NO_CS {
559 let mut m = self.eqtb.meaning(self.cur_cs).clone();
560 if m.cmd != self.cur_cmd {
563 m = Meaning::prim(self.cur_cmd, self.cur_chr);
564 }
565 m
566 } else {
567 Meaning::prim(self.cur_cmd, self.cur_chr)
568 }
569 }
570
571 fn shorthand_def(&mut self, global: bool) {
573 let n = self.cur_chr;
574 self.get_r_token();
575 let p = self.cur_cs;
576 self.define_cs(global, p, Meaning::prim(Cmd::Relax, RELAX_CHR));
577 self.scan_optional_equals();
578 let m = match n {
579 shorthand::CHAR_DEF => {
580 self.scan_char_num();
581 Meaning::prim(Cmd::CharGiven, self.cur_val as u32)
582 }
583 shorthand::MATH_CHAR_DEF => {
584 let g = self.scan_math_given();
586 Meaning::prim(Cmd::MathGiven, g)
587 }
588 shorthand::UMATH_CHAR_DEF => {
589 let code = self.scan_umath_triple();
590 Meaning::prim(Cmd::XmathGiven, code as u32)
591 }
592 shorthand::UMATH_CHAR_NUM_DEF => {
593 self.scan_int();
594 Meaning::prim(Cmd::XmathGiven, self.cur_val as u32)
595 }
596 _ => {
597 self.scan_register_num();
598 let r = self.cur_val as u32 + REG_OFFSET;
599 match n {
600 shorthand::COUNT_DEF => Meaning::prim(Cmd::AssignInt, r),
601 shorthand::ATTRIBUTE_DEF => Meaning::prim(Cmd::AssignAttr, r),
602 shorthand::DIMEN_DEF => Meaning::prim(Cmd::AssignDimen, r),
603 shorthand::SKIP_DEF => Meaning::prim(Cmd::AssignGlue, r),
604 shorthand::MU_SKIP_DEF => Meaning::prim(Cmd::AssignMuGlue, r),
605 _ => Meaning::prim(Cmd::AssignToks, r),
606 }
607 }
608 };
609 self.define_cs(global, p, m);
610 }
611
612 pub(crate) fn scan_umath_triple(&mut self) -> i64 {
614 self.scan_int();
615 let class = self.cur_val;
616 self.scan_math_family();
617 let fam = self.cur_val;
618 self.scan_char_num();
619 let chr = self.cur_val;
620 if !(0..=7).contains(&class) {
621 self.print_err("Invalid math code");
622 self.help(&["I'm going to use 0 instead of that illegal code value."]);
623 self.error();
624 return 0;
625 }
626 crate::codes::math_code(class as u32, fam as u32, chr as u32)
627 }
628
629 fn assign_toks(&mut self, global: bool) {
631 let q = self.cur_cs;
632 let loc = if self.cur_cmd == Cmd::ToksRegister {
633 self.scan_register_num();
634 Loc::Toks(self.cur_val as u32)
635 } else if self.cur_chr >= REG_OFFSET {
636 Loc::Toks(self.cur_chr - REG_OFFSET)
637 } else {
638 Loc::ToksPar(self.cur_chr)
639 };
640 self.scan_optional_equals();
641 self.get_nonblank_nonrelax_noncall();
642 if self.cur_cmd != Cmd::LeftBrace {
643 let src = if self.cur_cmd == Cmd::ToksRegister {
645 self.scan_register_num();
646 Some(self.eqtb.toks[self.cur_val as usize].val.clone())
647 } else if self.cur_cmd == Cmd::AssignToks {
648 Some(if self.cur_chr >= REG_OFFSET {
649 self.eqtb.toks[(self.cur_chr - REG_OFFSET) as usize]
650 .val
651 .clone()
652 } else {
653 self.eqtb.toks_param(self.cur_chr).clone()
654 })
655 } else {
656 None
657 };
658 if let Some(list) = src {
659 self.define(global, loc, Val::Toks(list));
660 return;
661 }
662 }
663 self.back_input();
664 self.cur_cs = q;
665 self.def_ref_cs = q;
666 let mut toks = self.scan_toks(false, false);
667 self.def_ref_cs = NO_CS;
668 if !toks.is_empty() && loc == Loc::ToksPar(pg::assign_toks::OUTPUT) {
669 toks.insert(0, Token::LEFT_BRACE);
670 toks.push(Token::RIGHT_BRACE);
671 }
672 let list: TokenList = if toks.is_empty() {
673 crate::token::empty_list()
674 } else {
675 Rc::from(toks)
676 };
677 self.define(global, loc, Val::Toks(list));
678 }
679
680 fn combine_toks(&mut self) {
682 let code = self.cur_chr;
683 let global = code >= 4;
684 let expand = code % 2 == 1;
685 let append = code % 4 < 2;
686 self.get_nonblank_nonrelax_noncall();
689 let loc = if self.cur_cmd == Cmd::AssignToks {
690 if self.cur_chr >= REG_OFFSET {
691 Loc::Toks(self.cur_chr - REG_OFFSET)
692 } else {
693 Loc::ToksPar(self.cur_chr)
694 }
695 } else if self.cur_cmd == Cmd::ToksRegister {
696 self.scan_register_num();
697 Loc::Toks(self.cur_val as u32)
698 } else {
699 self.back_input();
700 self.scan_register_num();
701 Loc::Toks(self.cur_val as u32)
702 };
703 let new = if expand {
704 self.scan_toks_expanded()
705 } else {
706 self.scan_general_text().to_vec()
707 };
708 let old = match self.eqtb.get(loc) {
709 Val::Toks(t) => t,
710 _ => crate::token::empty_list(),
711 };
712 let mut v: Vec<Token> = Vec::with_capacity(old.len() + new.len());
713 if append {
714 v.extend_from_slice(&old);
715 v.extend_from_slice(&new);
716 } else {
717 v.extend_from_slice(&new);
718 v.extend_from_slice(&old);
719 }
720 self.define(global, loc, Val::Toks(Rc::from(v)));
721 }
722
723 fn invalid_code(&mut self, v: i32, n: i32, range: bool) {
724 self.print_err("Invalid code (");
725 self.printer.print_int(v.into());
726 if range {
727 self.printer.print_str("), should be in the range 0..");
728 } else {
729 self.printer.print_str("), should be at most ");
730 }
731 self.printer.print_int(n.into());
732 self.help(&["I'm going to use 0 instead of that illegal code value."]);
733 self.error();
734 self.cur_val = 0;
735 }
736
737 fn def_char_code(&mut self, global: bool) {
739 use pg::def_char_code as dc;
740 let which = self.cur_chr;
741 let n: i32 = match which {
742 dc::CATCODE => 15,
743 dc::MATHCODE => 0x8000,
744 dc::SFCODE => 0o77777,
745 _ => 0x10_FFFF,
746 };
747 self.scan_char_num();
748 let c = self.cur_val as u32;
749 self.scan_optional_equals();
750 self.scan_int();
751 if self.cur_val < 0 || self.cur_val > n {
752 self.invalid_code(self.cur_val, n, true);
753 }
754 let v = self.cur_val;
755 match which {
756 dc::CATCODE => {
757 let table = self.eqtb.cur_cat_table();
758 self.define(global, Loc::Cat { table, ch: c }, Val::Int(i64::from(v)));
759 }
760 dc::LCCODE => self.define(global, Loc::Code(CodeKind::Lc, c), Val::Int(v.into())),
761 dc::UCCODE => self.define(global, Loc::Code(CodeKind::Uc, c), Val::Int(v.into())),
762 dc::SFCODE => self.define(global, Loc::Code(CodeKind::Sf, c), Val::Int(v.into())),
763 _ => {
764 let code = if v == 0x8000 {
765 crate::codes::MATH_ACTIVE
766 } else {
767 let v = v as u32;
768 crate::codes::math_code(v >> 12, (v >> 8) & 0xF, v & 0xFF)
769 };
770 self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
771 }
772 }
773 }
774
775 fn do_register_command(&mut self, global: bool) {
777 let q = self.cur_cmd;
778 let (loc, p): (Loc, ValLevel) = 'found: {
780 if q != Cmd::Register {
781 self.get_x_token();
782 let r = self.cur_chr;
783 let l = match self.cur_cmd {
784 Cmd::AssignInt => Some((
785 if r >= REG_OFFSET {
786 Loc::Count(r - REG_OFFSET)
787 } else {
788 Loc::IntPar(r)
789 },
790 ValLevel::Int,
791 )),
792 Cmd::AssignAttr => Some((Loc::Attr(r - REG_OFFSET.min(r)), ValLevel::Int)),
793 Cmd::AssignDimen => Some((
794 if r >= REG_OFFSET {
795 Loc::Dimen(r - REG_OFFSET)
796 } else {
797 Loc::DimenPar(r)
798 },
799 ValLevel::Dimen,
800 )),
801 Cmd::AssignGlue => Some((
802 if r >= REG_OFFSET {
803 Loc::Skip(r - REG_OFFSET)
804 } else {
805 Loc::GluePar(r)
806 },
807 ValLevel::Glue,
808 )),
809 Cmd::AssignMuGlue => Some((
810 if r >= REG_OFFSET {
811 Loc::MuSkip(r - REG_OFFSET)
812 } else {
813 Loc::GluePar(r + MU_GLUE_OFFSET)
814 },
815 ValLevel::Mu,
816 )),
817 _ => None,
818 };
819 if let Some(x) = l {
820 break 'found x;
821 }
822 if self.cur_cmd != Cmd::Register {
823 self.print_err("You can't use `");
824 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
825 self.printer.print_str("' after ");
826 self.print_cmd_chr(q, 0);
827 self.help(&["I'm forgetting what you said and not changing anything."]);
828 self.error();
829 return;
830 }
831 }
832 let kind = self.cur_chr;
833 self.scan_register_num();
834 let n = self.cur_val as u32;
835 match kind {
836 reg::COUNT => (Loc::Count(n), ValLevel::Int),
837 reg::ATTRIBUTE => (Loc::Attr(n), ValLevel::Int),
838 reg::DIMEN => (Loc::Dimen(n), ValLevel::Dimen),
839 reg::SKIP => (Loc::Skip(n), ValLevel::Glue),
840 _ => (Loc::MuSkip(n), ValLevel::Mu),
841 }
842 };
843 if q == Cmd::Register {
844 self.scan_optional_equals();
845 } else {
846 let _ = self.scan_keyword("by");
847 }
848 let mut arith_error = false;
849 let old = self.eqtb.get(loc);
850 let result: Val = if q < Cmd::Multiply {
851 if p < ValLevel::Glue {
852 if p == ValLevel::Int {
853 self.scan_int();
854 } else {
855 self.scan_normal_dimen();
856 }
857 let mut v = self.cur_val;
858 if q == Cmd::Advance
859 && let Val::Int(o) = old
860 {
861 v = v.wrapping_add(o as i32);
862 }
863 Val::Int(i64::from(v))
864 } else {
865 self.scan_glue(p);
866 let mut g = self.cur_glue;
867 if q == Cmd::Advance
868 && let Val::Glue(r) = old
869 {
870 g = add_glue(g, r);
871 }
872 Val::Glue(g)
873 }
874 } else {
875 self.scan_int();
876 let f = self.cur_val;
877 let op = |x: i32, err: &mut bool| -> i32 {
878 let r = if q == Cmd::Multiply {
879 if p == ValLevel::Int {
880 arith::mult_integers(x, f)
881 } else {
882 arith::nx_plus_y(x, f, 0)
883 }
884 } else {
885 arith::x_over_n(x, f).map(|d| d.quotient)
886 };
887 r.unwrap_or_else(|_| {
888 *err = true;
889 0
890 })
891 };
892 match old {
893 Val::Int(x) => Val::Int(i64::from(op(x as i32, &mut arith_error))),
894 Val::Glue(g) => {
895 let mut n = g;
896 n.width = op(g.width, &mut arith_error);
897 n.stretch = op(g.stretch, &mut arith_error);
898 n.shrink = op(g.shrink, &mut arith_error);
899 Val::Glue(n)
900 }
901 other => other,
902 }
903 };
904 if arith_error {
905 self.print_err("Arithmetic overflow");
906 self.help(&[
907 "I can't carry out that multiplication or division,",
908 "since the result is out of range.",
909 ]);
910 self.error();
911 return;
912 }
913 self.define(global, loc, result);
914 }
915
916 pub fn new_interaction(&mut self) {
918 self.printer.print_ln();
919 self.err.interaction = crate::error::Interaction::from_i32(self.cur_chr as i32);
920 self.printer.selector = match (self.err.interaction, self.printer.log_opened()) {
921 (crate::error::Interaction::Batch, true) => crate::print::Selector::LogOnly,
922 (crate::error::Interaction::Batch, false) => crate::print::Selector::NoPrint,
923 (_, true) => crate::print::Selector::TermAndLog,
924 (_, false) => crate::print::Selector::TermOnly,
925 };
926 }
927
928 fn alter_integer(&mut self) {
931 let c = self.cur_chr;
932 self.scan_optional_equals();
933 self.scan_int();
934 match c {
935 0 => self.dead_cycles = self.cur_val,
936 1 => self.insert_penalties = self.cur_val,
937 _ => {
938 if (0..=3).contains(&self.cur_val) {
939 self.cur_chr = self.cur_val as u32;
940 self.new_interaction();
941 } else {
942 self.print_err("Bad interaction mode (");
943 self.printer.print_int(self.cur_val.into());
944 self.printer.print_char(')' as u32);
945 self.help(&[
946 "Modes are 0=batch, 1=nonstop, 2=scroll, and",
947 "3=errorstop. Proceed, and I'll ignore this case.",
948 ]);
949 self.int_error(self.cur_val);
950 }
951 }
952 }
953 }
954}
955
956pub fn add_glue(mut q: Glue, r: Glue) -> Glue {
958 q.width = q.width.wrapping_add(r.width);
959 if q.stretch == 0 {
960 q.stretch_order = crate::glue::NORMAL;
961 }
962 if q.stretch_order == r.stretch_order {
963 q.stretch = q.stretch.wrapping_add(r.stretch);
964 } else if q.stretch_order < r.stretch_order && r.stretch != 0 {
965 q.stretch = r.stretch;
966 q.stretch_order = r.stretch_order;
967 }
968 if q.shrink == 0 {
969 q.shrink_order = crate::glue::NORMAL;
970 }
971 if q.shrink_order == r.shrink_order {
972 q.shrink = q.shrink.wrapping_add(r.shrink);
973 } else if q.shrink_order < r.shrink_order && r.shrink != 0 {
974 q.shrink = r.shrink;
975 q.shrink_order = r.shrink_order;
976 }
977 q
978}