mindustry logic execution, map- and schematic- parsing and rendering
correct draw order
| -rw-r--r-- | lemu/Cargo.toml | 1 | ||||
| -rw-r--r-- | lemu/src/executor/builder.rs | 11 | ||||
| -rw-r--r-- | lemu/src/executor/mod.rs | 41 | ||||
| -rw-r--r-- | lemu/src/instructions/draw.rs | 471 | ||||
| -rw-r--r-- | lemu/src/instructions/mod.rs | 2 | ||||
| -rw-r--r-- | lemu/src/main.rs | 2 | ||||
| -rw-r--r-- | lemu/src/memory.rs | 7 |
7 files changed, 371 insertions, 164 deletions
diff --git a/lemu/Cargo.toml b/lemu/Cargo.toml index 11bc0a9..714ecfe 100644 --- a/lemu/Cargo.toml +++ b/lemu/Cargo.toml @@ -18,6 +18,7 @@ rust-fuzzy-search = { version = "0.1.1", optional = true } beef = "0.5" lerr = { version = "0.1.5", optional = true } comat = { version = "0.1.2", optional = true } +vecto = "0.1.1" [features] bin = ["fimg/save", "diagnose"] diff --git a/lemu/src/executor/builder.rs b/lemu/src/executor/builder.rs index 3f83159..fbc7284 100644 --- a/lemu/src/executor/builder.rs +++ b/lemu/src/executor/builder.rs @@ -2,8 +2,8 @@ use fimg::Image; use std::{collections::VecDeque, io::Write as Wr}; use super::{ - Display, Drawing, Executor, ExecutorContext, Instruction, Limit, Memory, PInstr, UPInstr, - BANK_SIZE, CELL_SIZE, + Display, DisplayState, Drawing, Executor, ExecutorContext, Instruction, Limit, Memory, PInstr, + UPInstr, BANK_SIZE, CELL_SIZE, }; use crate::{ code::Code, @@ -15,7 +15,7 @@ use crate::{ /// for internal use by [parser](crate::parser) only pub struct ExecutorBuilderInternal<'v, W: Wr> { - displays: Vec<Image<Vec<u8>, 4>>, + displays: Box<[(Image<Vec<u8>, 4>, DisplayState)]>, pub(crate) program: Vec<UPInstr<'v>>, output: Option<W>, banks: Vec<f64>, @@ -30,7 +30,10 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> { pub(crate) fn new(w: Option<W>, d: Vec<Image<Vec<u8>, 4>>) -> Self { Self { output: w, - displays: d, + displays: d + .into_iter() + .map(|d| (d, DisplayState::default())) + .collect(), program: vec![], banks: vec![], cells: vec![], diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs index 94b9c57..fb33c91 100644 --- a/lemu/src/executor/mod.rs +++ b/lemu/src/executor/mod.rs @@ -1,10 +1,13 @@ mod builder; -use crate::debug::{info::DebugInfo, printable::Printable}; +use crate::{ + debug::{info::DebugInfo, printable::Printable}, + instructions::draw::Drawn, +}; use super::{ code::{Code, PInstr}, - instructions::{DrawInstr, DrawInstruction, Flow, Instr, LInstruction}, + instructions::{DrawInstr, Flow, Frozen, Instr, LInstruction}, lexer::Token, memory::{LAddress, LRegistry, LVar}, }; @@ -132,13 +135,12 @@ pub enum UPInstr<'s> { } pub struct Drawing { - pub displays: Box<[fimg::Image<Vec<u8>, 4>]>, - /// points to `Executor.program` - pub buffer: VecDeque<*const DrawInstr>, + pub displays: Box<[(fimg::Image<Vec<u8>, 4>, DisplayState)]>, + pub buffer: VecDeque<Drawn>, } impl Drawing { - fn buffer(&mut self, i: &DrawInstr) { + fn buffer(&mut self, i: Drawn) { self.buffer.push_back(i); } } @@ -155,8 +157,11 @@ pub struct ExecutorContext<'strings, W: Write> { pub iterations: usize, } +/// State of a display. pub struct DisplayState { + /// Color to draw pub color: (u8, u8, u8, u8), + /// Stroke to draw pub stroke: f64, } @@ -176,15 +181,17 @@ impl Default for DisplayState { } impl<'s, W: Write> ExecutorContext<'s, W> { + pub fn buffer(&mut self, instr: &DrawInstr) { + if let Some(i) = instr.freeze(&self.memory) { + self.display.buffer(i) + } + } + pub fn flush(&mut self, to: Display) { - let mut state = DisplayState::default(); - // SAFETY: safe as long as the instruction isnt held too long - 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, - ); + let (ref mut img, ref mut state) = &mut self.display.displays[to.0]; + while let Some(d) = self.display.buffer.pop_front() { + use crate::instructions::draw::Apply; + d.apply(img.as_mut(), state); } } @@ -217,7 +224,7 @@ 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>]>, + 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 @@ -227,7 +234,7 @@ pub struct Output<W: Write> { 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 { + for (display, _) in &mut *self.inner.display.displays { // TODO make the instructions draw flipped-ly display.flip_v(); } @@ -256,7 +263,7 @@ impl<'s, W: Write> Executor<'s, W> { i.run(&mut self.inner) } PInstr::Draw(i) => { - self.inner.display.buffer(i); + self.inner.buffer(i); Flow::Continue } _ => Flow::Continue, diff --git a/lemu/src/instructions/draw.rs b/lemu/src/instructions/draw.rs index bc22d4c..7bcca94 100644 --- a/lemu/src/instructions/draw.rs +++ b/lemu/src/instructions/draw.rs @@ -6,34 +6,91 @@ use crate::{ }; use enum_dispatch::enum_dispatch; use fimg::Image; -use std::fmt; +use std::fmt::{self, Display as Disp}; +use vecto::Vec2; pub const INSTRS: &[&str] = &[ "clear", "color", "col", "stroke", "line", "rect", "lineRect", "triangle", "poly", "linePoly", ]; #[enum_dispatch] -pub trait DrawInstruction: Printable { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ); +pub trait Apply: Disp { + fn apply(self, image: Image<&mut [u8], 4>, state: &mut DisplayState); +} + +#[derive(Debug)] +#[enum_dispatch(Apply)] +pub enum Drawn { + Line(LineD), + RectBordered(RectBorderedD), + RectFilled(RectFilledD), + Triangle(TriangleD), + Clear(ClearD), + SetColor(SetColorD), + SetStroke(SetStrokeD), + Poly(PolyD), + LinePoly(LinePolyD), +} + +impl std::fmt::Display for Drawn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Line(i) => write!(f, "{i}"), + Self::RectBordered(i) => write!(f, "{i}"), + Self::RectFilled(i) => write!(f, "{i}"), + Self::Triangle(i) => write!(f, "{i}"), + Self::Clear(i) => write!(f, "{i}"), + Self::SetColor(i) => write!(f, "{i}"), + Self::SetStroke(i) => write!(f, "{i}"), + Self::Poly(i) => write!(f, "{i}"), + Self::LinePoly(i) => write!(f, "{i}"), + } + } } -#[derive(Debug, Copy, Clone)] -#[enum_dispatch(DrawInstruction)] -pub enum DrawInstr { - Line(Line), - RectBordered(RectBordered), - RectFilled(RectFilled), - Triangle(Triangle), - Clear(Clear), - SetColor(SetColor), - SetStroke(SetStroke), - Poly(Poly), - LinePoly(LinePoly), +pub trait Frozen<A: Apply>: Printable { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<A>; +} + +macro_rules! dinstr { + [$($x:ident),+] => { + #[derive(Debug, Copy, Clone)] + pub enum DrawInstr { + $($x($x),)+ + } + + $(impl From<$x> for DrawInstr { + fn from(v: $x) -> Self { Self::$x(v) } + })+ + } +} + +dinstr! { + Line, + RectBordered, + RectFilled, + Triangle, + Clear, + SetColor, + SetStroke, + Poly, + LinePoly +} + +impl Frozen<Drawn> for DrawInstr { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<Drawn> { + Some(match self { + Self::Line(i) => Drawn::from(i.freeze(mem)?), + Self::RectBordered(i) => Drawn::from(i.freeze(mem)?), + Self::RectFilled(i) => Drawn::from(i.freeze(mem)?), + Self::Triangle(i) => Drawn::from(i.freeze(mem)?), + Self::Clear(i) => Drawn::from(i.freeze(mem)?), + Self::SetColor(i) => Drawn::from(i.freeze(mem)?), + Self::SetStroke(i) => Drawn::from(i.freeze(mem)?), + Self::Poly(i) => Drawn::from(i.freeze(mem)?), + Self::LinePoly(i) => Drawn::from(i.freeze(mem)?), + }) + } } impl Printable for DrawInstr { @@ -60,25 +117,28 @@ pub struct Clear { pub b: LAddress, } -impl DrawInstruction for Clear { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - _: &mut DisplayState, - ) { +#[derive(Debug, Copy, Clone)] +pub struct ClearD(u8, u8, u8); + +impl Apply for ClearD { + fn apply(self, mut image: Image<&mut [u8], 4>, _: &mut DisplayState) { + for [r2, g2, b2, a2] in image.chunked_mut() { + (*r2, *b2, *g2, *a2) = (self.0, self.1, self.2, 255); + } + } +} + +impl Frozen<ClearD> for Clear { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<ClearD> { macro_rules! u8 { ($v:ident) => { match mem.get(self.$v) { LVar::Num(n) => n.round() as u8, - _ => return, + _ => return None, } }; } - let (r, g, b) = (u8!(r), u8!(g), u8!(b)); - for [r2, g2, b2, a2] in image.chunked_mut() { - (*r2, *b2, *g2, *a2) = (r, g, b, 255); - } + Some(ClearD(u8!(r), u8!(g), u8!(b))) } } @@ -92,6 +152,21 @@ impl Printable for Clear { } } +impl Disp for ClearD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "draw clear {} {} {}", self.0, self.1, self.2) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SetColorD((u8, u8, u8, u8)); + +impl Apply for SetColorD { + fn apply(self, _: Image<&mut [u8], 4>, state: &mut DisplayState) { + state.color = self.0; + } +} + #[derive(Debug, Copy, Clone)] pub struct SetColor { @@ -100,22 +175,18 @@ pub struct SetColor { pub b: LAddress, pub a: LAddress, } -impl DrawInstruction for SetColor { - fn draw( - &self, - mem: &mut LRegistry<'_>, - _: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { + +impl Frozen<SetColorD> for SetColor { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<SetColorD> { macro_rules! u8 { ($v:ident) => { match mem.get(self.$v) { LVar::Num(n) => n.round() as u8, - _ => return, + _ => return None, } }; } - state.color = (u8!(r), u8!(g), u8!(b), u8!(a)); + Some(SetColorD((u8!(r), u8!(g), u8!(b), u8!(a)))) } } @@ -129,21 +200,40 @@ impl Printable for SetColor { } } +impl Disp for SetColorD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw color {} {} {} {}", + self.0.0, self.0.1, self.0.2, self.0.3 + ) + } +} + #[derive(Debug, Copy, Clone)] pub struct SetStroke { pub size: LAddress, } -impl DrawInstruction for SetStroke { - fn draw( - &self, - mem: &mut LRegistry<'_>, - _: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { - if let &LVar::Num(n) = mem.get(self.size) { - state.stroke = n; - } + +#[derive(Debug, Copy, Clone)] +pub struct SetStrokeD(f64); + +impl Apply for SetStrokeD { + fn apply(self, _: Image<&mut [u8], 4>, state: &mut DisplayState) { + state.stroke = self.0; + } +} + +impl Frozen<SetStrokeD> for SetStroke { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<SetStrokeD> { + mem.get(self.size).num().map(SetStrokeD) + } +} + +impl Disp for SetStrokeD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "draw stroke {}", self.0) } } @@ -156,11 +246,9 @@ impl Printable for SetStroke { pub type Point = (LAddress, LAddress); #[rustfmt::skip] macro_rules! point { - ($mem:ident@$point:expr) => {{ - let &LVar::Num(a) = $mem.get($point.0) else { return }; - let &LVar::Num(b) = $mem.get($point.1) else { return }; - (a,b) - }} + ($mem:ident@$point:expr) => { + ($mem.get($point.0).num()?, $mem.get($point.1).num()?) + } } macro_rules! map { @@ -176,16 +264,31 @@ pub struct Line { pub point_b: Point, } -impl DrawInstruction for Line { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { - let a = map!(point!([email protected]_a), |n| n as f32); - let b = map!(point!([email protected]_b), |n| n as f32); - image.thick_line(a, b, state.stroke as f32, state.col()); +#[derive(Debug)] +pub struct LineD(Vec2, Vec2); + +impl Apply for LineD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.thick_line(self.0, self.1, state.stroke as f32, state.col()); + } +} + +impl Frozen<LineD> for Line { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<LineD> { + Some(LineD( + map!(point!([email protected]_a), |n| n as f32).into(), + map!(point!([email protected]_b), |n| n as f32).into(), + )) + } +} + +impl Disp for LineD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw line {} {} {} {}", + self.0.x, self.0.y, self.1.x, self.1.y + ) } } @@ -207,17 +310,34 @@ pub struct RectFilled { pub height: LAddress, } -impl DrawInstruction for RectFilled { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { - let pos = map!(point!([email protected]), |n| n as u32); - let width = get_num!(mem.get(self.width)) as u32; - let height = get_num!(mem.get(self.height)) as u32; - image.filled_box(pos, width, height, state.col()); +#[derive(Debug)] +pub struct RectFilledD((u32, u32), (u32, u32)); + +impl Apply for RectFilledD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.filled_box(self.0, self.1.0, self.1.1, state.col()); + } +} + +impl Frozen<RectFilledD> for RectFilled { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<RectFilledD> { + Some(RectFilledD( + map!(point!([email protected]), |n| n as u32), + ( + get_num!(mem.get(self.width)) as u32, + get_num!(mem.get(self.height)) as u32, + ), + )) + } +} + +impl Disp for RectFilledD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw rect {} {} {} {}", + self.0.0, self.0.1, self.1.0, self.1.1 + ) } } @@ -239,6 +359,16 @@ pub struct RectBordered { pub height: LAddress, } +impl Disp for RectBorderedD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw lineRect {} {} {} {}", + self.0.0, self.0.1, self.1.0, self.1.1 + ) + } +} + impl Printable for RectBordered { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!( @@ -249,17 +379,30 @@ impl Printable for RectBordered { } } -impl DrawInstruction for RectBordered { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { - let pos = map!(point!([email protected]), |n| n as u32); - let width = get_num!(mem.get(self.width)) as u32; - let height = get_num!(mem.get(self.height)) as u32; - image.stroked_box(pos, width, height, state.stroke.round() as u32, state.col()); +#[derive(Debug)] +pub struct RectBorderedD((u32, u32), (u32, u32)); + +impl Apply for RectBorderedD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.stroked_box( + self.0, + self.1.0, + self.1.1, + state.stroke.round() as u32, + state.col(), + ); + } +} + +impl Frozen<RectBorderedD> for RectBordered { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<RectBorderedD> { + Some(RectBorderedD( + map!(point!([email protected]), |n| n as u32), + ( + get_num!(mem.get(self.width)) as u32, + get_num!(mem.get(self.height)) as u32, + ), + )) } } @@ -268,22 +411,36 @@ impl DrawInstruction for RectBordered { pub struct Triangle { pub points: (Point, Point, Point), } -impl DrawInstruction for Triangle { - fn draw( - &self, - mem: &mut LRegistry<'_>, - i: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { - let to32 = |n| n as f32; - let (a, b, c) = ( - map!(point!([email protected]), to32), - map!(point!([email protected]), to32), - map!(point!([email protected]), to32), - ); - i.tri::<f32>(a, b, c, state.col()); + +#[derive(Debug)] +pub struct TriangleD(Vec2, Vec2, Vec2); + +impl Apply for TriangleD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.tri::<f32>(self.0, self.1, self.2, state.col()); + } +} + +impl Frozen<TriangleD> for Triangle { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<TriangleD> { + Some(TriangleD( + map!(point!([email protected]), |n| n as f32).into(), + map!(point!([email protected]), |n| n as f32).into(), + map!(point!([email protected]), |n| n as f32).into(), + )) } } + +impl Disp for TriangleD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw triangle {} {} {} {} {} {}", + self.0.x, self.0.y, self.1.x, self.1.y, self.2.x, self.2.y + ) + } +} + impl Printable for Triangle { fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { write!( @@ -308,28 +465,49 @@ pub struct Poly { pub(crate) rot: LAddress, } -impl DrawInstruction for Poly { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { +#[derive(Debug)] +pub enum PolyD { + Poly(Vec2, usize, f32, f32), + Circle((i32, i32), i32), +} + +impl Apply for PolyD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + match self { + PolyD::Poly(pos, sides, radius, rotation) => { + image.poly(pos, sides, radius, rotation, state.col()) + } + PolyD::Circle(pos, radius) => image.circle(pos, radius, state.col()), + } + } +} + +impl Frozen<PolyD> for Poly { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<PolyD> { let sides = get_num!(mem.get(self.sides)).round() as usize; - if sides < 90 { - image.poly( - map!(point!([email protected]), |n| n as f32), + Some(if sides < 90 { + PolyD::Poly( + map!(point!([email protected]), |n| n as f32).into(), sides, get_num!(mem.get(self.radius)) as f32, get_num!(mem.get(self.rot)) as f32, - state.col(), - ); + ) } else { - image.circle( + PolyD::Circle( map!(point!([email protected]), |n: f64| n.round() as i32), get_num!(mem.get(self.radius)).round() as i32, - state.col(), - ); + ) + }) + } +} + +impl Disp for PolyD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PolyD::Poly(Vec2 { x, y }, sides, radius, rot) => { + write!(f, "draw poly {x} {y} {sides} {radius} {rot}") + } + PolyD::Circle((x, y), sides) => write!(f, "draw poly {x} {y} {sides}"), } } } @@ -353,30 +531,41 @@ pub struct LinePoly { pub(crate) rot: LAddress, } -impl DrawInstruction for LinePoly { - fn draw( - &self, - mem: &mut LRegistry<'_>, - image: &mut Image<&mut [u8], 4>, - state: &mut DisplayState, - ) { - let sides = get_num!(mem.get(self.sides)).round() as usize; - if sides < 90 { - image.border_poly( - map!(point!([email protected]), |n| n as f32), - sides, - get_num!(mem.get(self.radius)) as f32, - get_num!(mem.get(self.rot)) as f32, - state.stroke as f32, - state.col(), - ); - } else { - image.border_circle( - map!(point!([email protected]), |n: f64| n.round() as i32), - get_num!(mem.get(self.radius)).round() as i32, - state.col(), - ); - } +#[derive(Debug)] +/// border_Circle doesnt let you specify a stroke +pub struct LinePolyD(Vec2, usize, f32, f32); + +impl Apply for LinePolyD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.border_poly( + self.0, + self.1, + self.2, + self.3, + state.stroke as f32, + state.col(), + ) + } +} + +impl Frozen<LinePolyD> for LinePoly { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<LinePolyD> { + Some(LinePolyD( + map!(point!([email protected]), |n| n as f32).into(), + get_num!(mem.get(self.sides)).round() as usize, + get_num!(mem.get(self.radius)) as f32, + get_num!(mem.get(self.rot)) as f32, + )) + } +} + +impl Disp for LinePolyD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw linePoly {} {} {} {} {}", + self.0.x, self.0.y, self.1, self.2, self.3 + ) } } diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs index f4d6565..e072a24 100644 --- a/lemu/src/instructions/mod.rs +++ b/lemu/src/instructions/mod.rs @@ -19,7 +19,7 @@ mod mop; mod mop2; pub use cop::ConditionOp; -pub use draw::{DrawInstr, DrawInstruction}; +pub use draw::{DrawInstr, Frozen}; use enum_dispatch::enum_dispatch; pub use mop::MathOp1; pub use mop2::MathOp2; diff --git a/lemu/src/main.rs b/lemu/src/main.rs index 7231eb9..bbb45b4 100644 --- a/lemu/src/main.rs +++ b/lemu/src/main.rs @@ -23,7 +23,7 @@ fn main() -> ExitCode { lex.run(); dbg!(lex.instructions_ran); let Output { displays, .. } = lex.output(); - for (d, i) in displays.iter().zip(1..=displays.len()) { + for ((d, _), i) in displays.iter().zip(1..=displays.len()) { d.save(format!("image{i}.png")); } } diff --git a/lemu/src/memory.rs b/lemu/src/memory.rs index ddb0e8a..86024c3 100644 --- a/lemu/src/memory.rs +++ b/lemu/src/memory.rs @@ -28,6 +28,13 @@ impl LVar<'_> { pub const fn null() -> LVar<'static> { LVar::Num(0.0) } + + pub fn num(&self) -> Option<f64> { + match *self { + Self::Num(n) => Some(n), + Self::String(_) => None, + } + } } #[derive(Clone, Copy)] |