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