use crate::ksm::errors::CodeSectionParseError;
use crate::ksm::{Instr, IntSize};
use crate::{BufferIterator, FromBytes, ToBytes};
use std::slice::Iter;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CodeType {
Function,
Initialization,
Main,
}
impl TryFrom<u8> for CodeType {
type Error = u8;
fn try_from(byte: u8) -> Result<Self, Self::Error> {
match byte {
b'F' => Ok(CodeType::Function),
b'I' => Ok(CodeType::Initialization),
b'M' => Ok(CodeType::Main),
_ => Err(byte),
}
}
}
impl From<CodeType> for u8 {
fn from(c: CodeType) -> Self {
match c {
CodeType::Function => b'F',
CodeType::Initialization => b'I',
CodeType::Main => b'M',
}
}
}
#[derive(Debug, Clone)]
pub struct CodeSection {
pub section_type: CodeType,
instructions: Vec<Instr>,
}
impl CodeSection {
const BEGIN_SIZE: usize = 2;
pub fn new(section_type: CodeType) -> Self {
CodeSection {
section_type,
instructions: Vec::new(),
}
}
pub fn with_instructions(mut self, iter: impl IntoIterator<Item = Instr>) -> Self {
self.instructions.extend(iter);
self
}
pub fn instructions(&self) -> Iter<Instr> {
self.instructions.iter()
}
pub fn add(&mut self, instr: Instr) {
self.instructions.push(instr);
}
pub fn size_bytes(&self, index_bytes: IntSize) -> usize {
Self::BEGIN_SIZE
+ self
.instructions
.iter()
.map(|i| i.size_bytes(index_bytes))
.sum::<usize>()
}
pub fn write(&self, buf: &mut Vec<u8>, index_bytes: IntSize) {
buf.push(b'%');
u8::from(self.section_type).to_bytes(buf);
for instr in self.instructions.iter() {
instr.write(buf, index_bytes);
}
}
pub fn parse(
source: &mut BufferIterator,
index_bytes: IntSize,
) -> Result<Self, CodeSectionParseError> {
let raw_section_type =
u8::from_bytes(source).map_err(|_| CodeSectionParseError::MissingCodeSectionType)?;
let section_type = CodeType::try_from(raw_section_type)
.map_err(CodeSectionParseError::InvalidCodeSectionType)?;
let mut instructions = Vec::new();
loop {
if let Some(next) = source.peek() {
if next == b'%' {
break;
}
let instr = Instr::parse(source, index_bytes)
.map_err(CodeSectionParseError::InstrParseError)?;
instructions.push(instr);
} else {
return Err(CodeSectionParseError::EOF);
}
}
Ok(CodeSection {
section_type,
instructions,
})
}
}
#[cfg(test)]
impl PartialEq for CodeSection {
fn eq(&self, other: &Self) -> bool {
if self.section_type != other.section_type {
return false;
}
for (value1, value2) in self.instructions.iter().zip(other.instructions.iter()) {
if value1 != value2 {
return false;
}
}
true
}
}