mod builder;
use super::{
instructions::{DrawInstr, DrawInstruction, Flow, Instr, LInstruction},
lexer::Token,
memory::{LAddress, LRegistry, LVar},
};
pub use builder::ExecutorBuilderInternal;
use fimg::Image;
use std::{collections::VecDeque, io::Write, num::NonZeroUsize, pin::Pin};
#[derive(Debug, Copy, Clone, Default)]
pub struct Display(pub usize);
#[derive(Debug, Copy, Clone)]
pub struct Memory(pub(crate) i8);
impl Memory {
pub(crate) const fn fits(self, i: usize) -> bool {
if self.0 < 0 {
i < BANK_SIZE
} else {
i < CELL_SIZE
}
}
pub(crate) fn size(self) -> usize {
if self.0 < 0 {
BANK_SIZE
} else {
CELL_SIZE
}
}
}
pub const BANK_SIZE: usize = 512;
pub const CELL_SIZE: usize = 64;
#[derive(Copy, Clone)]
pub struct Instruction(usize);
impl Instruction {
pub const unsafe fn new(n: usize) -> Self {
Self(n)
}
pub fn get(self) -> usize {
self.0
}
}
impl std::fmt::Debug for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Instruction#{}", self.0)
}
}
#[derive(Debug)]
pub enum PInstr<'s> {
Instr(Instr<'s>),
Draw(DrawInstr<'s>),
Code(Box<[Token<'s>]>),
NoOp,
}
#[derive(Debug, Copy, Clone)]
pub enum Limit {
Limited(NonZeroUsize),
Unlimited,
}
impl Limit {
pub fn limited(n: usize) -> Self {
Self::Limited(n.try_into().expect("nonzero"))
}
}
impl Limit {
pub(crate) const fn reached(self, n: usize) -> bool {
match self {
Self::Limited(v) => v.get() <= n,
Self::Unlimited => false,
}
}
}
pub struct Executor<'varnames, W: Write> {
pub instruction_limit: Limit,
pub iteration_limit: Limit,
pub(crate) inner: ExecutorContext<'varnames, W>,
pub(crate) program: Pin<Box<[PInstr<'varnames>]>>,
pub instructions_ran: usize,
}
pub enum UPInstr<'s> {
Instr(Instr<'s>),
Draw(DrawInstr<'s>),
UnfinishedJump,
Code(Box<[Token<'s>]>),
NoOp,
}
pub struct Drawing<'v> {
pub displays: Box<[fimg::Image<Vec<u8>, 4>]>,
pub buffer: VecDeque<*const DrawInstr<'v>>,
}
impl<'v> Drawing<'v> {
fn buffer(&mut self, i: &DrawInstr<'v>) {
self.buffer.push_back(i);
}
}
pub struct ExecutorContext<'varnames, W: Write> {
pub cells: Box<[[f64; CELL_SIZE]]>, pub banks: Box<[[f64; BANK_SIZE]]>,
pub memory: LRegistry<'varnames>,
pub counter: usize,
pub display: Drawing<'varnames>,
pub output: Option<W>,
pub iterations: usize,
}
pub struct DisplayState {
pub color: (u8, u8, u8, u8),
pub stroke: f64,
}
impl DisplayState {
pub const fn col(&self) -> [u8; 4] {
[self.color.0, self.color.1, self.color.2, self.color.3]
}
}
impl Default for DisplayState {
fn default() -> Self {
Self {
color: Default::default(),
stroke: 1.0,
}
}
}
impl<'s, W: Write> ExecutorContext<'s, W> {
pub fn flush(&mut self, to: Display) {
let mut state = DisplayState::default();
while let Some(d) = unsafe { self.display.buffer.pop_front().map(|v| &*v) } {
d.draw(
&mut self.memory,
&mut self.display.displays[to.0].as_mut(),
&mut state,
);
}
}
pub fn mem(&mut self, Memory(m): Memory) -> &mut [f64] {
if m < 0 {
let m = (m + 1).unsigned_abs() as usize;
&mut self.banks[m]
} else {
let m = m as usize;
&mut self.cells[m]
}
}
pub fn set(&mut self, a: &LAddress<'s>, b: LAddress<'s>) -> bool {
self.memory.set(a, b)
}
pub fn get_mut(&mut self, a: &LAddress<'s>) -> Option<&mut LVar<'s>> {
self.memory.get_mut(a)
}
pub fn jump(&mut self, Instruction(n): Instruction) {
self.counter = n;
}
pub fn get<'a>(&'a self, a: &'a LAddress<'s>) -> &LVar<'s> {
self.memory.get(a)
}
}
pub struct Output<W: Write> {
pub output: Option<W>,
pub displays: Box<[Image<Vec<u8>, 4>]>,
pub cells: Box<[[f64; CELL_SIZE]]>,
pub banks: Box<[[f64; BANK_SIZE]]>,
}
impl<'s, W: Write> Executor<'s, W> {
pub fn output(mut self) -> Output<W> {
for display in &mut *self.inner.display.displays {
display.flip_v();
}
Output {
output: self.inner.output,
displays: self.inner.display.displays,
cells: self.inner.cells,
banks: self.inner.banks,
}
}
unsafe fn run_current(&mut self) -> Flow {
match unsafe { self.program.get_unchecked(self.inner.counter) } {
PInstr::Instr(i) => {
i.run(&mut self.inner)
}
PInstr::Draw(i) => {
self.inner.display.buffer(i);
Flow::Continue
}
_ => Flow::Continue,
}
}
pub fn run(&mut self) {
while !self.instruction_limit.reached(self.instructions_ran)
&& !self.iteration_limit.reached(self.inner.iterations)
{
match unsafe { self.run_current() } {
Flow::Continue => {}
Flow::Exit => break,
Flow::Stay => {
self.instructions_ran += 1;
continue;
}
};
self.instructions_ran += 1;
self.inner.counter += 1;
if self.inner.counter >= self.program.len() {
self.inner.counter = 0;
self.inner.iterations += 1;
self.inner.memory.clear();
}
}
}
}