Решение на Reversible Interpreter от Юмит Яйя
Резултати
- 13 точки от тестове
- 0 бонус точки
- 13 точки общо
- 10 успешни тест(а)
- 2 неуспешни тест(а)
Код
use std::collections::VecDeque;
use std::ops::Deref;
use std::borrow::BorrowMut;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
pub instructionsBack: VecDeque<String>,
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
Interpreter {
instructions: VecDeque::new(),
stack: Vec::new(),
instructionsBack: VecDeque::new()
}
// instructions = ;
// stack = Vec::new();
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str]) {
for str in instructions {
self.instructions.push_back(String::from(str.clone()));
}
}
/// Връща 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> {
let instruction = self.instructions.front();
// let parts: Vec<&str> = "instruction".trim().split(" ").collect();
// if (parts.get(0) == "PUSH" {
// // return
// }
match instruction {
Some(str) => {
let parts: Vec<&str> = str.trim().split(" ").collect();
match parts.get(0) {
Some(&"PUSH") => {
// println!("PUSH-");
if parts.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
let num = parts.get(1).unwrap().parse::<i32>();
match num {
Ok(number) => {
self.stack.push(number);
self.instructionsBack.push_back(String::from("POP ".to_owned() + &number.to_string()));
self.instructions.pop_front();
return Ok(());
},
Err(_) => return Err(RuntimeError::InvalidCommand)
}
},
Some(&"POP") => {
// println!("POP-");
if parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.len() < 1 {
return Err(RuntimeError::StackUnderflow);
}
let pushNum = self.stack.pop().unwrap();
self.instructionsBack.push_back(String::from("PUSH ".to_owned() + &pushNum.to_string()));
self.instructions.pop_front();
return Ok(());
// let num = parts.get(1).parse::<i32>();
// match num {
// Some(number) => {
// },
// None => Err(RuntimeError::InvalidCommand)
// }
},
Some(&"ADD") => {
// println!("ADD-");
if parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let num1 = self.stack.pop().unwrap();
let num2 = self.stack.pop().unwrap();
self.stack.push(num1 + num2);
// self.instructionsBack.push_back(String::from("ADD"));
// self.instructionsBack.push_back(String::from("PUSH " + num1));
// self.instructionsBack.push_back(String::from("PUSH " + num2));
// self.instructionsBack.push_back(String::from("POP"));
self.instructionsBack.push_back(String::from("REV ".to_owned() + &num2.to_string() + " " + &num1.to_string() + " ADD"));
self.instructions.pop_front();
return Ok(());
},
Some(&"SUB") => {
// println!("SUB-");
if parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let num1 = self.stack.pop().unwrap();
let num2 = self.stack.pop().unwrap();
self.stack.push(num1 - num2);
self.instructionsBack.push_back(String::from("REV ".to_owned() + &num2.to_string() + " " + &num1.to_string() + " SUB"));
self.instructions.pop_front();
return Ok(());
},
Some(&"MUL") => {
// println!("MUL-");
if parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let num1 = self.stack.pop().unwrap();
let num2 = self.stack.pop().unwrap();
self.stack.push(num1 * num2);
self.instructionsBack.push_back(String::from("REV ".to_owned() + &num2.to_string() + " " + &num1.to_string() + " MUL"));
self.instructions.pop_front();
return Ok(());
},
Some(&"DIV") => {
if parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
// println!("DIV-");
let num1 = self.stack.pop().unwrap();
let num2 = self.stack.pop().unwrap();
if num2 == 0 {
// println!("DIV-0");
return Err(RuntimeError::DivideByZero);
}
self.stack.push(num1 / num2);
self.instructionsBack.push_back(String::from("REV ".to_owned() + &num2.to_string() + " " + &num1.to_string() + " DIV"));
self.instructions.pop_front();
return Ok(());
},
Some(_) => {
return Err(RuntimeError::InvalidCommand);
},
None => return Err(RuntimeError::InvalidCommand)
}
},
None => return Err(RuntimeError::NoInstructions)
}
}
/// Вика `.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> {
let instruction = self.instructionsBack.pop_back().unwrap();
let parts: Vec<&str> = instruction.trim().split(" ").collect();
match parts.get(0) {
Some(&"PUSH") => {
let num = parts.get(1).unwrap().parse::<i32>().unwrap();
self.stack.push(num);
self.instructions.push_front(String::from("POP"));
return Ok(());
},
Some(&"POP") => {
let num = parts.get(1).unwrap().parse::<i32>().unwrap();
// self.stack.push(num);
self.stack.pop().unwrap();
self.instructions.push_front(String::from("PUSH ".to_owned() + &num.to_string()));
return Ok(());
},
Some(&"REV") => {
self.stack.pop().unwrap();
let mut num = parts.get(1).unwrap().parse::<i32>().unwrap();
self.stack.push(num);
num = parts.get(2).unwrap().parse::<i32>().unwrap();
self.stack.push(num);
let instr = parts.get(3).unwrap();
self.instructions.push_front(String::from(*instr));
return Ok(());
},
Some(_) => {
return Err(RuntimeError::InvalidCommand);
}
None => {
return Err(RuntimeError::InvalidCommand);
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-1jdodsl/solution) warning: unused import: `std::ops::Deref` --> src/lib.rs:2:9 | 2 | use std::ops::Deref; | ^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unused import: `std::borrow::BorrowMut` --> src/lib.rs:3:9 | 3 | use std::borrow::BorrowMut; | ^^^^^^^^^^^^^^^^^^^^^^ warning: structure field `instructionsBack` should have a snake case name --> src/lib.rs:17:13 | 17 | pub instructionsBack: VecDeque<String>, | ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `instructions_back` | = note: `#[warn(non_snake_case)]` on by default warning: variable `pushNum` should have a snake case name --> src/lib.rs:99:33 | 99 | ... let pushNum = self.stack.pop().unwrap(); | ^^^^^^^ help: convert the identifier to snake case: `push_num` warning: 4 warnings emitted Finished test [unoptimized + debuginfo] target(s) in 2.51s 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 ... FAILED 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_errors_1 stdout ---- thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:216:64 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- solution_test::test_instructions_after_error stdout ---- thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:230:38 failures: solution_test::test_errors_1 solution_test::test_instructions_after_error test result: FAILED. 10 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--test solution_test'