Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'lemu/src/executor/mod.rs')
-rw-r--r--lemu/src/executor/mod.rs320
1 files changed, 320 insertions, 0 deletions
diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs
new file mode 100644
index 0000000..0989bc0
--- /dev/null
+++ b/lemu/src/executor/mod.rs
@@ -0,0 +1,320 @@
+mod builder;
+const COUNTER: LAddress = unsafe { LAddress::addr(0) };
+use crate::{
+ debug::{info::DebugInfo, printable::Printable},
+ instructions::draw::Drawn,
+};
+
+use super::{
+ code::{Code, PInstr},
+ instructions::{DrawInstr, Flow, Frozen, Instr, LInstruction},
+ lexer::Token,
+ memory::{LAddress, LRegistry, LVar},
+};
+pub use builder::ExecutorBuilderInternal;
+use fimg::Image;
+use std::{collections::VecDeque, io::Write, num::NonZeroUsize};
+
+#[derive(Debug, Copy, Clone, Default)]
+pub struct Display(pub usize);
+
+impl std::fmt::Display for Display {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "display{}", self.0 + 1)
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Memory {
+ Cell(u8),
+ Bank(u8),
+}
+
+impl Memory {
+ pub(crate) const fn fits(self, i: usize) -> bool {
+ match self {
+ Self::Bank(_) => i < BANK_SIZE,
+ Self::Cell(_) => i < CELL_SIZE,
+ }
+ }
+
+ pub(crate) const fn size(&self) -> usize {
+ match self {
+ Self::Bank(_) => BANK_SIZE,
+ Self::Cell(_) => CELL_SIZE,
+ }
+ }
+}
+
+impl std::fmt::Display for Memory {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Bank(n) => write!(f, "bank{}", n + 1),
+ Self::Cell(n) => write!(f, "cell{}", n + 1),
+ }
+ }
+}
+
+pub const BANK_SIZE: usize = 512;
+pub const CELL_SIZE: usize = 64;
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Instruction(usize);
+
+impl Instruction {
+ /// # Safety
+ /// verify n is valid.
+ pub const unsafe fn new(n: usize) -> Self {
+ Self(n)
+ }
+
+ pub const 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, Copy, Clone)]
+pub enum Limit {
+ /// limited to n
+ Limited(NonZeroUsize),
+ /// unlimited
+ Unlimited,
+}
+
+impl Limit {
+ /// panics if n != 0
+ 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,
+ }
+ }
+}
+
+/// One time use logic executor.
+pub struct Executor<'varnames, W: Write> {
+ /// if limited, will run n instructions before exiting.
+ pub instruction_limit: Limit,
+ /// if limtited, will loop(go from a end to the start) n times before exiting
+ /// both unlimited does not mean this function will never return;
+ /// a `Stop` instruction will break the loop.
+ pub iteration_limit: Limit,
+ pub(crate) inner: ExecutorContext<'varnames, W>,
+ /// gets pointed to by drawbuf (pls no move)
+ pub(crate) program: Code<'varnames>,
+ /// Counter for the number of instructions we have run so far.
+ pub instructions_ran: usize,
+ debug_info: DebugInfo<'varnames>,
+}
+
+impl<W: Write> std::fmt::Display for Executor<'_, W> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.program.print(&self.debug_info, f)
+ }
+}
+
+#[derive(Debug)]
+pub enum UPInstr<'s> {
+ Instr(Instr),
+ Draw(DrawInstr),
+ UnfinishedJump,
+ Code(Box<[Token<'s>]>),
+ Comment(&'s str),
+}
+
+pub struct Drawing {
+ pub displays: Box<[(fimg::Image<Vec<u8>, 4>, DisplayState)]>,
+ pub buffer: VecDeque<Drawn>,
+}
+
+impl Drawing {
+ fn buffer(&mut self, i: Drawn) {
+ self.buffer.push_back(i);
+ }
+}
+pub struct ExecutorContext<'strings, W: Write> {
+ // maximum of 128 elements, so can use ~60KB
+ pub cells: Box<[[f64; CELL_SIZE]]>, // screw world cells
+ // maximum of 127 elements, so can use ~500KB
+ pub banks: Box<[[f64; BANK_SIZE]]>,
+ pub memory: LRegistry<'strings>,
+ pub display: Drawing,
+ pub output: Option<W>,
+ /// Counter for the number of iterations we have run so far.
+ pub iterations: usize,
+}
+
+/// State of a display.
+#[derive(Debug)]
+pub struct DisplayState {
+ /// Color to draw
+ pub color: (u8, u8, u8, u8),
+ /// Stroke to draw
+ 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: 5.0,
+ }
+ }
+}
+
+impl<'s, W: Write> ExecutorContext<'s, W> {
+ pub fn flush(&mut self, to: Display) {
+ let (img, state) = &mut self.display.displays[to.0];
+ while let Some(d) = self.display.buffer.pop_front() {
+ use crate::instructions::draw::Apply;
+ #[cfg(feature = "debug")]
+ comat::cprintln!("{d:blue}");
+ d.apply(img.as_mut(), state);
+ }
+ }
+
+ pub fn mem(&mut self, m: Memory) -> &mut [f64] {
+ match m {
+ Memory::Bank(m) => &mut self.banks[m as usize],
+ Memory::Cell(m) => &mut self.cells[m as usize],
+ }
+ }
+
+ pub fn set(&mut self, a: LAddress, b: LAddress) {
+ self.memory[a] = self.memory[b].clone();
+ }
+
+ pub fn get_mut(&mut self, a: LAddress) -> &mut LVar<'s> {
+ &mut self.memory[a]
+ }
+
+ pub fn jump(&mut self, Instruction(n): Instruction) {
+ *self.counter() = n as f64;
+ }
+
+ pub fn counter(&mut self) -> &mut f64 {
+ unsafe {
+ self.memory
+ .0
+ .get_unchecked_mut(0)
+ .num_mut()
+ .unwrap_unchecked()
+ }
+ }
+
+ pub fn get<'a>(&'a self, a: LAddress) -> &LVar<'s> {
+ &self.memory[a]
+ }
+}
+
+/// Returned by the [`output`](Executor::output).function.
+pub struct Output<W: Write> {
+ /// Everything created by a `print` instruction.
+ pub output: Option<W>,
+ /// Logic displays that were drawn with `draw` instructions.
+ pub displays: Box<[(Image<Vec<u8>, 4>, DisplayState)]>,
+ /// Memory banks, written to with the `write`/`read` instructions
+ pub cells: Box<[[f64; CELL_SIZE]]>,
+ /// Memory cells, written to with the `write`/`read` instructions
+ pub banks: Box<[[f64; BANK_SIZE]]>,
+}
+
+impl<'s, W: Write> Executor<'s, W> {
+ /// Consume this executor, returning all output.
+ pub fn output(mut self) -> Output<W> {
+ for (display, _) in &mut *self.inner.display.displays {
+ // TODO make the instructions draw flipped-ly
+ display.flip_v();
+ }
+ Output {
+ output: self.inner.output,
+ displays: self.inner.display.displays,
+ cells: self.inner.cells,
+ banks: self.inner.banks,
+ }
+ }
+
+ /// # Safety
+ ///
+ /// `counter` *must* be in bounds.
+ unsafe fn run_current(&mut self) -> Flow {
+ // SAFETY: yee
+ let c = self.inner.counter();
+ let i = unsafe { self.program.get_unchecked(*c as usize) };
+ *c += 1.0;
+ match i {
+ PInstr::Instr(i) => {
+ #[cfg(feature = "debug")]
+ {
+ let mut instr = String::new();
+ i.print(&self.debug_info, &mut instr).unwrap();
+ let mut mem = String::new();
+ self.inner.memory.print(&self.debug_info, &mut mem).unwrap();
+ comat::cprintln!(
+ "{black}{:0<2} | {green}{instr} {black}({mem}){reset}",
+ self.inner.counter(),
+ );
+ }
+
+ i.run(&mut self.inner)
+ }
+ PInstr::Draw(i) => {
+ if let Some(i) = i.freeze(&self.inner.memory) {
+ #[cfg(feature = "debug")]
+ {
+ let mut mem = String::new();
+ self.inner.memory.print(&self.debug_info, &mut mem).unwrap();
+ comat::cprintln!(
+ "{black}{:0<2} | {magenta}{i} {black}({mem}){reset}",
+ self.inner.counter()
+ );
+ }
+ self.inner.display.buffer(i)
+ }
+ Flow::Continue
+ }
+ _ => Flow::Continue,
+ }
+ }
+
+ /// Begin code execution.
+ pub fn run(&mut self) {
+ while !self.instruction_limit.reached(self.instructions_ran)
+ && !self.iteration_limit.reached(self.inner.iterations)
+ {
+ // SAFETY: we have a check
+ match unsafe { self.run_current() } {
+ Flow::Continue => {}
+ Flow::Exit => break,
+ Flow::Stay => {
+ self.instructions_ran += 1;
+ continue;
+ }
+ };
+ self.instructions_ran += 1;
+ let c = self.inner.counter();
+ if *c as usize >= self.program.len() {
+ *c = 0.0;
+ self.inner.iterations += 1;
+ }
+ }
+ }
+}