Решение на Reversible Interpreter от Ралица Добрева
Резултати
- 14 точки от тестове
- 0 бонус точки
- 14 точки общо
- 11 успешни тест(а)
- 1 неуспешни тест(а)
Код
#[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 stack_data: Vec<i32>,
pub instructions_data: VecDeque<String>,
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
Interpreter{ instructions: VecDeque::new(), stack: Vec::new(),instructions_data: VecDeque::new(), stack_data: 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(instr.to_string());
}
}
/// Връща mutable reference към инструкцията, която ще се изпълни при
/// следващия `.forward()` -- първата в списъка/дека.
///
pub fn current_instruction(&mut self) -> Option<&mut String> {
return self.instructions.front_mut();
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
if self.instructions.len() == 0 {
return Err(RuntimeError::NoInstructions);
}
if let Some(instr) = self.instructions.front() {
if instr.contains("PUSH") {
// println!("PUSH");
let _n = match get_number(&instr) {
Ok(i) => {
self.stack.push(i);
self.stack_data.push(i);
},
Err(_e) => return Err(RuntimeError::InvalidCommand),
};
} else if instr == "POP" {
// println!("POP");
if self.stack.len() == 0 {
return Err(RuntimeError::StackUnderflow);
}
self.stack_data.push(self.stack.pop().unwrap());
} else if instr == "ADD" {
// println!("ADD");
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let x = self.stack.pop().unwrap();
let y = self.stack.pop().unwrap();
self.stack.push(x + y);
} else if instr == "MUL" {
// println!("MUL");
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let x = self.stack.pop().unwrap();
let y = self.stack.pop().unwrap();
self.stack.push(x * y);
} else if instr == "SUB" {
// println!("SUB");
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let x = self.stack.pop().unwrap();
let y = self.stack.pop().unwrap();
self.stack.push(x - y);
} else if instr == "DIV" {
// println!("DIV");
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let x = self.stack.pop().unwrap();
let y = self.stack.pop().unwrap();
if y == 0 {
self.stack.push(y);
self.stack.push(x);
return Err(RuntimeError::DivideByZero);
}
self.stack.push(x / y);
} else {
return Err(RuntimeError::InvalidCommand);
}
}
self.instructions_data.push_back( self.instructions.front().unwrap().to_string());
self.instructions.pop_front();
return Ok(());
}
/// Вика `.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.instructions_data.len() == 0 {
return Err(RuntimeError::NoInstructions);
}
if let Some(instr) = self.instructions_data.pop_back() {
self.instructions.push_front(instr.to_string());
if instr.contains("PUSH") {
self.stack_data.pop();
self.stack.pop();
} else if instr == "POP" {
let x = self.stack_data.pop().unwrap();
self.stack.push(x);
} else if instr == "ADD" {
let x = self.stack_data.pop().unwrap();
let y = self.stack_data.pop().unwrap();
self.stack.pop();
self.stack.push(y);
self.stack.push(x);
} else if instr == "MUL" {
let x = self.stack_data.pop().unwrap();
let y = self.stack_data.pop().unwrap();
self.stack.pop();
self.stack.push(y);
self.stack.push(x);
} else if instr == "SUB" {
let x = self.stack_data.pop().unwrap();
let y = self.stack_data.pop().unwrap();
self.stack.pop();
self.stack.push(y);
self.stack.push(x);
} else if instr == "DIV" {
let x = self.stack_data.pop().unwrap();
let y = self.stack_data.pop().unwrap();
self.stack.pop();
self.stack.push(y);
self.stack.push(x);
} else {
return Err(RuntimeError::InvalidCommand);
}
}
return Ok(());
}
}
fn get_number(s: & str) -> Result<i32, RuntimeError> {
let mut n = s.replace("PUSH", "").to_string();
n = n.trim().to_string();
let _i = match n.parse::<i32>() {
Ok(i) => return Ok(i),
Err(_e) => return Err(RuntimeError::InvalidCommand),
};
}
fn main() {
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-11pewwp/solution) warning: function is never used: `main` --> src/lib.rs:216:4 | 216 | fn main() { | ^^^^ | = note: `#[warn(dead_code)]` on by default warning: 1 warning emitted Finished test [unoptimized + debuginfo] target(s) in 2.71s Running target/debug/deps/solution_test-8916805fc40a2dab running 12 tests test solution_test::test_arg_number ... ok test solution_test::test_arithmetic_back ... FAILED 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 failures: ---- solution_test::test_arithmetic_back stdout ---- thread 'main' panicked at 'assertion failed: `(left == right)` left: `[4, 5]`, right: `[-5, 5]`', tests/solution_test.rs:103:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: solution_test::test_arithmetic_back test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--test solution_test'