use crate::{exec::RtError::*, inst};
use std::io::{Read, Write};
inst!(
pub nop {}
);
inst!(
pub end (ctx) {
ctx.end = true;
}
);
inst!(
pub out (ctx, op) {
match op {
Null => {
ctx.io.write.write_all(&[ctx.acc.try_into().map_err(|_| InvalidUtf8Byte(ctx.acc))?])?;
}
src if src.is_usizeable() => {
let src = ctx.read(src)?;
ctx.io.write.write_all(&[src.try_into().map_err(|_| InvalidUtf8Byte(src))?])?;
}
MultiOp(ops) if ops.iter().all(inst::Op::is_usizeable) => for op in ops {
out(ctx, op)?;
}
_ => return Err(InvalidOperand),
}
}
);
inst!(
#[cfg(feature = "extended")]
pub print (ctx, op) {
match op {
MultiOp(ops) => match &ops[..] {
&[ref addr, Literal(n)] if addr.is_address() => {
let addr = ctx.as_address(addr)?;
let mut buf = Vec::with_capacity(n);
for address in addr..addr+n {
let byte = ctx.read(&Addr(address))?;
buf.push(byte.try_into().map_err(|_| InvalidUtf8Byte(byte))?);
}
ctx.io.write.write_all(&buf)?;
}
_ => return Err(InvalidMultiOp),
}
_ => return Err(InvalidOperand)
}
}
);
inst!(
pub inp (ctx, op) {
match op {
Null => {
let mut buf = [0; 1];
ctx.io.read.read_exact(&mut buf)?;
ctx.acc = buf[0] as usize;
}
dest if dest.is_read_write() => {
let mut buf = [0; 1];
ctx.io.read.read_exact(&mut buf)?;
ctx.modify(dest, |d| *d = buf[0] as usize)?;
}
_ => return Err(InvalidOperand),
}
}
);
inst!(
#[cfg(feature = "extended")]
pub read (ctx, op) {
match op {
MultiOp(ops) => match &ops[..] {
&[ref start, Literal(n)] if start.is_address() => {
let start = ctx.as_address(start)?;
let mut buf = Vec::with_capacity(n);
ctx.io.read.read_exact(&mut buf)?;
for (start, byte) in (start..start+n).zip(buf) {
ctx.modify(&Addr(start), |d| *d = byte as usize)?;
}
}
_ => return Err(InvalidMultiOp)
}
_ => 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(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}")?;
}
);
inst!(
#[cfg(feature = "extended")]
pub rin (ctx, op) {
use std::io::BufRead;
use super::RtResult;
const LF: u8 = 0xA;
fn input(inp: &mut impl BufRead) -> RtResult<usize> {
let mut buf = Vec::with_capacity(32);
inp.read_until(LF, &mut buf)?;
let str = String::from_utf8_lossy(&buf);
let str = str.trim();
let res = str.parse()
.map_err(|e| format!("Unable to parse {str:?} because {e}"))?;
Ok(res)
}
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;
}
);