Решение на Reversible Interpreter от Велин Клисуров

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

Към профила на Велин Клисуров

Резултати

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

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
pub reverseInstructions: Vec<String>,
pub reverseStack: Vec<i32>
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
return Self { instructions: VecDeque::new(),
stack: Vec::new(),
reverseInstructions: Vec::new(),
reverseStack: Vec::new()
};
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str]) {
for instr in instructions{
self.instructions.push_back(String::from(*instr));
}
}
/// Връща mutable reference към инструкцията, която ще се изпълни при
/// следващия `.forward()` -- първата в списъка/дека.
///
pub fn current_instruction(&mut self) -> Option<&mut String> {
let len = self.instructions.len();
return if len > 0 {
Some(&mut self.instructions[len - 1])
} else {
None
}
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
return if self.instructions.len() == 0 {
Err(RuntimeError::NoInstructions)
} else {
let instr = self.instructions.pop_front().unwrap();
let mut iter = instr.split_whitespace();
let part1 = iter.next();
let part2 = iter.next();
match part1 {
Some("PUSH") => {
match part2 {
None => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
},
Some(x) => {
if x.parse::<i32>().is_ok() {
self.stack.push(x.parse::<i32>().unwrap());
self.reverseInstructions.push(instr);
Ok(())
} else {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
}
Some("POP") => {
match part2 {
None => {
if self.stack.len() > 0 {
let num = self.stack.pop().unwrap();
self.reverseInstructions.push(instr);
self.reverseStack.push(num);
Ok(())
} else {
self.instructions.push_front(instr);
Err(RuntimeError::StackUnderflow)
}
},
Some(_) => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
Some("ADD") => {
match part2 {
None => {
if self.stack.len() > 1 {
let num2 = self.stack.pop().unwrap();
let num1 = self.stack.pop().unwrap();
self.stack.push(num1 + num2);
self.reverseInstructions.push(instr);
self.reverseStack.push(num2);
self.reverseStack.push(num1);
Ok(())
} else {
self.instructions.push_front(instr);
Err(RuntimeError::StackUnderflow)
}
},
Some(_) => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
Some("MUL") => {
match part2 {
None => {
if self.stack.len() > 1 {
let num2 = self.stack.pop().unwrap();
let num1 = self.stack.pop().unwrap();
self.stack.push(num1 * num2);
self.reverseInstructions.push(instr);
self.reverseStack.push(num2);
self.reverseStack.push(num1);
Ok(())
} else {
self.instructions.push_front(instr);
Err(RuntimeError::StackUnderflow)
}
},
Some(_) => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
Some("SUB") => {
match part2 {
None => {
if self.stack.len() > 1 {
let num2 = self.stack.pop().unwrap();
let num1 = self.stack.pop().unwrap();
self.stack.push(num1 - num2);
self.reverseInstructions.push(instr);
self.reverseStack.push(num2);
self.reverseStack.push(num1);
Ok(())
} else {
self.instructions.push_front(instr);
Err(RuntimeError::StackUnderflow)
}
},
Some(_) => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
Some("DIV") => {
match part2 {
None => {
if self.stack.len() > 1 {
let num2 = self.stack.pop().unwrap();
let num1 = self.stack.pop().unwrap();
if num1 != 0 {
self.stack.push(num1 / num2);
self.reverseInstructions.push(instr);
self.reverseStack.push(num2);
self.reverseStack.push(num1);
Ok(())
} else {
Err(RuntimeError::DivideByZero)
}
} else {
self.instructions.push_front(instr);
Err(RuntimeError::StackUnderflow)
}
},
Some(_) => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
_ => {
self.instructions.push_front(instr);
Err(RuntimeError::InvalidCommand)
}
}
}
}
/// Вика `.forward()` докато не свършат инструкциите (може и да се имплементира по други
/// начини, разбира се) или има грешка.
///
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
/// "Обръща" последно-изпълнената инструкция с `.forward()`. Това може да се изпълнява отново и
/// отново за всяка инструкция, изпълнена с `.forward()` -- тоест, не пазим само последната
/// инструкция, а списък/стек от всичките досега.
///
/// Ако няма инструкция за връщане, очакваме `RuntimeError::NoInstructions`.
///
pub fn back(&mut self) -> Result<(), RuntimeError> {
if self.reverseInstructions.len() == 0 {
return Err(RuntimeError::NoInstructions);
} else {
let instr = self.reverseInstructions.pop().unwrap();
let mut iter = instr.split_whitespace();
let part1 = iter.next();
match part1 {
Some("PUSH") => {
self.stack.pop();
self.instructions.push_front(instr);
}
Some("POP") => {
self.stack.push(self.reverseStack.pop().unwrap());
self.instructions.push_front(instr);
}
Some("ADD") => {
self.stack.pop();
self.stack.push(self.reverseStack.pop().unwrap());
self.stack.push(self.reverseStack.pop().unwrap());
self.instructions.push_front(instr);
}
Some("MUL") => {
self.stack.pop();
self.stack.push(self.reverseStack.pop().unwrap());
self.stack.push(self.reverseStack.pop().unwrap());
self.instructions.push_front(instr);
}
Some("SUB") => {
self.stack.pop();
self.stack.push(self.reverseStack.pop().unwrap());
self.stack.push(self.reverseStack.pop().unwrap());
self.instructions.push_front(instr);
}
Some("DIV") => {
self.stack.pop();
self.stack.push(self.reverseStack.pop().unwrap());
self.stack.push(self.reverseStack.pop().unwrap());
self.instructions.push_front(instr);
}
_ => {
return Err(RuntimeError::NoInstructions);
}
}
}
return Ok(());
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-1065buj/solution)
warning: structure field `reverseInstructions` should have a snake case name
  --> src/lib.rs:15:9
   |
15 |     pub reverseInstructions: Vec<String>,
   |         ^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `reverse_instructions`
   |
   = note: `#[warn(non_snake_case)]` on by default

warning: structure field `reverseStack` should have a snake case name
  --> src/lib.rs:16:9
   |
16 |     pub reverseStack: Vec<i32>
   |         ^^^^^^^^^^^^ help: convert the identifier to snake case: `reverse_stack`

warning: 2 warnings emitted

    Finished test [unoptimized + debuginfo] target(s) in 4.01s
     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 ... FAILED
test solution_test::test_div_2 ... FAILED
test solution_test::test_errors_1 ... ok
test solution_test::test_errors_2 ... ok
test solution_test::test_instructions_after_error ... FAILED
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: `Ok(())`,
 right: `Err(InvalidCommand)`: Should have been invalid: PUSH 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: `[5, 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: `5`,
 right: `-5`', tests/solution_test.rs:73:5

---- solution_test::test_div_1 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[0]`,
 right: `[2]`', tests/solution_test.rs:132:5

---- solution_test::test_div_2 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[0]`,
 right: `[1]`', tests/solution_test.rs:149:5

---- solution_test::test_instructions_after_error stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["PUSH 3"]`,
 right: `["DIV", "PUSH 3"]`', tests/solution_test.rs:215:5


failures:
    solution_test::test_arg_number
    solution_test::test_arithmetic_back
    solution_test::test_arithmetic_basic
    solution_test::test_div_1
    solution_test::test_div_2
    solution_test::test_instructions_after_error

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

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

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

Велин качи първо решение на 20.01.2021 11:20 (преди над 4 години)