Решение на Reversible Interpreter от Ивайло Иванов

Обратно към всички решения

Към профила на Ивайло Иванов

Резултати

  • 10 точки от тестове
  • 0 бонус точки
  • 10 точки общо
  • 8 успешни тест(а)
  • 4 неуспешни тест(а)

Код

use std::{collections::VecDeque};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum ReverseInstruction{
PUSH(i32),
POP(i32),
ADD(i32, i32),
MUL(i32, i32),
SUB(i32, i32),
DIV(i32, i32)
}
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
reverse_instructions: Vec<ReverseInstruction>,
}
impl Interpreter {
pub fn new() -> Self {
Interpreter{
instructions: VecDeque::<String>::new(),
stack: Vec::<i32>::new(),
reverse_instructions: Vec::<ReverseInstruction>::new()
}
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for instruction in instructions {
self.instructions.push_back(instruction.to_string());
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
//check if there are instructions
let current_instruction = self.current_instruction();
let result = match current_instruction {
Some(instruction) => {
let mut instruction_split = instruction.split_whitespace();
//get instruction name and match accordingly
match instruction_split.next() {
Some("PUSH") => match instruction_split.next().map(str::parse::<i32>) {
Some(Ok(number)) => {
self.reverse_instructions.push(ReverseInstruction::PUSH(number));
Ok(self.stack.push(number))
},
Some(Err(_)) => Err(RuntimeError::InvalidCommand),
None => Err(RuntimeError::InvalidCommand)
},
Some("POP") => match instruction_split.next() {
None => match self.stack.pop() {
Some(num) => {
self.reverse_instructions.push(ReverseInstruction::POP(num));
Ok(())
},
None => Err(RuntimeError::InvalidCommand)
},
Some(_) => Err(RuntimeError::InvalidCommand)
},
Some("ADD") => {
match (self.stack.pop(), self.stack.pop()) {
(Some(first), Some(second)) =>{
self.reverse_instructions.push(ReverseInstruction::ADD(first, second));
Ok(self.stack.push(first + second))
},
(Some(num), None) => {
self.stack.push(num);
Err(RuntimeError::StackUnderflow)
},
_ => Err(RuntimeError::StackUnderflow)
}
},
Some("MUL") => {
match (self.stack.pop(), self.stack.pop()) {
(Some(first), Some(second)) =>{
self.reverse_instructions.push(ReverseInstruction::MUL(first, second));
Ok(self.stack.push(first + second))
},
(Some(num), None) => {
self.stack.push(num);
Err(RuntimeError::StackUnderflow)
},
_ => Err(RuntimeError::StackUnderflow)
}
},
Some("SUB") => {
match (self.stack.pop(), self.stack.pop()) {
(Some(first), Some(second)) =>{
self.reverse_instructions.push(ReverseInstruction::SUB(first, second));
Ok(self.stack.push(first + second))
},
(Some(num), None) => {
self.stack.push(num);
Err(RuntimeError::StackUnderflow)
},
_ => Err(RuntimeError::StackUnderflow)
}
},
Some("DIV") => {
match (self.stack.pop(), self.stack.pop()) {
(Some(_), Some(0)) => Err(RuntimeError::DivideByZero),
(Some(first), Some(second)) =>{
self.reverse_instructions.push(ReverseInstruction::DIV(first, second));
Ok(self.stack.push(first / second))
},
(Some(num), None) => {
self.stack.push(num);
Err(RuntimeError::StackUnderflow)
},
_ => Err(RuntimeError::StackUnderflow)
}
},
_ => Err(RuntimeError::InvalidCommand)
}
},
None => Err(RuntimeError::NoInstructions)
};
if result == Ok(()) {
self.instructions.pop_front();
}
return result
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
pub fn back(&mut self) -> Result<(), RuntimeError> {
match self.reverse_instructions.pop() {
Some(ReverseInstruction::PUSH(num)) => {
self.stack.pop();
self.instructions.push_front(format!("PUSH {}", num))
},
Some(ReverseInstruction::POP(num)) => {
self.stack.push(num);
self.instructions.push_front("POP".to_string());
},
Some(ReverseInstruction::ADD(first, second)) => {
self.stack.pop();
self.stack.push(second);
self.stack.push(first);
self.instructions.push_front("ADD".to_string());
},
Some(ReverseInstruction::MUL(first, second)) => {
self.stack.pop();
self.stack.push(second);
self.stack.push(first);
self.instructions.push_front("MUL".to_string());
},
Some(ReverseInstruction::SUB(first, second)) => {
self.stack.pop();
self.stack.push(second);
self.stack.push(first);
self.instructions.push_front("SUB".to_string());
},
Some(ReverseInstruction::DIV(first, second)) => {
self.stack.pop();
self.stack.push(second);
self.stack.push(first);
self.instructions.push_front("DIV".to_string());
},
None => return Err(RuntimeError::NoInstructions)
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{Interpreter, RuntimeError};
#[test]
fn basic_test() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 0",
"PUSH 42",
"DIV",
]);
interpreter.forward().unwrap(); // PUSH 0
interpreter.forward().unwrap(); // PUSH 42
println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
// Instructions: ["DIV"]
// Stack: [0, 42]
println!("{:?}", interpreter.forward());
// => Err(DivideByZero)
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.current_instruction().map(|i| *i = String::from("PUSH 2"));
println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
// Instructions: ["PUSH 2", "PUSH 42", "DIV"]
// Stack: []
interpreter.run().unwrap();
println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
// Instructions: []
// Stack: [21]
}
#[test]
fn test_basic() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1",
"PUSH 2",
"PUSH 3",
"ADD",
]);
assert_eq!(interpreter.instructions, &[
"PUSH 1",
"PUSH 2",
"PUSH 3",
"ADD",
]);
assert_eq!(interpreter.stack, &[]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.instructions, &["ADD"]);
assert_eq!(interpreter.stack, &[1, 2, 3]);
interpreter.run().unwrap();
assert_eq!(interpreter.instructions.len(), 0);
assert_eq!(interpreter.stack, &[1, 5]);
interpreter.back().unwrap();
interpreter.back().unwrap();
println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
assert_eq!(interpreter.instructions, &[
"PUSH 3",
"ADD",
]);
assert_eq!(interpreter.stack, &[1, 2]);
interpreter.add_instructions(&["ADD", "ADD"]);
assert_eq!(interpreter.run(), Err(RuntimeError::StackUnderflow));
assert_eq!(interpreter.current_instruction().unwrap(), "ADD");
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20210120-1538662-eh70u7/solution)
    Finished test [unoptimized + debuginfo] target(s) in 2.33s
     Running target/debug/deps/solution_test-8916805fc40a2dab

