Решение на Reversible Interpreter от Ралица Димитрова

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

Към профила на Ралица Димитрова

Резултати

  • 15 точки от тестове
  • 0 бонус точки
  • 15 точки общо
  • 12 успешни тест(а)
  • 0 неуспешни тест(а)

Код

use std::collections::VecDeque;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub history: Vec<Op>,
pub stack: Vec<i32>,
}
impl Interpreter {
pub fn new() -> Self {
Interpreter { instructions: VecDeque::new(), history: Vec::new(), stack: Vec::new() }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for &instruction in instructions {
self.instructions.push_back(instruction.to_string().trim().to_uppercase())
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
if self.instructions.is_empty() {
return Result::Err(RuntimeError::NoInstructions)
}
let instruction = self.current_instruction().unwrap().to_owned();
let split: Vec<&str> = instruction.split_whitespace().collect::<Vec<&str>>();
if split.is_empty() {
return Result::Err(RuntimeError::InvalidCommand)
}
let op = *split.get(0).unwrap();
match op {
"PUSH" => {
if split.len() != 2 {
return Result::Err(RuntimeError::InvalidCommand)
}
let x = *split.get(1).unwrap();
self.stack.push(x.parse::<i32>().unwrap());
self.history.push(Op::Push(x.parse::<i32>().unwrap()));
},
"POP" => {
if split.len() != 1 {
return Result::Err(RuntimeError::InvalidCommand)
}
let x = self.stack.pop();
if x.is_none() {
return Result::Err(RuntimeError::StackUnderflow)
}
self.history.push(Op::Pop(x.unwrap()));
},
"ADD" | "MUL" | "SUB" | "DIV" => {
if split.len() != 1 {
return Result::Err(RuntimeError::InvalidCommand)
}
if self.stack.len() < 2 {
return Result::Err(RuntimeError::StackUnderflow)
}
let x = self.stack.pop().unwrap();
let y = self.stack.pop().unwrap();
let op_result: i32;
let reverse_op: Op;
match op {
"ADD" => {
op_result = x + y;
reverse_op = Op::Add(x, y);
},
"SUB" => {
op_result = x - y;
reverse_op = Op::Sub(x, y);
},
"MUL" => {
op_result = x * y;
reverse_op = Op::Mul(x, y);
},
"DIV" => {
if y == 0 {
return Result::Err(RuntimeError::DivideByZero)
}
op_result = x / y;
reverse_op = Op::Div(x, y);
},
_ => return Result::Err(RuntimeError::InvalidCommand)
}
self.stack.push(op_result);
self.history.push(reverse_op);
},
_ => return Result::Err(RuntimeError::InvalidCommand)
}
self.instructions.pop_front();
Ok(())
}
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> {
if self.history.is_empty() {
return Result::Err(RuntimeError::NoInstructions)
}
let reverse_op = self.history.pop().unwrap();
match reverse_op {
Op::Add(x, y) => self.reverse_arithmetic_op("ADD", x, y),
Op::Sub(x, y) => self.reverse_arithmetic_op("SUB", x, y),
Op::Mul(x, y) => self.reverse_arithmetic_op("MUL", x, y),
Op::Div(x, y) => self.reverse_arithmetic_op("DIV", x, y),
Op::Push(x) => {
self.stack.pop();
self.instructions.push_front(format!("PUSH {}", x))
},
Op::Pop(x) => {
self.stack.push(x);
self.instructions.push_front(String::from("POP"));
}
}
Ok(())
}
fn reverse_arithmetic_op(&mut self, instruction: &str, x: i32, y: i32) {
self.stack.pop();
self.stack.push(y);
self.stack.push(x);
self.instructions.push_front(instruction.to_string());
}
}
#[derive(Debug)]
pub enum Op {
Add(i32, i32),
Sub(i32, i32),
Mul(i32, i32),
Div(i32, i32),
Push(i32),
Pop(i32)
}
#[test]
fn test_revese_add() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1",
"PUSH 2",
"ADD"
]);
interpreter.run().unwrap();
assert!(interpreter.instructions.is_empty());
assert_eq!(interpreter.stack, &[3]);
interpreter.back().unwrap();
assert_eq!(interpreter.instructions, &["ADD"]);
assert_eq!(interpreter.stack, &[1, 2]);
}
#[test]
fn test_revese_subtract() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1",
"PUSH 2",
"SUB"
]);
interpreter.run().unwrap();
assert!(interpreter.instructions.is_empty());
assert_eq!(interpreter.stack, &[1]);
interpreter.back().unwrap();
assert_eq!(interpreter.instructions, &["SUB"]);
assert_eq!(interpreter.stack, &[1, 2]);
}
#[test]
fn test_no_invalid_instr_is_saved_to_history() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"POP"
]);
assert_eq!(interpreter.run(), Err(RuntimeError::StackUnderflow));
assert_eq!(interpreter.instructions, &["POP"]);
assert!(interpreter.history.is_empty());
assert!(interpreter.stack.is_empty());
}
#[test]
fn test_inv_instr() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"POSH"
]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
assert_eq!(interpreter.stack.len(), 0);
assert_eq!(interpreter.history.len(), 0);
assert_eq!(interpreter.instructions, &[
"POSH",
]);
}
#[test]
fn test_empty_instr() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
""
]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
assert_eq!(interpreter.stack.len(), 0);
assert_eq!(interpreter.history.len(), 0);
assert_eq!(interpreter.instructions, &[
"",
]);
}
#[test]
fn test_push_inv_too_many_args() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1 2"
]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
assert_eq!(interpreter.stack.len(), 0);
assert_eq!(interpreter.history.len(), 0);
assert_eq!(interpreter.instructions, &["PUSH 1 2"]);
}
#[test]
fn test_push_inv_no_args() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH",
"PUSH "
]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
assert_eq!(interpreter.stack.len(), 0);
assert_eq!(interpreter.history.len(), 0);
assert_eq!(interpreter.instructions, &[
"PUSH",
"PUSH"
]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
assert_eq!(interpreter.stack.len(), 0);
assert_eq!(interpreter.history.len(), 0);
assert_eq!(interpreter.instructions, &[
"PUSH",
"PUSH"
]);
}
#[test]
fn test_add() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1",
"PUSH 3",
"PUSH 8",
"ADD",
"ADD"
]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[1, 3, 8]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[1, 11]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[12]);
}
#[test]
fn test_add_negative() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH -1",
"PUSH 3",
"PUSH -8",
"ADD",
"ADD"
]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-1, 3, -8]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-1, -5]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-6]);
}
#[test]
fn test_sub() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 11",
"PUSH 3",
"PUSH 8",
"SUB",
"SUB"
]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[11, 3, 8]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[11, 5]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-6]);
}
#[test]
fn test_sub_negative() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH -12",
"PUSH 8",
"PUSH -3",
"PUSH 1",
"SUB",
"SUB",
"SUB"
]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-12, 8, -3, 1]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-12, 8, 4]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[-12, -4]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[8]);
}
#[test]
fn test_mul() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1",
"PUSH 3",
"PUSH 8",
"MUL",
"MUL"
]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[1, 3, 8]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[1, 24]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[24]);
}
#[test]
fn test_div() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 9",
"PUSH 4",
"PUSH 12",
"DIV",
"DIV"
]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[9, 4, 12]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[9, 3]);
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[0]);
}
#[test]
fn test_divide_by_zero() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 0",
"PUSH 12",
"DIV",
]);
assert_eq!(interpreter.run(), Err(RuntimeError::DivideByZero));
}

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

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

running 12 tests
test solution_test::test_arg_number ... ok
test solution_test::test_arithmetic_back ... ok
test solution_test::test_arithmetic_basic ... ok
test solution_test::test_div_1 ... ok
test solution_test::test_div_2 ... ok
test solution_test::test_errors_1 ... ok
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

test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

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

Ралица качи първо решение на 20.01.2021 16:39 (преди 6 месеца)