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