use core::{fmt::Write, marker::PhantomData, mem::size_of, ptr::NonNull};
use crate::{
dictionary::{BuiltinEntry, DictLocation, DictionaryEntry, EntryHeader, EntryKind},
fastr::comptime_fastr,
vm::TmpFaStr,
word::Word,
Error, Forth, Lookup, Mode, ReplaceErr,
};
#[cfg(feature = "floats")]
pub mod floats;
#[macro_export]
macro_rules! builtin {
($name:literal, $func:expr) => {
BuiltinEntry {
hdr: EntryHeader {
name: comptime_fastr($name),
kind: EntryKind::StaticBuiltin,
len: 0,
_pd: core::marker::PhantomData,
},
func: $func,
}
};
}
#[macro_export]
macro_rules! async_builtin {
($name:literal) => {
$crate::dictionary::AsyncBuiltinEntry {
hdr: $crate::dictionary::EntryHeader {
name: $crate::fastr::comptime_fastr($name),
kind: $crate::dictionary::EntryKind::AsyncBuiltin,
len: 0,
_pd: core::marker::PhantomData,
},
}
};
}
#[macro_export]
macro_rules! builtin_if_feature {
($feature:literal, $name:literal, $func:expr) => {
#[cfg(feature = $feature)]
builtin!($name, $func)
};
}
impl<T: 'static> Forth<T> {
pub const FULL_BUILTINS: &'static [BuiltinEntry<T>] = &[
builtin!("+", Self::add),
builtin!("-", Self::minus),
builtin!("/", Self::div),
builtin!("mod", Self::modu),
builtin!("/mod", Self::div_mod),
builtin!("*", Self::mul),
builtin!("abs", Self::abs),
builtin!("negate", Self::negate),
builtin!("min", Self::min),
builtin!("max", Self::max),
builtin_if_feature!("floats", "f+", Self::float_add),
builtin_if_feature!("floats", "f-", Self::float_minus),
builtin_if_feature!("floats", "f/", Self::float_div),
builtin_if_feature!("floats", "fmod", Self::float_modu),
builtin_if_feature!("floats", "f/mod", Self::float_div_mod),
builtin_if_feature!("floats", "f*", Self::float_mul),
builtin_if_feature!("floats", "fabs", Self::float_abs),
builtin_if_feature!("floats", "fnegate", Self::float_negate),
builtin_if_feature!("floats", "fmin", Self::float_min),
builtin_if_feature!("floats", "fmax", Self::float_max),
builtin!("*/", Self::star_slash),
builtin!("*/mod", Self::star_slash_mod),
builtin!("not", Self::invert),
builtin!("and", Self::and),
builtin!("=", Self::equal),
builtin!(">", Self::greater),
builtin!("<", Self::less),
builtin!("0=", Self::zero_equal),
builtin!("0>", Self::zero_greater),
builtin!("0<", Self::zero_less),
builtin!("swap", Self::swap),
builtin!("dup", Self::dup),
builtin!("over", Self::over),
builtin!("rot", Self::rot),
builtin!("drop", Self::ds_drop),
builtin!("2swap", Self::swap_2),
builtin!("2dup", Self::dup_2),
builtin!("2over", Self::over_2),
builtin!("2drop", Self::ds_drop_2),
builtin!("emit", Self::emit),
builtin!("cr", Self::cr),
builtin!("space", Self::space),
builtin!("spaces", Self::spaces),
builtin!(".", Self::pop_print),
builtin!("u.", Self::unsigned_pop_print),
builtin_if_feature!("floats", "f.", Self::float_pop_print),
builtin!(":", Self::colon),
builtin!("forget", Self::forget),
builtin!("d>r", Self::data_to_return_stack),
builtin!("2d>2r", Self::data2_to_return2_stack),
builtin!("r>d", Self::return_to_data_stack),
builtin!("i", Self::loop_i),
builtin!("i'", Self::loop_itick),
builtin!("j", Self::loop_j),
builtin!("leave", Self::loop_leave),
builtin!("@", Self::var_load),
builtin!("!", Self::var_store),
builtin!("b@", Self::byte_var_load),
builtin!("b!", Self::byte_var_store),
builtin!("w+", Self::word_add),
builtin!("'", Self::addr_of),
builtin!("execute", Self::execute),
builtin!("0", Self::zero_const),
builtin!("1", Self::one_const),
builtin!("builtins", Self::list_builtins),
builtin!("dict", Self::list_dict),
builtin!(".s", Self::list_stack),
builtin!("free", Self::dict_free),
builtin!("(write-str)", Self::write_str_lit),
builtin!("(jmp-doloop)", Self::jump_doloop),
builtin!("(jump-zero)", Self::jump_if_zero),
builtin!("(jmp)", Self::jump),
builtin!("(literal)", Self::literal),
builtin!("(rliteral)", Self::rliteral),
builtin!("(constant)", Self::constant),
builtin!("(variable)", Self::variable),
builtin!("panic", Self::panic),
];
pub fn panic(&mut self) -> Result<(), Error> {
writeln!(&mut self.output, "cstack",)?;
while let Some(c) = self.call_stack.pop() {
writeln!(
&mut self.output,
"{} ({}/{})",
unsafe { (*c.eh.as_ptr()).name.as_str() },
c.idx,
c.len,
)?;
}
writeln!(&mut self.output, "\ndstack",)?;
while self.pop_print().is_ok() {}
writeln!(&mut self.output, "\nrstack",)?;
while self.return_to_data_stack().is_ok() && self.pop_print().is_ok() {}
Ok(())
}
pub fn dict_free(&mut self) -> Result<(), Error> {
let capa = self.dict.alloc.capacity();
let used = self.dict.alloc.used();
let free = capa - used;
writeln!(
&mut self.output,
"{}/{} bytes free ({} used)",
free, capa, used
)?;
Ok(())
}
pub fn list_stack(&mut self) -> Result<(), Error> {
let depth = self.data_stack.depth();
write!(&mut self.output, "<{}> ", depth)?;
for d in (0..depth).rev() {
let val = self.data_stack.try_peek_back_n(d)?;
write!(&mut self.output, "{} ", val.into_data())?;
}
self.output.push_str("\n")?;
Ok(())
}
pub fn list_builtins(&mut self) -> Result<(), Error> {
let Self {
builtins, output, ..
} = self;
output.write_str("builtins: ")?;
for bi in builtins.iter() {
output.write_str(bi.hdr.name.as_str())?;
output.write_str(", ")?;
}
output.write_str("\n")?;
Ok(())
}
pub fn list_dict(&mut self) -> Result<(), Error> {
let Self { output, dict, .. } = self;
output.write_str("dictionary: ")?;
for item in dict.entries() {
output.write_str(unsafe { item.entry().as_ref() }.hdr.name.as_str())?;
if let DictLocation::Parent(_) = item {
output.write_str("*")?;
}
output.write_str(", ")?;
}
output.write_str("\n")?;
Ok(())
}
pub fn word_add(&mut self) -> Result<(), Error> {
let w_offset = self.data_stack.try_pop()?;
let w_addr = self.data_stack.try_pop()?;
let new_addr = unsafe {
let offset = isize::try_from(w_offset.data).replace_err(Error::BadWordOffset)?;
w_addr.ptr.cast::<Word>().offset(offset)
};
self.data_stack.push(Word::ptr(new_addr))?;
Ok(())
}
pub fn byte_var_load(&mut self) -> Result<(), Error> {
let w = self.data_stack.try_pop()?;
let ptr = unsafe { w.ptr.cast::<u8>() };
let val = unsafe { Word::data(i32::from(ptr.read())) };
self.data_stack.push(val)?;
Ok(())
}
pub fn byte_var_store(&mut self) -> Result<(), Error> {
let w_addr = self.data_stack.try_pop()?;
let w_val = self.data_stack.try_pop()?;
unsafe {
w_addr
.ptr
.cast::<u8>()
.write((w_val.into_data() & 0xFF) as u8);
}
Ok(())
}
pub fn var_load(&mut self) -> Result<(), Error> {
let w = self.data_stack.try_pop()?;
let ptr = unsafe { w.ptr.cast::<Word>() };
let val = unsafe { ptr.read() };
self.data_stack.push(val)?;
Ok(())
}
pub fn var_store(&mut self) -> Result<(), Error> {
let w_addr = self.data_stack.try_pop()?;
let w_val = self.data_stack.try_pop()?;
unsafe {
w_addr.ptr.cast::<Word>().write(w_val);
}
Ok(())
}
pub fn zero_const(&mut self) -> Result<(), Error> {
self.data_stack.push(Word::data(0))?;
Ok(())
}
pub fn one_const(&mut self) -> Result<(), Error> {
self.data_stack.push(Word::data(1))?;
Ok(())
}
pub fn constant(&mut self) -> Result<(), Error> {
let me = self.call_stack.try_peek()?;
let de = me.eh.cast::<DictionaryEntry<T>>();
let cfa = unsafe { DictionaryEntry::<T>::pfa(de) };
let val = unsafe { cfa.as_ptr().read() };
self.data_stack.push(val)?;
Ok(())
}
pub fn variable(&mut self) -> Result<(), Error> {
let me = self.call_stack.try_peek()?;
let de = me.eh.cast::<DictionaryEntry<T>>();
let cfa = unsafe { DictionaryEntry::<T>::pfa(de) };
let val = Word::ptr(cfa.as_ptr());
self.data_stack.push(val)?;
Ok(())
}
pub fn forget(&mut self) -> Result<(), Error> {
self.input.advance();
let word = match self.input.cur_word() {
None => return Err(Error::ForgetWithoutWordName),
Some(s) => s,
};
let word_tmp = TmpFaStr::new_from(word);
let defn = match self.find_in_dict(&word_tmp) {
None => {
if self.find_in_bis(&word_tmp).is_some() {
return Err(Error::CantForgetBuiltins);
} else {
return Err(Error::ForgetNotInDict);
}
}
Some(d) => d,
};
match defn {
DictLocation::Current(defn) => {
let name_ptr = unsafe { defn.as_ref().hdr.name.as_ptr().cast_mut() };
self.dict.tail = unsafe { defn.as_ref().link };
let addr = defn.as_ptr();
let name_contains = self.dict.alloc.contains(name_ptr.cast());
let contains = self.dict.alloc.contains(addr.cast());
let ordered = (addr as usize) <= (self.dict.alloc.cur as usize);
if !(name_contains && contains && ordered) {
return Err(Error::InternalError);
}
let len = (self.dict.alloc.cur as usize) - (name_ptr as usize);
unsafe {
name_ptr.write_bytes(0x00, len);
}
self.dict.alloc.cur = name_ptr;
}
DictLocation::Parent(_de) => {
todo!("eliza: forget parent definitions");
}
}
Ok(())
}
pub fn over(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_peek_back_n(1)?;
self.data_stack.push(a)?;
Ok(())
}
pub fn over_2(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_peek_back_n(2)?;
let b = self.data_stack.try_peek_back_n(3)?;
self.data_stack.push(b)?;
self.data_stack.push(a)?;
Ok(())
}
pub fn rot(&mut self) -> Result<(), Error> {
let n1 = self.data_stack.try_pop()?;
let n2 = self.data_stack.try_pop()?;
let n3 = self.data_stack.try_pop()?;
self.data_stack.push(n2)?;
self.data_stack.push(n1)?;
self.data_stack.push(n3)?;
Ok(())
}
pub fn ds_drop(&mut self) -> Result<(), Error> {
let _a = self.data_stack.try_pop()?;
Ok(())
}
pub fn ds_drop_2(&mut self) -> Result<(), Error> {
let _a = self.data_stack.try_pop()?;
let _b = self.data_stack.try_pop()?;
Ok(())
}
pub fn swap(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack.push(a)?;
self.data_stack.push(b)?;
Ok(())
}
pub fn swap_2(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let c = self.data_stack.try_pop()?;
let d = self.data_stack.try_pop()?;
self.data_stack.push(b)?;
self.data_stack.push(a)?;
self.data_stack.push(d)?;
self.data_stack.push(c)?;
Ok(())
}
pub fn space(&mut self) -> Result<(), Error> {
self.output.push_bstr(b" ")?;
Ok(())
}
pub fn spaces(&mut self) -> Result<(), Error> {
let num = self.data_stack.try_pop()?;
let num = num.into_data();
if num.is_negative() {
return Err(Error::LoopCountIsNegative);
}
for _ in 0..num {
self.space()?;
}
Ok(())
}
pub fn cr(&mut self) -> Result<(), Error> {
self.output.push_bstr(b"\n")?;
Ok(())
}
fn skip_literal(&mut self) -> Result<(), Error> {
let parent = self.call_stack.try_peek_back_n_mut(1)?;
parent.offset(1)?;
Ok(())
}
pub fn invert(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let val = if a == Word::data(0) {
Word::data(-1)
} else {
Word::data(0)
};
self.data_stack.push(val)?;
Ok(())
}
pub fn and(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let val = Word::data(a.into_data() & b.into_data());
self.data_stack.push(val)?;
Ok(())
}
pub fn equal(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let val = if a == b {
Word::data(-1)
} else {
Word::data(0)
};
self.data_stack.push(val)?;
Ok(())
}
pub fn greater(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let val = if b.into_data() > a.into_data() { -1 } else { 0 };
self.data_stack.push(Word::data(val))?;
Ok(())
}
pub fn less(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let val = if b.into_data() < a.into_data() { -1 } else { 0 };
self.data_stack.push(Word::data(val))?;
Ok(())
}
pub fn zero_equal(&mut self) -> Result<(), Error> {
self.data_stack.push(Word::data(0))?;
self.equal()
}
pub fn zero_greater(&mut self) -> Result<(), Error> {
self.data_stack.push(Word::data(0))?;
self.greater()
}
pub fn zero_less(&mut self) -> Result<(), Error> {
self.data_stack.push(Word::data(0))?;
self.less()
}
pub fn div_mod(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
if a.into_data() == 0 {
return Err(Error::DivideByZero);
}
let rem = Word::data(b.into_data() % a.into_data());
self.data_stack.push(rem)?;
let val = Word::data(b.into_data() / a.into_data());
self.data_stack.push(val)?;
Ok(())
}
pub fn div(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let val = {
if a.into_data() == 0 {
return Err(Error::DivideByZero);
}
Word::data(b.into_data() / a.into_data())
};
self.data_stack.push(val)?;
Ok(())
}
pub fn modu(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
let val = {
if a.into_data() == 0 {
return Err(Error::DivideByZero);
}
Word::data(b.into_data() % a.into_data())
};
self.data_stack.push(val)?;
Ok(())
}
pub fn loop_i(&mut self) -> Result<(), Error> {
let a = self.return_stack.try_peek()?;
self.data_stack.push(a)?;
Ok(())
}
pub fn loop_itick(&mut self) -> Result<(), Error> {
let a = self.return_stack.try_peek_back_n(1)?;
self.data_stack.push(a)?;
Ok(())
}
pub fn loop_j(&mut self) -> Result<(), Error> {
let a = self.return_stack.try_peek_back_n(2)?;
self.data_stack.push(a)?;
Ok(())
}
pub fn loop_leave(&mut self) -> Result<(), Error> {
let _ = self.return_stack.try_pop()?;
let _ = self.return_stack.try_pop()?;
let idx = self.return_stack.try_pop()?;
let idx = idx.into_data();
let idx = u16::try_from(idx).map_err(|_| Error::BadCfaOffset)?;
let parent = self.call_stack.try_peek_back_n_mut(1)?;
parent.idx = idx;
Ok(())
}
pub fn jump_doloop(&mut self) -> Result<(), Error> {
let a = self.return_stack.try_pop()?;
let b = self.return_stack.try_peek()?;
let ctr = Word::data(a.into_data() + 1);
let do_jmp = ctr != b;
if do_jmp {
self.return_stack.push(ctr)?;
self.jump()
} else {
self.return_stack.try_pop()?;
self.return_stack.try_pop()?;
self.skip_literal()
}
}
pub fn emit(&mut self) -> Result<(), Error> {
let val = self.data_stack.try_pop()?;
let val = val.into_data();
self.output.push_bstr(&[val as u8])?;
Ok(())
}
pub fn jump_if_zero(&mut self) -> Result<(), Error> {
let do_jmp = {
let val = self.data_stack.try_pop()?;
val.into_data() == 0
};
if do_jmp {
self.jump()
} else {
self.skip_literal()
}
}
pub fn jump(&mut self) -> Result<(), Error> {
let parent = self.call_stack.try_peek_back_n_mut(1)?;
let offset = parent.get_current_val()?;
parent.offset(offset)?;
Ok(())
}
pub fn dup(&mut self) -> Result<(), Error> {
let val = self.data_stack.try_peek()?;
self.data_stack.push(val)?;
Ok(())
}
pub fn dup_2(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack.push(b)?;
self.data_stack.push(a)?;
self.data_stack.push(b)?;
self.data_stack.push(a)?;
Ok(())
}
pub fn return_to_data_stack(&mut self) -> Result<(), Error> {
let val = self.return_stack.try_pop()?;
self.data_stack.push(val)?;
Ok(())
}
pub fn data_to_return_stack(&mut self) -> Result<(), Error> {
let val = self.data_stack.try_pop()?;
self.return_stack.push(val)?;
Ok(())
}
pub fn data2_to_return2_stack(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.return_stack.push(b)?;
self.return_stack.push(a)?;
Ok(())
}
pub fn pop_print(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
write!(&mut self.output, "{} ", a.into_data())?;
Ok(())
}
pub fn unsigned_pop_print(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
write!(&mut self.output, "{} ", a.into_data() as u32)?;
Ok(())
}
pub fn add(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack.push(Word::ptr_data(unsafe {
let a = a.ptr as isize;
let b = b.ptr as isize;
a.wrapping_add(b)
}))?;
Ok(())
}
pub fn mul(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack
.push(Word::data(a.into_data().wrapping_mul(b.into_data())))?;
Ok(())
}
pub fn abs(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
self.data_stack
.push(Word::data(a.into_data().wrapping_abs()))?;
Ok(())
}
pub fn negate(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
self.data_stack
.push(Word::data(a.into_data().wrapping_neg()))?;
Ok(())
}
pub fn min(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack
.push(Word::data(a.into_data().min(b.into_data())))?;
Ok(())
}
pub fn max(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack
.push(Word::data(a.into_data().max(b.into_data())))?;
Ok(())
}
pub fn minus(&mut self) -> Result<(), Error> {
let a = self.data_stack.try_pop()?;
let b = self.data_stack.try_pop()?;
self.data_stack.push(Word::ptr_data(unsafe {
let a = a.ptr as isize;
let b = b.ptr as isize;
b.wrapping_sub(a)
}))?;
Ok(())
}
pub fn star_slash(&mut self) -> Result<(), Error> {
let n3 = self.data_stack.try_pop()?;
let n2 = self.data_stack.try_pop()?;
let n1 = self.data_stack.try_pop()?;
self.data_stack.push(Word::data({
(i64::from(n1.into_data()))
.wrapping_mul(i64::from(n2.into_data()))
.wrapping_div(i64::from(n3.into_data())) as i32
}))?;
Ok(())
}
pub fn star_slash_mod(&mut self) -> Result<(), Error> {
let n3 = self.data_stack.try_pop()?;
let n2 = self.data_stack.try_pop()?;
let n1 = self.data_stack.try_pop()?;
let top = i64::from(n1.into_data()).wrapping_mul(i64::from(n2.into_data()));
let div = i64::from(n3.into_data());
let quo = top / div;
let rem = top % div;
self.data_stack.push(Word::data(rem as i32))?;
self.data_stack.push(Word::data(quo as i32))?;
Ok(())
}
pub fn colon(&mut self) -> Result<(), Error> {
let old_mode = core::mem::replace(&mut self.mode, Mode::Compile);
let name = self.munch_name()?;
let dict_base = self.dict.alloc.bump::<DictionaryEntry<T>>()?;
let mut len = 0u16;
loop {
let munched = self.munch_one(&mut len)?;
if munched == 0 {
match self.input.cur_word() {
Some(";") => {
unsafe {
dict_base.as_ptr().write(DictionaryEntry {
hdr: EntryHeader {
name,
kind: EntryKind::Dictionary,
len,
_pd: PhantomData,
},
func: Self::interpret,
link: self.dict.tail.take(),
parameter_field: [],
});
}
self.dict.tail = Some(dict_base);
self.mode = old_mode;
return Ok(());
}
Some(_) => {}
None => {
return Err(Error::ColonCompileMissingSemicolon);
}
}
}
}
}
pub fn write_str_lit(&mut self) -> Result<(), Error> {
let parent = self.call_stack.try_peek_back_n_mut(1)?;
let len = parent.get_current_val()?;
let len_u16 = u16::try_from(len).replace_err(Error::LiteralStringTooLong)?;
let word_size = size_of::<Word>();
let len_words = 1 + ((usize::from(len_u16) + (word_size - 1)) / word_size);
let len_and_str = parent.get_next_n_words(len_words as u16)?;
unsafe {
let start = len_and_str.as_ptr().add(1).cast::<u8>();
let u8_sli = core::slice::from_raw_parts(start, len_u16.into());
self.output.push_bstr(u8_sli)?;
}
parent.offset(len_words as i32)?;
Ok(())
}
pub fn rliteral(&mut self) -> Result<(), Error> {
let parent = self.call_stack.try_peek_back_n_mut(1)?;
let literal = parent.get_current_word()?;
parent.offset(1)?;
self.return_stack.push(literal)?;
Ok(())
}
pub fn literal(&mut self) -> Result<(), Error> {
let parent = self.call_stack.try_peek_back_n_mut(1)?;
let literal = parent.get_current_word()?;
parent.offset(1)?;
self.data_stack.push(literal)?;
Ok(())
}
pub fn addr_of(&mut self) -> Result<(), Error> {
self.input.advance();
let name = self.input.cur_word().ok_or(Error::AddrOfMissingName)?;
match self.lookup(name)? {
Lookup::Dict(DictLocation::Current(de)) => {
self.data_stack.push(Word::ptr(de.as_ptr()))?
}
Lookup::Dict(DictLocation::Parent(de)) => {
self.data_stack.push(Word::ptr(de.as_ptr()))?
}
Lookup::Builtin { bi } => self.data_stack.push(Word::ptr(bi.as_ptr()))?,
#[cfg(feature = "async")]
Lookup::Async { bi } => self.data_stack.push(Word::ptr(bi.as_ptr()))?,
_ => return Err(Error::AddrOfNotAWord),
}
Ok(())
}
pub fn execute(&mut self) -> Result<(), Error> {
let w = self.data_stack.try_pop()?;
self.call_stack.pop();
unsafe {
let eh = w.ptr.cast::<EntryHeader<T>>();
self.call_stack.push(crate::vm::CallContext {
eh: NonNull::new_unchecked(eh),
len: (*eh).len,
idx: 0,
})?;
};
Err(Error::PendingCallAgain)
}
}