From 5a4708e36f10bb366097238f14f325b27d561e1f Mon Sep 17 00:00:00 2001 From: Wyatt Ross <2treecko@gmail.com> Date: Wed, 17 Apr 2024 20:59:57 -0400 Subject: [PATCH] Reader && Undoable Instructions (#90) - Added input instructions - Added transformations model of instructions to allow for undo operations - Added cheese! --------- Co-authored-by: michaelni678 Co-authored-by: Trevor Brunette Co-authored-by: Aleks Bekker --- example/terminal.ez | 30 +++ rezasm-app/rezasm-cli/src/util/application.rs | 11 +- .../rezasm-cli/src/util/cli_arguments.rs | 11 +- rezasm-app/rezasm-cli/src/util/cli_io.rs | 71 +++--- rezasm-app/rezasm-tauri/src/main.rs | 28 ++- rezasm-app/rezasm-tauri/src/tauri_reader.rs | 45 ++++ rezasm-app/rezasm-wasm/src/lib.rs | 18 +- .../implementation/arithmetic_instructions.rs | 102 ++++++-- .../implementation/branch_instructions.rs | 58 ++++- .../implementation/comparison_instructions.rs | 94 +++++--- .../float_arithmetic_instructions.rs | 70 ++++-- .../implementation/function_instructions.rs | 94 ++++---- .../implementation/memory_instructions.rs | 124 +++++++--- .../src/instructions/implementation/mod.rs | 2 + .../terminal_input_instructions.rs | 224 ++++++++++++++++++ .../terminal_output_instructions.rs | 11 +- .../src/instructions/instruction.rs | 13 +- .../src/instructions/instruction_registry.rs | 5 + .../rezasm-core/src/simulation/mod.rs | 2 + .../rezasm-core/src/simulation/reader.rs | 48 ++++ .../rezasm-core/src/simulation/reader_cell.rs | 83 +++++++ .../rezasm-core/src/simulation/registry.rs | 4 +- .../rezasm-core/src/simulation/simulator.rs | 94 ++++++-- .../src/simulation/transform/transformable.rs | 35 ++- .../simulation/transform/transformation.rs | 18 +- .../transform/transformation_sequence.rs | 35 ++- rezasm-source/rezasm-core/src/util/error.rs | 23 ++ rezasm-source/rezasm-web-core/src/lib.rs | 40 +++- src/components/Code.jsx | 1 + src/components/Console.jsx | 3 +- src/components/Controls.jsx | 4 +- src/components/simulator.js | 17 ++ src/rust_functions.js | 1 + tests/common/mod.rs | 1 + tests/common/reader.rs | 46 ++++ tests/core.rs | 50 +++- 36 files changed, 1254 insertions(+), 262 deletions(-) create mode 100644 example/terminal.ez create mode 100644 rezasm-app/rezasm-tauri/src/tauri_reader.rs create mode 100644 rezasm-source/rezasm-core/src/instructions/implementation/terminal_input_instructions.rs create mode 100644 rezasm-source/rezasm-core/src/simulation/reader.rs create mode 100644 rezasm-source/rezasm-core/src/simulation/reader_cell.rs create mode 100644 tests/common/reader.rs diff --git a/example/terminal.ez b/example/terminal.ez new file mode 100644 index 0000000..2b08f61 --- /dev/null +++ b/example/terminal.ez @@ -0,0 +1,30 @@ +# Read an integer. +prints "Input an integer: " +readi $t0 +# Print the read integer. +printi $t0 +prints "\n" +# Allocate memory according to what was inputted. +alloc $s0 $t0 +# Subtract 1 from the allocation size. This is because room is needed for the null terminator. +sub $t0 $t0 1 +# Read string (sized). +prints "Input a string: " +reads $s0 $t0 +# Print the read string. +prints $s0 +prints "\n" +# Read a float. +prints "Input a float: " +readf $fs0 +printf $fs0 +prints "\n" +prints "Input a char: " +readc $t1 +printc $t1 +prints "\n" +alloc $t2 100 +prints "Input a line: " +readln $t2 100 +prints $t2 +printc '\n' diff --git a/rezasm-app/rezasm-cli/src/util/application.rs b/rezasm-app/rezasm-cli/src/util/application.rs index 2632d14..4360b2e 100644 --- a/rezasm-app/rezasm-cli/src/util/application.rs +++ b/rezasm-app/rezasm-cli/src/util/application.rs @@ -1,4 +1,3 @@ -use crate::util::cli_io::InputSource; use rezasm_core::parser::lexer; use rezasm_core::simulation::registry; use rezasm_core::simulation::simulator::Simulator; @@ -8,19 +7,13 @@ use rezasm_core::util::io::RezasmFileReader; pub struct Application { simulator: Simulator, code_file: RezasmFileReader, - input_file: InputSource, } impl Application { - pub fn new( - simulator: Simulator, - code_file: RezasmFileReader, - input_file: InputSource, - ) -> Application { + pub fn new(simulator: Simulator, code_file: RezasmFileReader) -> Application { Application { simulator, code_file, - input_file, } } @@ -40,7 +33,7 @@ impl Application { } while !self.simulator.is_done() { - self.simulator.run_line_from_pc()? + self.simulator.run_line_from_pc()?; } let r = self diff --git a/rezasm-app/rezasm-cli/src/util/cli_arguments.rs b/rezasm-app/rezasm-cli/src/util/cli_arguments.rs index 643eb4d..9dfc013 100644 --- a/rezasm-app/rezasm-cli/src/util/cli_arguments.rs +++ b/rezasm-app/rezasm-cli/src/util/cli_arguments.rs @@ -1,6 +1,7 @@ use crate::util::application::Application; use crate::util::cli::Arguments; use crate::util::cli_io::{InputSource, OutputSink}; +use rezasm_core::simulation::reader_cell::ReaderCell; use rezasm_core::simulation::simulator::Simulator; use rezasm_core::util::error::{EzasmError, IoError, SimulatorError}; use rezasm_core::util::io::{RezasmFileReader, RezasmFileWriter}; @@ -60,8 +61,12 @@ pub fn handle_arguments(arguments: Arguments) -> Result None => OutputSink::new_console(), }; - let simulator: Simulator = - Simulator::new_custom(&word_size, memory_size, Box::new(output_file)); + let simulator: Simulator = Simulator::new_custom( + &word_size, + memory_size, + ReaderCell::new(input_file), + Box::new(output_file), + ); - Ok(Application::new(simulator, code_file, input_file)) + Ok(Application::new(simulator, code_file)) } diff --git a/rezasm-app/rezasm-cli/src/util/cli_io.rs b/rezasm-app/rezasm-cli/src/util/cli_io.rs index 31805e0..9ee32c0 100644 --- a/rezasm-app/rezasm-cli/src/util/cli_io.rs +++ b/rezasm-app/rezasm-cli/src/util/cli_io.rs @@ -1,62 +1,69 @@ use crate::util::cli_io::InputSource::{ConsoleInput, FileInput}; use crate::util::cli_io::OutputSink::{ConsoleOutput, FileOutput}; +use rezasm_core::simulation::reader::Reader; use rezasm_core::simulation::writer::Writer; use rezasm_core::util::as_any::AsAny; use rezasm_core::util::error::IoError; use rezasm_core::util::io::{RezasmFileReader, RezasmFileWriter}; -use scanner_rust::Scanner; +use scanner_rust::{Scanner, ScannerAscii}; use std::any::Any; -use std::io::{stdin, stdout, Stdin, Write}; +use std::io::{self, stdin, stdout, Stdin, Write}; +#[derive(Debug)] pub enum InputSource { FileInput(Scanner), - ConsoleInput(Scanner), + ConsoleInput(Stdin), } impl InputSource { pub fn new_console() -> InputSource { - ConsoleInput(Scanner::new(stdin())) + ConsoleInput(stdin()) } pub fn new_file(file: RezasmFileReader) -> InputSource { FileInput(Scanner::new(file)) } - pub fn read_line(&mut self) -> Result { - let s = match self { - FileInput(s) => s.next_line()?, - ConsoleInput(s) => s.next_line()?, + pub fn read_raw(&mut self) -> Result { + let b = match self { + FileInput(s) => s.next_bytes(1)?, + ConsoleInput(s) => ScannerAscii::new(s).next_bytes(1)?, }; - Ok(s.ok_or(IoError::OutOfBoundsError)?.trim().to_string()) + Ok(b.ok_or(IoError::OutOfBoundsError)?[0]) } +} - pub fn read_word(&mut self) -> Result { - let s = match self { - FileInput(s) => s.next()?, - ConsoleInput(s) => s.next()?, - }; - s.ok_or(IoError::OutOfBoundsError) - } +impl Reader for InputSource {} - pub fn read_char(&mut self) -> Result { - let c = match self { - FileInput(s) => s.next_char()?, - ConsoleInput(s) => s.next_char()?, - } - .ok_or(IoError::OutOfBoundsError)?; - if char::is_whitespace(c) { - self.read_char() - } else { - Ok(c) +impl io::Read for InputSource { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + match self { + ConsoleInput(readable) => readable.read(buf), + FileInput(file) => { + let next = file.next().unwrap().unwrap(); + buf.write(next.as_bytes()) + } } } +} - pub fn read_raw(&mut self) -> Result { - let b = match self { - FileInput(s) => s.next_bytes(1)?, - ConsoleInput(s) => s.next_bytes(1)?, - }; - Ok(b.ok_or(IoError::OutOfBoundsError)?[0]) +impl io::Write for InputSource { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(0) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl AsAny for InputSource { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self } } diff --git a/rezasm-app/rezasm-tauri/src/main.rs b/rezasm-app/rezasm-tauri/src/main.rs index 75d8bd6..1a48837 100644 --- a/rezasm-app/rezasm-tauri/src/main.rs +++ b/rezasm-app/rezasm-tauri/src/main.rs @@ -1,6 +1,7 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +mod tauri_reader; mod tauri_writer; extern crate lazy_static; @@ -8,15 +9,20 @@ extern crate tauri; use lazy_static::lazy_static; use rezasm_core::instructions::implementation::register_instructions; +use rezasm_core::simulation::reader_cell::ReaderCell; use rezasm_web_core::{ get_exit_status, get_memory_bounds, get_memory_slice, get_register_names, get_register_value, - get_register_values, get_word_size, is_completed, load, receive_input, register_writer, reset, - step, stop, + get_register_values, get_simulator_mut, get_word_size, initialize_simulator, is_completed, + load, reset, step, step_back, stop, }; use tauri::{Manager, Window}; +use tauri_reader::TauriReader; use crate::tauri_writer::TauriWriter; -use std::sync::{Arc, RwLock}; +use std::{ + io::Write, + sync::{Arc, RwLock}, +}; lazy_static! { static ref WINDOW: Arc>> = Arc::new(RwLock::new(None)); @@ -58,6 +64,11 @@ fn tauri_step() -> Result<(), String> { step() } +#[tauri::command()] +fn tauri_step_back() -> Result<(), String> { + step_back() +} + #[tauri::command] fn tauri_is_completed() -> bool { is_completed() @@ -100,12 +111,18 @@ fn tauri_get_word_size() -> usize { #[tauri::command] fn tauri_receive_input(data: &str) { - receive_input(data); + let mut simulator = get_simulator_mut(); + let reader = simulator.get_reader_mut(); + reader.write(data.as_bytes()).unwrap(); + reader.write(&[b'\n']).unwrap(); } fn main() { register_instructions(); - register_writer(Box::new(TauriWriter::new())); + initialize_simulator( + Some(ReaderCell::new(TauriReader::new())), + Some(Box::new(TauriWriter::new())), + ); tauri::Builder::default() .setup(|app| Ok(set_window(app.get_window(WINDOW_NAME).unwrap()))) @@ -113,6 +130,7 @@ fn main() { tauri_load, tauri_reset, tauri_step, + tauri_step_back, tauri_stop, tauri_is_completed, tauri_get_exit_status, diff --git a/rezasm-app/rezasm-tauri/src/tauri_reader.rs b/rezasm-app/rezasm-tauri/src/tauri_reader.rs new file mode 100644 index 0000000..adf6d18 --- /dev/null +++ b/rezasm-app/rezasm-tauri/src/tauri_reader.rs @@ -0,0 +1,45 @@ +use std::collections::VecDeque; +use std::io::{Read, Write}; + +use rezasm_core::{simulation::reader::Reader, util::as_any::AsAny}; + +#[derive(Debug)] +pub struct TauriReader { + buffer: VecDeque, +} + +impl TauriReader { + pub fn new() -> TauriReader { + TauriReader { + buffer: VecDeque::new(), + } + } +} + +impl Reader for TauriReader {} + +impl Read for TauriReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.buffer.read(buf) + } +} + +impl Write for TauriReader { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buffer.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.buffer.flush() + } +} + +impl AsAny for TauriReader { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} diff --git a/rezasm-app/rezasm-wasm/src/lib.rs b/rezasm-app/rezasm-wasm/src/lib.rs index 9d28efa..d1f5f78 100644 --- a/rezasm-app/rezasm-wasm/src/lib.rs +++ b/rezasm-app/rezasm-wasm/src/lib.rs @@ -7,10 +7,13 @@ extern crate wasm_bindgen; use crate::wasm_writer::WasmWriter; use rezasm_core::instructions::implementation::register_instructions; +use rezasm_core::simulation::reader::DummyReader; +use rezasm_core::simulation::reader_cell::ReaderCell; +use rezasm_core::util::as_any::AsAny; use rezasm_web_core::{ get_exit_status, get_memory_bounds, get_memory_slice, get_register_names, get_register_value, - get_register_values, get_word_size, is_completed, load, receive_input, register_writer, reset, - step, stop, + get_register_values, get_simulator_mut, get_word_size, initialize_simulator, is_completed, + load, reset, step, stop, }; use wasm_bindgen::prelude::*; @@ -76,11 +79,18 @@ pub fn wasm_get_word_size() -> usize { #[wasm_bindgen] pub fn wasm_receive_input(data: &str) { - receive_input(data); + let mut simulator = get_simulator_mut(); + let reader = simulator.get_reader_mut(); + // TODO make wasm_reader and expand buffer for it, then use it here + let _ = reader.as_any_mut().downcast_mut::().unwrap(); + let _ = data; } #[wasm_bindgen(start)] pub fn wasm_initialize_backend() { register_instructions(); - register_writer(Box::new(WasmWriter::new())); + initialize_simulator( + Some(ReaderCell::new(DummyReader::new())), + Some(Box::new(WasmWriter::new())), + ); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/arithmetic_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/arithmetic_instructions.rs index 39aaa2d..c421809 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/arithmetic_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/arithmetic_instructions.rs @@ -7,9 +7,11 @@ use crate::instructions::instruction_registry::register_instruction; use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::instructions::targets::input_target::Input; use crate::instructions::targets::input_target::InputTarget; -use crate::instructions::targets::output_target::Output; use crate::simulation::register::Register; use crate::simulation::registry; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation::Transformation; +use crate::simulation::transform::transformation_sequence::TransformationSequence; use crate::util::error::SimulatorError; use crate::util::raw_data::RawData; use crate::util::word_size::WordSize; @@ -23,7 +25,12 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); let k = value1 + value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SUB: Instruction = instruction!(sub, |simulator: Simulator, @@ -33,7 +40,12 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); let k = value1 - value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref MUL: Instruction = instruction!(mul, |simulator: Simulator, @@ -66,7 +78,12 @@ lazy_static! { let k = value1 * value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref DIV: Instruction = instruction!(div, |simulator: Simulator, @@ -79,7 +96,12 @@ lazy_static! { return Err(SimulatorError::DivideByZeroError); } else { let k = value1 / value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); } }); pub static ref AND: Instruction = @@ -90,7 +112,12 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); let k = value1 & value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref OR: Instruction = instruction!(or, |simulator: Simulator, @@ -100,7 +127,12 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); let k = value1 | value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref XOR: Instruction = instruction!(xor, |simulator: Simulator, @@ -110,7 +142,12 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); let k = value1 ^ value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref NOT: Instruction = instruction!(not, |simulator: Simulator, @@ -118,7 +155,12 @@ lazy_static! { input1: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let k = !value1; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref MOD: Instruction = instruction!(_mod, |simulator: Simulator, @@ -131,7 +173,12 @@ lazy_static! { return Err(SimulatorError::DivideByZeroError); } else { let k = value1 % value2; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); } }); pub static ref SLL: Instruction = @@ -149,7 +196,12 @@ lazy_static! { value << shift }; - return output.set(simulator, RawData::from_int(k, word_size)); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SRL: Instruction = instruction!(srl, |simulator: Simulator, @@ -169,7 +221,12 @@ lazy_static! { } }; - return output.set(simulator, RawData::from_int(k, word_size)); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SRA: Instruction = instruction!(sra, |simulator: Simulator, @@ -186,19 +243,34 @@ lazy_static! { value >> shift }; - return output.set(simulator, RawData::from_int(k, word_size)); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref INC: Instruction = instruction!(inc, |simulator: Simulator, output: InputOutputTarget| { let value = output.get(&simulator)?.int_value(); let k = value + 1; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref DEC: Instruction = instruction!(dec, |simulator: Simulator, output: InputOutputTarget| { let value = output.get(&simulator)?.int_value(); let k = value - 1; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/branch_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/branch_instructions.rs index 3c4093e..bd8429e 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/branch_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/branch_instructions.rs @@ -9,7 +9,9 @@ use crate::instructions::instruction_registry::register_instruction; use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::instructions::targets::input_target::Input; use crate::instructions::targets::input_target::InputTarget; -use crate::instructions::targets::output_target::Output; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation::Transformation; +use crate::simulation::transform::transformation_sequence::TransformationSequence; lazy_static! { pub static ref BEQ: Instruction = @@ -22,9 +24,14 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); if value1 == value2 { - return output.set(simulator, label.get(&simulator)?); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + label.get(simulator)?, + ); + return Ok(TransformationSequence::new_single(transformation)); } else { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } }); pub static ref BNE: Instruction = @@ -37,9 +44,14 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); if value1 != value2 { - return output.set(simulator, label.get(&simulator)?); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + label.get(simulator)?, + ); + return Ok(TransformationSequence::new_single(transformation)); } else { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } }); pub static ref BLT: Instruction = @@ -52,9 +64,14 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); if value1 < value2 { - return output.set(simulator, label.get(&simulator)?); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + label.get(simulator)?, + ); + return Ok(TransformationSequence::new_single(transformation)); } else { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } }); pub static ref BLE: Instruction = @@ -67,9 +84,14 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); if value1 <= value2 { - return output.set(simulator, label.get(&simulator)?); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + label.get(simulator)?, + ); + return Ok(TransformationSequence::new_single(transformation)); } else { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } }); pub static ref BGT: Instruction = @@ -82,9 +104,14 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); if value1 > value2 { - return output.set(simulator, label.get(&simulator)?); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + label.get(simulator)?, + ); + return Ok(TransformationSequence::new_single(transformation)); } else { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } }); pub static ref BGE: Instruction = @@ -97,9 +124,14 @@ lazy_static! { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); if value1 >= value2 { - return output.set(simulator, label.get(&simulator)?); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + label.get(simulator)?, + ); + return Ok(TransformationSequence::new_single(transformation)); } else { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } }); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/comparison_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/comparison_instructions.rs index 944bc30..a6b13a3 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/comparison_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/comparison_instructions.rs @@ -7,7 +7,9 @@ use crate::instructions::instruction_registry::register_instruction; use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::instructions::targets::input_target::Input; use crate::instructions::targets::input_target::InputTarget; -use crate::instructions::targets::output_target::Output; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation::Transformation; +use crate::simulation::transform::transformation_sequence::TransformationSequence; use crate::util::raw_data::RawData; lazy_static! { @@ -18,11 +20,16 @@ lazy_static! { input2: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); - if value1 == value2 { - return output.set(simulator, RawData::from_int(1, simulator.get_word_size())); - } else { - return output.set(simulator, RawData::from_int(0, simulator.get_word_size())); - } + let k = match value1 == value2 { + true => 1, + false => 0, + }; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SNE: Instruction = instruction!(sne, |simulator: Simulator, @@ -31,11 +38,16 @@ lazy_static! { input2: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); - if value1 != value2 { - return output.set(simulator, RawData::from_int(1, simulator.get_word_size())); - } else { - return output.set(simulator, RawData::from_int(0, simulator.get_word_size())); - } + let k = match value1 != value2 { + true => 1, + false => 0, + }; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SLT: Instruction = instruction!(slt, |simulator: Simulator, @@ -44,11 +56,16 @@ lazy_static! { input2: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); - if value1 < value2 { - return output.set(simulator, RawData::from_int(1, simulator.get_word_size())); - } else { - return output.set(simulator, RawData::from_int(0, simulator.get_word_size())); - } + let k = match value1 < value2 { + true => 1, + false => 0, + }; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SLE: Instruction = instruction!(sle, |simulator: Simulator, @@ -57,11 +74,16 @@ lazy_static! { input2: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); - if value1 <= value2 { - return output.set(simulator, RawData::from_int(1, simulator.get_word_size())); - } else { - return output.set(simulator, RawData::from_int(0, simulator.get_word_size())); - } + let k = match value1 <= value2 { + true => 1, + false => 0, + }; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SGT: Instruction = instruction!(sgt, |simulator: Simulator, @@ -70,11 +92,16 @@ lazy_static! { input2: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); - if value1 > value2 { - return output.set(simulator, RawData::from_int(1, simulator.get_word_size())); - } else { - return output.set(simulator, RawData::from_int(0, simulator.get_word_size())); - } + let k = match value1 > value2 { + true => 1, + false => 0, + }; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SGE: Instruction = instruction!(sge, |simulator: Simulator, @@ -83,11 +110,16 @@ lazy_static! { input2: InputTarget| { let value1 = input1.get(&simulator)?.int_value(); let value2 = input2.get(&simulator)?.int_value(); - if value1 >= value2 { - return output.set(simulator, RawData::from_int(1, simulator.get_word_size())); - } else { - return output.set(simulator, RawData::from_int(0, simulator.get_word_size())); - } + let k = match value1 >= value2 { + true => 1, + false => 0, + }; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size()), + ); + return Ok(TransformationSequence::new_single(transformation)); }); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/float_arithmetic_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/float_arithmetic_instructions.rs index eb55d59..3426b6a 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/float_arithmetic_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/float_arithmetic_instructions.rs @@ -1,5 +1,3 @@ -use std::f64::NAN; - use lazy_static::lazy_static; use crate::instructions::instruction::instruction; @@ -9,7 +7,9 @@ use crate::instructions::instruction_registry::register_instruction; use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::instructions::targets::input_target::Input; use crate::instructions::targets::input_target::InputTarget; -use crate::instructions::targets::output_target::Output; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation::Transformation; +use crate::simulation::transform::transformation_sequence::TransformationSequence; use crate::util::error::SimulatorError; use crate::util::raw_data::RawData; @@ -19,14 +19,22 @@ pub static ref DECF: Instruction = output: InputOutputTarget| { let value1 = output.get(&simulator)?.float_value(); let k = value1 - 1.0; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref INCF: Instruction = instruction!(incf, |simulator: Simulator, output: InputOutputTarget| { let value1 = output.get(&simulator)?.float_value(); let k = value1 + 1.0; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref ADDF: Instruction = instruction!(addf, |simulator: Simulator, @@ -36,7 +44,11 @@ pub static ref ADDF: Instruction = let value1 = input1.get(&simulator)?.float_value(); let value2 = input2.get(&simulator)?.float_value(); let k = value1 + value2 ; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref SUBF: Instruction = instruction!(subf, |simulator: Simulator, @@ -46,7 +58,11 @@ pub static ref SUBF: Instruction = let value1 = input1.get(&simulator)?.float_value(); let value2 = input2.get(&simulator)?.float_value(); let k = value1 - value2 ; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref MULF: Instruction = instruction!(mulf, |simulator: Simulator, @@ -56,7 +72,11 @@ pub static ref MULF: Instruction = let value1 = input1.get(&simulator)?.float_value(); let value2 = input2.get(&simulator)?.float_value(); let k = value1 * value2 ; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref DIVF: Instruction = instruction!(divf, |simulator: Simulator, @@ -66,14 +86,15 @@ pub static ref DIVF: Instruction = let value1 = input1.get(&simulator)?.float_value(); let value2 = input2.get(&simulator)?.float_value(); - if value2 == 0f64 { - if value1 == 0f64 { - return output.set(simulator, RawData::from_float(NAN, simulator.get_word_size())); - } + if value2 == 0f64 && value1 != 0f64 { return Err(SimulatorError::DivideByZeroError); } let k = value1 / value2 ; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref MODF: Instruction = instruction!(modf, |simulator: Simulator, @@ -83,10 +104,7 @@ pub static ref MODF: Instruction = let value1 = input1.get(&simulator)?.float_value(); let value2 = input2.get(&simulator)?.float_value(); - if value2 == 0f64 { - if value1 == 0f64 { - return output.set(simulator, RawData::from_float(NAN, simulator.get_word_size())); - } + if value2 == 0f64 && value1 != 0f64 { return Err(SimulatorError::DivideByZeroError); } @@ -95,7 +113,11 @@ pub static ref MODF: Instruction = let k = divvalue1 - f64::floor(divvalue1/divvalue2) * divvalue2; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref ITOF: Instruction = instruction!(itof, |simulator: Simulator, @@ -105,7 +127,11 @@ pub static ref ITOF: Instruction = let k = value1 as f64; - return output.set(simulator, RawData::from_float(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_float(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref FTOI: Instruction = instruction!(ftoi, |simulator: Simulator, @@ -121,7 +147,11 @@ pub static ref FTOI: Instruction = } let k = value1 as i64; - return output.set(simulator, RawData::from_int(k, simulator.get_word_size())); + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + RawData::from_int(k, simulator.get_word_size())); + return Ok(TransformationSequence::new_single(transformation)); }); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/function_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/function_instructions.rs index 35f1fd3..2699c5f 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/function_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/function_instructions.rs @@ -1,11 +1,16 @@ #![cfg_attr(rustfmt, rustfmt_skip)] // prevent rustfmt from breaking 0 argument instruction macros use crate::instructions::argument_type::ArgumentType; +use crate::instructions::implementation::memory_instructions; use crate::instructions::implementation::memory_instructions::POP; use crate::instructions::implementation::memory_instructions::PUSH; use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::simulation::registry::FID_NUMBER; use crate::simulation::registry::PC_NUMBER; +use crate::simulation::registry::RA_NUMBER; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation::Transformation; +use crate::simulation::transform::transformation_sequence::TransformationSequence; use lazy_static::lazy_static; use crate::instructions::instruction::instruction; @@ -27,7 +32,7 @@ lazy_static! { let address = input.get(&simulator)?.int_value() as usize; let given_file = simulator.get_memory().get_string(address)?; if simulator.get_program().file_exists(&given_file) { - return Ok(()); + return Ok(TransformationSequence::new_empty()); } let mut relative_location = simulator.get_program().main_file(); relative_location = match relative_location.rsplit_once('/') { @@ -48,21 +53,41 @@ lazy_static! { None => {} } } - simulator.add_lines(lines, given_file) + simulator.add_lines(lines, given_file)?; + Ok(TransformationSequence::new_empty()) }); pub static ref JUMP: Instruction = instruction!(jump, |simulator: Simulator, input: InputTarget| { let word_size = simulator.get_word_size().clone(); let value = input.get(&simulator)?; - let pc = simulator.get_registers_mut().get_pc_mut(); - pc.set_data(value.clone()); - Ok(()) + let output = InputOutputTarget::new_register(&PC_NUMBER)?; + let transformation = Transformation::new( + Transformable::InputOutputTransformable(output), + output.get(simulator)?, + value.clone() + ); + return Ok(TransformationSequence::new_single(transformation)); }); pub static ref CALL: Instruction = instruction!(call, |simulator: Simulator, input: InputTarget| { + let ra_output = InputOutputTarget::new_register(&RA_NUMBER)?; + let ra_transformable = Transformable::InputOutputTransformable(ra_output); + let pc_output = InputOutputTarget::new_register(&PC_NUMBER)?; + let pc_transformable = Transformable::InputOutputTransformable(pc_output); + let fid_input = InputTarget::new_register(&FID_NUMBER)?; + let fid_output = InputOutputTarget::new_register(&FID_NUMBER)?; + let fid_transformable = Transformable::InputOutputTransformable(fid_output); + let ra_register = ArgumentType::Input(InputTarget::RegisterInput(RA_NUMBER)); + let pc_register = ArgumentType::Input(InputTarget::RegisterInput(PC_NUMBER)); + let fid_register = ArgumentType::Input(InputTarget::RegisterInput(FID_NUMBER)); let word_size = simulator.get_word_size().clone(); + let mut final_sequence = TransformationSequence::new_empty(); + + final_sequence.concatenate(PUSH.call_function(simulator, &vec![ra_register])?); + final_sequence.concatenate(memory_instructions::consecutive_push(simulator, fid_input, 1)?); + let (fid, pc) = match input { InputTarget::LabelReferenceInput(label) => { match simulator.get_program().resolve_label(&label) { @@ -75,41 +100,32 @@ lazy_static! { simulator.get_registers().get_fid().get_data().int_value(), ), }; - let pc_register = ArgumentType::Input(InputTarget::RegisterInput(PC_NUMBER)); - let fid_register = ArgumentType::Input(InputTarget::RegisterInput(FID_NUMBER)); - PUSH.call_function(simulator, &vec![pc_register])?; - PUSH.call_function(simulator, &vec![fid_register])?; - simulator - .get_registers_mut() - .get_pc_mut() - .set_data(RawData::from_int(pc, &word_size)); - simulator - .get_registers_mut() - .get_fid_mut() - .set_data(RawData::from_int(fid, &word_size)); - Ok(()) + final_sequence.concatenate(TransformationSequence::new_single(pc_transformable.create_transformation(simulator, RawData::from_int(pc, &word_size))?)); + final_sequence.concatenate(TransformationSequence::new_single(fid_transformable.create_transformation(simulator, RawData::from_int(fid, &word_size))?)); + Ok(final_sequence) }); pub static ref RETURN: Instruction = instruction!(_return, |simulator: Simulator,| { + let ra_output = InputOutputTarget::new_register(&RA_NUMBER)?; + let mut final_sequence = TransformationSequence::new_empty(); + let ra_register = ArgumentType::Input(InputTarget::RegisterInput(RA_NUMBER)); let pc_register = ArgumentType::InputOutput(InputOutputTarget::RegisterInputOutput(PC_NUMBER)); let fid_register = ArgumentType::InputOutput(InputOutputTarget::RegisterInputOutput(FID_NUMBER)); - POP.call_function(simulator, &vec![fid_register])?; - POP.call_function(simulator, &vec![pc_register])?; - Ok(()) + final_sequence.concatenate(JUMP.call_function(simulator, &vec![ra_register.clone()])?); + final_sequence.concatenate(POP.call_function(simulator, &vec![fid_register])?); + final_sequence.concatenate(memory_instructions::consecutive_pop(simulator, ra_output, 1)?); + Ok(final_sequence) }); pub static ref EXIT: Instruction = instruction!(exit, |simulator: Simulator,| { + let pc_transformable = Transformable::InputOutputTransformable(InputOutputTarget::RegisterInputOutput(PC_NUMBER)); + let fid_transformable = Transformable::InputOutputTransformable(InputOutputTarget::RegisterInputOutput(FID_NUMBER)); let word_size = simulator.get_word_size().clone(); let end = simulator.get_program().end_pc(0) - 1; - simulator - .get_registers_mut() - .get_pc_mut() - .set_data(RawData::from_int(end as i64, &word_size)); - simulator - .get_registers_mut() - .get_fid_mut() - .set_data(RawData::from_int(0i64, &word_size)); - Ok(()) + let mut final_sequence = TransformationSequence::new_empty(); + final_sequence.concatenate(TransformationSequence::new_single(pc_transformable.create_transformation(simulator, RawData::from_int(end as i64, &word_size))?)); + final_sequence.concatenate(TransformationSequence::new_single(fid_transformable.create_transformation(simulator, RawData::from_int(0i64, &word_size))?)); + Ok(final_sequence) }); pub static ref EXIT_STATUS: Instruction = @@ -119,18 +135,16 @@ lazy_static! { .get_registers_mut() .get_register_mut(®istry::R0.into()) .unwrap(); - r0.set_data(return_value); + let r0_transformable = Transformable::InputOutputTransformable(InputOutputTarget::RegisterInputOutput(registry::get_register_number(®istry::R0.to_string())?)); + let pc_transformable = Transformable::InputOutputTransformable(InputOutputTarget::RegisterInputOutput(PC_NUMBER)); + let fid_transformable = Transformable::InputOutputTransformable(InputOutputTarget::RegisterInputOutput(FID_NUMBER)); let word_size = simulator.get_word_size().clone(); let end = simulator.get_program().end_pc(0) - 1; - simulator - .get_registers_mut() - .get_pc_mut() - .set_data(RawData::from_int(end as i64, &word_size)); - simulator - .get_registers_mut() - .get_fid_mut() - .set_data(RawData::from_int(0i64, &word_size)); - Ok(()) + let mut final_sequence = TransformationSequence::new_empty(); + final_sequence.concatenate(TransformationSequence::new_single(pc_transformable.create_transformation(simulator, RawData::from_int(end as i64, &word_size))?)); + final_sequence.concatenate(TransformationSequence::new_single(fid_transformable.create_transformation(simulator, RawData::from_int(0i64, &word_size))?)); + final_sequence.concatenate(TransformationSequence::new_single(r0_transformable.create_transformation(simulator, input.get(simulator)?)?)); + Ok(final_sequence) }); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/memory_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/memory_instructions.rs index 427b924..966a349 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/memory_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/memory_instructions.rs @@ -7,42 +7,78 @@ use crate::instructions::instruction_registry::register_instruction; use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::instructions::targets::input_target::Input; use crate::instructions::targets::input_target::InputTarget; -use crate::instructions::targets::output_target::Output; use crate::simulation::registry; +use crate::simulation::simulator::Simulator; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation::Transformation; +use crate::simulation::transform::transformation_sequence::TransformationSequence; +use crate::util::error::SimulatorError; use crate::util::raw_data::RawData; +pub fn consecutive_push( + simulator: &Simulator, + input: InputTarget, + times: i64, +) -> Result { + let offset = times * simulator.get_word_size().value() as i64; + let sp_target = InputOutputTarget::new_register(®istry::SP_NUMBER)?; + let sp = + sp_target.get(simulator)?.int_value() - simulator.get_word_size().value() as i64 - offset; + let sp_transformable = Transformable::InputOutputTransformable(sp_target); + + let t1 = sp_transformable + .create_transformation(simulator, RawData::from_int(sp, simulator.get_word_size()))?; + + let memory_transformer = Transformable::MemoryTransformable(t1.get_to().int_value() as usize); + let t2 = Transformation::new( + memory_transformer, + memory_transformer.get(simulator)?, + input.get(simulator)?, + ); + Ok(TransformationSequence::new(vec![t1, t2])) +} + +pub fn consecutive_pop( + simulator: &Simulator, + output: InputOutputTarget, + times: i64, +) -> Result { + let offset = times * simulator.get_word_size().value() as i64; + let sp_target = InputOutputTarget::new_register(®istry::SP_NUMBER)?; + let io = Transformable::InputOutputTransformable(output); + let sp = + sp_target.get(simulator)?.int_value() - simulator.get_word_size().value() as i64 + offset; + let t1 = io.create_transformation( + simulator, + RawData::from_int( + simulator + .get_memory() + .read(sp_target.get(simulator)?.int_value() as usize)? + .int_value() + + offset, + simulator.get_word_size(), + ), + )?; + let t2 = Transformable::InputOutputTransformable(sp_target).create_transformation( + simulator, + RawData::from_int( + sp_target.get(simulator)?.int_value() + + simulator.get_word_size().value() as i64 + + offset, + simulator.get_word_size(), + ), + )?; + Ok(TransformationSequence::new(vec![t1, t2])) +} + lazy_static! { pub static ref PUSH: Instruction = instruction!(push, |simulator: Simulator, input: InputTarget| { - let ws = simulator.get_word_size().clone(); - let data = input.get(simulator)?; - let sp = simulator - .get_registers_mut() - .get_register_mut(®istry::SP.into())? - .get_data() - .int_value() - - ws.value() as i64; - simulator - .get_registers_mut() - .get_register_mut(®istry::SP.into())? - .set_data(RawData::from_int(sp, &ws)); - simulator.get_memory_mut().write(sp as usize, &data) + consecutive_push(simulator, input, 0) }); pub static ref POP: Instruction = instruction!(pop, |simulator: Simulator, output: InputOutputTarget| { - let ws = simulator.get_word_size().clone(); - let wsv = ws.value() as i64; - let sp = simulator - .get_registers_mut() - .get_register_mut(®istry::SP.into())? - .get_data() - .int_value(); - output.set(simulator, simulator.get_memory().read(sp as usize)?)?; - simulator - .get_registers_mut() - .get_register_mut(®istry::SP.into())? - .set_data(RawData::from_int(sp + wsv, &ws)); - Ok(()) + consecutive_pop(simulator, output, 0) }); pub static ref LOAD: Instruction = instruction!(load, |simulator: Simulator, @@ -50,7 +86,10 @@ lazy_static! { input: InputTarget| { let memory = simulator.get_memory(); let word = memory.read(input.get(simulator)?.int_value() as usize)?; - output.set(simulator, word) + let out_transformable = Transformable::InputOutputTransformable(output); + Ok(TransformationSequence::new_single( + out_transformable.create_transformation(simulator, input.get(simulator)?)?, + )) }); pub static ref STORE: Instruction = instruction!(store, |simulator: Simulator, @@ -58,25 +97,36 @@ lazy_static! { input2: InputTarget| { let address = input2.get(simulator)?.int_value() as usize; let data = input1.get(simulator)?; - let memory = simulator.get_memory_mut(); - memory.write(address, &data) + let transformation = Transformable::MemoryTransformable(address) + .create_transformation(simulator, data)?; + Ok(TransformationSequence::new_single(transformation)) }); pub static ref ALLOC: Instruction = instruction!(alloc, |simulator: Simulator, output: InputOutputTarget, input: InputTarget| { - let offset = input.get(simulator)?.int_value() as usize; - let memory = simulator.get_memory_mut(); - let heap_pointer = memory.current_heap_pointer(); - memory.set_heap_pointer(heap_pointer + offset)?; - let bytes = RawData::from_int(heap_pointer as i64, simulator.get_word_size()); - output.set(simulator, bytes) + let heap_pointer_transformable = Transformable::HeapPointerTransformable; + let output_transformable = Transformable::InputOutputTransformable(output); + let t1 = Transformation::new( + heap_pointer_transformable, + heap_pointer_transformable.get(simulator)?, + RawData::from_int( + heap_pointer_transformable.get(simulator)?.int_value() + + input.get(simulator)?.int_value(), + simulator.get_word_size(), + ), + ); + let t2 = + output_transformable.create_transformation(simulator, t1.get_from().clone())?; + Ok(TransformationSequence::new(vec![t1, t2])) }); pub static ref MOVE: Instruction = instruction!(_move, |simulator: Simulator, output: InputOutputTarget, input: InputTarget| { - output.set(simulator, input.get(simulator)?) + let output_transformable = Transformable::InputOutputTransformable(output) + .create_transformation(simulator, input.get(simulator)?)?; + Ok(TransformationSequence::new_single(output_transformable)) }); } diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/mod.rs b/rezasm-source/rezasm-core/src/instructions/implementation/mod.rs index 6d3fb64..3a88c79 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/mod.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/mod.rs @@ -4,6 +4,7 @@ pub mod comparison_instructions; pub mod float_arithmetic_instructions; pub mod function_instructions; pub mod memory_instructions; +pub mod terminal_input_instructions; pub mod terminal_output_instructions; pub fn register_instructions() { @@ -11,6 +12,7 @@ pub fn register_instructions() { branch_instructions::register_instructions(); comparison_instructions::register_instructions(); float_arithmetic_instructions::register_instructions(); + terminal_input_instructions::register_instructions(); function_instructions::register_instructions(); memory_instructions::register_instructions(); terminal_output_instructions::register_instructions(); diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/terminal_input_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/terminal_input_instructions.rs new file mode 100644 index 0000000..c19d5d7 --- /dev/null +++ b/rezasm-source/rezasm-core/src/instructions/implementation/terminal_input_instructions.rs @@ -0,0 +1,224 @@ +use crate::instruction; +use crate::instructions::instruction::Instruction; +use crate::instructions::instruction_registry::register_instruction; +use crate::instructions::targets::input_target::InputTarget; +use crate::instructions::targets::{input_output_target::InputOutputTarget, input_target::Input}; +use crate::simulation::reader::Reader; +use crate::simulation::transform::transformable::Transformable; +use crate::simulation::transform::transformation_sequence::TransformationSequence; +use crate::util::error::IoError; +use crate::util::raw_data::RawData; +use lazy_static::lazy_static; + +lazy_static! { + + /// Definition of the `readi` instruction, used to read an integer + pub static ref READI: Instruction = + instruction!(readi, |simulator: Simulator, output: InputOutputTarget| { + let scanner = simulator.get_scanner_mut(); + scanner.skip_whitespaces()?; + + let Some(num) = scanner.next_i64()? else { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let word_size = simulator.get_word_size(); + + let data = RawData::from_int(num, word_size); + let transformation = Transformable::InputOutputTransformable(output) + .create_transformation(simulator, data)?; + + Ok(TransformationSequence::new_single(transformation)) + }); + + pub static ref READF: Instruction = + instruction!(readf, |simulator: Simulator, output: InputOutputTarget| { + let scanner = simulator.get_scanner_mut(); + scanner.skip_whitespaces()?; + + let Some(num) = scanner.next_f64()? else { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let word_size = simulator.get_word_size(); + + let data = RawData::from_float(num, word_size); + let transformation = Transformable::InputOutputTransformable(output) + .create_transformation(simulator, data)?; + + Ok(TransformationSequence::new_single(transformation)) + }); + + pub static ref READC: Instruction = + instruction!(readc, |simulator: Simulator, output: InputOutputTarget| { + let scanner = simulator.get_scanner_mut(); + scanner.skip_whitespaces()?; + + let Some(ch) = scanner.next_char()? else { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let word_size = simulator.get_word_size(); + + let data = RawData::from_int(ch as i64, word_size); + let transformation = Transformable::InputOutputTransformable(output) + .create_transformation(simulator, data)?; + + Ok(TransformationSequence::new_single(transformation)) + }); + + pub static ref READS: Instruction = instruction!( + reads, + |simulator: Simulator, input1: InputOutputTarget, input2: InputTarget| { + let len = input2.get(simulator)?.int_value() as usize; + if len == 1 { + return Ok(TransformationSequence::new_empty()); + } + let mut bytes = vec![0u8; len - 1]; + let read_count = read_to_sized(simulator.get_reader_mut(), &mut bytes, |c| { + c.is_ascii_whitespace() + }).map_err(IoError::from)?; + + if read_count == 0 { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let mut words = pad_bytes(&bytes[0..read_count], simulator.get_word_size().value()); + words.append(&mut vec![0u8; 4]); + + let address = input1.get(simulator)?.int_value() as usize; + let word_size = simulator.get_word_size().value(); + + let transformation = Transformable::MemoryTransformable(address) + .create_transformation(simulator, RawData::new(&words))?; + + Ok(TransformationSequence::new_single(transformation)) + } + ); + + pub static ref READS_UNSIZED: Instruction = + instruction!(reads, |simulator: Simulator, input1: InputOutputTarget| { + let scanner = simulator.get_scanner_mut(); + scanner.skip_whitespaces()?; + + let Some(input) = scanner.next()? else { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let words = pad_bytes(input.as_bytes(), simulator.get_word_size().value()); + + let address = input1.get(simulator)?.int_value() as usize; + let word_size = simulator.get_word_size().value(); + + let transformation = Transformable::MemoryTransformable(address) + .create_transformation(simulator, RawData::new(&words))?; + + Ok(TransformationSequence::new_single(transformation)) + }); + + pub static ref READLN: Instruction = instruction!( + readln, + |simulator: Simulator, input1: InputOutputTarget, input2: InputTarget| { + let len = input2.get(simulator)?.int_value() as usize; + if len == 1 { + return Ok(TransformationSequence::new_empty()); + } + let mut bytes = vec![0u8; len - 1]; + let read_count = read_to_sized(simulator.get_reader_mut(), &mut bytes, |c| { + *c == '\n' as u8 + }).map_err(IoError::from)?; + + if read_count == 0 { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let mut words = pad_bytes(&bytes[0..read_count], simulator.get_word_size().value()); + words.append(&mut vec![0u8; 4]); + + let address = input1.get(simulator)?.int_value() as usize; + let word_size = simulator.get_word_size().value(); + + let transformation = Transformable::MemoryTransformable(address) + .create_transformation(simulator, RawData::new(&words))?; + + Ok(TransformationSequence::new_single(transformation)) + } + ); + + pub static ref READLN_UNSIZED: Instruction = + instruction!(readln, |simulator: Simulator, input1: InputOutputTarget| { + let scanner = simulator.get_scanner_mut(); + + let Some(input) = scanner.next_line()? else { + return Ok(TransformationSequence::new_nullop(simulator)?); + }; + + let mut words = pad_bytes(input.as_bytes(), simulator.get_word_size().value()); + words.append(&mut vec![0u8; 4]); + + let address = input1.get(simulator)?.int_value() as usize; + let word_size = simulator.get_word_size().value(); + + let transformation = Transformable::MemoryTransformable(address) + .create_transformation(simulator, RawData::new(&words))?; + + Ok(TransformationSequence::new_single(transformation)) + }); +} + +fn pad_bytes(bytes: &[u8], word_size: usize) -> Vec { + let pad_buffer = vec![0u8; word_size - 1]; + bytes + .iter() + .map(|byte| { + let mut new_bytes = pad_buffer.clone(); + new_bytes.push(*byte); + return new_bytes; + }) + .flatten() + .collect() +} + +/// Uses a boxed custom reader to read until whitespace or a size is reached. +/// +/// # Arguments +/// +/// * `reader` - the boxed reader used to get input. +/// * `target` - the buffer to which to read. +/// +/// # Returns +/// +/// * `()` - if the read works. +/// * `io::Error` - if the read fails for some reason. +fn read_to_sized( + reader: &mut R, + target: &mut [u8], + terminator: fn(&u8) -> bool, +) -> std::io::Result { + let mut buf = [0u8]; + let len = target.len(); + for idx in 0usize..len { + if reader.read(&mut buf)? == 0usize { + buf[0] = 0u8; + } + + if terminator(&buf[0]) || buf[0] == 0u8 { + return Ok(idx); + } + + target[idx] = buf[0]; + } + + Ok(len) +} + +/// Registers the instructions found in this file +pub fn register_instructions() { + register_instruction(&READI); + register_instruction(&READF); + register_instruction(&READC); + register_instruction(&READS); + register_instruction(&READS_UNSIZED); + register_instruction(&READLN); + register_instruction(&READLN_UNSIZED); +} diff --git a/rezasm-source/rezasm-core/src/instructions/implementation/terminal_output_instructions.rs b/rezasm-source/rezasm-core/src/instructions/implementation/terminal_output_instructions.rs index 45d3a00..149cfa6 100644 --- a/rezasm-source/rezasm-core/src/instructions/implementation/terminal_output_instructions.rs +++ b/rezasm-source/rezasm-core/src/instructions/implementation/terminal_output_instructions.rs @@ -6,6 +6,7 @@ use crate::instructions::instruction_registry::register_instruction; use crate::instructions::targets::input_target::Input; use crate::instructions::targets::input_target::InputTarget; +use crate::simulation::transform::transformation_sequence::TransformationSequence; use crate::simulation::writer::WriterBox; use crate::util::error::IoError; @@ -15,21 +16,21 @@ lazy_static! { let value = input.get(&simulator)?.int_value(); let output = format!("{}", value); write(simulator.get_writer_mut(), &output)?; - Ok(()) + Ok(TransformationSequence::new_empty()) }); pub static ref PRINTF: Instruction = instruction!(printf, |simulator: Simulator, input: InputTarget| { let value = input.get(&simulator)?.float_value(); let output = format!("{}", value); write(simulator.get_writer_mut(), &output)?; - Ok(()) + Ok(TransformationSequence::new_empty()) }); pub static ref PRINTC: Instruction = instruction!(printc, |simulator: Simulator, input: InputTarget| { let value = input.get(&simulator)?.int_value(); let output = format!("{}", value as u8 as char); write(simulator.get_writer_mut(), &output)?; - Ok(()) + Ok(TransformationSequence::new_empty()) }); pub static ref PRINTS_SIZED: Instruction = instruction!(prints, |simulator: Simulator, @@ -41,14 +42,14 @@ lazy_static! { .get_memory() .get_string_sized(address as usize, size as usize)?; write(simulator.get_writer_mut(), &output)?; - Ok(()) + Ok(TransformationSequence::new_empty()) }); pub static ref PRINTS: Instruction = instruction!(prints, |simulator: Simulator, input: InputTarget| { let address = input.get(&simulator)?.int_value(); let output = simulator.get_memory().get_string(address as usize)?; write(simulator.get_writer_mut(), &output)?; - Ok(()) + Ok(TransformationSequence::new_empty()) }); } diff --git a/rezasm-source/rezasm-core/src/instructions/instruction.rs b/rezasm-source/rezasm-core/src/instructions/instruction.rs index 1d12049..31407ed 100644 --- a/rezasm-source/rezasm-core/src/instructions/instruction.rs +++ b/rezasm-source/rezasm-core/src/instructions/instruction.rs @@ -3,10 +3,14 @@ use std::fmt::{Debug, Formatter}; use crate::instructions::argument_type::ArgumentType; use crate::simulation::simulator::Simulator; +use crate::simulation::transform::transformation_sequence::TransformationSequence; use crate::util::error::SimulatorError; -pub type TInstructionFunction = - fn(&mut Simulator, &Vec, &Vec) -> Result<(), SimulatorError>; +pub type TInstructionFunction = fn( + &mut Simulator, + &Vec, + &Vec, +) -> Result; #[derive(Clone)] pub struct Instruction { @@ -45,7 +49,7 @@ impl Instruction { &self, simulator: &mut Simulator, arguments: &Vec, - ) -> Result<(), SimulatorError> { + ) -> Result { (self.function)(simulator, &self.types, arguments) } } @@ -57,7 +61,7 @@ macro_rules! instruction { #[allow(unused_mut)] let mut v: Vec = Vec::new(); $(v.push(std::any::TypeId::of::<&mut $types>());)* - fn $name($simulator_name: &mut crate::simulation::simulator::Simulator, types: &Vec, arguments: &Vec) -> Result<(), crate::util::error::SimulatorError> { + fn $name($simulator_name: &mut crate::simulation::simulator::Simulator, types: &Vec, arguments: &Vec) -> Result { let mut _counter: usize = 0; $( #[allow(unused_mut)] @@ -67,6 +71,7 @@ macro_rules! instruction { }; _counter = _counter + 1; )* + #[allow(unused_braces)] $func } let mut instruction_name = std::stringify!($name); diff --git a/rezasm-source/rezasm-core/src/instructions/instruction_registry.rs b/rezasm-source/rezasm-core/src/instructions/instruction_registry.rs index 2a141df..06c9356 100644 --- a/rezasm-source/rezasm-core/src/instructions/instruction_registry.rs +++ b/rezasm-source/rezasm-core/src/instructions/instruction_registry.rs @@ -54,6 +54,11 @@ lazy_static! { static ref INSTRUCTIONS: Mutex = Mutex::new(InstructionRegistry::new()); } +/// Registers an instruction with the static INSTRUCTIONS +/// +/// # Arguments +/// +/// * instruction: the static-lifetime instruction to register pub fn register_instruction(instruction: &'static Instruction) { INSTRUCTIONS .lock() diff --git a/rezasm-source/rezasm-core/src/simulation/mod.rs b/rezasm-source/rezasm-core/src/simulation/mod.rs index 78c87ef..b61f1f5 100644 --- a/rezasm-source/rezasm-core/src/simulation/mod.rs +++ b/rezasm-source/rezasm-core/src/simulation/mod.rs @@ -1,5 +1,7 @@ pub mod memory; pub mod program; +pub mod reader; +pub mod reader_cell; pub mod register; pub mod registry; pub mod simulator; diff --git a/rezasm-source/rezasm-core/src/simulation/reader.rs b/rezasm-source/rezasm-core/src/simulation/reader.rs new file mode 100644 index 0000000..cf7e268 --- /dev/null +++ b/rezasm-source/rezasm-core/src/simulation/reader.rs @@ -0,0 +1,48 @@ +use crate::util::as_any::AsAny; +use std::any::Any; +use std::fmt::Debug; +use std::io; + +/// A trait for any readers used with EzASM +pub trait Reader: io::Read + io::Write + AsAny + Sync + Send + Debug {} + +/// Placeholder reader that the GUI is created with, which should be replaced by said GUI during +/// its initialization. +/// +/// HACK: in reality, the GUI should just be initialized with the correct reader +#[derive(Debug)] +pub struct DummyReader {} + +impl DummyReader { + pub fn new() -> DummyReader { + DummyReader {} + } +} + +impl Reader for DummyReader {} + +impl AsAny for DummyReader { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +impl io::Read for DummyReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + Ok(0usize) + } +} + +impl io::Write for DummyReader { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(0) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/rezasm-source/rezasm-core/src/simulation/reader_cell.rs b/rezasm-source/rezasm-core/src/simulation/reader_cell.rs new file mode 100644 index 0000000..072b222 --- /dev/null +++ b/rezasm-source/rezasm-core/src/simulation/reader_cell.rs @@ -0,0 +1,83 @@ +use std::cell::RefCell; +use std::io::{self, Read, Write}; +use std::rc::Rc; + +use scanner_rust::ScannerAscii; + +use crate::util::as_any::AsAny; + +use super::reader::Reader; + +/// ASCII Scanner of a reader cell. +pub type Scanner = ScannerAscii; + +/// Structure for a reference-counted pointer to a `Reader` with interior mutability. +/// +/// Essentially, this means that multiple structures/variables can create mutable references to the +/// enclosed `Reader`. +/// +/// # Trait Implementations +/// +/// * `Reader` - This structure passes through the implementation of the enclosed `Reader` trait, +/// by providing the following implementations: +/// * `io::Read` - for consuming part of the reader's buffer. +/// * `AsAny` +/// * `Debug` +/// * `Send` +/// * `Sync` +/// +/// # Panics +/// +/// * When a mutable reference to the interior `Reader` already exists, yet another one is +/// requested, the program must panic in order to preserve Rust's memory safety guarantees. +#[derive(Debug)] +pub struct ReaderCell(Rc>); + +impl ReaderCell { + /// Creates a new reader cell from a reader. + /// + /// # Examples + /// + /// ``` + /// let reader_cell = ReaderCell::new(std::io::stdin()); + /// ``` + pub fn new(reader: R) -> Self { + Self(Rc::new(RefCell::new(reader))) + } +} + +impl Reader for ReaderCell {} +unsafe impl Send for ReaderCell {} +unsafe impl Sync for ReaderCell {} + +impl Clone for ReaderCell { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Read for ReaderCell { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (*self.0.borrow_mut()).read(buf) + } +} + +impl Write for ReaderCell { + fn write(&mut self, buf: &[u8]) -> io::Result { + (*self.0.borrow_mut()).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + (*self.0.borrow_mut()).flush() + } +} + +impl AsAny for ReaderCell { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} diff --git a/rezasm-source/rezasm-core/src/simulation/registry.rs b/rezasm-source/rezasm-core/src/simulation/registry.rs index 8681baa..434b739 100644 --- a/rezasm-source/rezasm-core/src/simulation/registry.rs +++ b/rezasm-source/rezasm-core/src/simulation/registry.rs @@ -11,9 +11,11 @@ use crate::util::word_size::WordSize; const REGISTERS_COUNT: usize = 54; pub const ZERO_NUMBER: usize = 0; +pub const FID_NUMBER: usize = 2; pub const PC_NUMBER: usize = 3; pub const SP_NUMBER: usize = 4; -pub const FID_NUMBER: usize = 2; +pub const RA_NUMBER: usize = 5; +pub const R0_NUMBER: usize = 9; lazy_static! { pub static ref REGISTERS_MAP: BiMap = { diff --git a/rezasm-source/rezasm-core/src/simulation/simulator.rs b/rezasm-source/rezasm-core/src/simulation/simulator.rs index 2825916..4c37dc1 100644 --- a/rezasm-source/rezasm-core/src/simulation/simulator.rs +++ b/rezasm-source/rezasm-core/src/simulation/simulator.rs @@ -1,12 +1,19 @@ use std::fmt::Debug; +use scanner_rust::ScannerAscii; + +use super::reader::DummyReader; +use super::reader_cell::{ReaderCell, Scanner}; +use super::transform::transformable::Transformable; +use super::transform::transformation_sequence::TransformationSequence; +use crate::instructions::targets::input_output_target::InputOutputTarget; use crate::parser::line::Line; use crate::simulation::memory; use crate::simulation::memory::Memory; use crate::simulation::program::Program; use crate::simulation::registry; use crate::simulation::registry::Registry; -use crate::simulation::writer::{DummyWriter, Writer, WriterBox}; +use crate::simulation::writer::{DummyWriter, WriterBox}; use crate::util::error::SimulatorError; use crate::util::raw_data::RawData; use crate::util::word_size::{WordSize, DEFAULT_WORD_SIZE}; @@ -17,7 +24,11 @@ pub struct Simulator { registry: Registry, program: Program, word_size: WordSize, + scanner: Scanner, + reader: ReaderCell, writer: WriterBox, + sequence: Vec, + can_undo: bool, } impl Simulator { @@ -25,25 +36,36 @@ impl Simulator { Simulator::new_custom( &DEFAULT_WORD_SIZE, memory::DEFAULT_MEMORY_WORDS, + ReaderCell::new(DummyReader::new()), Box::new(DummyWriter::new()), ) } - pub fn new_writer(writer: Box) -> Simulator { - Simulator::new_custom(&DEFAULT_WORD_SIZE, memory::DEFAULT_MEMORY_WORDS, writer) + pub fn new_custom_reader_writer(reader: ReaderCell, writer: WriterBox) -> Simulator { + Simulator::new_custom( + &DEFAULT_WORD_SIZE, + memory::DEFAULT_MEMORY_WORDS, + reader, + writer, + ) } pub fn new_custom( word_size: &WordSize, memory_size: usize, - writer: Box, + reader: ReaderCell, + writer: WriterBox, ) -> Simulator { let mut sim = Simulator { memory: Memory::new_sized(word_size, memory_size), registry: Registry::new(word_size), program: Program::new(), word_size: word_size.clone(), + scanner: ScannerAscii::new(reader.clone()), + reader, writer, + sequence: Vec::new(), + can_undo: true, }; sim.initialize(); sim @@ -62,6 +84,7 @@ impl Simulator { pub fn reset_data(&mut self) { self.memory.reset(); self.registry.reset(); + self.sequence.clear(); } pub fn reset(&mut self) { @@ -99,6 +122,14 @@ impl Simulator { &self.program } + pub fn get_scanner_mut(&mut self) -> &mut Scanner { + &mut self.scanner + } + + pub fn get_reader_cell(&self) -> ReaderCell { + self.reader.clone() + } + pub fn get_writer(&self) -> &WriterBox { &self.writer } @@ -119,12 +150,12 @@ impl Simulator { &mut self.program } - pub fn get_writer_mut(&mut self) -> &mut WriterBox { - &mut self.writer + pub fn get_reader_mut(&mut self) -> &mut ReaderCell { + &mut self.reader } - pub fn set_writer(&mut self, writer: WriterBox) { - self.writer = writer; + pub fn get_writer_mut(&mut self) -> &mut WriterBox { + &mut self.writer } pub fn end_pc(&self) -> usize { @@ -172,18 +203,17 @@ impl Simulator { fn run_line(&mut self, line: &Line) -> Result<(), SimulatorError> { let result = match line { Line::Instruction(instruction, args) => { - instruction.get_function()(self, instruction.get_types(), &args) + instruction.get_function()(self, instruction.get_types(), &args)? } Line::Label(label) => { // no-op - Ok(()) + TransformationSequence::new_empty() } }; - let new_pc = self.registry.get_pc().get_data().int_value() + 1; - self.registry - .get_pc_mut() - .set_data(RawData::from_int(new_pc, &self.word_size)); - result + if result.contains_nullop() { + return Ok(()); + } + self.apply_transformation(result) } pub fn run_line_from_pc(&mut self) -> Result<(), SimulatorError> { @@ -201,8 +231,38 @@ impl Simulator { self.run_line(&line.clone()) } - pub fn apply_transformation(&self) -> Result<(), SimulatorError> { - todo!() + pub fn apply_transformation( + &mut self, + mut transform: TransformationSequence, + ) -> Result<(), SimulatorError> { + transform.apply(self)?; + let pc_transformable = Transformable::InputOutputTransformable( + InputOutputTarget::RegisterInputOutput(registry::PC_NUMBER), + ); + let pc_transformation = pc_transformable.create_transformation( + self, + RawData::from_int(pc_transformable.get(self)?.int_value() + 1, &self.word_size), + )?; + pc_transformation.apply(self)?; + + if self.can_undo { + transform.concatenate(TransformationSequence::new_single(pc_transformation)); + self.sequence.push(transform.clone()); + if transform.contains_nullop() { + return Ok(()); + } + } + Ok(()) + } + + pub fn undo_last_transformation(&mut self) -> Result { + if !self.can_undo || self.sequence.is_empty() { + Ok(false) + } else { + // unwrap is safe because emptiness is checked + self.sequence.pop().unwrap().invert().apply(self)?; + Ok(true) + } } pub fn get_label_line_number(&self, label: &String) -> Result { diff --git a/rezasm-source/rezasm-core/src/simulation/transform/transformable.rs b/rezasm-source/rezasm-core/src/simulation/transform/transformable.rs index 990ca46..2d8232e 100644 --- a/rezasm-source/rezasm-core/src/simulation/transform/transformable.rs +++ b/rezasm-source/rezasm-core/src/simulation/transform/transformable.rs @@ -5,12 +5,16 @@ use crate::simulation::simulator::Simulator; use crate::util::error::SimulatorError; use crate::util::raw_data::RawData; -#[derive(Copy)] +use super::transformation::Transformation; + +/// NullOpTransformable is primarily for signalling the simulator to enter AWAITING +#[derive(Copy, Debug)] pub enum Transformable { FileReadTransformable(i64), HeapPointerTransformable, MemoryTransformable(usize), InputOutputTransformable(InputOutputTarget), + NullOpTransformable, } impl Transformable { @@ -27,9 +31,13 @@ impl Transformable { Transformable::FileReadTransformable(cursor) => { Ok(RawData::from_int(cursor.clone(), simulator.get_word_size())) } + Transformable::NullOpTransformable => { + Ok(RawData::empty_data(simulator.get_word_size())) + } } } - pub fn set(&mut self, data: RawData, simulator: &mut Simulator) -> Result<(), SimulatorError> { + + pub fn set(&self, data: RawData, simulator: &mut Simulator) -> Result<(), SimulatorError> { match self { Transformable::InputOutputTransformable(input_output) => { input_output.set(simulator, data) @@ -40,7 +48,27 @@ impl Transformable { Transformable::MemoryTransformable(address) => { simulator.get_memory_mut().write(address.clone(), &data) } - Transformable::FileReadTransformable(cursor) => todo!(), //must be todo until read instructions are done + Transformable::FileReadTransformable(cursor) => todo!(), + Transformable::NullOpTransformable => Ok(()), + } + } + + pub fn create_transformation( + &self, + simulator: &Simulator, + output: RawData, + ) -> Result { + Ok(Transformation::new( + self.clone(), + self.get(simulator)?, + output, + )) + } + + pub fn is_nullop(&self) -> bool { + match self { + Transformable::NullOpTransformable => true, + _ => false, } } } @@ -58,6 +86,7 @@ impl Clone for Transformable { Transformable::FileReadTransformable(cursor) => { Transformable::FileReadTransformable(cursor.clone()) } + Transformable::NullOpTransformable => Transformable::NullOpTransformable, } } } diff --git a/rezasm-source/rezasm-core/src/simulation/transform/transformation.rs b/rezasm-source/rezasm-core/src/simulation/transform/transformation.rs index 10820e4..e407308 100644 --- a/rezasm-source/rezasm-core/src/simulation/transform/transformation.rs +++ b/rezasm-source/rezasm-core/src/simulation/transform/transformation.rs @@ -5,6 +5,7 @@ use crate::{ use super::transformable::Transformable; +#[derive(Debug)] pub struct Transformation { output: Transformable, from: RawData, @@ -12,6 +13,17 @@ pub struct Transformation { } impl Transformation { + pub fn get_to(&self) -> &RawData { + &self.to + } + + pub fn get_from(&self) -> &RawData { + &self.from + } + + pub fn new(output: Transformable, from: RawData, to: RawData) -> Transformation { + Transformation { output, from, to } + } pub fn invert(&mut self) -> Transformation { Transformation { output: self.output.clone(), @@ -20,9 +32,13 @@ impl Transformation { } } - pub fn apply(&mut self, simulator: &mut Simulator) -> Result<(), SimulatorError> { + pub fn apply(&self, simulator: &mut Simulator) -> Result<(), SimulatorError> { self.output.set(self.to.clone(), simulator) } + + pub fn is_nullop(&self) -> bool { + self.output.is_nullop() + } } impl Clone for Transformation { diff --git a/rezasm-source/rezasm-core/src/simulation/transform/transformation_sequence.rs b/rezasm-source/rezasm-core/src/simulation/transform/transformation_sequence.rs index fd1ee6a..34e566d 100644 --- a/rezasm-source/rezasm-core/src/simulation/transform/transformation_sequence.rs +++ b/rezasm-source/rezasm-core/src/simulation/transform/transformation_sequence.rs @@ -1,12 +1,22 @@ -use crate::{simulation::simulator::Simulator, util::error::SimulatorError}; +use crate::{ + simulation::simulator::Simulator, + util::{error::SimulatorError, raw_data::RawData}, +}; -use super::transformation::Transformation; +use super::{transformable::Transformable, transformation::Transformation}; +#[derive(Debug, Clone)] pub struct TransformationSequence { transformations: Vec, } impl TransformationSequence { + pub fn new_single(transformation: Transformation) -> TransformationSequence { + TransformationSequence { + transformations: vec![transformation], + } + } + pub fn new(transformations: Vec) -> TransformationSequence { TransformationSequence { transformations } } @@ -17,6 +27,14 @@ impl TransformationSequence { } } + pub fn new_nullop(simulator: &Simulator) -> Result { + let word_size = simulator.get_word_size(); + let data = RawData::empty_data(word_size); + let transformation = + Transformable::NullOpTransformable.create_transformation(simulator, data)?; + Ok(TransformationSequence::new_single(transformation)) + } + pub fn concatenate(&mut self, other: TransformationSequence) { self.transformations.extend(other.transformations.clone()) } @@ -33,8 +51,17 @@ impl TransformationSequence { } } - pub fn apply(&mut self, simulator: &mut Simulator) -> Result<(), SimulatorError> { - for transformation in &mut self.transformations { + pub fn contains_nullop(&self) -> bool { + for t in &self.transformations { + if t.is_nullop() { + return true; + } + } + false + } + + pub fn apply(&self, simulator: &mut Simulator) -> Result<(), SimulatorError> { + for transformation in &self.transformations { transformation.apply(simulator)? } Ok(()) diff --git a/rezasm-source/rezasm-core/src/util/error.rs b/rezasm-source/rezasm-core/src/util/error.rs index 9674240..4ae73ad 100644 --- a/rezasm-source/rezasm-core/src/util/error.rs +++ b/rezasm-source/rezasm-core/src/util/error.rs @@ -1,3 +1,5 @@ +use scanner_rust::ScannerError; +use std::char::ParseCharError; use std::num::{ParseFloatError, ParseIntError}; use std::process; use thiserror::Error; @@ -69,6 +71,9 @@ pub enum InternalError { #[error("improper usage of get_input_output_target")] GetInputOutputTargetError, + + #[error("null op on transformation sequence")] + NullOpError, } #[derive(Error, Debug)] @@ -132,6 +137,9 @@ pub enum SimulatorError { #[error("attempted to convert NaN value to an integer")] NaNConversionError, + + #[error("could not read type {0}")] + ReadError(String), } #[derive(Error, Debug)] @@ -157,6 +165,9 @@ pub enum IoError { #[error("some bytes are not UTF-8 in the input file")] UnsupportedEncodingError, + #[error("read operation failed")] + ReadError, + #[error("write operation failed")] WriteError, @@ -176,6 +187,18 @@ impl From for ParserError { } } +impl From for ParserError { + fn from(error: ParseCharError) -> Self { + ParserError::StringImmediateError(error.to_string()) + } +} + +impl From for SimulatorError { + fn from(err: ScannerError) -> Self { + SimulatorError::IoError(IoError::ScannerError(err)) + } +} + pub fn handle_error(error: EzasmError) -> ! { println!("{}", error); process::exit(1); diff --git a/rezasm-source/rezasm-web-core/src/lib.rs b/rezasm-source/rezasm-web-core/src/lib.rs index 650cca5..ce4fbed 100644 --- a/rezasm-source/rezasm-web-core/src/lib.rs +++ b/rezasm-source/rezasm-web-core/src/lib.rs @@ -1,4 +1,5 @@ use rezasm_core::parser::lexer; +use rezasm_core::simulation::reader_cell::ReaderCell; use rezasm_core::simulation::registry; use rezasm_core::simulation::simulator::Simulator; @@ -6,23 +7,35 @@ use rezasm_core::simulation::writer::WriterBox; use std::string::ToString; use std::sync::{OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard}; -fn internal_simulator() -> &'static RwLock { +fn internal_simulator( + reader: Option, + writer: Option, +) -> &'static RwLock { static SIMULATOR: OnceLock> = OnceLock::new(); - SIMULATOR.get_or_init(|| RwLock::new(Simulator::new())) + if reader.is_some() && writer.is_some() { + SIMULATOR.get_or_init(|| { + RwLock::new(Simulator::new_custom_reader_writer( + reader.unwrap(), + writer.unwrap(), + )) + }) + } else { + SIMULATOR.get_or_init(|| RwLock::new(Simulator::new())) + } +} + +pub fn initialize_simulator(reader: Option, writer: Option) { + internal_simulator(reader, writer); } type SimulatorRef = RwLockReadGuard<'static, Simulator>; pub fn get_simulator() -> SimulatorRef { - internal_simulator().read().unwrap() + internal_simulator(None, None).read().unwrap() } type SimulatorMutRef = RwLockWriteGuard<'static, Simulator>; pub fn get_simulator_mut() -> SimulatorMutRef { - internal_simulator().write().unwrap() -} - -pub fn register_writer(writer: WriterBox) { - get_simulator_mut().set_writer(writer); + internal_simulator(None, None).write().unwrap() } pub fn stop() { @@ -76,6 +89,13 @@ pub fn step() -> Result<(), String> { } } +pub fn step_back() -> Result<(), String> { + match get_simulator_mut().undo_last_transformation() { + Ok(_) => Ok(()), + Err(error) => return Err(format!("Program error: {}", error)), + } +} + pub fn is_completed() -> bool { get_simulator().is_done() } @@ -145,7 +165,3 @@ pub fn get_memory_slice(address: usize, length: usize) -> Result, Strin pub fn get_word_size() -> usize { get_simulator().get_word_size().value() } - -pub fn receive_input(data: &str) { - println!("{}", data); -} diff --git a/src/components/Code.jsx b/src/components/Code.jsx index af3215f..d6afab6 100644 --- a/src/components/Code.jsx +++ b/src/components/Code.jsx @@ -22,6 +22,7 @@ function Code() { start, stop, step, + stepBack, load, reset, } = useSimulator(); diff --git a/src/components/Console.jsx b/src/components/Console.jsx index fd1f5c0..56f6152 100644 --- a/src/components/Console.jsx +++ b/src/components/Console.jsx @@ -1,7 +1,8 @@ import React, {useCallback, useEffect, useReducer, useRef, useState} from "react"; import {listen} from "@tauri-apps/api/event"; -import {CALLBACKS_TRIGGERS, CALLBACK_TYPES} from "./simulator.js"; +import {CALLBACKS_TRIGGERS, CALLBACK_TYPES, STATE} from "./simulator.js"; import {RUST} from "../rust_functions.js"; +import { debounce } from "lodash"; const ENTER = 13; diff --git a/src/components/Controls.jsx b/src/components/Controls.jsx index 17ffe61..d6b4567 100644 --- a/src/components/Controls.jsx +++ b/src/components/Controls.jsx @@ -5,7 +5,7 @@ import _ from "lodash"; const debounce = _.debounce((func) => func(), 250, {leading: true, trailing: false, maxWait: 250}); -function Controls({state, setState, start, stop, step, reset, load, error}) { +function Controls({state, setState, start, stop, step, stepBack, reset, load, error}) { const isErrorState = error.current !== ""; return ( @@ -60,7 +60,7 @@ function Controls({state, setState, start, stop, step, reset, load, error}) { diff --git a/src/components/simulator.js b/src/components/simulator.js index 9764667..20be245 100644 --- a/src/components/simulator.js +++ b/src/components/simulator.js @@ -137,6 +137,22 @@ export const useSimulator = () => { } }, [checkProgramCompletion, handleStepCall, load, reset, state]); + const stepBack = useCallback(async () => { + if (state.current > STATE.RUNNING || state.current == STATE.AWAITING) { + console.log(state.current); + RUST.STEP_BACK({}) + .catch((error) => { + setError(error); + setState(STATE.STOPPED) + }) + .finally(() => { + callStepCallbacks(); + }) + + } + } + , [setError, setState, callStepCallbacks]) + const recursiveStep = useCallback(async () => { if (state.current === STATE.STOPPED) { return; @@ -170,6 +186,7 @@ export const useSimulator = () => { start, stop, step, + stepBack, load, reset, }; diff --git a/src/rust_functions.js b/src/rust_functions.js index 641b02d..45b4139 100644 --- a/src/rust_functions.js +++ b/src/rust_functions.js @@ -56,6 +56,7 @@ const get_rust_function = (name, shape) => { const RUST = { LOAD: get_rust_function("load", ["lines"]), STEP: get_rust_function("step"), + STEP_BACK: get_rust_function("step_back"), RESET: get_rust_function("reset"), STOP: get_rust_function("stop"), IS_COMPLETED: get_rust_function("is_completed"), diff --git a/tests/common/mod.rs b/tests/common/mod.rs index d3baa81..c9134a0 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1 +1,2 @@ +pub mod reader; pub mod writer; diff --git a/tests/common/reader.rs b/tests/common/reader.rs new file mode 100644 index 0000000..3805a2f --- /dev/null +++ b/tests/common/reader.rs @@ -0,0 +1,46 @@ +use std::collections::VecDeque; +use std::io::{Read, Write}; + +use rezasm_core::simulation::reader::Reader; +use rezasm_core::util::as_any::AsAny; + +#[derive(Debug)] +pub struct TestReader { + buffer: VecDeque, +} + +impl TestReader { + pub fn new() -> TestReader { + TestReader { + buffer: VecDeque::new(), + } + } +} + +impl Reader for TestReader {} + +impl Read for TestReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.buffer.read(buf) + } +} + +impl Write for TestReader { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buffer.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.buffer.flush() + } +} + +impl AsAny for TestReader { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} diff --git a/tests/core.rs b/tests/core.rs index 29550f8..76e9062 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -1,6 +1,7 @@ mod common; use crate::common::writer::TestWriter; +use common::reader::TestReader; use rezasm_core::instructions::implementation::arithmetic_instructions::ADD; use rezasm_core::instructions::implementation::register_instructions; use rezasm_core::parser::lexer::{ @@ -8,10 +9,12 @@ use rezasm_core::parser::lexer::{ }; use rezasm_core::parser::line::Line; use rezasm_core::simulation::memory::Memory; -use rezasm_core::simulation::register::Register; +use rezasm_core::simulation::reader::DummyReader; +use rezasm_core::simulation::reader_cell::ReaderCell; use rezasm_core::simulation::registry::Registry; use rezasm_core::simulation::registry::{self, get_register_number}; use rezasm_core::simulation::simulator::Simulator; +use rezasm_core::simulation::writer::DummyWriter; use rezasm_core::util::error::ParserError; use rezasm_core::util::io::RezasmFileReader; use rezasm_core::util::raw_data::RawData; @@ -102,7 +105,8 @@ pub fn test_macro() { let foo = &ADD; match foo.call_function(&mut simulator, &args) { - Ok(_) => { + Ok(seq) => { + seq.apply(&mut simulator).unwrap(); assert_eq!( simulator .get_registers() @@ -145,7 +149,8 @@ pub fn test_simulator_instruction() { pub fn test_print_instructions() { register_instructions(); let writer = Box::new(TestWriter::new()); - let mut simulator: Simulator = Simulator::new_writer(writer); + let reader = ReaderCell::new(DummyReader::new()); + let mut simulator: Simulator = Simulator::new_custom_reader_writer(reader, writer); let program = " move $s2 \"Print Instructions Work!\\n\" add $s1 '\\n' 0 @@ -171,6 +176,45 @@ pub fn test_print_instructions() { assert_eq!(output.as_str(), "3\n1.5\nPrint Instructions Work!\n"); } +#[test] +pub fn test_read_instructions() { + // Test assumes all other instructions work properly + register_instructions(); + let buffer = "10\n10.5\na\nHello"; //doesn't cover everything, should be close enough + let program = " + readi $t0 + readf $t1 + alloc $s0 $t0 + readc $t2 + reads $s0 + printi $t0 + printc '\\n' + printf $t1 + printc '\\n' + printc $t2 + printc '\\n' + prints $s0" + .to_string(); + + let mut reader = ReaderCell::new(TestReader::new()); + let _ = reader.write(buffer.as_bytes()).unwrap(); + let writer = Box::new(TestWriter::new()); + let mut simulator: Simulator = Simulator::new_custom_reader_writer(reader, writer); + let lines = parse_lines(&program, simulator.get_word_size()).unwrap(); + simulator.add_lines(lines, "".to_string()).unwrap(); + while !simulator.is_done() { + simulator.run_line_from_pc().unwrap(); + } + let writer = simulator.get_writer(); + let output = writer + .deref() + .as_any() + .downcast_ref::() + .unwrap() + .get_data(); + assert_eq!(output.as_str(), "10\n10.5\na\nHello"); +} + #[test] pub fn test_simulator_labels() { register_instructions();