use alloc::vec::Vec;
use std::collections::BTreeMap;
use crate::isa::{GotoTarget, Instruction};
use crate::library::assembler::AssemblerError;
use crate::{Lib, LibId, LibSite};
#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum CompilerError<Isa: Instruction<LibId>> {
#[from]
#[display(inner)]
Assemble(AssemblerError),
InvalidRef(Isa, usize, u16, Vec<u16>),
InvalidLib(Isa, usize, u16, LibId),
}
pub struct CompiledLib {
id: LibId,
lib: Lib,
routines: Vec<u16>,
}
impl CompiledLib {
pub fn compile<Isa>(
mut code: impl AsMut<[Isa]>,
deps: &[&CompiledLib],
) -> Result<Self, CompilerError<Isa>>
where
Isa: Instruction<LibId>,
{
let deps = deps
.iter()
.map(|lib| (lib.id, lib))
.collect::<BTreeMap<_, _>>();
let code = code.as_mut();
let mut routines = vec![];
let mut cursor = 0u16;
for instr in &*code {
if instr.is_goto_target() {
routines.push(cursor);
}
cursor += instr.code_byte_len();
}
let mut cursor = 0u16;
for (no, instr) in code.iter_mut().enumerate() {
if let GotoTarget::Absolute(goto_pos) = instr.local_goto_pos() {
let Some(pos) = routines.get(*goto_pos as usize) else {
return Err(CompilerError::InvalidRef(instr.clone(), no, cursor, routines));
};
*goto_pos = *pos;
}
let cloned_instr = instr.clone();
if let Some(remote_pos) = instr.remote_goto_pos() {
let Some(lib) = deps.get(&remote_pos.prog_id) else {
return Err(CompilerError::InvalidLib(
cloned_instr,
no,
cursor,
remote_pos.prog_id,
));
};
remote_pos.offset = lib.routine(remote_pos.offset).offset;
}
cursor += instr.code_byte_len();
}
let lib = Lib::assemble(code)?;
let id = lib.lib_id();
Ok(Self { id, lib, routines })
}
pub fn routines_count(&self) -> usize { self.routines.len() }
pub fn routine(&self, no: u16) -> LibSite {
let pos = self.routines[no as usize];
LibSite::new(self.id, pos)
}
pub fn as_lib(&self) -> &Lib { &self.lib }
pub fn into_lib(self) -> Lib { self.lib }
}