mindustry logic execution, map- and schematic- parsing and rendering
get 4294901760 more variables
| -rw-r--r-- | lemu/src/debug/info.rs | 27 | ||||
| -rw-r--r-- | lemu/src/executor/builder.rs | 8 | ||||
| -rw-r--r-- | lemu/src/memory.rs | 32 | ||||
| -rw-r--r-- | lemu/src/parser/error.rs | 4 | ||||
| -rw-r--r-- | lemu/src/parser/mod.rs | 38 |
5 files changed, 53 insertions, 56 deletions
diff --git a/lemu/src/debug/info.rs b/lemu/src/debug/info.rs index 5b38f81..e23f23b 100644 --- a/lemu/src/debug/info.rs +++ b/lemu/src/debug/info.rs @@ -6,7 +6,7 @@ use crate::{ }; pub struct DebugInfo<'s> { - variables: Box<[Option<VarInfo<'s>>; 65536]>, + pub variables: Box<[VarInfo<'s>]>, /// maps "start" to 0 pub labels: Vec<(&'s str, Instruction)>, } @@ -14,7 +14,7 @@ pub struct DebugInfo<'s> { impl<'s> Default for DebugInfo<'s> { fn default() -> Self { Self { - variables: vec![None; 65536].try_into().unwrap(), + variables: vec![].into(), labels: vec![], } } @@ -30,31 +30,12 @@ impl<'s> std::ops::Index<LAddress> for DebugInfo<'s> { type Output = VarData<'s>; fn index(&self, index: LAddress) -> &Self::Output { - &self.variables[index.address as usize] - .as_ref() - .unwrap() - .data - } -} - -impl<'s> DebugInfo<'s> { - pub fn set_var(&mut self, at: u16, name: &'s str, span: Range<usize>) { - self.variables[at as usize] = Some(VarInfo { - data: VarData::Variable(name), - span, - }); - } - - pub fn set_const(&mut self, at: u16, var: impl Into<LVar<'s>>, span: Range<usize>) { - self.variables[at as usize] = Some(VarInfo { - data: VarData::Constant(var.into()), - span, - }); + &self.variables[index.address as usize].data } } #[derive(Clone, Debug)] -struct VarInfo<'s> { +pub struct VarInfo<'s> { pub data: VarData<'s>, #[allow(dead_code)] pub span: Range<usize>, diff --git a/lemu/src/executor/builder.rs b/lemu/src/executor/builder.rs index ae78996..936a3b6 100644 --- a/lemu/src/executor/builder.rs +++ b/lemu/src/executor/builder.rs @@ -10,7 +10,7 @@ use crate::{ debug::info::DebugInfo, instructions::{DrawInstr, Instr}, lexer::Token, - memory::LRegistry, + memory::{LRegistry, LVar}, }; /// for internal use by [parser](crate::parser) only @@ -22,7 +22,7 @@ pub struct ExecutorBuilderInternal<'v, W: Wr> { cells: Vec<f64>, iteration_limit: Limit, instruction_limit: Limit, - pub(crate) mem: LRegistry<'v>, + pub(crate) mem: Vec<LVar<'v>>, pub(crate) debug_info: DebugInfo<'v>, } @@ -39,7 +39,7 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> { cells: vec![], iteration_limit: Limit::limited(1), instruction_limit: Limit::Unlimited, - mem: LRegistry::default(), + mem: Vec::with_capacity(64), debug_info: DebugInfo::default(), } } @@ -143,7 +143,7 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> { inner: ExecutorContext { cells: cst::<CELL_SIZE>(cells), banks: cst::<BANK_SIZE>(banks), - memory: mem, + memory: LRegistry(mem.into()), counter: 0, iterations: 0, display: Drawing { diff --git a/lemu/src/memory.rs b/lemu/src/memory.rs index 133fe8e..ff99dc8 100644 --- a/lemu/src/memory.rs +++ b/lemu/src/memory.rs @@ -39,11 +39,14 @@ impl LVar<'_> { #[derive(Clone, Copy)] pub struct LAddress { - pub address: u16, + pub address: u32, } impl LAddress { - pub(crate) const fn addr(address: u16) -> Self { + /// # Safety + /// + /// ensure that address is valid + pub(crate) const unsafe fn addr(address: u32) -> Self { LAddress { address } } } @@ -98,27 +101,27 @@ impl<'s> From<Cow<'s, str>> for LVar<'s> { } } -/// whats a megabyte among friends #[derive(Debug)] -pub struct LRegistry<'str>(pub Box<[LVar<'str>; 65536]>); +pub struct LRegistry<'str>(pub Box<[LVar<'str>]>); 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] + debug_assert!((index.address as usize) < self.0.len()); + // SAFETY: LAddress promises to be in our bounds. + // lreg has no constructors, so unless you keep one around, grab a laddr, and use it on the old lreg, + // this is safeish. + // lreg is private to the outside world, so its ok + unsafe { self.0.get_unchecked(index.address as usize) } } } 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] - } -} - -impl<'s> Default for LRegistry<'s> { - fn default() -> Self { - Self(vec![LVar::null(); 65536].try_into().unwrap()) + debug_assert!((index.address as usize) < self.0.len()); + // SAFETY: see above + unsafe { self.0.get_unchecked_mut(index.address as usize) } } } @@ -156,9 +159,10 @@ impl Printable for LRegistry<'_> { let mut iter = self .0 .iter() - .zip(0..u16::MAX) + .zip(0..self.0.len() as u32) .filter(|&(v, _)| v != &LVar::null()) - .map(|(v, i)| (&info[LAddress::addr(i)], v)) + // SAFETY: the address comes from me + .map(|(v, i)| (&info[unsafe { LAddress::addr(i) }], v)) .filter_map(|(d, v)| match d { VarData::Variable(d) => Some((*d, v)), VarData::Constant(_) => None, diff --git a/lemu/src/parser/error.rs b/lemu/src/parser/error.rs index 41a0296..c2b053b 100644 --- a/lemu/src/parser/error.rs +++ b/lemu/src/parser/error.rs @@ -144,7 +144,7 @@ pub enum Error<'s> { /// .display(); /// ``` NoDisplay(usize, Span), - /// We have a limit of [`u16::MAX`] variables. + /// We have a limit of [`u32::MAX`] variables. #[error("too many variables")] TooManyVariables(Span), } @@ -433,7 +433,7 @@ impl Error<'_> { } Self::TooManyVariables(s) => { msg!("{error}: {bold_red}too many variables{reset}. ") - .label((s, cmt!("we only have 255 variable slots"))) + .label((s, cmt!("we only have 65536 variable slots"))) .note(cmt!("consider not using variables")); } }; diff --git a/lemu/src/parser/mod.rs b/lemu/src/parser/mod.rs index 6a5b601..cfc0fc8 100644 --- a/lemu/src/parser/mod.rs +++ b/lemu/src/parser/mod.rs @@ -4,6 +4,7 @@ mod error; pub use error::Error; use super::{ + debug::info::{VarData, VarInfo}, executor::{ExecutorBuilderInternal, Instruction, UPInstr}, instructions::{ draw::{ @@ -94,29 +95,37 @@ pub fn parse<'source, W: Wr>( mut tokens: Lexer<'source>, executor: &mut ExecutorBuilderInternal<'source, W>, ) -> Result<(), Error<'source>> { - let mut used = 0u16; - let mut mem: Box<[Option<&str>; 65536]> = vec![None; 65536].try_into().unwrap(); // maps &str to usize + let mut used = 0u32; + let mut mem: Vec<Option<&str>> = Vec::with_capacity(64); // maps &str to usize + let mut dbg_info: Vec<VarInfo> = Vec::with_capacity(64); macro_rules! push { // push a ident ($var:expr) => {{ let v = $var; - executor.debug_info.set_var(used, v, tokens.span()); - mem[used as usize] = Some(v); + dbg_info.push(VarInfo { + data: VarData::Variable(v), + span: tokens.span(), + }); + executor.mem.push(LVar::null()); + mem.push(Some(v)); used = used .checked_add(1) .ok_or(Error::TooManyVariables(tokens.span()))?; - Ok(LAddress::addr(used - 1)) + // SAFETY: just initialized executor.mem + unsafe { Ok(LAddress::addr(used - 1)) } }}; (const $var:expr) => {{ let v = $var; - executor - .debug_info - .set_const(used, v.clone(), tokens.span()); - executor.mem[LAddress::addr(used)] = LVar::from(v); + dbg_info.push(VarInfo { + data: VarData::Constant(v.clone().into()), + span: tokens.span(), + }); + executor.mem.push(LVar::from(v)); + mem.push(None); used = used .checked_add(1) .ok_or(Error::TooManyVariables(tokens.span()))?; - Ok(LAddress::addr(used - 1)) + unsafe { Ok(LAddress::addr(used - 1)) } }}; } macro_rules! addr { @@ -128,7 +137,7 @@ pub fn parse<'source, W: Wr>( .find(|(&v, _)| v == Some(val)) .map(|(_, i)| i) { - Some(n) => Ok(LAddress::addr(n)), + Some(n) => unsafe { Ok(LAddress::addr(n)) }, None => push!(val), } }}; @@ -331,7 +340,8 @@ pub fn parse<'source, W: Wr>( let set = take_numvar!(tok!()?)?; let container = take_memory!(); let index = take_numvar!(tok!()?)?; - if let LVar::Num(v) = executor.mem[index] + // this is the parser so i wont bother getting unchecked + if let LVar::Num(v) = executor.mem[index.address as usize] && !container.fits(v.round() as usize) { yeet!(IndexOutOfBounds(v.round() as usize, container.size())); @@ -347,7 +357,7 @@ pub fn parse<'source, W: Wr>( let output = take_var!(tok!()?)?; let container = take_memory!(); let index = take_numvar!(tok!()?)?; - if let LVar::Num(v) = executor.mem[index] + if let LVar::Num(v) = executor.mem[index.address as usize] && !container.fits(v.round() as usize) { yeet!(IndexOutOfBounds(v.round() as usize, container.size())); @@ -823,5 +833,7 @@ pub fn parse<'source, W: Wr>( } } + executor.debug_info.variables = dbg_info.into(); + Ok(()) } |