mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'lemu/src/lib.rs')
-rw-r--r--lemu/src/lib.rs173
1 files changed, 173 insertions, 0 deletions
diff --git a/lemu/src/lib.rs b/lemu/src/lib.rs
new file mode 100644
index 0000000..3d1fc11
--- /dev/null
+++ b/lemu/src/lib.rs
@@ -0,0 +1,173 @@
+//! crate for [MLOG](https://mindustrygame.github.io/wiki/logic/0-introduction/#what-is-mindustry-logic) emulation.
+#![feature(let_chains)]
+#![allow(clippy::redundant_closure_call)]
+#![warn(
+ clippy::multiple_unsafe_ops_per_block,
+ clippy::missing_const_for_fn,
+ clippy::redundant_pub_crate,
+ clippy::missing_safety_doc,
+ clippy::imprecise_flops,
+ unsafe_op_in_unsafe_fn,
+ clippy::dbg_macro,
+ missing_docs
+)]
+mod executor;
+mod instructions;
+mod lexer;
+mod memory;
+mod parser;
+
+use std::io::Write;
+
+pub use executor::{Executor, Output};
+use executor::{ExecutorBuilderInternal, Limit};
+use fimg::Image;
+pub use parser::Error;
+
+impl<W: Write + Default> Executor<'_, W> {
+ /// Create a new [`ExecutorBuilder`]
+ pub fn build() -> ExecutorBuilder<W> {
+ ExecutorBuilder::default()
+ }
+}
+impl<W: Write> Executor<'_, W> {
+ /// Create a new [`ExecutorBuilder`] with a output.
+ ///
+ /// Output simply must impement [`Write`], so this can be set to stdout.
+ /// Or simply set it to [`Vec<u8>`].
+ pub fn with_output(w: W) -> ExecutorBuilder<W> {
+ ExecutorBuilder {
+ displays: Vec::new(),
+ output: Some(w),
+ instruction_limit: Limit::Unlimited,
+ iteration_limit: Limit::limited(1),
+ }
+ }
+}
+
+/// Builder for a [`Executor`].
+pub struct ExecutorBuilder<W: Write> {
+ output: Option<W>,
+ displays: Vec<Image<Vec<u8>, 4>>,
+ instruction_limit: Limit,
+ iteration_limit: Limit,
+}
+
+impl<W: Write> Default for ExecutorBuilder<W> {
+ fn default() -> Self {
+ Self {
+ output: None,
+ displays: Vec::new(),
+ instruction_limit: Limit::Unlimited,
+ iteration_limit: Limit::limited(1),
+ }
+ }
+}
+
+impl<W: Write> ExecutorBuilder<W> {
+ /// Limit the number of iterations.
+ pub fn limit_iterations(self, n: usize) -> Self {
+ Self {
+ iteration_limit: Limit::limited(n),
+ ..self
+ }
+ }
+
+ /// Unlimit the number of iterations.
+ pub fn unlimit_iterations(self) -> Self {
+ Self {
+ iteration_limit: Limit::Unlimited,
+ ..self
+ }
+ }
+
+ /// Unlimit the number of instructions.
+ /// Make sure to limit the number of the iterations, else it will possibly run forever.
+ pub fn unlimit_instructions(self) -> Self {
+ Self {
+ iteration_limit: Limit::Unlimited,
+ ..self
+ }
+ }
+
+ /// Limit the number of processed instructions.
+ ///
+ /// Use this if you want it to *definetly finish*.
+ pub fn limit_instructions(self, n: usize) -> Self {
+ Self {
+ instruction_limit: Limit::limited(n),
+ ..self
+ }
+ }
+
+ /// Add a small (`80x80`) logic display.
+ pub fn display(self) -> Self {
+ let mut d = self.displays;
+ d.push(Image::alloc(80, 80));
+ Self {
+ displays: d,
+ ..self
+ }
+ }
+
+ /// Add a large (`176x176`) logic display.
+ pub fn large_display(self) -> Self {
+ let mut d = self.displays;
+ d.push(Image::alloc(176, 176));
+ Self {
+ displays: d,
+ ..self
+ }
+ }
+
+ /// Build the [`Executor`] with this code.
+ ///
+ /// # Errors
+ ///
+ /// errors if the code is malformed.
+ pub fn program(self, program: &str) -> Result<Executor<'_, W>, Error<'_>> {
+ let Self {
+ output,
+ displays,
+ instruction_limit,
+ iteration_limit,
+ } = self;
+ let mut executor = ExecutorBuilderInternal::new(output, displays);
+ executor
+ .inslimit(instruction_limit)
+ .itrlimit(iteration_limit);
+ // #[cfg(debug_assertions)]
+ // lexer::print_stream(lexer::lex(program));
+ parser::parse(lexer::lex(program), &mut executor)?;
+ Ok(executor.finish())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ macro_rules! test {
+ (run $fn:ident.mlog $($times:literal times)?;
+ $(output = $to_be:literal $(;)?)?
+ $(cell[$cell_n:literal][$cell_index:literal] = $what:literal $(;)?)?
+ ) => {
+ #[test]
+ fn $fn() -> Result<(), Error<'static>> {
+ let v = vec![];
+ let mut lex = Executor::with_output(v)
+ .unlimit_instructions()
+ $(.limit_iterations($times))?
+ .program(include_str!(concat!(stringify!($fn), ".mlog")))?;
+ lex.run();
+ let output = lex.output();
+ $(assert_eq!(output.output.unwrap(), $to_be);)?
+ $(assert_eq!(output.cells[$cell_n][$cell_index], $what);)?
+ Ok(())
+ }
+ };
+ }
+
+ test!(run fib.mlog; output = b"12586269025");
+ test!(run celliterate.mlog 500 times; cell[0][0] = 500.0);
+}