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