use crate::{
operation::{Operation, RegisterOperation},
runtime_state::Register,
ExecutionResult,
};
struct Runtime {
registers: Vec<Register>,
code: Vec<u8>,
}
impl Runtime {
fn new(code: Vec<u8>) -> Result<Self, String> {
let mut code = code.into_iter();
let Some(reg_count) = code.next() else {
return Err("Expected number of registers, got end of file".to_string());
};
let mut registers: Vec<Register> = Vec::new();
for _reg in 0..reg_count {
let mut content = Vec::new();
content.resize(
((code.next().ok_or("Expected register size, got eof")? as u32) << 8
| code.next().ok_or("Expected register size, got eof")? as u32)
.try_into()
.unwrap(),
0,
);
registers.push(Register { content, cursor: 0 });
}
let code = code.collect::<Vec<u8>>();
let remainder = code.len() % 4;
if remainder > 0 {
return Err(format!(
"Invalid amounts of bytes in bytecode, {remainder} remaining"
));
}
Ok(Self { code, registers })
}
fn get_register(&mut self, index: u32) -> Result<&mut Register, String> {
self.registers
.get_mut(index as usize)
.ok_or(format!("Register {index} not defined"))
}
pub fn run(mut self, debug: bool) -> Result<ExecutionResult, String> {
let mut pc = 0;
loop {
if pc >= self.code.len() {
return Ok(ExecutionResult::Full(
self.registers.into_iter().map(|r| r.content).collect(),
));
}
let mut jumped = false;
let reg = self.code[pc];
let is_reg = reg > 0;
let reg = reg.checked_sub(1).unwrap_or_default();
let ins_and_is_reg = self.code[pc + 1];
let op = ins_and_is_reg.checked_shr(5).unwrap_or_default();
let val_is_reg = ins_and_is_reg & 1 == 1;
let val = ((self.code[pc + 2] as u32) << 8) + self.code[pc + 3] as u32;
let num = if val_is_reg {
self.get_register(val - 1)?.get()
} else {
val
};
match Operation::try_from_u8(op) {
Some(Operation::Register(RegisterOperation::Right)) => {
let register = self.registers.get_mut(reg as usize);
match register {
Some(register) => register.right(num.max(1)),
None => return Err(format!("Invalid register in > instruction: {reg}")),
}
}
Some(Operation::Register(RegisterOperation::Left)) => {
self.get_register(reg.into())?.left(num)
}
Some(Operation::Take) => {
let val =
self.get_register(val - 1)?.remove() + self.get_register(reg.into())?.get();
self.get_register(reg.into())?.insert(val)
}
Some(Operation::Register(RegisterOperation::Add)) => {
self.get_register(reg.into())?.add(num)
}
Some(Operation::Register(RegisterOperation::Subtract)) => {
self.get_register(reg.into())?.sub(num)
}
Some(Operation::Jump) => {
if val_is_reg {
return Err(format!("Can't jump to register {num}"));
} else if num != 0 && (!is_reg || self.get_register(reg.into())?.get() > 0) {
pc = (num * 4) as usize;
jumped = true;
}
}
Some(Operation::Result) => {
let result = if is_reg {
ExecutionResult::List(self.get_register(reg.into())?.content.clone())
} else {
ExecutionResult::Number(num)
};
if debug {
match result {
ExecutionResult::Full(_) => todo!(),
ExecutionResult::List(list) => {
println!(
"{}",
list.into_iter()
.map(|c| format!("{c}"))
.collect::<Vec<String>>()
.join(" ")
)
}
ExecutionResult::Number(num) => println!("{num}"),
}
} else {
return Ok(result);
}
}
None => return Err(format!("Invalid instruction: {op}")),
};
if !jumped {
pc += 4;
}
}
}
}
pub fn run(code: Vec<u8>, debug: bool) -> Result<ExecutionResult, String> {
Runtime::new(code)?.run(debug)
}