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 }
493 }
494
495 fn let_command(&mut self, global: bool) {
497 let n = self.cur_chr;
498 let mut global = global;
499 if n == 3 {
500 self.scan_int();
502 let c = self.cur_val;
503 if !(1..=0x10_FFFF).contains(&c) {
504 self.print_err("invalid number for \\letcharcode");
505 self.help(&[]);
506 self.error();
507 return;
508 }
509 let p = self.hash.active(c as u32);
510 self.scan_optional_equals();
511 self.get_token();
512 let m = self.token_meaning();
513 self.define_cs(global, p, m);
514 return;
515 }
516 if n == 0 {
517 global = true; }
519 self.get_r_token();
520 let p = self.cur_cs;
521 if n <= 1 {
522 loop {
523 self.get_token();
524 if self.cur_cmd != Cmd::Spacer {
525 break;
526 }
527 }
528 if self.cur_tok == Token::other('=' as u32) {
529 self.get_token();
530 if self.cur_cmd == Cmd::Spacer {
531 self.get_token();
532 }
533 }
534 } else {
535 self.get_token();
536 let q = self.cur_tok;
537 self.get_token();
538 self.back_input();
539 self.cur_tok = q;
540 self.back_input(); }
542 let m = self.token_meaning();
543 self.define_cs(global, p, m);
544 }
545
546 fn token_meaning(&self) -> Meaning {
548 if self.cur_cs != NO_CS {
549 let mut m = self.eqtb.meaning(self.cur_cs).clone();
550 if m.cmd != self.cur_cmd {
553 m = Meaning::prim(self.cur_cmd, self.cur_chr);
554 }
555 m
556 } else {
557 Meaning::prim(self.cur_cmd, self.cur_chr)
558 }
559 }
560
561 fn shorthand_def(&mut self, global: bool) {
563 let n = self.cur_chr;
564 self.get_r_token();
565 let p = self.cur_cs;
566 self.define_cs(global, p, Meaning::prim(Cmd::Relax, RELAX_CHR));
567 self.scan_optional_equals();
568 let m = match n {
569 shorthand::CHAR_DEF => {
570 self.scan_char_num();
571 Meaning::prim(Cmd::CharGiven, self.cur_val as u32)
572 }
573 shorthand::MATH_CHAR_DEF => {
574 let g = self.scan_math_given();
576 Meaning::prim(Cmd::MathGiven, g)
577 }
578 shorthand::UMATH_CHAR_DEF => {
579 let code = self.scan_umath_triple();
580 Meaning::prim(Cmd::XmathGiven, code as u32)
581 }
582 shorthand::UMATH_CHAR_NUM_DEF => {
583 self.scan_int();
584 Meaning::prim(Cmd::XmathGiven, self.cur_val as u32)
585 }
586 _ => {
587 self.scan_register_num();
588 let r = self.cur_val as u32 + REG_OFFSET;
589 match n {
590 shorthand::COUNT_DEF => Meaning::prim(Cmd::AssignInt, r),
591 shorthand::ATTRIBUTE_DEF => Meaning::prim(Cmd::AssignAttr, r),
592 shorthand::DIMEN_DEF => Meaning::prim(Cmd::AssignDimen, r),
593 shorthand::SKIP_DEF => Meaning::prim(Cmd::AssignGlue, r),
594 shorthand::MU_SKIP_DEF => Meaning::prim(Cmd::AssignMuGlue, r),
595 _ => Meaning::prim(Cmd::AssignToks, r),
596 }
597 }
598 };
599 self.define_cs(global, p, m);
600 }
601
602 pub(crate) fn scan_umath_triple(&mut self) -> i64 {
604 self.scan_int();
605 let class = self.cur_val;
606 self.scan_math_family();
607 let fam = self.cur_val;
608 self.scan_char_num();
609 let chr = self.cur_val;
610 if !(0..=7).contains(&class) {
611 self.print_err("Invalid math code");
612 self.help(&["I'm going to use 0 instead of that illegal code value."]);
613 self.error();
614 return 0;
615 }
616 crate::codes::math_code(class as u32, fam as u32, chr as u32)
617 }
618
619 fn assign_toks(&mut self, global: bool) {
621 let q = self.cur_cs;
622 let loc = if self.cur_cmd == Cmd::ToksRegister {
623 self.scan_register_num();
624 Loc::Toks(self.cur_val as u32)
625 } else if self.cur_chr >= REG_OFFSET {
626 Loc::Toks(self.cur_chr - REG_OFFSET)
627 } else {
628 Loc::ToksPar(self.cur_chr)
629 };
630 self.scan_optional_equals();
631 self.get_nonblank_nonrelax_noncall();
632 if self.cur_cmd != Cmd::LeftBrace {
633 let src = if self.cur_cmd == Cmd::ToksRegister {
635 self.scan_register_num();
636 Some(self.eqtb.toks[self.cur_val as usize].val.clone())
637 } else if self.cur_cmd == Cmd::AssignToks {
638 Some(if self.cur_chr >= REG_OFFSET {
639 self.eqtb.toks[(self.cur_chr - REG_OFFSET) as usize]
640 .val
641 .clone()
642 } else {
643 self.eqtb.toks_param(self.cur_chr).clone()
644 })
645 } else {
646 None
647 };
648 if let Some(list) = src {
649 self.define(global, loc, Val::Toks(list));
650 return;
651 }
652 }
653 self.back_input();
654 self.cur_cs = q;
655 self.def_ref_cs = q;
656 let mut toks = self.scan_toks(false, false);
657 self.def_ref_cs = NO_CS;
658 if !toks.is_empty() && loc == Loc::ToksPar(pg::assign_toks::OUTPUT) {
659 toks.insert(0, Token::LEFT_BRACE);
660 toks.push(Token::RIGHT_BRACE);
661 }
662 let list: TokenList = if toks.is_empty() {
663 crate::token::empty_list()
664 } else {
665 Rc::from(toks)
666 };
667 self.define(global, loc, Val::Toks(list));
668 }
669
670 fn combine_toks(&mut self) {
672 let code = self.cur_chr;
673 let global = code >= 4;
674 let expand = code % 2 == 1;
675 let append = code % 4 < 2;
676 self.get_nonblank_nonrelax_noncall();
679 let loc = if self.cur_cmd == Cmd::AssignToks {
680 if self.cur_chr >= REG_OFFSET {
681 Loc::Toks(self.cur_chr - REG_OFFSET)
682 } else {
683 Loc::ToksPar(self.cur_chr)
684 }
685 } else if self.cur_cmd == Cmd::ToksRegister {
686 self.scan_register_num();
687 Loc::Toks(self.cur_val as u32)
688 } else {
689 self.back_input();
690 self.scan_register_num();
691 Loc::Toks(self.cur_val as u32)
692 };
693 let new = if expand {
694 self.scan_toks_expanded()
695 } else {
696 self.scan_general_text().to_vec()
697 };
698 let old = match self.eqtb.get(loc) {
699 Val::Toks(t) => t,
700 _ => crate::token::empty_list(),
701 };
702 let mut v: Vec<Token> = Vec::with_capacity(old.len() + new.len());
703 if append {
704 v.extend_from_slice(&old);
705 v.extend_from_slice(&new);
706 } else {
707 v.extend_from_slice(&new);
708 v.extend_from_slice(&old);
709 }
710 self.define(global, loc, Val::Toks(Rc::from(v)));
711 }
712
713 fn invalid_code(&mut self, v: i32, n: i32, range: bool) {
714 self.print_err("Invalid code (");
715 self.printer.print_int(v.into());
716 if range {
717 self.printer.print_str("), should be in the range 0..");
718 } else {
719 self.printer.print_str("), should be at most ");
720 }
721 self.printer.print_int(n.into());
722 self.help(&["I'm going to use 0 instead of that illegal code value."]);
723 self.error();
724 self.cur_val = 0;
725 }
726
727 fn def_char_code(&mut self, global: bool) {
729 use pg::def_char_code as dc;
730 let which = self.cur_chr;
731 let n: i32 = match which {
732 dc::CATCODE => 15,
733 dc::MATHCODE => 0x8000,
734 dc::SFCODE => 0o77777,
735 _ => 0x10_FFFF,
736 };
737 self.scan_char_num();
738 let c = self.cur_val as u32;
739 self.scan_optional_equals();
740 self.scan_int();
741 if self.cur_val < 0 || self.cur_val > n {
742 self.invalid_code(self.cur_val, n, true);
743 }
744 let v = self.cur_val;
745 match which {
746 dc::CATCODE => {
747 let table = self.eqtb.cur_cat_table();
748 self.define(global, Loc::Cat { table, ch: c }, Val::Int(i64::from(v)));
749 }
750 dc::LCCODE => self.define(global, Loc::Code(CodeKind::Lc, c), Val::Int(v.into())),
751 dc::UCCODE => self.define(global, Loc::Code(CodeKind::Uc, c), Val::Int(v.into())),
752 dc::SFCODE => self.define(global, Loc::Code(CodeKind::Sf, c), Val::Int(v.into())),
753 _ => {
754 let code = if v == 0x8000 {
755 crate::codes::MATH_ACTIVE
756 } else {
757 let v = v as u32;
758 crate::codes::math_code(v >> 12, (v >> 8) & 0xF, v & 0xFF)
759 };
760 self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
761 }
762 }
763 }
764
765 fn do_register_command(&mut self, global: bool) {
767 let q = self.cur_cmd;
768 let (loc, p): (Loc, ValLevel) = 'found: {
770 if q != Cmd::Register {
771 self.get_x_token();
772 let r = self.cur_chr;
773 let l = match self.cur_cmd {
774 Cmd::AssignInt => Some((
775 if r >= REG_OFFSET {
776 Loc::Count(r - REG_OFFSET)
777 } else {
778 Loc::IntPar(r)
779 },
780 ValLevel::Int,
781 )),
782 Cmd::AssignAttr => Some((Loc::Attr(r - REG_OFFSET.min(r)), ValLevel::Int)),
783 Cmd::AssignDimen => Some((
784 if r >= REG_OFFSET {
785 Loc::Dimen(r - REG_OFFSET)
786 } else {
787 Loc::DimenPar(r)
788 },
789 ValLevel::Dimen,
790 )),
791 Cmd::AssignGlue => Some((
792 if r >= REG_OFFSET {
793 Loc::Skip(r - REG_OFFSET)
794 } else {
795 Loc::GluePar(r)
796 },
797 ValLevel::Glue,
798 )),
799 Cmd::AssignMuGlue => Some((
800 if r >= REG_OFFSET {
801 Loc::MuSkip(r - REG_OFFSET)
802 } else {
803 Loc::GluePar(r + MU_GLUE_OFFSET)
804 },
805 ValLevel::Mu,
806 )),
807 _ => None,
808 };
809 if let Some(x) = l {
810 break 'found x;
811 }
812 if self.cur_cmd != Cmd::Register {
813 self.print_err("You can't use `");
814 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
815 self.printer.print_str("' after ");
816 self.print_cmd_chr(q, 0);
817 self.help(&["I'm forgetting what you said and not changing anything."]);
818 self.error();
819 return;
820 }
821 }
822 let kind = self.cur_chr;
823 self.scan_register_num();
824 let n = self.cur_val as u32;
825 match kind {
826 reg::COUNT => (Loc::Count(n), ValLevel::Int),
827 reg::ATTRIBUTE => (Loc::Attr(n), ValLevel::Int),
828 reg::DIMEN => (Loc::Dimen(n), ValLevel::Dimen),
829 reg::SKIP => (Loc::Skip(n), ValLevel::Glue),
830 _ => (Loc::MuSkip(n), ValLevel::Mu),
831 }
832 };
833 if q == Cmd::Register {
834 self.scan_optional_equals();
835 } else {
836 let _ = self.scan_keyword("by");
837 }
838 let mut arith_error = false;
839 let old = self.eqtb.get(loc);
840 let result: Val = if q < Cmd::Multiply {
841 if p < ValLevel::Glue {
842 if p == ValLevel::Int {
843 self.scan_int();
844 } else {
845 self.scan_normal_dimen();
846 }
847 let mut v = self.cur_val;
848 if q == Cmd::Advance
849 && let Val::Int(o) = old
850 {
851 v = v.wrapping_add(o as i32);
852 }
853 Val::Int(i64::from(v))
854 } else {
855 self.scan_glue(p);
856 let mut g = self.cur_glue;
857 if q == Cmd::Advance
858 && let Val::Glue(r) = old
859 {
860 g = add_glue(g, r);
861 }
862 Val::Glue(g)
863 }
864 } else {
865 self.scan_int();
866 let f = self.cur_val;
867 let op = |x: i32, err: &mut bool| -> i32 {
868 let r = if q == Cmd::Multiply {
869 if p == ValLevel::Int {
870 arith::mult_integers(x, f)
871 } else {
872 arith::nx_plus_y(x, f, 0)
873 }
874 } else {
875 arith::x_over_n(x, f).map(|d| d.quotient)
876 };
877 r.unwrap_or_else(|_| {
878 *err = true;
879 0
880 })
881 };
882 match old {
883 Val::Int(x) => Val::Int(i64::from(op(x as i32, &mut arith_error))),
884 Val::Glue(g) => {
885 let mut n = g;
886 n.width = op(g.width, &mut arith_error);
887 n.stretch = op(g.stretch, &mut arith_error);
888 n.shrink = op(g.shrink, &mut arith_error);
889 Val::Glue(n)
890 }
891 other => other,
892 }
893 };
894 if arith_error {
895 self.print_err("Arithmetic overflow");
896 self.help(&[
897 "I can't carry out that multiplication or division,",
898 "since the result is out of range.",
899 ]);
900 self.error();
901 return;
902 }
903 self.define(global, loc, result);
904 }
905
906 pub fn new_interaction(&mut self) {
908 self.printer.print_ln();
909 self.err.interaction = crate::error::Interaction::from_i32(self.cur_chr as i32);
910 self.printer.selector = match (self.err.interaction, self.printer.log_opened()) {
911 (crate::error::Interaction::Batch, true) => crate::print::Selector::LogOnly,
912 (crate::error::Interaction::Batch, false) => crate::print::Selector::NoPrint,
913 (_, true) => crate::print::Selector::TermAndLog,
914 (_, false) => crate::print::Selector::TermOnly,
915 };
916 }
917
918 fn alter_integer(&mut self) {
921 let c = self.cur_chr;
922 self.scan_optional_equals();
923 self.scan_int();
924 match c {
925 0 => self.dead_cycles = self.cur_val,
926 1 => self.insert_penalties = self.cur_val,
927 _ => {
928 if (0..=3).contains(&self.cur_val) {
929 self.cur_chr = self.cur_val as u32;
930 self.new_interaction();
931 } else {
932 self.print_err("Bad interaction mode (");
933 self.printer.print_int(self.cur_val.into());
934 self.printer.print_char(')' as u32);
935 self.help(&[
936 "Modes are 0=batch, 1=nonstop, 2=scroll, and",
937 "3=errorstop. Proceed, and I'll ignore this case.",
938 ]);
939 self.int_error(self.cur_val);
940 }
941 }
942 }
943 }
944}
945
946pub fn add_glue(mut q: Glue, r: Glue) -> Glue {
948 q.width = q.width.wrapping_add(r.width);
949 if q.stretch == 0 {
950 q.stretch_order = crate::glue::NORMAL;
951 }
952 if q.stretch_order == r.stretch_order {
953 q.stretch = q.stretch.wrapping_add(r.stretch);
954 } else if q.stretch_order < r.stretch_order && r.stretch != 0 {
955 q.stretch = r.stretch;
956 q.stretch_order = r.stretch_order;
957 }
958 if q.shrink == 0 {
959 q.shrink_order = crate::glue::NORMAL;
960 }
961 if q.shrink_order == r.shrink_order {
962 q.shrink = q.shrink.wrapping_add(r.shrink);
963 } else if q.shrink_order < r.shrink_order && r.shrink != 0 {
964 q.shrink = r.shrink;
965 q.shrink_order = r.shrink_order;
966 }
967 q
968}