Решение на Reversible Interpreter от Цветелина Стоянова
Към профила на Цветелина Стоянова
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 12 успешни тест(а)
- 0 неуспешни тест(а)
Код
use std::collections::VecDeque;
pub fn is_valid_command(command: &str) -> bool {
let commands : Vec<&str> = vec![ "PUSH", "POP", "ADD", "MUL", "SUB", "DIV" ];
return commands.contains(&command);
}
#[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 command_stack: Vec<Command>,
}
#[derive(Debug, Default)]
pub struct Command {
pub first: i32,
pub second: i32,
pub operation: String,
}
impl Interpreter {
pub fn new() -> Self {
let instructions: VecDeque<String> = VecDeque::new();
let stack: Vec<i32> = Vec::new();
let commands: Vec<Command> = Vec::new();
return Interpreter {
instructions: instructions,
stack: stack,
command_stack: commands
}
}
/// Добавя инструкции от дадения списък към края на `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();
}
fn fill_stacks(&mut self, first: i32, second: i32, operation: &str) -> Result<(), RuntimeError> {
let c = Command { first:first , second: second, operation: operation.to_string() };
self.command_stack.push(c);
self.instructions.pop_front();
return Ok(());
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
// PUSH: Приема точно 1 аргумент, разделен с един интервал (или поне ще тестваме само с един интервал, но може просто да ползвате .split_whitespace()). Добавя стойността на върха на стека.
// POP: Приема точно 0 аргументи, иначе RuntimeError::InvalidCommand. Премахва стойността на върха на стека.
// ADD: Взема върха на стека, после новия връх, събира двете стойности и слага новата стойност на върха. Приема точно 0 аргументи.
// MUL: Същото, само че с умножение. Приема точно 0 аргументи.
// SUB: Взема върха на стека X, взема новия връх Y, смята X - Y, слага резултата на върха на стека. Приема точно 0 аргументи.
// DIV: Същото, само че с деление. Ако Y е 0, връщаме RuntimeError::DivideByZero. Приема точно 0 аргументи.
// Ако някоя от командите, които вадят нещо от стека се извикат на празен стек, очакваме грешка RuntimeError::StackUnderflow. Примерно PUSH 1 последвано от ADD връща stack underflow -- има само една стойност за събиране.
// При различен брой аргументи от описания (примерно ADD 1, PUSH 1 2, PUSH), командата е невалидна с RuntimeError::InvalidCommand. При аргумент, който не е валидно i32 число, пак връщаме RuntimeError::InvalidCommand.
// При каквато и да е друга инструкция, очакваме RuntimeError::InvalidCommand.
pub fn forward(&mut self) -> Result<(), RuntimeError> {
if self.instructions.is_empty(){
return Err(RuntimeError::NoInstructions);
}
let instruction = self.instructions.front().unwrap().clone();
let mut iter_components = instruction.split_whitespace();
let first_component = iter_components.next();
let second_component = iter_components.next();
let third_component = iter_components.next();
if first_component == None || third_component != None {
return Err(RuntimeError::InvalidCommand);
}
let operation = first_component.unwrap();
if !is_valid_command(operation) {
return Err(RuntimeError::InvalidCommand);
}
if operation == "PUSH" && second_component == None {
return Err(RuntimeError::InvalidCommand);
}
if operation != "PUSH" && second_component != None {
return Err(RuntimeError::InvalidCommand);
}
match operation {
"PUSH" => {
let number = match second_component.unwrap().parse::<i32>() {
Ok(n) => n,
Err(_) => return Err(RuntimeError::InvalidCommand),
};
self.stack.push(number);
return self.fill_stacks(number, 0, operation);
},
"POP" => {
let popped = self.stack.pop();
if popped == None {
return Err(RuntimeError::StackUnderflow);
}
return self.fill_stacks(popped.unwrap(), 0, operation);
},
"ADD" => {
let first = self.stack.pop();
let second = self.stack.pop();
if first == None || second == None{
return Err(RuntimeError::StackUnderflow);
}
let first = first.unwrap();
let second = second.unwrap();
self.stack.push(first + second);
return self.fill_stacks(first, second, operation);
},
"SUB" => {
let first = self.stack.pop();
let second = self.stack.pop();
if first == None || second == None{
return Err(RuntimeError::StackUnderflow);
}
let first = first.unwrap();
let second = second.unwrap();
self.stack.push(first - second);
return self.fill_stacks(first, second, operation);
},
"MUL" => {
let first = self.stack.pop();
let second = self.stack.pop();
if first == None || second == None{
return Err(RuntimeError::StackUnderflow);
}
let first = first.unwrap();
let second = second.unwrap();
self.stack.push(first * second);
return self.fill_stacks(first, second, operation);
},
"DIV" => {
let first = self.stack.pop();
let second = self.stack.pop();
if first == None || second == None{
return Err(RuntimeError::StackUnderflow);
}
if second.unwrap()==0{
return Err(RuntimeError::DivideByZero);
}
let first = first.unwrap();
let second = second.unwrap();
self.stack.push(first / second);
return self.fill_stacks(first, second, operation);
},
_ => return Err(RuntimeError::InvalidCommand),
}
}
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.command_stack.is_empty(){
return Err(RuntimeError::NoInstructions);
}
let last_command = self.command_stack.pop().unwrap();
if last_command.operation == "PUSH" {
self.stack.pop();
let number_and_operation = "PUSH ".to_owned() + &last_command.first.to_string();
self.instructions.push_front(number_and_operation);
return Ok(());
}
if last_command.operation == "POP" {
self.stack.push(last_command.first);
self.instructions.push_front("POP".to_string());
return Ok(());
}
self.stack.pop();
self.stack.push(last_command.second);
self.stack.push(last_command.first);
self.instructions.push_front(last_command.operation.to_string());
return Ok(());
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-l7duia/solution) Finished test [unoptimized + debuginfo] target(s) in 2.44s 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