use super::Native;
use crate::Engine;
use crate::cmd::Cmd;
use crate::eqtb::{CodeKind, Loc, Val};
use crate::maincontrol::{Mode, ModeKind};
use crate::primitives_gen::assign_int as ip;
use crate::scan::REG_OFFSET;
use crate::token::{Token, TokenList};
pub(super) const ADD_TO_XML: Native = Native {
name: "hyxmp@add@to@xml",
fingerprints: &[(75, 0x7121_40da_a940_9c09)],
executes: true,
run,
};
const LOOP: &[u64] = &[0x0bef_e01f_8672_876e];
fn run(e: &mut Engine, args: &[TokenList]) -> bool {
if e.eqtb.int(ip::GLOBALDEFS) != 0
|| e.after_token.0 != 0
|| !matches!(e.mode.abs_kind(), ModeKind::Vertical | ModeKind::Horizontal)
|| e.mode == Mode::None
{
return false;
}
let cs = |name: &[u8]| e.hash.lookup(name);
let meaning = |name: &[u8]| cs(name).map(|id| e.eqtb.meaning(id));
let prim = |name: &[u8], cmd: Cmd, chr: u32| {
meaning(name).is_some_and(|m| m.cmd == cmd && m.chr == chr && m.mac.is_none())
};
let count_reg = |name: &[u8]| {
meaning(name)
.filter(|m| m.cmd == Cmd::AssignInt && m.chr >= REG_OFFSET && m.mac.is_none())
.map(|m| (m.chr - REG_OFFSET) as usize)
};
let (Some(count), Some(tempb)) = (count_reg(b"@hyxmp@count"), count_reg(b"@tempcntb")) else {
return false;
};
let unicode = if prim(b"ifhyxmp@unicodetex", Cmd::IfTest, 14) {
true
} else if prim(b"ifhyxmp@unicodetex", Cmd::IfTest, 15) {
false
} else {
return false;
};
let bgroup_ok = meaning(b"bgroup").is_some_and(|m| m.cmd == Cmd::LeftBrace && m.mac.is_none());
let loop_ok = meaning(b"loop").is_some_and(|m| {
matches!(m.cmd, Cmd::Call | Cmd::LongCall)
&& m.mac
.as_ref()
.is_some_and(|mac| LOOP.contains(&e.macro_fingerprint(mac)))
});
if super::debug()
&& !loop_ok
&& let Some(m) = meaning(b"loop")
&& let Some(mac) = &m.mac
{
eprintln!("[native] \\loop: 0x{:016x}", e.macro_fingerprint(mac));
}
if count == tempb
|| !bgroup_ok
|| !loop_ok
|| !prim(b"repeat", Cmd::FiOrElse, 2)
|| !prim(b"lccode", Cmd::DefCharCode, 1)
|| !prim(b"advance", Cmd::Advance, 0)
|| !prim(b"ifnum", Cmd::IfTest, 2)
|| !prim(b"relax", Cmd::Relax, 1_114_112)
{
if super::debug() {
for n in ["bgroup", "repeat", "lccode", "advance", "ifnum", "relax"] {
if let Some(m) = meaning(n.as_bytes()) {
eprintln!("[native] \\{n}: {:?} {}", m.cmd, m.chr);
}
}
eprintln!("[native] hyxmp@add@to@xml declines (meanings)");
}
return false;
}
let (Some(iterate), Some(relax), Some(this)) =
(cs(b"iterate"), cs(b"relax"), cs(b"hyxmp@add@to@xml"))
else {
return false;
};
let Some(body) = e.eqtb.meaning(this).mac.clone() else {
return false;
};
let Some(lower) = cs(b"lowercase").map(Token::cs) else {
return false;
};
let Some(start) = body.tokens.iter().position(|&t| t == lower) else {
return false;
};
let mut tail = Vec::with_capacity(body.tokens.len() - start + args[0].len());
for &t in &body.tokens[start..] {
if t == Token::out_param(1) {
tail.extend_from_slice(&args[0]);
} else {
tail.push(t);
}
}
let upto: u32 = if unicode { 65536 } else { 256 };
e.new_save_level(crate::save::GroupCode::Simple);
e.reset_local_counts(true);
e.define_codes_identity(CodeKind::Lc, upto);
e.eq_define(Loc::Count(count as u32), Val::Int(i64::from(upto)));
e.eq_define(Loc::Count(tempb as u32), Val::Int(i64::from(upto)));
let relax_m = e.eqtb.meaning(relax).clone();
e.eq_define(Loc::Cs(iterate), Val::Meaning(relax_m));
for (c, v) in [
(u32::from(b'_'), 32),
(3, i64::from(b',')),
(21, i64::from(b'_')),
] {
e.eq_define(Loc::Code(CodeKind::Lc, c), Val::Int(v));
}
e.native_insert(tail);
true
}