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