Решение на 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 stack: Vec<i32>,
history: Vec<String>,
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
let vecdeq = VecDeque::new();
let stack = Vec::new();
let hist = Vec::new();
Interpreter {
instructions: vecdeq,
stack: stack,
history: hist,
}
}
/// Добавя инструкции от дадения списък към края на `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> {
self.instructions.front_mut()
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
pub fn forward(&mut self) -> Result<(), RuntimeError> {
let instr: String;
match self.instructions.pop_front() {
Some(cur) => instr = cur,
None => return Err(RuntimeError::NoInstructions),
}
match self.execute_command(&instr, true) {
Ok(_) => Ok(()),
Err(err) => {
self.instructions.push_front(instr);
Err(err)
}
}
}
/// Вика `.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 instr: String;
match self.history.pop() {
Some(cur) => instr = cur,
None => return Err(RuntimeError::NoInstructions),
}
let from_instr = instr.split('_').collect::<Vec<&str>>()[1];
self.instructions.push_front(String::from(from_instr));
let instr_cnt: i32;
if from_instr.contains("PUSH") || from_instr == "POP" {
instr_cnt = 1;
} else {
instr_cnt = 3;
}
for _ in 0..instr_cnt {
let instr = self.history.pop().unwrap();
self.execute_command(&instr, false).unwrap();
}
Ok(())
}
fn execute_command(&mut self, instr: &String, push_hist: bool) -> Result<(), RuntimeError> {
let parsed: Vec<&str> = instr.split_whitespace().collect();
let cmd = parsed[0];
match cmd {
"PUSH" => {
if parsed.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
let arg: i32;
match parsed[1].parse::<i32>() {
Ok(val) => arg = val,
Err(_) => return Err(RuntimeError::InvalidCommand),
};
self.stack.push(arg);
if push_hist {
self.history.push(String::from("POP"));
self.history.push(format!("from_PUSH {}", arg));
}
Ok(())
}
"POP" => {
if parsed.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
let arg: i32 = Self::pop_or_underflow(&mut self.stack, |_| {})?;
if push_hist {
self.history.push(format!("PUSH {}", arg));
self.history.push(String::from("from_POP"));
}
Ok(())
}
"ADD" => {
if parsed.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
let arg1: i32 = Self::pop_or_underflow(&mut self.stack, |_| {})?;
let arg2: i32 = Self::pop_or_underflow(&mut self.stack, |stack| stack.push(arg1))?;
self.stack.push(arg1 + arg2);
if push_hist {
self.history.push(format!("PUSH {}", arg1));
self.history.push(format!("PUSH {}", arg2));
self.history.push(String::from("POP"));
self.history.push(String::from("from_ADD"));
}
Ok(())
}
"MUL" => {
if parsed.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
let arg1: i32 = Self::pop_or_underflow(&mut self.stack, |_| {})?;
let arg2: i32 = Self::pop_or_underflow(&mut self.stack, |stack| stack.push(arg1))?;
self.stack.push(arg1 * arg2);
if push_hist {
self.history.push(format!("PUSH {}", arg1));
self.history.push(format!("PUSH {}", arg2));
self.history.push(String::from("POP"));
self.history.push(String::from("from_MUL"));
}
Ok(())
}
"SUB" => {
if parsed.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
let arg1: i32 = Self::pop_or_underflow(&mut self.stack, |_| {})?;
let arg2: i32 = Self::pop_or_underflow(&mut self.stack, |stack| stack.push(arg1))?;
self.stack.push(arg1 - arg2);
if push_hist {
self.history.push(format!("PUSH {}", arg1));
self.history.push(format!("PUSH {}", arg2));
self.history.push(String::from("POP"));
self.history.push(String::from("from_SUB"));
}
Ok(())
}
"DIV" => {
if parsed.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
let arg1: i32 = Self::pop_or_underflow(&mut self.stack, |_| {})?;
let arg2: i32 = Self::pop_or_underflow(&mut self.stack, |stack| stack.push(arg1))?;
if arg2 == 0 {
self.stack.push(arg2);
self.stack.push(arg1);
return Err(RuntimeError::DivideByZero);
}
self.stack.push(arg1 / arg2);
if push_hist {
self.history.push(format!("PUSH {}", arg1));
self.history.push(format!("PUSH {}", arg2));
self.history.push(String::from("POP"));
self.history.push(String::from("from_DIV"));
}
Ok(())
}
_ => Err(RuntimeError::InvalidCommand),
}
}
fn pop_or_underflow<F>(stack: &mut Vec<i32>, restore_fn: F) -> Result<i32, RuntimeError>
where
F: Fn(&mut Vec<i32>),
{
match stack.pop() {
Some(val) => Ok(val),
None => {
restore_fn(stack);
Err(RuntimeError::StackUnderflow)
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-3x5mvg/solution) 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 ... 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