use std::cell::RefCell;
use super::Native;
use super::deps::Deps;
use crate::Engine;
use crate::token::{Token, TokenList};
pub(super) const TRIM_SPACES: Native = Native {
name: "__tl_trim_spaces:nn",
fingerprints: &[(16, 0x8450_0cf1_5dbc_0126), (16, 0xab79_0cc9_daf1_fe55)],
executes: false,
run: run_trim,
};
const NAMES: &[&str] = &[
"__tl_trim_spaces_auxi:w",
"__tl_trim_spaces_auxii:w",
"__tl_trim_spaces_auxiii:w",
"__tl_trim_spaces_auxiv:w",
"__tl_trim_mark:",
"s__tl_nil",
];
const KNOWN: &[u64] = &[0x101c_e402_7c72_cb09];
const NAMES_OLD: &[&str] = &[
"__tl_trim_spaces_auxi:w",
"__tl_trim_spaces_auxii:w",
"__tl_trim_spaces_auxiii:w",
"__tl_trim_spaces_auxiv:w",
"__tl_trim_mark:",
"s__tl_nil",
"s__tl_stop",
];
const KNOWN_OLD: &[u64] = &[0xce28_fb3a_c46c_1751];
thread_local! {
static DEPS: RefCell<Deps> = RefCell::new(Deps::new("trim_spaces", NAMES, KNOWN));
static DEPS_OLD: RefCell<Deps> =
RefCell::new(Deps::new("trim_spaces (one argument)", NAMES_OLD, KNOWN_OLD));
}
fn run_trim(e: &mut Engine, args: &[TokenList]) -> bool {
if args.len() == 1 {
return run_trim_old(e, &args[0]);
}
if !DEPS.with(|d| d.borrow_mut().check(e)) {
return false;
}
let mut markers = Vec::with_capacity(NAMES.len());
for n in NAMES {
let Some(cs) = e.hash.lookup(n.as_bytes()) else {
return false;
};
markers.push(Token::cs(cs));
}
let tl = &args[1];
if tl.iter().any(|t| markers.contains(t)) {
return false;
}
e.native_insert(result(&args[0], tl, markers[4]));
true
}
fn result(cont: &[Token], tl: &[Token], mark: Token) -> Vec<Token> {
let start = tl
.iter()
.position(|&t| t != Token::SPACE)
.unwrap_or(tl.len());
let end = tl
.iter()
.rposition(|&t| t != Token::SPACE)
.map_or(start, |i| i + 1);
let mut out = Vec::with_capacity(cont.len() + end - start + 3);
out.extend_from_slice(cont);
out.push(Token::LEFT_BRACE);
out.push(mark);
out.extend_from_slice(&tl[start..end]);
out.push(Token::RIGHT_BRACE);
out
}
fn run_trim_old(e: &mut Engine, arg: &[Token]) -> bool {
if !DEPS_OLD.with(|d| d.borrow_mut().check(e)) {
return false;
}
let mut markers = Vec::with_capacity(NAMES_OLD.len());
for n in NAMES_OLD {
let Some(cs) = e.hash.lookup(n.as_bytes()) else {
return false;
};
markers.push(Token::cs(cs));
}
let Some((&first, tl)) = arg.split_first() else {
return false;
};
if first != markers[4] || tl.iter().any(|t| markers.contains(t)) {
return false;
}
let Some(mut f) = e.native_following_subst() else {
return false;
};
let mut i = 0;
while f.get(e, i) == Some(Token::SPACE) {
i += 1;
}
let Some(t) = f.get(e, i) else {
return false;
};
let cont: Vec<Token>;
let used;
if t.is_cs() {
let m = e.eqtb.meaning(t.cs_id());
if matches!(
m.cmd,
crate::cmd::Cmd::OuterCall | crate::cmd::Cmd::LongOuterCall
) {
return false;
}
cont = vec![t];
used = i + 1;
} else if t.cmd_code() == crate::cmd::Cmd::LeftBrace as u32 {
let mut depth = 0usize;
let mut k = i;
let mut body = Vec::new();
loop {
let Some(u) = f.get(e, k) else {
return false;
};
k += 1;
if !u.is_cs() && u.cmd_code() == crate::cmd::Cmd::LeftBrace as u32 {
depth += 1;
if depth == 1 {
continue;
}
} else if !u.is_cs() && u.cmd_code() == crate::cmd::Cmd::RightBrace as u32 {
depth -= 1;
if depth == 0 {
break;
}
} else if u.is_cs()
&& matches!(
e.eqtb.meaning(u.cs_id()).cmd,
crate::cmd::Cmd::OuterCall | crate::cmd::Cmd::LongOuterCall
)
{
return false;
}
body.push(u);
}
cont = body;
used = k;
} else if t.cmd_code() == crate::cmd::Cmd::RightBrace as u32 {
return false;
} else {
cont = vec![t];
used = i + 1;
}
if f.count(e, used).is_none() {
return false;
}
e.native_consume(&f, used);
e.native_insert(result(&cont, tl, markers[4]));
true
}