running 12 tests
test solution_test::test_arg_number ... FAILED
test solution_test::test_arithmetic_back ... FAILED
test solution_test::test_arithmetic_basic ... FAILED
test solution_test::test_div_1 ... ok
test solution_test::test_div_2 ... ok
test solution_test::test_errors_1 ... FAILED
test solution_test::test_errors_2 ... ok
test solution_test::test_instructions_after_error ... ok
test solution_test::test_invalid_args ... ok
test solution_test::test_pop ... ok
test solution_test::test_push ... ok
test solution_test::test_restoring_instructions ... ok

failures:

---- solution_test::test_arg_number stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Err(StackUnderflow)`,
 right: `Err(InvalidCommand)`: Should have been invalid: ADD 1 2', tests/solution_test.rs:242:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_arithmetic_back stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[10, 5]`,
 right: `[-5, 5]`', tests/solution_test.rs:103:5

---- solution_test::test_arithmetic_basic stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `6`,
 right: `9`', tests/solution_test.rs:65:5

---- solution_test::test_errors_1 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Err(InvalidCommand)`,
 right: `Err(StackUnderflow)`', tests/solution_test.rs:159:5


failures:
    solution_test::test_arg_number
    solution_test::test_arithmetic_back
    solution_test::test_arithmetic_basic
    solution_test::test_errors_1

test result: FAILED. 8 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (1 версия и 0 коментара)

Ивайло качи първо решение на 19.01.2021 17:58 (преди 6 месеца)