//! supported instrs //! //! ```text //! jump //! op //! stop //! end //! set //! read //! write //! print //! packcolor //! //! draw {color, col, flush, line, rect, lineRect, triangle, stroke, clear} //! ``` mod cop; pub mod draw; pub mod io; mod mop; mod mop2; pub use cop::ConditionOp; pub use draw::{DrawInstr, Frozen}; use enum_dispatch::enum_dispatch; pub use mop::MathOp1; pub use mop2::MathOp2; use std::{fmt, io::Write}; use crate::debug::{info::DebugInfo, printable::Printable}; use super::{ executor::{ExecutorContext, Instruction}, memory::{LAddress, LVar}, }; pub const OPS: &[&str] = &[ "equal", "notEqual", "lessThan", "lessThanEq", "greaterThan", "greaterThanEq", "strictEqual", "always", "add", "sub", "mul", "div", "idiv", "mod", "pow", "land", "not", "shl", "shr", "or", "and", "xor", "max", "min", "angle", "angleDiff", "len", "noise", "abs", "log", "log10", "floor", "ceil", "sqrt", "rand", "sin", "cos", "tan", "asin", "acos", "atan", ]; #[must_use = "to change control flow"] #[derive(Default)] pub enum Flow { #[default] Continue, Stay, Exit, } #[enum_dispatch] pub trait LInstruction: Printable { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow; } #[derive(Debug, Copy, Clone)] #[enum_dispatch(LInstruction)] pub enum Instr { Op2(Op2), Jump(Jump), AlwaysJump(AlwaysJump), Set(Set), Op1(Op1), Read(io::Read), Write(io::Write), DrawFlush(draw::Flush), DynJump(DynJump), Print(io::Print), Stop(Stop), PackColor(PackColor), Select(Select), Noop(Noop), End(End), } impl Printable for Instr { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { match self { Self::Op2(i) => i.print(info, f), Self::Jump(i) => i.print(info, f), Self::AlwaysJump(i) => i.print(info, f), Self::Set(i) => i.print(info, f), Self::Op1(i) => i.print(info, f), Self::Read(i) => i.print(info, f), Self::Write(i) => i.print(info, f), Self::DrawFlush(i) => i.print(info, f), Self::DynJump(i) => i.print(info, f), Self::Print(i) => i.print(info, f), Self::Stop(i) => i.print(info, f), Self::End(i) => i.print(info, f), Self::PackColor(i) => i.print(info, f), Self::Select(i) => i.print(info, f), Self::Noop(i) => i.print(info, f), } } } #[derive(Debug, Copy, Clone)] pub struct Set { pub(crate) from: LAddress, pub(crate) to: LAddress, } impl LInstruction for Set { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { exec.set(self.from, self.to); Flow::Continue } } impl Printable for Set { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!(f, "set {} {}", info[self.from], info[self.to]) } } macro_rules! op_enum { ($v:vis enum $name:ident { $($variant:ident),+ $(,)? }) => { #[derive(Debug, Copy, Clone, Eq, PartialEq)] $v enum $name { $($variant),+ } impl<'a> TryFrom> for $name { type Error = Token<'a>; fn try_from(value: Token<'a>) -> Result { match value { $(Token::$variant => Ok(Self::$variant),)+ v => Err(v) } } } impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!(f, "{}", Token::from(*self)) } } impl<'a> From<$name> for Token<'a> { fn from(value: $name) -> Self { match value { $($name::$variant => Self::$variant,)+ } } } } } use op_enum; // not part of op_enum due to rem macro_rules! op_impl { ($name:ident, ptr type = $ptr:ty { $($own:ident => $fn:ident,)+ }) => { impl $name { pub const fn get_fn(self) -> $ptr { match self { $(Self::$own => $fn,)+ } } } impl TryFrom<$ptr> for $name { type Error = (); fn try_from(f:$ptr) -> Result { match f { $(f if f == $fn => Ok(Self::$own),)+ _ => Err(()), } } } } } use op_impl; macro_rules! get_num { ($x:expr) => { match $x { LVar::Num(x) => *x, _ => return Default::default(), } }; } use get_num; #[derive(Debug, Copy, Clone)] pub struct Op1 { op: for<'v> fn(&LVar<'v>) -> f64, x: LAddress, out: LAddress, } impl Op1 { pub(crate) const fn new(op: MathOp1, x: LAddress, out: LAddress) -> Self { Self { op: op.get_fn(), x, out, } } } impl LInstruction for Op1 { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { let x = (self.op)(exec.get(self.x)); *exec.get_mut(self.out) = LVar::Num(x); Flow::Continue } } impl Printable for Op1 { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { let op = mop::MathOp1::try_from(self.op).unwrap(); write!(f, "op {op} {} {}", info[self.out], info[self.x]) } } #[derive(Debug, Copy, Clone)] pub struct Op2 { op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> f64, a: LAddress, b: LAddress, out: LAddress, } impl Op2 { pub(crate) const fn new(op: MathOp2, a: LAddress, b: LAddress, out: LAddress) -> Self { Self { op: op.get_fn(), a, b, out, } } } impl LInstruction for Op2 { #[inline] fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { let x = (self.op)(exec.get(self.a), exec.get(self.b)); exec.memory[self.out] = LVar::Num(x); Flow::Continue } } impl Printable for Op2 { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { let op = mop2::MathOp2::try_from(self.op).unwrap(); write!( f, "op {op} {} {} {}", info[self.out], info[self.a], info[self.b] ) } } #[derive(Debug, Copy, Clone)] pub struct End {} impl LInstruction for End { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { exec.iterations += 1; // SAFETY: if we exist, 0 exists. unsafe { exec.jump(Instruction::new(0)) }; Flow::Stay } } impl Printable for End { fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!(f, "end") } } #[derive(Debug, Copy, Clone)] pub struct Noop {} impl LInstruction for Noop { fn run(&self, _: &mut ExecutorContext<'_, W>) -> Flow { Flow::Continue } } impl Printable for Noop { fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!(f, "noop") } } #[derive(Debug, Copy, Clone)] pub struct AlwaysJump { pub(crate) to: Instruction, } impl LInstruction for AlwaysJump { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { exec.jump(self.to); Flow::Stay } } impl Printable for AlwaysJump { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!(f, "jump ")?; match info.label(self.to) { Some(l) => f.write_str(l)?, None => write!(f, "{}", self.to.get())?, } write!(f, " always") } } #[derive(Debug, Copy, Clone)] pub struct Jump { op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool, pub(crate) to: Instruction, a: LAddress, b: LAddress, } impl Jump { pub fn new(op: ConditionOp, to: Instruction, a: LAddress, b: LAddress) -> Self { Self { op: op.get_fn(), to, a, b, } } } impl LInstruction for Jump { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { if (self.op)(exec.get(self.a), exec.get(self.b)) { exec.jump(self.to); Flow::Stay } else { Flow::Continue } } } impl Printable for Jump { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { let op = ConditionOp::try_from(self.op).unwrap(); write!(f, "jump {op} ")?; match info.label(self.to) { Some(l) => f.write_str(l)?, None => write!(f, "{}", self.to.get())?, }; write!(f, " {} {}", info[self.a], info[self.b]) } } #[derive(Debug, Copy, Clone)] pub struct DynJump { pub to: LAddress, pub proglen: usize, } impl LInstruction for DynJump { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { if let &LVar::Num(n) = exec.get(self.to) { let i = n.round() as usize; if i < self.proglen { // SAFETY: just checked bounds exec.jump(unsafe { Instruction::new(i) }); return Flow::Stay; } } Flow::Continue } } impl Printable for DynJump { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!(f, "set @counter {}", info[self.to]) } } #[derive(Debug, Copy, Clone)] pub struct Stop {} impl LInstruction for Stop { fn run(&self, _: &mut ExecutorContext<'_, W>) -> Flow { Flow::Exit } } impl Printable for Stop { fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!(f, "stop") } } #[derive(Copy, Clone, Debug)] pub struct PackColor { pub out: LAddress, pub r: LAddress, pub g: LAddress, pub b: LAddress, pub a: LAddress, } impl LInstruction for PackColor { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { macro_rules! num { ($($n:ident)+) => { ($(match exec.memory[self.$n] { LVar::Num(n) => (n.clamp(0.0, 1.0) * 255.0) as u8, _ => 255, },)+) }; } let (r, g, b, a) = num!(r g b a); exec.memory[self.out] = LVar::from(fimg::Pack::pack(&[r, g, b, a]) as f64); Flow::Continue } } impl Printable for PackColor { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!( f, "packcolor {} {} {} {} {}", info[self.out], info[self.r], info[self.g], info[self.b], info[self.a] ) } } #[derive(Debug, Copy, Clone)] pub struct Select { pub op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool, pub(crate) to: LAddress, pub x: LAddress, pub y: LAddress, pub a: LAddress, pub b: LAddress, } impl LInstruction for Select { fn run(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { *exec.get_mut(self.to) = if (self.op)(exec.get(self.x), exec.get(self.y)) { exec.get(self.a) } else { exec.get(self.b) } .clone(); Flow::Continue } } impl Printable for Select { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!( f, "select {} {} {} {} {} {}", info[self.to], ConditionOp::try_from(self.op).unwrap(), info[self.x], info[self.y], info[self.a], info[self.b] ) } }