use std::rc::Rc;
use retex_arith as arith;
use crate::Engine;
use crate::cmd::Cmd;
use crate::eqtb::{CodeKind, Loc, MU_GLUE_OFFSET, Meaning, Val};
use crate::expand::RELAX_CHR;
use crate::glue::Glue;
use crate::hash::{CsId, NO_CS};
use crate::primitives_gen as pg;
use crate::scan::{REG_OFFSET, ValLevel, reg};
use crate::token::{Macro, Token, TokenList};
pub mod prefix {
pub const LONG: u32 = 1;
pub const OUTER: u32 = 2;
pub const GLOBAL: u32 = 4;
pub const PROTECTED: u32 = 8;
}
#[allow(missing_docs)]
pub mod shorthand {
pub const CHAR_DEF: u32 = 0;
pub const MATH_CHAR_DEF: u32 = 1;
pub const UMATH_CHAR_DEF: u32 = 2;
pub const COUNT_DEF: u32 = 3;
pub const ATTRIBUTE_DEF: u32 = 4;
pub const DIMEN_DEF: u32 = 5;
pub const SKIP_DEF: u32 = 6;
pub const MU_SKIP_DEF: u32 = 7;
pub const TOKS_DEF: u32 = 8;
pub const UMATH_CHAR_NUM_DEF: u32 = 9;
}
impl Engine {
pub fn get_r_token(&mut self) {
loop {
loop {
self.get_token();
if self.cur_tok != Token::SPACE {
break;
}
}
let frozen = self.cur_cs != NO_CS
&& matches!(self.hash.kind(self.cur_cs), crate::hash::CsKind::Frozen);
if self.cur_cs == NO_CS || frozen {
self.print_err("Missing control sequence inserted");
self.help(&[
"Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",
"I've inserted an inaccessible control sequence so that your",
"definition will be completed without mixing me up too badly.",
"You can recover graciously from this error, if you're",
"careful; see exercise 27.2 in The TeXbook.",
]);
if self.cur_cs == NO_CS {
self.back_input();
}
self.cur_tok = Token::cs(self.cs.frozen_protection);
self.ins_error();
continue;
}
return;
}
}
fn define_cs(&mut self, global: bool, p: CsId, meaning: Meaning) {
self.define(global, Loc::Cs(p), Val::Meaning(meaning));
}
pub fn prefixed_command(&mut self) {
if self.cur_cmd != Cmd::Prefix && self.eqtb.int(pg::assign_int::GLOBALDEFS) == 0 {
match self.cur_cmd {
Cmd::Let => self.let_command(false),
Cmd::DefCharCode => self.def_char_code(false),
Cmd::Register | Cmd::Advance | Cmd::Multiply | Cmd::Divide => {
self.do_register_command(false)
}
Cmd::AssignInt => self.assign_int_command(false),
_ => return self.prefixed_command_general(),
}
if self.after_token.0 != 0 {
self.cur_tok = self.after_token;
self.back_input();
self.after_token = Token(0);
}
return;
}
self.prefixed_command_general();
}
fn assign_int_command(&mut self, global: bool) {
let p = self.cur_chr;
self.scan_optional_equals();
self.scan_int();
let loc = if p >= REG_OFFSET {
Loc::Count(p - REG_OFFSET)
} else {
Loc::IntPar(p)
};
self.assign_int_param(global, loc, self.cur_val);
if (p == pg::assign_int::LOCALINTERLINEPENALTY || p == pg::assign_int::LOCALBROKENPENALTY)
&& self.mode == crate::maincontrol::Mode::Horizontal
{
let lp = self.new_local_par();
self.tail_append(lp);
}
}
#[inline(never)]
fn prefixed_command_general(&mut self) {
let mut a: u32 = 0;
while self.cur_cmd == Cmd::Prefix {
if a & self.cur_chr == 0 {
a += self.cur_chr;
}
self.get_nonblank_nonrelax_noncall();
if self.cur_cmd <= Cmd::MAX_NON_PREFIXED {
self.print_err("You can't use a prefix with `");
self.print_cmd_chr(self.cur_cmd, self.cur_chr);
self.printer.print_char('\'' as u32);
self.help(&[
"I'll pretend you didn't say \\long or \\outer or \\global or",
"\\protected.",
]);
self.back_error();
return;
}
if self.eqtb.int(pg::assign_int::TRACINGCOMMANDS) > 2 {
self.show_cur_cmd_chr();
}
}
if self.cur_cmd != Cmd::Def
&& self.cur_cmd != Cmd::DefLuaCall
&& (a & (prefix::LONG | prefix::OUTER | prefix::PROTECTED)) != 0
&& self.eqtb.int(pg::assign_int::SUPPRESSLONGERROR) <= 0
{
self.print_err("You can't use `");
self.print_esc_str("long");
self.printer.print_str("' or `");
self.print_esc_str("outer");
self.help(&["I'll pretend you didn't say \\long or \\outer or \\protected here."]);
self.printer.print_str("' or `");
self.print_esc_str("protected");
self.printer.print_str("' with `");
self.print_cmd_chr(self.cur_cmd, self.cur_chr);
self.printer.print_char('\'' as u32);
self.error();
}
let global_defs = self.eqtb.int(pg::assign_int::GLOBALDEFS);
if global_defs != 0 {
if global_defs < 0 {
a &= !prefix::GLOBAL;
} else {
a |= prefix::GLOBAL;
}
}
let global = a & prefix::GLOBAL != 0;
match self.cur_cmd {
Cmd::SetFont => {
let f = self.cur_chr;
self.define(global, Loc::CurFont, Val::Int(i64::from(f)));
}
Cmd::Def => {
let mut a = a;
if self.cur_chr & 1 != 0 && !global && global_defs >= 0 {
a |= prefix::GLOBAL;
}
let e = self.cur_chr >= 2;
self.get_r_token();
let p = self.cur_cs;
self.def_ref_cs = p;
let toks = self.scan_toks(true, e);
self.def_ref_cs = NO_CS;
let cmd = Cmd::from_u8(Cmd::Call as u8 + (a & 3) as u8);
let mac = Macro {
protected: a & prefix::PROTECTED != 0,
tokens: Rc::from(toks),
};
self.define_cs(
a & prefix::GLOBAL != 0,
p,
Meaning {
cmd,
chr: 0,
mac: Some(Rc::new(mac)),
},
);
}
Cmd::Let => self.let_command(global),
Cmd::ShorthandDef => self.shorthand_def(global),
Cmd::ReadToCs => {
let j = self.cur_chr;
self.scan_int();
let n = self.cur_val;
if !self.scan_keyword("to") {
self.print_err("Missing `to' inserted");
self.help(&[
"You should have said `\\read<number> to \\cs'.",
"I'm going to look for the \\cs now.",
]);
self.error();
}
self.get_r_token();
let p = self.cur_cs;
let toks = self.read_toks(n, p, j);
self.define_cs(
global,
p,
Meaning {
cmd: Cmd::Call,
chr: 0,
mac: Some(Rc::new(Macro {
protected: false,
tokens: Rc::from(toks),
})),
},
);
}
Cmd::ToksRegister | Cmd::AssignToks => self.assign_toks(global),
Cmd::Combinetoks => self.combine_toks(),
Cmd::AssignInt => self.assign_int_command(global),
Cmd::AssignAttr => {
let p = self.cur_chr - REG_OFFSET.min(self.cur_chr);
self.scan_optional_equals();
self.scan_int();
self.define(global, Loc::Attr(p), Val::Int(i64::from(self.cur_val)));
}
Cmd::AssignDir | Cmd::AssignDirection => {
let p = self.cur_chr;
let by_name = self.cur_cmd == Cmd::AssignDir;
self.scan_optional_equals();
let d = if by_name {
self.scan_direction_spec()
} else {
self.scan_int();
self.cur_val
};
self.assign_dir_param(global, p, d);
if p == crate::primitives_gen::assign_dir::TEXTDIR {
self.text_dir_assigned(d);
}
}
Cmd::AssignDimen => {
let p = self.cur_chr;
self.scan_optional_equals();
self.scan_normal_dimen();
let loc = if p >= REG_OFFSET {
Loc::Dimen(p - REG_OFFSET)
} else {
Loc::DimenPar(p)
};
self.define(global, loc, Val::Int(i64::from(self.cur_val)));
}
Cmd::AssignGlue | Cmd::AssignMuGlue => {
let p = self.cur_chr;
let mu = self.cur_cmd == Cmd::AssignMuGlue;
self.scan_optional_equals();
self.scan_glue(if mu { ValLevel::Mu } else { ValLevel::Glue });
let g = self.cur_glue;
let loc = match (mu, p >= REG_OFFSET) {
(false, true) => Loc::Skip(p - REG_OFFSET),
(true, true) => Loc::MuSkip(p - REG_OFFSET),
(false, false) => Loc::GluePar(p),
(true, false) => Loc::GluePar(p + MU_GLUE_OFFSET),
};
self.define(global, loc, Val::Glue(g));
}
Cmd::DefCharCode => self.def_char_code(global),
Cmd::DefDelCode => {
self.scan_char_num();
let c = self.cur_val as u32;
self.scan_optional_equals();
self.scan_int();
let v = self.cur_val;
if v > 0o77777777 {
self.invalid_code(v, 0o77777777, false);
}
let code = crate::codes::delcode_from_classic(self.cur_val);
self.define(global, Loc::Code(CodeKind::Del, c), Val::Int(code));
}
Cmd::ExtdefMathCode => {
let num = self.cur_chr != pg::extdef_math_code::UMATHCODE;
self.scan_char_num();
let c = self.cur_val as u32;
self.scan_optional_equals();
let code = if num {
self.scan_int();
i64::from(self.cur_val)
} else {
self.scan_umath_triple()
};
self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
}
Cmd::ExtdefDelCode => {
let num = self.cur_chr != pg::extdef_del_code::UDELCODE;
self.scan_char_num();
let c = self.cur_val as u32;
self.scan_optional_equals();
let code = if num {
self.scan_int();
i64::from(self.cur_val)
} else {
self.scan_math_family();
let fam = self.cur_val as i64;
self.scan_char_num();
(fam << 21) + i64::from(self.cur_val)
};
self.define(global, Loc::Code(CodeKind::Del, c), Val::Int(code));
}
Cmd::DefFamily => {
let size = self.cur_chr;
self.scan_math_family();
let fam = self.cur_val as u32;
self.scan_optional_equals();
self.scan_font_ident();
let f = self.cur_val;
self.define(
global,
Loc::MathFont(size * crate::eqtb::NUM_FAMILIES as u32 + fam),
Val::Int(i64::from(f)),
);
self.set_font_math_params(global, size, fam, f as u32);
}
Cmd::SetMathParam => {
let p = self.cur_chr;
let _ = self.scan_math_style();
let style = self.cur_val as u32;
self.scan_optional_equals();
let v = if crate::codes::math_param_is_glue(p) {
self.scan_glue(ValLevel::Mu);
self.store_math_param_glue(self.cur_glue)
} else if p == crate::mathparams::mp::RADICAL_DEGREE_RAISE {
self.scan_int();
self.cur_val
} else {
self.scan_normal_dimen();
self.cur_val
};
self.define(
global,
Loc::MathParam(p * 8 + style),
Val::Int(i64::from(v)),
);
}
Cmd::Register | Cmd::Advance | Cmd::Multiply | Cmd::Divide => {
self.do_register_command(global)
}
Cmd::SetBox => {
self.scan_register_num();
let n = self.cur_val as u32;
let ctx = if global {
crate::maincontrol::BoxContext::GlobalSetBox(n)
} else {
crate::maincontrol::BoxContext::SetBox(n)
};
self.scan_optional_equals();
if self.err.set_box_allowed {
self.scan_box(ctx);
} else {
self.print_err("Improper ");
self.print_esc_str("setbox");
self.help(&[
"Sorry, \\setbox is not allowed after \\halign in a display,",
"or between \\accent and an accented character.",
]);
self.error();
}
}
Cmd::SetAux => self.alter_aux(),
Cmd::SetPrevGraf => self.alter_prev_graf(),
Cmd::SetPageDimen => self.alter_page_so_far(),
Cmd::SetPageInt => self.alter_integer(),
Cmd::SetBoxDimen => self.alter_box_dimen(),
Cmd::SetTexShape => {
self.scan_optional_equals();
self.scan_int();
let n = self.cur_val;
let shape = if n <= 0 {
None
} else {
let mut v = Vec::with_capacity(2 * n as usize);
for _ in 0..n {
self.scan_normal_dimen();
v.push(self.cur_val);
self.scan_normal_dimen();
v.push(self.cur_val);
}
Some(Rc::from(v))
};
self.define(global, Loc::ParShape, Val::Shape(shape));
}
Cmd::SetEtexShape => {
let q = self.cur_chr;
self.scan_optional_equals();
self.scan_int();
let n = self.cur_val;
let shape = if n <= 0 {
None
} else {
let mut v = Vec::with_capacity(n as usize);
for _ in 0..n {
self.scan_int();
v.push(self.cur_val);
}
Some(Rc::from(v))
};
self.define(global, Loc::EtexShape(q), Val::Shape(shape));
}
Cmd::HyphData => self.hyph_data(global),
Cmd::AssignFontDimen => {
self.find_font_dimen(true);
let loc = self.font_dimen_loc;
self.scan_optional_equals();
self.scan_normal_dimen();
self.set_font_dimen(loc, self.cur_val);
}
Cmd::AssignFontInt => {
let n = self.cur_chr;
self.scan_font_ident();
let f = self.cur_val as u32;
if (2..=5).contains(&n) {
self.scan_char_num();
let c = self.cur_val as u32;
self.scan_optional_equals();
self.scan_int();
self.set_font_char_code(f, n, c, self.cur_val);
} else {
self.scan_optional_equals();
self.scan_int();
self.set_font_int_param(f, n, self.cur_val);
}
}
Cmd::DefFont => self.new_font(global),
Cmd::LetterspaceFont => self.letterspace_font(global),
Cmd::CopyFont => self.copy_font(global),
Cmd::SetFontId => {
self.scan_int();
let f = self.cur_val;
if self.font_exists(f) {
self.define(global, Loc::CurFont, Val::Int(i64::from(f)));
}
}
Cmd::SetInteraction => self.new_interaction(),
Cmd::DefLuaCall => self.def_lua_call(global, a & prefix::PROTECTED != 0),
Cmd::AssignBoxDir | Cmd::AssignBoxDirection => {
self.scan_register_num();
let n = self.cur_val as u32;
self.scan_optional_equals();
let d = if self.cur_cmd == Cmd::AssignBoxDir {
self.scan_direction_spec()
} else {
self.scan_int();
self.cur_val
};
self.set_box_dir(n, d);
}
_ => self.confusion("prefix"),
}
if self.after_token.0 != 0 {
self.cur_tok = self.after_token;
self.back_input();
self.after_token = Token(0);
}
}
fn assign_int_param(&mut self, global: bool, loc: Loc, v: i32) {
self.define(global, loc, Val::Int(i64::from(v)));
if let Loc::IntPar(i) = loc {
self.int_param_changed(i);
}
}
pub fn int_param_changed(&mut self, i: u32) {
if i == pg::assign_int::NEWLINECHAR {
self.printer.new_line_char = self.eqtb.int(i);
} else if i == pg::assign_int::SYNCTEX {
self.synctex_changed();
}
}
fn let_command(&mut self, global: bool) {
let n = self.cur_chr;
let mut global = global;
if n == 3 {
self.scan_int();
let c = self.cur_val;
if !(1..=0x10_FFFF).contains(&c) {
self.print_err("invalid number for \\letcharcode");
self.help(&[]);
self.error();
return;
}
let p = self.hash.active(c as u32);
self.scan_optional_equals();
self.get_token();
let m = self.token_meaning();
self.define_cs(global, p, m);
return;
}
if n == 0 {
global = true; }
self.get_r_token();
let p = self.cur_cs;
if n <= 1 {
loop {
self.get_token();
if self.cur_cmd != Cmd::Spacer {
break;
}
}
if self.cur_tok == Token::other('=' as u32) {
self.get_token();
if self.cur_cmd == Cmd::Spacer {
self.get_token();
}
}
} else {
self.get_token();
let q = self.cur_tok;
self.get_token();
self.back_input();
self.cur_tok = q;
self.back_input(); }
let m = self.token_meaning();
self.define_cs(global, p, m);
}
fn token_meaning(&self) -> Meaning {
if self.cur_cs != NO_CS {
let mut m = self.eqtb.meaning(self.cur_cs).clone();
if m.cmd != self.cur_cmd {
m = Meaning::prim(self.cur_cmd, self.cur_chr);
}
m
} else {
Meaning::prim(self.cur_cmd, self.cur_chr)
}
}
fn shorthand_def(&mut self, global: bool) {
let n = self.cur_chr;
self.get_r_token();
let p = self.cur_cs;
self.define_cs(global, p, Meaning::prim(Cmd::Relax, RELAX_CHR));
self.scan_optional_equals();
let m = match n {
shorthand::CHAR_DEF => {
self.scan_char_num();
Meaning::prim(Cmd::CharGiven, self.cur_val as u32)
}
shorthand::MATH_CHAR_DEF => {
let g = self.scan_math_given();
Meaning::prim(Cmd::MathGiven, g)
}
shorthand::UMATH_CHAR_DEF => {
let code = self.scan_umath_triple();
Meaning::prim(Cmd::XmathGiven, code as u32)
}
shorthand::UMATH_CHAR_NUM_DEF => {
self.scan_int();
Meaning::prim(Cmd::XmathGiven, self.cur_val as u32)
}
_ => {
self.scan_register_num();
let r = self.cur_val as u32 + REG_OFFSET;
match n {
shorthand::COUNT_DEF => Meaning::prim(Cmd::AssignInt, r),
shorthand::ATTRIBUTE_DEF => Meaning::prim(Cmd::AssignAttr, r),
shorthand::DIMEN_DEF => Meaning::prim(Cmd::AssignDimen, r),
shorthand::SKIP_DEF => Meaning::prim(Cmd::AssignGlue, r),
shorthand::MU_SKIP_DEF => Meaning::prim(Cmd::AssignMuGlue, r),
_ => Meaning::prim(Cmd::AssignToks, r),
}
}
};
self.define_cs(global, p, m);
}
pub(crate) fn scan_umath_triple(&mut self) -> i64 {
self.scan_int();
let class = self.cur_val;
self.scan_math_family();
let fam = self.cur_val;
self.scan_char_num();
let chr = self.cur_val;
if !(0..=7).contains(&class) {
self.print_err("Invalid math code");
self.help(&["I'm going to use 0 instead of that illegal code value."]);
self.error();
return 0;
}
crate::codes::math_code(class as u32, fam as u32, chr as u32)
}
fn assign_toks(&mut self, global: bool) {
let q = self.cur_cs;
let loc = if self.cur_cmd == Cmd::ToksRegister {
self.scan_register_num();
Loc::Toks(self.cur_val as u32)
} else if self.cur_chr >= REG_OFFSET {
Loc::Toks(self.cur_chr - REG_OFFSET)
} else {
Loc::ToksPar(self.cur_chr)
};
self.scan_optional_equals();
self.get_nonblank_nonrelax_noncall();
if self.cur_cmd != Cmd::LeftBrace {
let src = if self.cur_cmd == Cmd::ToksRegister {
self.scan_register_num();
Some(self.eqtb.toks[self.cur_val as usize].val.clone())
} else if self.cur_cmd == Cmd::AssignToks {
Some(if self.cur_chr >= REG_OFFSET {
self.eqtb.toks[(self.cur_chr - REG_OFFSET) as usize]
.val
.clone()
} else {
self.eqtb.toks_param(self.cur_chr).clone()
})
} else {
None
};
if let Some(list) = src {
self.define(global, loc, Val::Toks(list));
return;
}
}
self.back_input();
self.cur_cs = q;
self.def_ref_cs = q;
let mut toks = self.scan_toks(false, false);
self.def_ref_cs = NO_CS;
if !toks.is_empty() && loc == Loc::ToksPar(pg::assign_toks::OUTPUT) {
toks.insert(0, Token::LEFT_BRACE);
toks.push(Token::RIGHT_BRACE);
}
let list: TokenList = if toks.is_empty() {
crate::token::empty_list()
} else {
Rc::from(toks)
};
self.define(global, loc, Val::Toks(list));
}
fn combine_toks(&mut self) {
let code = self.cur_chr;
let global = code >= 4;
let expand = code % 2 == 1;
let append = code % 4 < 2;
self.get_nonblank_nonrelax_noncall();
let loc = if self.cur_cmd == Cmd::AssignToks {
if self.cur_chr >= REG_OFFSET {
Loc::Toks(self.cur_chr - REG_OFFSET)
} else {
Loc::ToksPar(self.cur_chr)
}
} else if self.cur_cmd == Cmd::ToksRegister {
self.scan_register_num();
Loc::Toks(self.cur_val as u32)
} else {
self.back_input();
self.scan_register_num();
Loc::Toks(self.cur_val as u32)
};
let new = if expand {
self.scan_toks_expanded()
} else {
self.scan_general_text().to_vec()
};
let old = match self.eqtb.get(loc) {
Val::Toks(t) => t,
_ => crate::token::empty_list(),
};
let mut v: Vec<Token> = Vec::with_capacity(old.len() + new.len());
if append {
v.extend_from_slice(&old);
v.extend_from_slice(&new);
} else {
v.extend_from_slice(&new);
v.extend_from_slice(&old);
}
self.define(global, loc, Val::Toks(Rc::from(v)));
}
fn invalid_code(&mut self, v: i32, n: i32, range: bool) {
self.print_err("Invalid code (");
self.printer.print_int(v.into());
if range {
self.printer.print_str("), should be in the range 0..");
} else {
self.printer.print_str("), should be at most ");
}
self.printer.print_int(n.into());
self.help(&["I'm going to use 0 instead of that illegal code value."]);
self.error();
self.cur_val = 0;
}
fn def_char_code(&mut self, global: bool) {
use pg::def_char_code as dc;
let which = self.cur_chr;
let n: i32 = match which {
dc::CATCODE => 15,
dc::MATHCODE => 0x8000,
dc::SFCODE => 0o77777,
_ => 0x10_FFFF,
};
self.scan_char_num();
let c = self.cur_val as u32;
self.scan_optional_equals();
self.scan_int();
if self.cur_val < 0 || self.cur_val > n {
self.invalid_code(self.cur_val, n, true);
}
let v = self.cur_val;
match which {
dc::CATCODE => {
let table = self.eqtb.cur_cat_table();
self.define(global, Loc::Cat { table, ch: c }, Val::Int(i64::from(v)));
}
dc::LCCODE => self.define(global, Loc::Code(CodeKind::Lc, c), Val::Int(v.into())),
dc::UCCODE => self.define(global, Loc::Code(CodeKind::Uc, c), Val::Int(v.into())),
dc::SFCODE => self.define(global, Loc::Code(CodeKind::Sf, c), Val::Int(v.into())),
_ => {
let code = if v == 0x8000 {
crate::codes::MATH_ACTIVE
} else {
let v = v as u32;
crate::codes::math_code(v >> 12, (v >> 8) & 0xF, v & 0xFF)
};
self.define(global, Loc::Code(CodeKind::Math, c), Val::Int(code));
}
}
}
fn do_register_command(&mut self, global: bool) {
let q = self.cur_cmd;
let (loc, p): (Loc, ValLevel) = 'found: {
if q != Cmd::Register {
self.get_x_token();
let r = self.cur_chr;
let l = match self.cur_cmd {
Cmd::AssignInt => Some((
if r >= REG_OFFSET {
Loc::Count(r - REG_OFFSET)
} else {
Loc::IntPar(r)
},
ValLevel::Int,
)),
Cmd::AssignAttr => Some((Loc::Attr(r - REG_OFFSET.min(r)), ValLevel::Int)),
Cmd::AssignDimen => Some((
if r >= REG_OFFSET {
Loc::Dimen(r - REG_OFFSET)
} else {
Loc::DimenPar(r)
},
ValLevel::Dimen,
)),
Cmd::AssignGlue => Some((
if r >= REG_OFFSET {
Loc::Skip(r - REG_OFFSET)
} else {
Loc::GluePar(r)
},
ValLevel::Glue,
)),
Cmd::AssignMuGlue => Some((
if r >= REG_OFFSET {
Loc::MuSkip(r - REG_OFFSET)
} else {
Loc::GluePar(r + MU_GLUE_OFFSET)
},
ValLevel::Mu,
)),
_ => None,
};
if let Some(x) = l {
break 'found x;
}
if self.cur_cmd != Cmd::Register {
self.print_err("You can't use `");
self.print_cmd_chr(self.cur_cmd, self.cur_chr);
self.printer.print_str("' after ");
self.print_cmd_chr(q, 0);
self.help(&["I'm forgetting what you said and not changing anything."]);
self.error();
return;
}
}
let kind = self.cur_chr;
self.scan_register_num();
let n = self.cur_val as u32;
match kind {
reg::COUNT => (Loc::Count(n), ValLevel::Int),
reg::ATTRIBUTE => (Loc::Attr(n), ValLevel::Int),
reg::DIMEN => (Loc::Dimen(n), ValLevel::Dimen),
reg::SKIP => (Loc::Skip(n), ValLevel::Glue),
_ => (Loc::MuSkip(n), ValLevel::Mu),
}
};
if q == Cmd::Register {
self.scan_optional_equals();
} else {
let _ = self.scan_keyword("by");
}
let mut arith_error = false;
let old = self.eqtb.get(loc);
let result: Val = if q < Cmd::Multiply {
if p < ValLevel::Glue {
if p == ValLevel::Int {
self.scan_int();
} else {
self.scan_normal_dimen();
}
let mut v = self.cur_val;
if q == Cmd::Advance
&& let Val::Int(o) = old
{
v = v.wrapping_add(o as i32);
}
Val::Int(i64::from(v))
} else {
self.scan_glue(p);
let mut g = self.cur_glue;
if q == Cmd::Advance
&& let Val::Glue(r) = old
{
g = add_glue(g, r);
}
Val::Glue(g)
}
} else {
self.scan_int();
let f = self.cur_val;
let op = |x: i32, err: &mut bool| -> i32 {
let r = if q == Cmd::Multiply {
if p == ValLevel::Int {
arith::mult_integers(x, f)
} else {
arith::nx_plus_y(x, f, 0)
}
} else {
arith::x_over_n(x, f).map(|d| d.quotient)
};
r.unwrap_or_else(|_| {
*err = true;
0
})
};
match old {
Val::Int(x) => Val::Int(i64::from(op(x as i32, &mut arith_error))),
Val::Glue(g) => {
let mut n = g;
n.width = op(g.width, &mut arith_error);
n.stretch = op(g.stretch, &mut arith_error);
n.shrink = op(g.shrink, &mut arith_error);
Val::Glue(n)
}
other => other,
}
};
if arith_error {
self.print_err("Arithmetic overflow");
self.help(&[
"I can't carry out that multiplication or division,",
"since the result is out of range.",
]);
self.error();
return;
}
self.define(global, loc, result);
}
pub fn new_interaction(&mut self) {
self.printer.print_ln();
self.err.interaction = crate::error::Interaction::from_i32(self.cur_chr as i32);
self.printer.selector = match (self.err.interaction, self.printer.log_opened()) {
(crate::error::Interaction::Batch, true) => crate::print::Selector::LogOnly,
(crate::error::Interaction::Batch, false) => crate::print::Selector::NoPrint,
(_, true) => crate::print::Selector::TermAndLog,
(_, false) => crate::print::Selector::TermOnly,
};
}
fn alter_integer(&mut self) {
let c = self.cur_chr;
self.scan_optional_equals();
self.scan_int();
match c {
0 => self.dead_cycles = self.cur_val,
1 => self.insert_penalties = self.cur_val,
_ => {
if (0..=3).contains(&self.cur_val) {
self.cur_chr = self.cur_val as u32;
self.new_interaction();
} else {
self.print_err("Bad interaction mode (");
self.printer.print_int(self.cur_val.into());
self.printer.print_char(')' as u32);
self.help(&[
"Modes are 0=batch, 1=nonstop, 2=scroll, and",
"3=errorstop. Proceed, and I'll ignore this case.",
]);
self.int_error(self.cur_val);
}
}
}
}
}
pub fn add_glue(mut q: Glue, r: Glue) -> Glue {
q.width = q.width.wrapping_add(r.width);
if q.stretch == 0 {
q.stretch_order = crate::glue::NORMAL;
}
if q.stretch_order == r.stretch_order {
q.stretch = q.stretch.wrapping_add(r.stretch);
} else if q.stretch_order < r.stretch_order && r.stretch != 0 {
q.stretch = r.stretch;
q.stretch_order = r.stretch_order;
}
if q.shrink == 0 {
q.shrink_order = crate::glue::NORMAL;
}
if q.shrink_order == r.shrink_order {
q.shrink = q.shrink.wrapping_add(r.shrink);
} else if q.shrink_order < r.shrink_order && r.shrink != 0 {
q.shrink = r.shrink;
q.shrink_order = r.shrink_order;
}
q
}