use crate::{exec::PasmError::*, inst};
use std::io::Read;
#[inline]
fn failed_read<T>(err: impl std::error::Error) -> T {
panic!("Unable to read from input because {err}")
}
#[inline]
fn failed_write<T>(err: impl std::error::Error) -> T {
panic!("Unable to write to output because {err}")
}
inst!(
pub nop {}
);
inst!(
pub end (ctx) {
ctx.end = true;
}
);
inst!(
pub out (ctx, op) {
match op {
Null => {
let x = ctx.acc;
if x > 255 {
return Err(InvalidUtf8Byte(x));
}
#[allow(clippy::cast_possible_truncation)]
let out = x as u8 as char;
write!(ctx.io.write, "{out}").unwrap_or_else(failed_write);
}
src if src.is_usizeable() => {
let src = ctx.read(src)?;
if src > 255 {
return Err(InvalidUtf8Byte(src));
}
#[allow(clippy::cast_possible_truncation)]
let out = src as u8 as char;
write!(ctx.io.write, "{out}").unwrap_or_else(failed_write);
}
_ => return Err(InvalidOperand),
}
}
);
inst!(
pub inp (ctx, op) {
match op {
Null => {
let mut buf = [0; 1];
ctx.io.read.read_exact(&mut buf).unwrap_or_else(failed_read);
ctx.acc = buf[0] as usize;
}
dest if dest.is_read_write() => {
let mut buf = [0; 1];
ctx.io.read.read_exact(&mut buf).unwrap_or_else(failed_read);
ctx.modify(dest, |d| *d = buf[0] as usize)?;
}
_ => return Err(InvalidOperand),
}
}
);
inst!(
#[cfg(feature = "extended")]
pub dbg (ctx, op) {
let out = match op {
Null => format!("{ctx:?}"),
src if src.is_usizeable() => format!("{}", ctx.read(src)?),
MultiOp(ops) if ops.iter().all(crate::inst::Op::is_usizeable) => ops
.iter()
.filter_map(|op| ctx.read(op).ok())
.enumerate()
.fold(String::new(), |acc, (idx, op)| {
if idx == ops.len() - 1 {
format!("{acc}{op}")
} else {
format!("{acc}{op}, ")
}
}),
MultiOp(_) => return Err(InvalidMultiOp),
_ => return Err(InvalidOperand),
};
writeln!(ctx.io.write, "{out}").unwrap_or_else(failed_write);
}
);
inst!(
#[cfg(feature = "extended")]
pub rin (ctx, op) {
use std::io::BufRead;
const LF: u8 = 0xA;
fn input(inp: &mut impl BufRead) -> usize {
let mut buf = Vec::with_capacity(32);
inp.read_until(LF, &mut buf).unwrap_or_else(failed_read);
let str = String::from_utf8_lossy(&buf);
let str = str.trim();
str.parse()
.unwrap_or_else(|e| panic!("Unable to parse {str:?} because {e}"))
}
match op {
Null => ctx.acc = input(&mut ctx.io.read),
dest if dest.is_read_write() => {
let input = input(&mut ctx.io.read);
ctx.modify(dest, |d| *d = input)?;
}
_ => return Err(InvalidOperand),
}
}
);
inst!(
#[cfg(feature = "extended")]
pub call (ctx, op) {
match op {
&Addr(addr) => {
ctx.ret = ctx.mar + 1;
ctx.override_flow_control();
ctx.mar = addr;
}
_ => return Err(InvalidOperand),
}
}
);
inst!(
#[cfg(feature = "extended")]
pub ret (ctx) {
ctx.override_flow_control();
ctx.mar = ctx.ret;
}
);