use crate::Engine;
use crate::cmd::Cmd;
use crate::hash::{CsKind, NO_CS, UNDEFINED_CS};
use crate::input::{Level, NO_EXPAND_FLAG, ScannerStatus};
use crate::primitives_gen::assign_int as ip;
use crate::scan::ValLevel;
pub const IF_CODE: u32 = 1;
pub const FI_CODE: u32 = 2;
pub const ELSE_CODE: u32 = 3;
pub const OR_CODE: u32 = 4;
pub const UNLESS_CODE: u32 = 32;
#[allow(missing_docs)]
pub mod if_codes {
pub const IF_CHAR: u32 = 0;
pub const IF_CAT: u32 = 1;
pub const IF_INT: u32 = 2;
pub const IF_DIM: u32 = 3;
pub const IF_ODD: u32 = 4;
pub const IF_VMODE: u32 = 5;
pub const IF_HMODE: u32 = 6;
pub const IF_MMODE: u32 = 7;
pub const IF_INNER: u32 = 8;
pub const IF_VOID: u32 = 9;
pub const IF_HBOX: u32 = 10;
pub const IF_VBOX: u32 = 11;
pub const IFX: u32 = 12;
pub const IF_EOF: u32 = 13;
pub const IF_TRUE: u32 = 14;
pub const IF_FALSE: u32 = 15;
pub const IF_CASE: u32 = 16;
pub const IF_DEF: u32 = 17;
pub const IF_CS: u32 = 18;
pub const IF_FONT_CHAR: u32 = 19;
pub const IF_IN_CSNAME: u32 = 20;
pub const IF_PRIMITIVE: u32 = 21;
pub const IF_ABS_NUM: u32 = 22;
pub const IF_ABS_DIM: u32 = 23;
pub const IF_CONDITION: u32 = 24;
}
use if_codes::*;
#[derive(Clone, Copy, Debug)]
pub struct CondRecord {
pub if_limit: u32,
pub cur_if: u32,
pub if_line: i32,
}
#[derive(Debug, Default, Clone)]
pub struct CondState {
pub stack: Vec<CondRecord>,
pub if_limit: u32,
pub cur_if: u32,
pub if_line: i32,
pub skip_line: i32,
pub is_in_csname: bool,
}
impl Engine {
pub fn pass_text(&mut self) {
let save = self.input.scanner_status;
self.input.scanner_status = ScannerStatus::Skipping;
let mut l = 0i32;
self.cond.skip_line = self.input.line();
loop {
self.skip_plain_tokens();
self.get_next();
if self.cur_cmd == Cmd::FiOrElse {
if l == 0 {
break;
}
if self.cur_chr == FI_CODE {
l -= 1;
}
} else if self.cur_cmd == Cmd::IfTest {
l += 1;
}
}
self.input.scanner_status = save;
if self.eqtb.int(ip::TRACINGIFS) > 0 {
self.show_cur_cmd_chr();
}
}
fn skip_plain_tokens(&mut self) {
let Some(Level::Tokens(t)) = self.input.stack.last_mut() else {
return;
};
let mut loc = t.loc;
let mut align = 0i32;
while let Some(&tok) = t.list.get(loc) {
if tok.is_cs() {
let m = self.eqtb.meaning(tok.cs_id());
let plain =
m.cmd < Cmd::TabMark || (m.cmd > Cmd::CarRet && !m.cmd.is_outer_or_special());
if !plain || m.cmd == Cmd::FiOrElse || m.cmd == Cmd::IfTest {
break;
}
} else {
let cmd = tok.cmd();
if !(cmd > Cmd::CarRet || cmd < Cmd::MathShift) {
break;
}
match cmd {
Cmd::LeftBrace => align += 1,
Cmd::RightBrace => align -= 1,
_ => {}
}
}
loc += 1;
}
t.loc = loc;
self.input.align_state += align;
}
fn push_cond(&mut self) {
self.cond.stack.push(CondRecord {
if_limit: self.cond.if_limit,
cur_if: self.cond.cur_if,
if_line: self.cond.if_line,
});
self.cond.cur_if = self.cur_chr;
self.cond.if_limit = IF_CODE;
self.cond.if_line = self.input.line();
}
pub fn pop_cond(&mut self) {
if let Some(r) = self.cond.stack.pop() {
self.cond.if_line = r.if_line;
self.cond.cur_if = r.cur_if;
self.cond.if_limit = r.if_limit;
}
}
fn change_if_limit(&mut self, l: u32, p: usize) {
if p == self.cond.stack.len() {
self.cond.if_limit = l;
} else if let Some(r) = self.cond.stack.get_mut(p) {
r.if_limit = l;
} else {
self.confusion("if");
}
}
fn get_x_token_or_active_char(&mut self) {
self.get_x_token();
if self.cur_cmd == Cmd::Relax
&& self.cur_chr == NO_EXPAND_FLAG
&& let CsKind::Active(c) = self.hash.kind(self.cur_tok.cs_id())
{
self.cur_cmd = Cmd::ParEnd; self.cur_chr = *c;
}
}
pub fn conditional(&mut self) {
if self.eqtb.int(ip::TRACINGIFS) > 0 && self.eqtb.int(ip::TRACINGCOMMANDS) <= 1 {
self.show_cur_cmd_chr();
}
let is_unless = self.cur_chr >= UNLESS_CODE;
let this_if = self.cur_chr % UNLESS_CODE;
if this_if == IF_CONDITION {
return;
}
self.push_cond();
let save_cond_ptr = self.cond.stack.len();
let mut b: bool;
match this_if {
IF_CHAR | IF_CAT => {
self.get_x_token_or_active_char();
let (m, n) = if self.cur_cmd > Cmd::ParEnd || self.cur_chr > 0x10_FFFF {
(Cmd::Relax, 0x11_0000)
} else {
(self.cur_cmd, self.cur_chr)
};
self.get_x_token_or_active_char();
if self.cur_cmd > Cmd::ParEnd || self.cur_chr > 0x10_FFFF {
self.cur_cmd = Cmd::Relax;
self.cur_chr = 0x11_0000;
}
b = if this_if == IF_CHAR {
n == self.cur_chr
} else {
m == self.cur_cmd
};
}
IF_INT | IF_DIM | IF_ABS_NUM | IF_ABS_DIM => {
let dim = this_if == IF_DIM || this_if == IF_ABS_DIM;
let abs = this_if == IF_ABS_NUM || this_if == IF_ABS_DIM;
if dim {
self.scan_normal_dimen();
} else {
self.scan_int();
}
let mut n = self.cur_val;
if abs {
n = n.wrapping_abs();
}
loop {
self.get_x_token();
if self.cur_cmd != Cmd::Spacer {
break;
}
}
let r = if self.cur_tok.0 == crate::token::Token::other('<' as u32).0
|| self.cur_tok.0 == crate::token::Token::other('=' as u32).0
|| self.cur_tok.0 == crate::token::Token::other('>' as u32).0
{
self.cur_tok.chr()
} else {
self.print_err("Missing = inserted for ");
self.print_cmd_chr(Cmd::IfTest, this_if);
self.help(&["I was expecting to see `<', `=', or `>'. Didn't."]);
self.back_error();
'=' as u32
};
if dim {
self.scan_normal_dimen();
} else {
self.scan_int();
}
let mut v = self.cur_val;
if abs {
v = v.wrapping_abs();
}
b = match char::from_u32(r) {
Some('<') => n < v,
Some('=') => n == v,
_ => n > v,
};
}
IF_ODD => {
self.scan_int();
b = self.cur_val % 2 != 0;
}
IF_VMODE => b = self.mode.abs_kind() == crate::maincontrol::ModeKind::Vertical,
IF_HMODE => b = self.mode.abs_kind() == crate::maincontrol::ModeKind::Horizontal,
IF_MMODE => b = self.mode.abs_kind() == crate::maincontrol::ModeKind::Math,
IF_INNER => b = self.mode.is_inner(),
IF_VOID | IF_HBOX | IF_VBOX => {
self.scan_register_num();
let p = self.eqtb.boxes[self.cur_val as usize].val;
b = match this_if {
IF_VOID => p.is_null(),
IF_HBOX => !p.is_null() && self.box_is_hlist(p),
_ => !p.is_null() && !self.box_is_hlist(p),
};
}
IFX => {
let save = self.input.scanner_status;
self.input.scanner_status = ScannerStatus::Normal;
self.get_next();
let n = self.cur_cs;
let p = self.cur_cmd;
let q = self.cur_chr;
let first_mac = if n != NO_CS {
self.eqtb.meaning(n).mac.clone()
} else {
None
};
self.get_next();
b = if self.cur_cmd != p {
false
} else if !self.cur_cmd.is_call() {
self.cur_chr == q
} else {
let second = if self.cur_cs != NO_CS {
self.eqtb.meaning(self.cur_cs).mac.clone()
} else {
None
};
match (first_mac, second) {
(Some(a), Some(c)) => {
std::rc::Rc::ptr_eq(&a, &c)
|| (a.protected == c.protected && a.tokens == c.tokens)
}
_ => false,
}
};
self.input.scanner_status = save;
}
IF_EOF => {
self.scan_four_bit_int_or_more();
b = !self.files.read_open(self.cur_val as u32);
}
IF_TRUE => b = true,
IF_FALSE => b = false,
IF_DEF => {
let save = self.input.scanner_status;
self.input.scanner_status = ScannerStatus::Normal;
self.get_next();
b = self.cur_cmd != Cmd::UndefinedCs;
self.input.scanner_status = save;
}
IF_CS => {
let e = self.cond.is_in_csname;
self.cond.is_in_csname = true;
let mut name: Vec<u8> = Vec::new();
loop {
self.get_x_token();
if self.cur_cs != NO_CS {
break;
}
crate::utf8x::push(&mut name, self.cur_chr);
}
if self.cur_cmd != Cmd::EndCsName {
self.complain_missing_endcsname();
}
let cs = self.hash.lookup(&name).unwrap_or(UNDEFINED_CS);
self.cur_cs = cs;
b = self.eqtb.meaning(cs).cmd != Cmd::UndefinedCs;
self.lookup_named_cs(&name);
self.cond.is_in_csname = e;
}
IF_FONT_CHAR => {
self.scan_font_ident();
let f = self.cur_val as u32;
self.scan_char_num();
b = self.font_has_char(f, self.cur_val as u32);
}
IF_IN_CSNAME => b = self.cond.is_in_csname,
IF_PRIMITIVE => {
let save = self.input.scanner_status;
self.input.scanner_status = ScannerStatus::Normal;
self.get_next();
b = self.cur_cs != NO_CS && self.has_primitive_meaning(self.cur_cs);
self.input.scanner_status = save;
}
IF_CASE => {
self.scan_int();
let mut n = self.cur_val;
if self.eqtb.int(ip::TRACINGCOMMANDS) > 1 {
self.begin_diagnostic();
self.printer.print_str("{case ");
self.printer.print_int(n.into());
self.printer.print_char('}' as u32);
self.end_diagnostic(false);
}
let mut ended = false;
while n != 0 {
self.pass_text();
if self.cond.stack.len() == save_cond_ptr {
if self.cur_chr == OR_CODE {
n -= 1;
} else {
ended = true;
break;
}
} else if self.cur_chr == FI_CODE {
self.pop_cond();
}
}
if !ended {
self.change_if_limit(OR_CODE, save_cond_ptr);
return;
}
self.common_ending();
return;
}
_ => b = false,
}
let _ = ValLevel::Int;
if is_unless {
b = !b;
}
if self.eqtb.int(ip::TRACINGCOMMANDS) > 1 {
self.begin_diagnostic();
self.printer.print_str(if b { "{true}" } else { "{false}" });
self.end_diagnostic(false);
}
if b {
self.change_if_limit(ELSE_CODE, save_cond_ptr);
return;
}
loop {
self.pass_text();
if self.cond.stack.len() == save_cond_ptr {
if self.cur_chr != OR_CODE {
break;
}
self.print_err("Extra ");
self.print_esc_str("or");
self.help(&["I'm ignoring this; it doesn't match any \\if."]);
self.error();
} else if self.cur_chr == FI_CODE {
self.pop_cond();
}
}
self.common_ending();
}
fn common_ending(&mut self) {
if self.cur_chr == FI_CODE {
self.pop_cond();
} else {
self.cond.if_limit = FI_CODE;
}
}
pub fn fi_or_else(&mut self) {
if self.eqtb.int(ip::TRACINGIFS) > 0 && self.eqtb.int(ip::TRACINGCOMMANDS) <= 1 {
self.show_cur_cmd_chr();
}
if self.cur_chr > self.cond.if_limit {
if self.cond.if_limit == IF_CODE {
self.insert_relax();
} else {
self.print_err("Extra ");
self.print_cmd_chr(Cmd::FiOrElse, self.cur_chr);
self.help(&["I'm ignoring this; it doesn't match any \\if."]);
self.error();
}
} else {
while self.cur_chr != FI_CODE {
self.pass_text();
}
self.pop_cond();
}
}
pub fn has_primitive_meaning(&self, cs: crate::hash::CsId) -> bool {
let name = self.hash.name(cs);
match crate::primitives::lookup(name) {
Some(p) => {
let m = self.eqtb.meaning(cs);
m.cmd == p.cmd && m.chr == p.chr && m.mac.is_none()
}
None => false,
}
}
}