mindustry logic execution, map- and schematic- parsing and rendering
-rw-r--r--lemu/src/code.rs4
-rw-r--r--lemu/src/executor/builder.rs14
-rw-r--r--lemu/src/executor/mod.rs34
-rw-r--r--lemu/src/instructions/draw.rs247
-rw-r--r--lemu/src/instructions/io.rs53
-rw-r--r--lemu/src/instructions/mod.rs203
-rw-r--r--lemu/src/lib.rs4
-rw-r--r--lemu/src/memory.rs113
-rw-r--r--lemu/src/parser/error.rs72
-rw-r--r--lemu/src/parser/mod.rs119
10 files changed, 457 insertions, 406 deletions
diff --git a/lemu/src/code.rs b/lemu/src/code.rs
index 9132279..bf9f58e 100644
--- a/lemu/src/code.rs
+++ b/lemu/src/code.rs
@@ -5,8 +5,8 @@ use super::{
#[derive(Debug)]
pub enum PInstr<'s> {
- Instr(Instr<'s>),
- Draw(DrawInstr<'s>),
+ Instr(Instr),
+ Draw(DrawInstr),
Code(Box<[Token<'s>]>),
Comment(&'s str),
}
diff --git a/lemu/src/executor/builder.rs b/lemu/src/executor/builder.rs
index eae3fb2..7ed4177 100644
--- a/lemu/src/executor/builder.rs
+++ b/lemu/src/executor/builder.rs
@@ -21,7 +21,7 @@ pub struct ExecutorBuilderInternal<'v, W: Wr> {
cells: Vec<f64>,
iteration_limit: Limit,
instruction_limit: Limit,
- mem: usize,
+ pub(crate) mem: LRegistry<'v>,
}
impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> {
@@ -34,7 +34,7 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> {
cells: vec![],
iteration_limit: Limit::limited(1),
instruction_limit: Limit::Unlimited,
- mem: 0,
+ mem: LRegistry::default(),
}
}
@@ -81,15 +81,11 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> {
Instruction(self.program.len() - 1)
}
- pub(crate) fn mem(&mut self, size: usize) {
- self.mem = size;
- }
-
- pub(crate) fn add(&mut self, i: impl Into<Instr<'s>>) {
+ pub(crate) fn add(&mut self, i: impl Into<Instr>) {
self.program.push(UPInstr::Instr(i.into()));
}
- pub(crate) fn draw(&mut self, i: impl Into<DrawInstr<'s>>) {
+ pub(crate) fn draw(&mut self, i: impl Into<DrawInstr>) {
self.program.push(UPInstr::Draw(i.into()));
}
@@ -140,7 +136,7 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> {
inner: ExecutorContext {
cells: cst::<CELL_SIZE>(cells),
banks: cst::<BANK_SIZE>(banks),
- memory: LRegistry::new(mem),
+ memory: mem,
counter: 0,
iterations: 0,
display: Drawing {
diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs
index 746ca7a..4b50cf6 100644
--- a/lemu/src/executor/mod.rs
+++ b/lemu/src/executor/mod.rs
@@ -113,33 +113,34 @@ pub struct Executor<'varnames, W: Write> {
pub instructions_ran: usize,
}
+#[derive(Debug)]
pub enum UPInstr<'s> {
- Instr(Instr<'s>),
- Draw(DrawInstr<'s>),
+ Instr(Instr),
+ Draw(DrawInstr),
UnfinishedJump,
Code(Box<[Token<'s>]>),
Comment(&'s str),
}
-pub struct Drawing<'v> {
+pub struct Drawing {
pub displays: Box<[fimg::Image<Vec<u8>, 4>]>,
/// points to `Executor.program`
- pub buffer: VecDeque<*const DrawInstr<'v>>,
+ pub buffer: VecDeque<*const DrawInstr>,
}
-impl<'v> Drawing<'v> {
- fn buffer(&mut self, i: &DrawInstr<'v>) {
+impl<'v> Drawing {
+ fn buffer(&mut self, i: &DrawInstr) {
self.buffer.push_back(i);
}
}
-pub struct ExecutorContext<'varnames, W: Write> {
+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<'varnames>,
+ pub memory: LRegistry<'strings>,
pub counter: usize,
- pub display: Drawing<'varnames>,
+ pub display: Drawing,
pub output: Option<W>,
/// Counter for the number of iterations we have run so far.
pub iterations: usize,
@@ -185,20 +186,20 @@ impl<'s, W: Write> ExecutorContext<'s, W> {
}
}
- pub fn set(&mut self, a: &LAddress<'s>, b: LAddress<'s>) -> bool {
- self.memory.set(a, b)
+ pub fn set(&mut self, a: LAddress, b: LAddress) {
+ self.memory[a] = self.memory[b].clone();
}
- pub fn get_mut(&mut self, a: &LAddress<'s>) -> Option<&mut LVar<'s>> {
- self.memory.get_mut(a)
+ 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;
}
- pub fn get<'a>(&'a self, a: &'a LAddress<'s>) -> &LVar<'s> {
- self.memory.get(a)
+ pub fn get<'a>(&'a self, a: LAddress) -> &LVar<'s> {
+ &self.memory[a]
}
}
@@ -236,7 +237,7 @@ impl<'s, W: Write> Executor<'s, W> {
// SAFETY: yee
match unsafe { self.program.get_unchecked(self.inner.counter) } {
PInstr::Instr(i) => {
- // println!("run {i:?} ({:?})", self.inner.memory);
+ // println!("run {i:?} ({})", self.inner.memory);
i.run(&mut self.inner)
}
PInstr::Draw(i) => {
@@ -266,7 +267,6 @@ impl<'s, W: Write> Executor<'s, W> {
if self.inner.counter >= self.program.len() {
self.inner.counter = 0;
self.inner.iterations += 1;
- self.inner.memory.clear();
}
}
}
diff --git a/lemu/src/instructions/draw.rs b/lemu/src/instructions/draw.rs
index fc93d8f..04b168c 100644
--- a/lemu/src/instructions/draw.rs
+++ b/lemu/src/instructions/draw.rs
@@ -12,8 +12,8 @@ pub const INSTRS: &[&str] = &[
];
#[enum_dispatch]
-pub trait DrawInstruction<'v>: Disp {
- fn draw(
+pub trait DrawInstruction: Disp {
+ fn draw<'v>(
&self,
mem: &mut LRegistry<'v>,
image: &mut Image<&mut [u8], 4>,
@@ -21,21 +21,21 @@ pub trait DrawInstruction<'v>: Disp {
);
}
-#[derive(Debug)]
+#[derive(Debug, Copy, Clone)]
#[enum_dispatch(DrawInstruction)]
-pub enum DrawInstr<'v> {
- Line(Line<'v>),
- RectBordered(RectBordered<'v>),
- RectFilled(RectFilled<'v>),
- Triangle(Triangle<'v>),
- Clear(Clear<'v>),
- SetColor(SetColor<'v>),
- SetStroke(SetStroke<'v>),
- Poly(Poly<'v>),
- LinePoly(LinePoly<'v>),
-}
-
-impl Disp for DrawInstr<'_> {
+pub enum DrawInstr {
+ Line(Line),
+ RectBordered(RectBordered),
+ RectFilled(RectFilled),
+ Triangle(Triangle),
+ Clear(Clear),
+ SetColor(SetColor),
+ SetStroke(SetStroke),
+ Poly(Poly),
+ LinePoly(LinePoly),
+}
+
+impl Disp for DrawInstr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Line(i) => write!(f, "{i}"),
@@ -51,18 +51,24 @@ impl Disp for DrawInstr<'_> {
}
}
-#[derive(Debug)]
-pub struct Clear<'v> {
- pub r: LAddress<'v>,
- pub g: LAddress<'v>,
- pub b: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct Clear {
+ pub r: LAddress,
+ pub g: LAddress,
+ pub b: LAddress,
}
-impl<'v> DrawInstruction<'v> for Clear<'v> {
- fn draw(&self, mem: &mut LRegistry<'v>, image: &mut Image<&mut [u8], 4>, _: &mut DisplayState) {
+impl DrawInstruction for Clear {
+ fn draw<'v>(
+ &self,
+ mem: &mut LRegistry<'v>,
+ image: &mut Image<&mut [u8], 4>,
+ _: &mut DisplayState,
+ ) {
macro_rules! u8 {
($v:ident) => {
- match mem.get(&self.$v) {
+ match mem.get(self.$v) {
LVar::Num(n) => n.round() as u8,
_ => return,
}
@@ -75,24 +81,30 @@ impl<'v> DrawInstruction<'v> for Clear<'v> {
}
}
-impl Disp for Clear<'_> {
+impl Disp for Clear {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "draw clear {} {} {}", self.r, self.g, self.b)
}
}
-#[derive(Debug)]
-pub struct SetColor<'v> {
- pub r: LAddress<'v>,
- pub g: LAddress<'v>,
- pub b: LAddress<'v>,
- pub a: LAddress<'v>,
-}
-impl<'v> DrawInstruction<'v> for SetColor<'v> {
- fn draw(&self, mem: &mut LRegistry<'v>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) {
+#[derive(Debug, Copy, Clone)]
+
+pub struct SetColor {
+ pub r: LAddress,
+ pub g: LAddress,
+ pub b: LAddress,
+ pub a: LAddress,
+}
+impl DrawInstruction for SetColor {
+ fn draw<'v>(
+ &self,
+ mem: &mut LRegistry<'v>,
+ _: &mut Image<&mut [u8], 4>,
+ state: &mut DisplayState,
+ ) {
macro_rules! u8 {
($v:ident) => {
- match mem.get(&self.$v) {
+ match mem.get(self.$v) {
LVar::Num(n) => n.round() as u8,
_ => return,
}
@@ -102,36 +114,42 @@ impl<'v> DrawInstruction<'v> for SetColor<'v> {
}
}
-impl Disp for SetColor<'_> {
+impl Disp for SetColor {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "draw color {} {} {} {}", self.r, self.g, self.b, self.a)
}
}
-#[derive(Debug)]
-pub struct SetStroke<'v> {
- pub size: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct SetStroke {
+ pub size: LAddress,
}
-impl<'v> DrawInstruction<'v> for SetStroke<'v> {
- fn draw(&self, mem: &mut LRegistry<'v>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) {
- if let &LVar::Num(n) = mem.get(&self.size) {
+impl DrawInstruction for SetStroke {
+ fn draw<'v>(
+ &self,
+ mem: &mut LRegistry<'v>,
+ _: &mut Image<&mut [u8], 4>,
+ state: &mut DisplayState,
+ ) {
+ if let &LVar::Num(n) = mem.get(self.size) {
state.stroke = n;
}
}
}
-impl Disp for SetStroke<'_> {
+impl Disp for SetStroke {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "draw stroke {}", self.size)
}
}
-pub type Point<'v> = (LAddress<'v>, LAddress<'v>);
+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; };
+ let &LVar::Num(a) = $mem.get($point.0) else { return };
+ let &LVar::Num(b) = $mem.get($point.1) else { return };
(a,b)
}}
}
@@ -142,14 +160,15 @@ macro_rules! map {
($fn(a), $fn(b))
}};
}
-#[derive(Debug)]
-pub struct Line<'v> {
- pub point_a: Point<'v>,
- pub point_b: Point<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct Line {
+ pub point_a: Point,
+ pub point_b: Point,
}
-impl<'v> DrawInstruction<'v> for Line<'v> {
- fn draw(
+impl DrawInstruction for Line {
+ fn draw<'v>(
&self,
mem: &mut LRegistry<'v>,
image: &mut Image<&mut [u8], 4>,
@@ -161,7 +180,7 @@ impl<'v> DrawInstruction<'v> for Line<'v> {
}
}
-impl Disp for Line<'_> {
+impl Disp for Line {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
@@ -171,28 +190,29 @@ impl Disp for Line<'_> {
}
}
-#[derive(Debug)]
-pub struct RectFilled<'v> {
- pub position: Point<'v>,
- pub width: LAddress<'v>,
- pub height: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct RectFilled {
+ pub position: Point,
+ pub width: LAddress,
+ pub height: LAddress,
}
-impl<'v> DrawInstruction<'v> for RectFilled<'v> {
- fn draw(
+impl DrawInstruction for RectFilled {
+ fn draw<'v>(
&self,
mem: &mut LRegistry<'v>,
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;
+ 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());
}
}
-impl Disp for RectFilled<'_> {
+impl Disp for RectFilled {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
@@ -202,14 +222,15 @@ impl Disp for RectFilled<'_> {
}
}
-#[derive(Debug)]
-pub struct RectBordered<'v> {
- pub position: Point<'v>,
- pub width: LAddress<'v>,
- pub height: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct RectBordered {
+ pub position: Point,
+ pub width: LAddress,
+ pub height: LAddress,
}
-impl Disp for RectBordered<'_> {
+impl Disp for RectBordered {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
@@ -219,26 +240,32 @@ impl Disp for RectBordered<'_> {
}
}
-impl<'v> DrawInstruction<'v> for RectBordered<'v> {
- fn draw(
+impl DrawInstruction for RectBordered {
+ fn draw<'v>(
&self,
mem: &mut LRegistry<'v>,
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;
+ 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 Triangle<'v> {
- pub points: (Point<'v>, Point<'v>, Point<'v>),
+#[derive(Debug, Copy, Clone)]
+
+pub struct Triangle {
+ pub points: (Point, Point, Point),
}
-impl<'v> DrawInstruction<'v> for Triangle<'v> {
- fn draw(&self, mem: &mut LRegistry<'v>, i: &mut Image<&mut [u8], 4>, state: &mut DisplayState) {
+impl DrawInstruction for Triangle {
+ fn draw<'v>(
+ &self,
+ mem: &mut LRegistry<'v>,
+ i: &mut Image<&mut [u8], 4>,
+ state: &mut DisplayState,
+ ) {
let to32 = |n| n as f32;
let (a, b, c) = (
map!(point!([email protected]), to32),
@@ -248,7 +275,7 @@ impl<'v> DrawInstruction<'v> for Triangle<'v> {
i.tri(a, b, c, state.col());
}
}
-impl Disp for Triangle<'_> {
+impl Disp for Triangle {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
@@ -263,41 +290,42 @@ impl Disp for Triangle<'_> {
}
}
-#[derive(Debug)]
-pub struct Poly<'v> {
- pub(crate) pos: Point<'v>,
- pub(crate) sides: LAddress<'v>,
- pub(crate) radius: LAddress<'v>,
- pub(crate) rot: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct Poly {
+ pub(crate) pos: Point,
+ pub(crate) sides: LAddress,
+ pub(crate) radius: LAddress,
+ pub(crate) rot: LAddress,
}
-impl<'v> DrawInstruction<'v> for Poly<'v> {
- fn draw(
+impl DrawInstruction for Poly {
+ fn draw<'v>(
&self,
mem: &mut LRegistry<'v>,
image: &mut Image<&mut [u8], 4>,
state: &mut DisplayState,
) {
- let sides = get_num!(mem.get(&self.sides)).round() as usize;
+ let sides = get_num!(mem.get(self.sides)).round() as usize;
if sides < 90 {
image.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,
+ get_num!(mem.get(self.radius)) as f32,
+ get_num!(mem.get(self.rot)) as f32,
state.col(),
);
} else {
image.circle(
map!(point!([email protected]), |n: f64| n.round() as i32),
- get_num!(mem.get(&self.radius)).round() as i32,
+ get_num!(mem.get(self.radius)).round() as i32,
state.col(),
);
}
}
}
-impl Disp for Poly<'_> {
+impl Disp for Poly {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
@@ -307,42 +335,43 @@ impl Disp for Poly<'_> {
}
}
-#[derive(Debug)]
-pub struct LinePoly<'v> {
- pub(crate) pos: Point<'v>,
- pub(crate) sides: LAddress<'v>,
- pub(crate) radius: LAddress<'v>,
- pub(crate) rot: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct LinePoly {
+ pub(crate) pos: Point,
+ pub(crate) sides: LAddress,
+ pub(crate) radius: LAddress,
+ pub(crate) rot: LAddress,
}
-impl<'v> DrawInstruction<'v> for LinePoly<'v> {
- fn draw(
+impl DrawInstruction for LinePoly {
+ fn draw<'v>(
&self,
mem: &mut LRegistry<'v>,
image: &mut Image<&mut [u8], 4>,
state: &mut DisplayState,
) {
- let sides = get_num!(mem.get(&self.sides)).round() as usize;
+ 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,
+ 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,
+ get_num!(mem.get(self.radius)).round() as i32,
state.col(),
);
}
}
}
-impl Disp for LinePoly<'_> {
+impl Disp for LinePoly {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
@@ -352,11 +381,12 @@ impl Disp for LinePoly<'_> {
}
}
-#[derive(Debug, Default)]
+#[derive(Debug, Copy, Clone, Default)]
+
pub struct Flush {
pub(crate) display: Display,
}
-impl LInstruction<'_> for Flush {
+impl LInstruction for Flush {
fn run<W: std::io::Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
exec.flush(self.display);
Flow::Continue
@@ -365,6 +395,7 @@ impl LInstruction<'_> for Flush {
impl Disp for Flush {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "drawflush {}", self.display)
+ let d = self.display;
+ write!(f, "drawflush {d}")
}
}
diff --git a/lemu/src/instructions/io.rs b/lemu/src/instructions/io.rs
index 6e8e325..d919f5e 100644
--- a/lemu/src/instructions/io.rs
+++ b/lemu/src/instructions/io.rs
@@ -8,42 +8,42 @@ use std::{
io::Write as Wr,
};
-#[derive(Debug)]
-pub struct Read<'v> {
- pub(crate) index: LAddress<'v>,
- pub(crate) output: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct Read {
+ pub(crate) index: LAddress,
+ pub(crate) output: LAddress,
pub(crate) container: Memory,
}
-impl<'v> LInstruction<'v> for Read<'v> {
- fn run<W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- let i = get_num!(exec.get(&self.index)).round() as usize;
+impl LInstruction for Read {
+ fn run<'v, W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
+ let i = get_num!(exec.get(self.index)).round() as usize;
if let Some(&n) = exec.mem(self.container).get(i) {
- if let Some(out) = exec.get_mut(&self.output) {
- *out = LVar::from(n);
- }
+ *exec.get_mut(self.output) = LVar::from(n);
};
Flow::Continue
}
}
-impl Display for Read<'_> {
+impl Display for Read {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "read {} {} {}", self.output, self.container, self.index)
}
}
-#[derive(Debug)]
-pub struct Write<'v> {
- pub(crate) index: LAddress<'v>,
- pub(crate) set: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct Write {
+ pub(crate) index: LAddress,
+ pub(crate) set: LAddress,
pub(crate) container: Memory,
}
-impl<'v> LInstruction<'v> for Write<'v> {
- fn run<W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- let i = get_num!(exec.get(&self.index)).round() as usize;
- if let &LVar::Num(b) = exec.get(&self.set) {
+impl LInstruction for Write {
+ fn run<'v, W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
+ let i = get_num!(exec.get(self.index)).round() as usize;
+ if let &LVar::Num(b) = exec.get(self.set) {
if let Some(a) = exec.mem(self.container).get_mut(i) {
*a = b;
}
@@ -52,19 +52,20 @@ impl<'v> LInstruction<'v> for Write<'v> {
}
}
-impl Display for Write<'_> {
+impl Display for Write {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "write {} {} {}", self.set, self.container, self.index)
}
}
-#[derive(Debug)]
-pub struct Print<'v> {
- pub(crate) val: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+
+pub struct Print {
+ pub(crate) val: LAddress,
}
-impl LInstruction<'_> for Print<'_> {
+impl LInstruction for Print {
fn run<W: Wr>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
- let v = exec.get(&self.val).clone();
+ let v = exec.get(self.val).clone();
if let Some(o) = &mut exec.output {
match v {
LVar::Num(n) => write!(o, "{n}"),
@@ -75,7 +76,7 @@ impl LInstruction<'_> for Print<'_> {
Flow::Continue
}
}
-impl Display for Print<'_> {
+impl Display for Print {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "print {}", self.val)
}
diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs
index 7bc2c57..39507a9 100644
--- a/lemu/src/instructions/mod.rs
+++ b/lemu/src/instructions/mod.rs
@@ -30,7 +30,6 @@ use std::{
use super::{
executor::{ExecutorContext, Instruction},
- lexer::Token,
memory::{LAddress, LVar},
};
@@ -88,27 +87,27 @@ pub enum Flow {
}
#[enum_dispatch]
-pub trait LInstruction<'v>: Display {
- fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow;
+pub trait LInstruction: Display {
+ fn run<'v, W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow;
}
-#[derive(Debug)]
+#[derive(Debug, Copy, Clone)]
#[enum_dispatch(LInstruction)]
-pub enum Instr<'v> {
- Op2(Op2<'v>),
- Jump(Jump<'v>),
- AlwaysJump(AlwaysJump<'v>),
- Set(Set<'v>),
- Op1(Op1<'v>),
- Read(io::Read<'v>),
- Write(io::Write<'v>),
+pub enum Instr {
+ Op2(Op2),
+ Jump(Jump),
+ AlwaysJump(AlwaysJump),
+ Set(Set),
+ Op1(Op1),
+ Read(io::Read),
+ Write(io::Write),
DrawFlush(draw::Flush),
- DynJump(DynJump<'v>),
- Print(io::Print<'v>),
+ DynJump(DynJump),
+ Print(io::Print),
Stop(Stop),
End(End),
}
-impl Display for Instr<'_> {
+impl Display for Instr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Op2(i) => write!(f, "{i}"),
@@ -127,19 +126,19 @@ impl Display for Instr<'_> {
}
}
-#[derive(Debug)]
-pub struct Set<'v> {
- pub(crate) from: LAddress<'v>,
- pub(crate) to: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+pub struct Set {
+ pub(crate) from: LAddress,
+ pub(crate) to: LAddress,
}
-impl<'v> LInstruction<'v> for Set<'v> {
- fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- exec.set(&self.from, self.to.clone());
+impl LInstruction for Set {
+ fn run<'v, W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
+ exec.set(self.from, self.to.clone());
Flow::Continue
}
}
-impl Display for Set<'_> {
+impl Display for Set {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "set {} {}", self.from, self.to)
}
@@ -185,17 +184,15 @@ macro_rules! get_num {
}
use get_num;
-#[derive(Debug)]
-pub struct Op1<'v> {
- op_id: MathOp1,
- op: fn(&LVar<'v>) -> f64,
- x: LAddress<'v>,
- out: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+pub struct Op1 {
+ op: for<'v> fn(&LVar<'v>) -> f64,
+ x: LAddress,
+ out: LAddress,
}
-impl<'v> Op1<'v> {
- pub(crate) const fn new(op: MathOp1, x: LAddress<'v>, out: LAddress<'v>) -> Self {
+impl<'v> Op1 {
+ pub(crate) const fn new(op: MathOp1, x: LAddress, out: LAddress) -> Self {
Self {
- op_id: op,
op: op.get_fn(),
x,
out,
@@ -203,40 +200,31 @@ impl<'v> Op1<'v> {
}
}
-impl<'s> LInstruction<'s> for Op1<'s> {
- fn run<W: Write>(&self, exec: &mut ExecutorContext<'s, W>) -> Flow {
- let x = (self.op)(exec.get(&self.x));
- if let Some(y) = exec.get_mut(&self.out) {
- *y = LVar::Num(x);
- }
+impl LInstruction for Op1 {
+ fn run<'s, W: Write>(&self, exec: &mut ExecutorContext<'s, W>) -> Flow {
+ let x = (self.op)(exec.get(self.x));
+ *exec.get_mut(self.out) = LVar::Num(x);
Flow::Continue
}
}
-impl Display for Op1<'_> {
+impl Display for Op1 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- let Self { op_id, x, out, .. } = self;
- write!(f, "op {} {out} {x}", Token::from(*op_id))
+ let Self { x, out, .. } = self;
+ write!(f, "op .. {out} {x}")
}
}
-#[derive(Debug)]
-pub struct Op2<'v> {
- op_id: MathOp2,
- op: fn(&LVar<'v>, &LVar<'v>) -> f64,
- a: LAddress<'v>,
- b: LAddress<'v>,
- out: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+pub struct Op2 {
+ op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> f64,
+ a: LAddress,
+ b: LAddress,
+ out: LAddress,
}
-impl<'v> Op2<'v> {
- pub(crate) const fn new(
- op: MathOp2,
- a: LAddress<'v>,
- b: LAddress<'v>,
- out: LAddress<'v>,
- ) -> Self {
+impl Op2 {
+ pub(crate) const fn new(op: MathOp2, a: LAddress, b: LAddress, out: LAddress) -> Self {
Self {
- op_id: op,
op: op.get_fn(),
a,
b,
@@ -245,31 +233,26 @@ impl<'v> Op2<'v> {
}
}
-impl<'v> LInstruction<'v> for Op2<'v> {
- fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- let x = (self.op)(exec.get(&self.a), exec.get(&self.b));
- if let Some(y) = exec.get_mut(&self.out) {
- *y = LVar::from(x);
- }
+impl LInstruction for Op2 {
+ fn run<'v, W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
+ let x = (self.op)(exec.get(self.a), exec.get(self.b));
+ exec.memory[self.out] = LVar::Num(x);
Flow::Continue
}
}
-impl Display for Op2<'_> {
+impl Display for Op2 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- let Self {
- op_id, a, b, out, ..
- } = self;
- write!(f, "op {} {out} {a} {b}", Token::from(*op_id))
+ let Self { a, b, out, .. } = self;
+ write!(f, "op .. {out} {a} {b}")
}
}
-#[derive(Debug)]
+#[derive(Debug, Copy, Clone)]
pub struct End {}
-impl LInstruction<'_> for End {
+impl LInstruction for End {
fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
- exec.memory.clear();
exec.iterations += 1;
// SAFETY: if we exist, 0 exists.
unsafe { exec.jump(Instruction::new(0)) };
@@ -283,48 +266,34 @@ impl Display for End {
}
}
-#[derive(Debug)]
-pub struct AlwaysJump<'s> {
+#[derive(Debug, Copy, Clone)]
+pub struct AlwaysJump {
pub(crate) to: Instruction,
- pub(crate) label: Option<&'s str>,
}
-impl LInstruction<'_> for AlwaysJump<'_> {
+impl LInstruction for AlwaysJump {
fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
exec.jump(self.to);
Flow::Stay
}
}
-impl Display for AlwaysJump<'_> {
+impl Display for AlwaysJump {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match self.label {
- None => write!(f, "jump {} always", self.to.get()),
- Some(l) => write!(f, "jump {l} always"),
- }
+ write!(f, "jump {} always", self.to.get())
}
}
-#[derive(Debug)]
-pub struct Jump<'v> {
- label: Option<&'v str>,
- op_id: ConditionOp,
- op: fn(&LVar<'v>, &LVar<'v>) -> bool,
+#[derive(Debug, Copy, Clone)]
+pub struct Jump {
+ op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool,
pub(crate) to: Instruction,
- a: LAddress<'v>,
- b: LAddress<'v>,
+ a: LAddress,
+ b: LAddress,
}
-impl<'v> Jump<'v> {
- pub fn new(
- op: ConditionOp,
- to: Instruction,
- a: LAddress<'v>,
- b: LAddress<'v>,
- label: Option<&'v str>,
- ) -> Self {
+impl Jump {
+ pub fn new(op: ConditionOp, to: Instruction, a: LAddress, b: LAddress) -> Self {
Self {
- op_id: op,
op: op.get_fn(),
- label,
to,
a,
b,
@@ -332,9 +301,9 @@ impl<'v> Jump<'v> {
}
}
-impl<'v> LInstruction<'v> for Jump<'v> {
- fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- if (self.op)(exec.get(&self.a), exec.get(&self.b)) {
+impl LInstruction for Jump {
+ fn run<'v, W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
+ if (self.op)(exec.get(self.a), exec.get(self.b)) {
exec.jump(self.to);
Flow::Stay
} else {
@@ -343,34 +312,22 @@ impl<'v> LInstruction<'v> for Jump<'v> {
}
}
-impl Display for Jump<'_> {
+impl Display for Jump {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- let Self {
- op_id,
- label,
- to,
- a,
- b,
- ..
- } = self;
- write!(f, "jump ")?;
- match label {
- Some(v) => write!(f, "{v} "),
- None => write!(f, "{} ", to.get()),
- }?;
- write!(f, "{} {a} {b}", Token::from(*op_id))
+ let Self { to, a, b, .. } = self;
+ write!(f, "jump .. {} {a} {b}", to.get())
}
}
-#[derive(Debug)]
-pub struct DynJump<'v> {
- pub to: LAddress<'v>,
+#[derive(Debug, Copy, Clone)]
+pub struct DynJump {
+ pub to: LAddress,
pub proglen: usize,
}
-impl<'v> LInstruction<'v> for DynJump<'v> {
- fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- if let &LVar::Num(n) = exec.get(&self.to) {
+impl LInstruction for DynJump {
+ fn run<'v, W: Write>(&self, exec: &mut ExecutorContext<'v, 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
@@ -382,15 +339,15 @@ impl<'v> LInstruction<'v> for DynJump<'v> {
}
}
-impl Display for DynJump<'_> {
+impl Display for DynJump {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "set @counter {}", self.to)
}
}
-#[derive(Debug)]
+#[derive(Debug, Copy, Clone)]
pub struct Stop {}
-impl LInstruction<'_> for Stop {
+impl LInstruction for Stop {
fn run<W: Write>(&self, _: &mut ExecutorContext<'_, W>) -> Flow {
Flow::Exit
}
diff --git a/lemu/src/lib.rs b/lemu/src/lib.rs
index ba643aa..d4c0f71 100644
--- a/lemu/src/lib.rs
+++ b/lemu/src/lib.rs
@@ -1,5 +1,5 @@
//! crate for [MLOG](https://mindustrygame.github.io/wiki/logic/0-introduction/#what-is-mindustry-logic) emulation.
-#![feature(let_chains, trace_macros)]
+#![feature(let_chains, inline_const)]
#![allow(clippy::redundant_closure_call)]
#![warn(
clippy::multiple_unsafe_ops_per_block,
@@ -177,6 +177,6 @@ mod test {
test!(run fib.mlog; output = b"12586269025");
test!(run primes.mlog; output = b"2 | 3 | 5 | 7 | 11 | 13 | 17 | 19 | 23 | 29 | 31 | 37 | 41 | 43 | 47 | 53 | 59 | 61 | 67 | 71 | 73 | 79 | 83 | 89 | 97 | 101 | 103 | 107 | 109 | 113 | 127 | 131 | 137 | 139 | 149 | 151 | 157 | 163 | 167 | 173 | 179 | 181 | 191 | 193 | 197 | 199 | 211 | 223 | 227 | 229 | 233 | 239 | 241 | 251 | 257 | 263 | 269 | 271 | 277 | 281 | 283 | 293 | 307 | 311 | 313 | 317 | 331 | 337 | 347 | 349 | 353 | 359 | 367 | 373 | 379 | 383 | 389 | 397 | 401 | 409 | 419 | 421 | 431 | 433 | 439 | 443 | 449 | 457 | 461 | 463 | 467 | 479 | 487 | 491 | 499 | 503 | 509 | ");
test!(run numbers.mlog; output = b"121212");
- test!(run celliterate.mlog 500 times; cell[0][0] = 500.0);
+ test!(run celliterate.mlog 5 times; cell[0][0] = 5.0);
test!(run hello.mlog; output = b"hello world");
}
diff --git a/lemu/src/memory.rs b/lemu/src/memory.rs
index 052314f..b71e44e 100644
--- a/lemu/src/memory.rs
+++ b/lemu/src/memory.rs
@@ -15,6 +15,12 @@ impl PartialEq for LVar<'_> {
}
}
+impl Default for LVar<'static> {
+ fn default() -> Self {
+ Self::Num(0.0)
+ }
+}
+
impl LVar<'_> {
// get null
pub const fn null() -> LVar<'static> {
@@ -22,22 +28,14 @@ impl LVar<'_> {
}
}
-#[derive(Clone)]
-pub enum LAddress<'str> {
- Const(LVar<'str>),
- Address(usize, &'str str, Priv),
+#[derive(Clone, Copy)]
+pub struct LAddress {
+ pub address: u8,
}
-impl<'v> LAddress<'v> {
- /// # Safety
- ///
- /// you must make sure that addr is in bounds of the memory.
- pub(crate) const unsafe fn addr(addr: usize, name: &'v str) -> Self {
- LAddress::Address(addr, name, Priv { _priv: () })
- }
-
- pub(crate) fn cnst(c: impl Into<LVar<'v>>) -> Self {
- Self::Const(c.into())
+impl LAddress {
+ pub(crate) const fn addr(address: u8) -> Self {
+ LAddress { address }
}
}
@@ -46,21 +44,15 @@ pub struct Priv {
_priv: (),
}
-impl std::fmt::Debug for LAddress<'_> {
+impl std::fmt::Debug for LAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Const(c) => write!(f, "{c}"),
- Self::Address(n, name, ..) => write!(f, "{name}@0x{n:x}"),
- }
+ write!(f, "{:x}", self.address)
}
}
-impl std::fmt::Display for LAddress<'_> {
+impl std::fmt::Display for LAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Const(c) => write!(f, "{c}"),
- Self::Address(_, n, ..) => write!(f, "{n}"),
- }
+ write!(f, "{:x}", self.address)
}
}
@@ -104,54 +96,49 @@ impl<'s> From<Cow<'s, str>> for LVar<'s> {
}
/// cleared every loop
-#[derive(Default, Debug)]
-pub struct LRegistry<'str>(Box<[LVar<'str>]>);
+#[derive(Debug)]
+pub struct LRegistry<'str>(pub [LVar<'str>; 255]);
-impl<'s> LRegistry<'s> {
- pub fn new(size: usize) -> Self {
- Self(vec![LVar::null(); size].into_boxed_slice())
+impl<'s> std::ops::Index<LAddress> for LRegistry<'s> {
+ type Output = LVar<'s>;
+
+ fn index(&self, index: LAddress) -> &Self::Output {
+ &self.0[index.address as usize]
}
+}
- pub fn clear(&mut self) {
- for var in &mut *self.0 {
- *var = LVar::null();
- }
+impl<'s> std::ops::IndexMut<LAddress> for LRegistry<'s> {
+ fn index_mut(&mut self, index: LAddress) -> &mut Self::Output {
+ &mut self.0[index.address as usize]
}
+}
- pub fn get<'a>(&'a self, a: &'a LAddress<'s>) -> &LVar<'s> {
- match a {
- // SAFETY: addr constructor requires bounds
- LAddress::Address(n, ..) => unsafe { self.0.get_unchecked(*n) },
- LAddress::Const(n) => n,
- }
+impl<'s> Default for LRegistry<'s> {
+ fn default() -> Self {
+ Self([const { LVar::null() }; 255])
}
+}
- pub fn set(&mut self, a: &LAddress<'s>, b: LAddress<'s>) -> bool {
- match a {
- LAddress::Const(_) => false,
- LAddress::Address(v, ..) => {
- match b {
- LAddress::Const(n) => {
- // SAFETY: v comes from Address, therefore safe
- *unsafe { self.0.get_unchecked_mut(*v) } = n;
- }
- LAddress::Address(n, ..) => {
- // SAFETY: n comes from Address, therefore safe
- let b = unsafe { self.0.get_unchecked(n).clone() };
- // SAFETY: v comes from Addr, therefore safe
- *unsafe { self.0.get_unchecked_mut(*v) } = b;
- }
- };
- true
- }
+impl std::fmt::Display for LRegistry<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "R[")?;
+ let mut iter = self
+ .0
+ .iter()
+ .enumerate()
+ .filter(|&(_, v)| v != &LVar::null());
+ if let Some((i, v)) = iter.next() {
+ write!(f, "{i}: {v}")?;
+ }
+ while let Some((i, v)) = iter.next() {
+ write!(f, ", {i}: {v}")?;
}
+ write!(f, "]")
}
+}
- pub fn get_mut(&mut self, a: &LAddress<'s>) -> Option<&mut LVar<'s>> {
- match a {
- LAddress::Const(_) => None,
- // SAFETY: addr constructor requires bounds
- LAddress::Address(n, ..) => Some(unsafe { self.0.get_unchecked_mut(*n) }),
- }
+impl<'s> LRegistry<'s> {
+ pub fn get<'a>(&'a self, a: LAddress) -> &LVar {
+ &self[a]
}
}
diff --git a/lemu/src/parser/error.rs b/lemu/src/parser/error.rs
index b9ffb66..658f236 100644
--- a/lemu/src/parser/error.rs
+++ b/lemu/src/parser/error.rs
@@ -144,6 +144,9 @@ pub enum Error<'s> {
/// .display();
/// ```
NoDisplay(usize, Span),
+ /// We have a limit of [`u8::MAX`] variables.
+ #[error("too many variables")]
+ TooManyVariables(Span),
}
impl Error<'_> {
@@ -172,11 +175,14 @@ impl Error<'_> {
write!(out, ", {}", op).unwrap();
}
out.write_char('}').unwrap();
- if let Some((mat,score)) = rust_fuzzy_search::fuzzy_search_best_n($op, $ops, 1).first() && *score > 0.5 {
+ if let Some((mat, score)) =
+ rust_fuzzy_search::fuzzy_search_best_n($op, $ops, 1).first()
+ && *score > 0.5
+ {
e.note(cmt!("{help}: you may have meant {bold_green}{mat}{reset}"));
}
out
- }}
+ }};
}
match self {
Self::UnexpectedEof => {
@@ -220,7 +226,12 @@ impl Error<'_> {
Self::ExpectedOp(t, s) => {
msg!("{error}: expected operator")
.label((s, cmt!("this was supposed to be a {bold_blue}operator{reset} (eg. {magenta}equal{reset})")));
- if let Some(i) = tokstr!(*t) && let Some((mat,score)) = rust_fuzzy_search::fuzzy_search_best_n(i, crate::instructions::OPS, 1).first() && *score > 0.5 {
+ if let Some(i) = tokstr!(*t)
+ && let Some((mat, score)) =
+ rust_fuzzy_search::fuzzy_search_best_n(i, crate::instructions::OPS, 1)
+ .first()
+ && *score > 0.5
+ {
e.note(cmt!("{help}: maybe you meant {bold_green}{mat}{reset}"));
}
}
@@ -236,9 +247,53 @@ impl Error<'_> {
Self::ExpectedInstr(t, s) => {
msg!("{error}: expected instruction")
.label((s, cmt!("this was supposed to be a {bold_blue}instruction{reset} (eg. {magenta}print{reset})")));
- if let Some(i) = tokstr!(*t) && let Some((mat,score)) = rust_fuzzy_search::fuzzy_search_best_n(i, &[
- "getlink", "read", "write", "set", "op", "end", "drawflush", "draw", "print", "packcolor", "jump", "stop", "printflush", "control", "radar", "sensor", "wait", "lookup", "packcolor", "ubind", "ucontrol", "uradar", "ulocate", "getblock", "setblock", "spawn", "status", "spawnwave", "setrule", "cutscene", "explosion", "setrate", "fetch", "getflag", "setflag", "setprop", "effect"
- ], 1).first() && *score > 0.5 {
+ if let Some(i) = tokstr!(*t)
+ && let Some((mat, score)) = rust_fuzzy_search::fuzzy_search_best_n(
+ i,
+ &[
+ "getlink",
+ "read",
+ "write",
+ "set",
+ "op",
+ "end",
+ "drawflush",
+ "draw",
+ "print",
+ "packcolor",
+ "jump",
+ "stop",
+ "printflush",
+ "control",
+ "radar",
+ "sensor",
+ "wait",
+ "lookup",
+ "packcolor",
+ "ubind",
+ "ucontrol",
+ "uradar",
+ "ulocate",
+ "getblock",
+ "setblock",
+ "spawn",
+ "status",
+ "spawnwave",
+ "setrule",
+ "cutscene",
+ "explosion",
+ "setrate",
+ "fetch",
+ "getflag",
+ "setflag",
+ "setprop",
+ "effect",
+ ],
+ 1,
+ )
+ .first()
+ && *score > 0.5
+ {
e.note(cmt!("{help}: maybe you meant {mat}"));
}
}
@@ -376,6 +431,11 @@ impl Error<'_> {
msg!("{error}: {bold_red}index{reset} {} out of bounds", index)
.label((s, cmt!("memory has only {magenta}{size}{reset} elements")));
}
+ Self::TooManyVariables(s) => {
+ msg!("{error}: {bold_red}too many variables{reset}. ")
+ .label((s, cmt!("we only have 255 variable slots")))
+ .note(cmt!("consider not using variables"));
+ }
};
e
}
diff --git a/lemu/src/parser/mod.rs b/lemu/src/parser/mod.rs
index 1091a82..077b35e 100644
--- a/lemu/src/parser/mod.rs
+++ b/lemu/src/parser/mod.rs
@@ -81,10 +81,10 @@ macro_rules! tokstr {
use tokstr;
#[derive(Debug)]
-enum UJump<'v> {
+enum UJump {
Sometimes {
- a: LAddress<'v>,
- b: LAddress<'v>,
+ a: LAddress,
+ b: LAddress,
op: ConditionOp,
},
Always,
@@ -94,7 +94,39 @@ pub fn parse<'source, W: Wr>(
mut tokens: Lexer<'source>,
executor: &mut ExecutorBuilderInternal<'source, W>,
) -> Result<(), Error<'source>> {
- let mut mem = Vec::new(); // maps &str to usize
+ let mut used = 0u8;
+ let mut mem: [Option<&str>; 255] = [None; 255]; // maps &str to usize
+ macro_rules! push {
+ ($var:expr) => {{
+ mem[used as usize] = Some($var);
+ used = used
+ .checked_add(1)
+ .ok_or(Error::TooManyVariables(tokens.span()))?;
+ Ok(LAddress::addr(used - 1))
+ }};
+ (const $var:expr) => {{
+ executor.mem[LAddress::addr(used)] = LVar::from($var);
+ used = used
+ .checked_add(1)
+ .ok_or(Error::TooManyVariables(tokens.span()))?;
+ Ok(LAddress::addr(used - 1))
+ }};
+ }
+ macro_rules! addr {
+ ($val:expr) => {{
+ let val = $val;
+ match mem
+ .iter()
+ .zip(0..used)
+ .find(|(&v, _)| v == Some(val))
+ .map(|(_, i)| i)
+ {
+ Some(n) => Ok(LAddress::addr(n)),
+ None => push!(val),
+ }
+ }};
+ }
+
// maps "start" to 0
let mut labels = Vec::new();
let mut unfinished_jumps = Vec::new();
@@ -113,7 +145,7 @@ pub fn parse<'source, W: Wr>(
return Err(Error::$e($($stuff,)+ tokens.span()))
};
}
- #[rustfmt::skip]
+ #[rustfmt::skip]
macro_rules! nextline {
() => {
while let Some(tok) = tokens.next() && tok != Token::Newline { }
@@ -161,25 +193,6 @@ pub fn parse<'source, W: Wr>(
}
}};
}
- macro_rules! addr {
- ($n:expr) => {{
- let n = $n;
- match mem
- .iter()
- .enumerate()
- .find(|(_, &v)| v == n)
- .map(|(i, _)| i)
- {
- // SAFETY: we tell it the size is mem.len(); i comes from mem, this is fine
- Some(i) => unsafe { LAddress::addr(i, n) },
- None => {
- mem.push(n);
- // SAFETY: see above
- unsafe { LAddress::addr(mem.len() - 1, n) }
- }
- }
- }};
- }
macro_rules! take_ident {
($tok:expr) => {{
let tok = $tok;
@@ -190,11 +203,11 @@ pub fn parse<'source, W: Wr>(
($tok:expr) => {{
let tok = $tok;
if let Some(i) = tokstr!(tok) {
- Ok(addr!(i))
+ addr!(i)
} else {
match tok {
- Token::Num(n) => Ok(LAddress::cnst(n)),
- Token::String(s) => Ok(LAddress::cnst(s)),
+ Token::Num(n) => push!(const n),
+ Token::String(s) => push!(const s),
t => Err(err!(ExpectedVar(t))),
}
}
@@ -204,10 +217,10 @@ pub fn parse<'source, W: Wr>(
($tok:expr) => {{
let tok = $tok;
if let Some(i) = tokstr!(tok) {
- Ok(addr!(i))
+ addr!(i)
} else {
match tok {
- Token::Num(n) => Ok(LAddress::cnst(n)),
+ Token::Num(n) => push!(const n),
t => Err(err!(ExpectedNum(t))),
}
}
@@ -233,7 +246,7 @@ pub fn parse<'source, W: Wr>(
let to = take_numvar!(tok!()?)?;
executor.add(DynJump { to, proglen: 0 });
} else {
- let from = addr!(take_ident!(from)?);
+ let from = addr!(take_ident!(from)?)?;
let to = take_var!(tok!()?)?;
executor.add(Set { from, to });
}
@@ -268,12 +281,12 @@ pub fn parse<'source, W: Wr>(
let to = unsafe { Instruction::new(n) };
let op = tok!()?;
if op == Token::Always {
- executor.add(AlwaysJump { to, label: None });
+ executor.add(AlwaysJump { to });
} else {
let op = op.try_into().map_err(|op| err!(ExpectedOp(op)))?;
let a = take_var!(tok!()?)?;
let b = take_var!(tok!()?)?;
- executor.add(Jump::new(op, to, a, b, None));
+ executor.add(Jump::new(op, to, a, b));
}
} else {
yeet!(ExpectedJump(tok));
@@ -301,7 +314,9 @@ pub fn parse<'source, W: Wr>(
let set = take_numvar!(tok!()?)?;
let container = take_memory!();
let index = take_numvar!(tok!()?)?;
- if let LAddress::Const(LVar::Num(v)) = index && !container.fits(v.round() as usize) {
+ if let LVar::Num(v) = executor.mem[index]
+ && !container.fits(v.round() as usize)
+ {
yeet!(IndexOutOfBounds(v.round() as usize, container.size()));
}
executor.add(Write {
@@ -315,7 +330,9 @@ pub fn parse<'source, W: Wr>(
let output = take_var!(tok!()?)?;
let container = take_memory!();
let index = take_numvar!(tok!()?)?;
- if let LAddress::Const(LVar::Num(v)) = index && !container.fits(v.round() as usize) {
+ if let LVar::Num(v) = executor.mem[index]
+ && !container.fits(v.round() as usize)
+ {
yeet!(IndexOutOfBounds(v.round() as usize, container.size()));
}
executor.add(Read {
@@ -352,12 +369,13 @@ pub fn parse<'source, W: Wr>(
let g = col & 0x00ff_0000 >> 16;
let b = col & 0x0000_ff00 >> 8;
let a = col & 0x0000_00ff;
- executor.draw(SetColor {
- r: LAddress::cnst(r),
- g: LAddress::cnst(g),
- b: LAddress::cnst(b),
- a: LAddress::cnst(a),
- });
+ let i = SetColor {
+ r: push!(const r)?,
+ g: push!(const g)?,
+ b: push!(const b)?,
+ a: push!(const a)?,
+ };
+ executor.draw(i);
}
"stroke" => {
let size = take_numvar!(tok!()?)?;
@@ -416,7 +434,9 @@ pub fn parse<'source, W: Wr>(
}
Token::DrawFlush => {
let t = tok!();
- if let Ok(t) = t && t != Token::Newline {
+ if let Ok(t) = t
+ && t != Token::Newline
+ {
let screen = take_ident!(t.clone())?;
let mut out = String::new();
for ch in screen.chars() {
@@ -433,7 +453,9 @@ pub fn parse<'source, W: Wr>(
yeet!(InvalidDisplayType(screen));
}
let n_span = tokens.span().start + screen.len()..tokens.span().end;
- let screen_n = out.parse::<usize>().map_err(|_| Error::ExpectedInt(t, n_span.clone()))?;
+ let screen_n = out
+ .parse::<usize>()
+ .map_err(|_| Error::ExpectedInt(t, n_span.clone()))?;
let display = executor
.display(screen_n)
.map_err(|n| Error::NoDisplay(n, n_span))?;
@@ -733,7 +755,9 @@ pub fn parse<'source, W: Wr>(
"effect" => {
let mut v = Vec::with_capacity(6);
v.push(Token::Ident("effect"));
- while let Some(tok) = tokens.next() && tok != Token::Newline {
+ while let Some(tok) = tokens.next()
+ && tok != Token::Newline
+ {
v.push(tok);
}
executor.code(v.into_boxed_slice());
@@ -755,11 +779,8 @@ pub fn parse<'source, W: Wr>(
.ok_or_else(|| Error::LabelNotFound(label, s))?
.1;
executor.program[i.get()] = UPInstr::Instr(match j {
- UJump::Always => Instr::from(AlwaysJump {
- to,
- label: Some(label),
- }),
- UJump::Sometimes { a, b, op } => Instr::from(Jump::new(op, to, a, b, Some(label))),
+ UJump::Always => Instr::from(AlwaysJump { to }),
+ UJump::Sometimes { a, b, op } => Instr::from(Jump::new(op, to, a, b)),
});
}
@@ -783,7 +804,5 @@ pub fn parse<'source, W: Wr>(
}
}
- executor.mem(mem.len());
-
Ok(())
}