hebi 0.4.0

A dynamic scripting language
Documentation
use std::fmt::Display;

use super::opcode::{symbolic, Width};
use crate::internal::value::constant::Constant;
use crate::util::{num_digits, JoinIter};

pub struct Instruction<'a> {
  pub name: &'a str,
  pub operands: Vec<&'a dyn Display>,
  pub constant: Option<Constant>,
  pub width: Width,
}

pub trait Disassemble {
  fn disassemble(&self, constants: &[Constant]) -> Instruction<'_>;
}

impl<'a> Display for Instruction<'a> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    let Self {
      name,
      operands,
      constant,
      width,
    } = self;

    let width = match width {
      Width::Normal => "",
      Width::Wide16 => "wide16.",
      Width::Wide32 => "wide32.",
    };

    write!(f, "{width}{name}")?;
    if !operands.is_empty() {
      write!(f, " {}", operands.iter().join(", "))?;
    }
    if let Some(constant) = constant {
      write!(f, "; {constant}")?;
    }
    Ok(())
  }
}

pub struct Disassembly<'a> {
  bytecode: &'a [u8],
  constants: &'a [Constant],
  padding: usize,
  offsets: bool,
}

impl<'a> Disassembly<'a> {
  pub fn new(bytecode: &'a [u8], constants: &'a [Constant], padding: usize, offsets: bool) -> Self {
    Self {
      bytecode,
      constants,
      padding,
      offsets,
    }
  }
}

impl<'a> Display for Disassembly<'a> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    let mut current_remainder = self.bytecode;
    let mut offset = 0;
    let offset_width = num_digits(self.bytecode.len());
    while !current_remainder.is_empty() {
      let (instruction, remainder) = symbolic::decode(current_remainder).ok_or(std::fmt::Error)?;
      let size = (remainder.as_ptr() as usize) - (current_remainder.as_ptr() as usize);
      current_remainder = remainder;
      let newline = if !current_remainder.is_empty() {
        "\n"
      } else {
        ""
      };
      if self.offsets {
        write!(
          f,
          "{:padding$}{offset: <offset_width$} | {}{newline}",
          "",
          instruction.disassemble(self.constants),
          padding = self.padding
        )?;
      } else {
        write!(
          f,
          "{:padding$}{}{newline}",
          "",
          instruction.disassemble(self.constants),
          padding = self.padding
        )?;
      }
      offset += size;
    }
    Ok(())
  }
}