mindustry logic execution, map- and schematic- parsing and rendering
dynamic indexes
bendn 2023-09-14
parent 7b3b31c · commit 5fe4b3a
-rw-r--r--lemu/Cargo.toml2
-rw-r--r--lemu/src/executor/mod.rs14
-rw-r--r--lemu/src/instructions/io.rs27
-rw-r--r--lemu/src/instructions/mod.rs2
-rw-r--r--lemu/src/parser.rs41
5 files changed, 60 insertions, 26 deletions
diff --git a/lemu/Cargo.toml b/lemu/Cargo.toml
index 49d88bd..7c643d5 100644
--- a/lemu/Cargo.toml
+++ b/lemu/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "lemu"
-version = "0.2.3"
+version = "0.2.4"
edition = "2021"
description = "M-LOG runner"
authors = ["bend-n <[email protected]>"]
diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs
index a3cce26..991c09f 100644
--- a/lemu/src/executor/mod.rs
+++ b/lemu/src/executor/mod.rs
@@ -13,11 +13,19 @@ pub struct Display(pub usize);
// negative means bank, positive means cell
pub struct Memory(pub(crate) i8);
impl Memory {
- pub(crate) fn limit(self, i: usize) -> usize {
+ pub(crate) fn fits(self, i: usize) -> bool {
if self.0 < 0 {
- i.min(BANK_SIZE)
+ i < BANK_SIZE
} else {
- i.min(CELL_SIZE)
+ i < CELL_SIZE
+ }
+ }
+
+ pub(crate) fn size(self) -> usize {
+ if self.0 < 0 {
+ BANK_SIZE
+ } else {
+ CELL_SIZE
}
}
}
diff --git a/lemu/src/instructions/io.rs b/lemu/src/instructions/io.rs
index 75b9c0e..8b1eed5 100644
--- a/lemu/src/instructions/io.rs
+++ b/lemu/src/instructions/io.rs
@@ -1,4 +1,4 @@
-use super::{Flow, LInstruction};
+use super::{get_num, Flow, LInstruction};
use crate::{
executor::{ExecutorContext, Memory},
memory::{LAddress, LVar},
@@ -7,37 +7,38 @@ use std::io::Write as Wr;
#[derive(Debug)]
pub struct Read<'v> {
- // index guranteed to never be out of bounds
- pub(crate) index: usize,
+ pub(crate) index: LAddress<'v>,
pub(crate) output: LAddress<'v>,
pub(crate) container: Memory,
}
impl<'v> LInstruction<'v> for Read<'v> {
fn run<W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- let to = exec.mem(self.container)[self.index];
- let Some(out) = exec.get_mut(&self.output) else {
- return Flow::Continue;
+ 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);
+ }
};
- *out = LVar::from(to);
Flow::Continue
}
}
#[derive(Debug)]
pub struct Write<'v> {
- // index guranteed to never be out of bounds
- pub(crate) index: usize,
+ pub(crate) index: LAddress<'v>,
pub(crate) set: LAddress<'v>,
pub(crate) container: Memory,
}
impl<'v> LInstruction<'v> for Write<'v> {
fn run<W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow {
- let &LVar::Num(n) = exec.get(&self.set) else {
- return Flow::Continue;
- };
- exec.mem(self.container)[self.index] = n;
+ 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;
+ }
+ }
Flow::Continue
}
}
diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs
index 8dbae7e..a6bd3ba 100644
--- a/lemu/src/instructions/mod.rs
+++ b/lemu/src/instructions/mod.rs
@@ -90,7 +90,9 @@ pub const OPS: &[&str] = &[
];
#[must_use = "to change control flow"]
+#[derive(Default)]
pub enum Flow {
+ #[default]
Continue,
Stay,
Exit,
diff --git a/lemu/src/parser.rs b/lemu/src/parser.rs
index 9fd08e1..b524bce 100644
--- a/lemu/src/parser.rs
+++ b/lemu/src/parser.rs
@@ -13,7 +13,7 @@ use super::{
AlwaysJump, ConditionOp, DynJump, End, Instr, Jump, MathOp1, MathOp2, Op1, Op2, Set, Stop,
},
lexer::{Lexer, Token},
- memory::LAddress,
+ memory::{LAddress, LVar},
};
/// Errors returned when parsing fails.
@@ -49,14 +49,17 @@ pub enum Error<'s> {
/// jump label always
/// ```
/// (typo: lable not label)
- #[error("unable to find label {0:?}")]
+ #[error("unable to find label {0}")]
LabelNotFound(&'s str, Span),
/// Occurs from eg `jump 4910294029 always`
#[error("unable to jump to instruction {0:?}")]
InvalidJump(Instruction, Span),
/// Occurs from eg `read bank9223372036854775807 5` (only `126` banks can exist)
- #[error("cannot get cell>{0:?}")]
+ #[error("cannot get cell>{0}")]
MemoryTooFar(usize, Span),
+ /// Occurs from eg `read bank1 512`
+ #[error("index {0} out of bounds ({1} max)")]
+ IndexOutOfBounds(usize, usize, Span),
/// Occurs from `read register1`
#[error("unknown memory type {0:?}, expected (cell)|(bank)")]
InvalidMemoryType(&'s str, Span),
@@ -217,11 +220,11 @@ impl Error<'_> {
));
}
Self::ExpectedVar(_, s) => {
- msg!("{error}: expected a variable")
+ msg!("{error}: expected variable")
.label(err!(s, "this was supposed to be a variable"));
}
Self::ExpectedIdent(_, s) => {
- msg!("{error}: expected a identifier")
+ msg!("{error}: expected identifier")
.label(err!(s, "this was supposed to be a identifier"));
}
Self::ExpectedJump(t, s) => {
@@ -298,6 +301,10 @@ impl Error<'_> {
Self::NoDisplay(disp, s) => {
msg!("{error}: no display allocated").label(err!(s, "display#{disp} has not been created")).note(format!("{note}: it is impossible for me to dynamically allocate displays, as 'display1' could be large or small"));
}
+ Self::IndexOutOfBounds(index, size, s) => {
+ msg!("{error}: index {index} out of bounds")
+ .label(err!(s, "memory has only {size} elements"));
+ }
};
e
}
@@ -327,12 +334,12 @@ pub fn parse<'source, W: Wr>(
};
}
macro_rules! err {
- ($e:ident($($stuff:expr)+)) => {
+ ($e:ident($($stuff:expr),+)) => {
Error::$e($($stuff,)+ tokens.span())
}
}
macro_rules! yeet {
- ($e:ident($($stuff:expr)+)) => {
+ ($e:ident($($stuff:expr),+)) => {
return Err(Error::$e($($stuff,)+ tokens.span()))
};
}
@@ -499,7 +506,15 @@ pub fn parse<'source, W: Wr>(
Token::Write => {
let set = take_numvar!(tok!()?)?;
let container = take_memory!();
- let index = container.limit(take_int!(tok!()?)?);
+ let index = take_numvar!(tok!()?)?;
+ match index {
+ LAddress::Const(LVar::Num(v)) => {
+ if !container.fits(v.round() as usize) {
+ yeet!(IndexOutOfBounds(v.round() as usize, container.size()));
+ }
+ }
+ _ => {}
+ }
executor.add(Write {
index,
@@ -511,7 +526,15 @@ pub fn parse<'source, W: Wr>(
Token::Read => {
let output = take_var!(tok!()?)?;
let container = take_memory!();
- let index = container.limit(take_int!(tok!()?)?);
+ let index = take_numvar!(tok!()?)?;
+ match index {
+ LAddress::Const(LVar::Num(v)) => {
+ if !container.fits(v.round() as usize) {
+ yeet!(IndexOutOfBounds(v.round() as usize, container.size()));
+ }
+ }
+ _ => {}
+ }
executor.add(Read {
index,
output,