holey-bytes/hbvm/src/validate.rs
2023-06-09 18:25:37 +02:00

75 lines
2.1 KiB
Rust

macro_rules! bail {
($kind:ident, $start:expr, $curr:expr, $offset:expr) => {
return Err(Error {
kind: ErrorKind::$kind,
index: ($curr.as_ptr() as usize) - ($start.as_ptr() as usize) + $offset,
})
};
($kind:ident, $start:expr, $curr:expr) => {
bail!($kind, $start, $curr, 0)
};
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorKind {
InvalidInstruction,
InvalidRegister,
Unimplemented,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Error {
kind: ErrorKind,
index: usize,
}
pub fn validate(mut program: &[u8]) -> Result<(), Error> {
use hbbytecode::opcode::*;
#[inline]
fn reg(regs: &[u8]) -> Option<usize> {
regs.iter()
.enumerate()
.filter_map(|(n, &r)| (r > 59).then_some(n))
.next()
}
let start = program;
loop {
program = match program {
[] => return Ok(()),
// N
[NOP | RET | ECALL, rest @ ..] => rest,
// RRR
[ADD..=CMPU | ADDF..=DIVF, _, _, _, rest @ ..] => {
if let Some(n) = reg(&program[1..=3]) {
bail!(InvalidRegister, start, program, n + 1);
}
rest
}
// RR
[NOT | CP, _, _, rest @ ..] => {
if let Some(n) = reg(&program[1..=2]) {
bail!(InvalidRegister, start, program, n + 1);
}
rest
}
// RI
[LI | JMP, reg, _, _, _, _, _, _, _, _, rest @ ..] => {
if *reg > 59 {
bail!(InvalidRegister, start, program, 1);
}
rest
}
// RRI
[ADDI..=MULFI | LB..=SO | JEQ..=JGTU, _, _, _, _, _, _, _, _, _, _, rest @ ..] => {
if let Some(n) = reg(&program[1..=2]) {
bail!(InvalidRegister, start, program, n + 1);
}
rest
}
_ => bail!(InvalidInstruction, start, program),
}
}
}