use crate::{obj::prelude::*, vm::{consts::ConstPool, inst::Inst}}; use shredder::Scan; use std::io::{self, Write}; /// A compiled package that can be executed by a VM. #[derive(Scan, Debug)] pub struct Package { names: Vec, // local names mappings const_pool: ConstPool, code: Vec, } impl Package { /// Creates a new VM executable package with the given local name mappings, constant pool, and /// executable code. pub fn new(names: Vec, const_pool: ConstPool, code: Vec) -> Self { Self { names, const_pool, code, } } /// Get the local name mappings for this package. pub fn names(&self) -> &Vec { &self.names } /// Gets the constant pool that is used by this package. pub fn const_pool(&self) -> &ConstPool { &self.const_pool } /// Gets the executable code that was compiled for this package. pub fn code(&self) -> &Vec { &self.code } /// Dumps a debug output of this package to the given writer. pub fn dump(&self, writer: &mut dyn Write) -> io::Result<()> { // column widths let addr_w = num_digits(self.code().len(), 16).max(4); let inst_col = 16 - addr_w; let inst_w = 16; for (addr, inst) in self.code().iter().enumerate() { let (param_val, mut param_name) = match inst { Inst::PushSym(sym) | Inst::GetAttr(sym) | Inst::SetAttr(sym) => ( sym.index().to_string(), global_sym_lookup(*sym).unwrap().to_string(), ), Inst::PushConst(hdl) => ( hdl.index().to_string(), { let obj_ref = self.const_pool().get(*hdl); read_obj!(let obj = obj_ref); format!("{:?}", obj) }, ), Inst::LoadName(local) => ( local.index().to_string(), global_sym_lookup(self.names()[local.index()]).unwrap().to_string(), ), Inst::Pop(local) => { if let Some(local) = local { let index = local.index(); let sym = self.names()[index]; let name = global_sym_lookup(sym).unwrap().to_string(); (index.to_string(), name) } else { ("(discarded)".to_string(), String::new()) } } Inst::Jump(addr) | Inst::JumpTrue(addr) => ( addr.to_string(), String::new(), ), Inst::Call(argc) => ( argc.to_string(), String::new(), ), _ => (String::new(), String::new()), }; if !param_name.is_empty() { param_name = format!("({})", param_name); } writeln!( writer, "{:0addr_w$x}{space: >inst_col$}{: usize { ((n as f64) + 1.0).log(radix as f64).ceil() as usize }