Решение на Reversible Interpreter от Васил Папукчиев

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

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

Резултати

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

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug, Default)]
struct BackOperations {
pub to_instructions: String,
pub to_stack: Vec<i32>
}
impl BackOperations {
pub fn new(to_instructions: String, to_stack: Vec<i32>) -> Self {
BackOperations { to_instructions, to_stack }
}
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
back_operations: Vec<BackOperations>
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
Interpreter { instructions: VecDeque::new(), stack: Vec::new(), back_operations: Vec::new() }
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str]) {
for command in instructions {
self.instructions.push_back(command.to_string());
}
}
/// Връща 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> {
if let Some(command) = self.instructions.pop_front() {
if let Err(e) = Interpreter::validate_command(&command) {
self.instructions.push_front(command);
return Err(e);
}
let mut splitted_command = command.split_whitespace();
match splitted_command.next().unwrap() {
"PUSH" => {
self.stack.push(splitted_command.next().unwrap().parse::<i32>().unwrap());
self.back_operations.push(BackOperations::new(command.to_string(), Vec::new()));
},
"POP" => {
if self.stack.len() == 0 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(number) = self.stack.pop() {
self.back_operations.push(BackOperations::new(command.to_string(), vec![number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"ADD" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
let sum = first_number + second_number;
self.stack.push(sum);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"MUL" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
let product = first_number * second_number;
self.stack.push(product);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"SUB" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
let diff = first_number - second_number;
self.stack.push(diff);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"DIV" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
if second_number == 0 {
self.stack.push(second_number);
self.stack.push(first_number);
self.instructions.push_front(command);
return Err(RuntimeError::DivideByZero);
}
let quotient = first_number / second_number;
self.stack.push(quotient);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
_ => {
return Err(RuntimeError::InvalidCommand);
}
}
Ok(())
}
else {
Err(RuntimeError::NoInstructions)
}
}
fn validate_command(command: &str) -> Result<(), RuntimeError> {
if command.len() == 0 {
return Err(RuntimeError::InvalidCommand);
}
let mut splitted_command = command.split_whitespace();
let count = splitted_command.clone().count();
match splitted_command.next().unwrap() {
"PUSH" => {
if count != 2 {
return Err(RuntimeError::InvalidCommand);
}
if let Err(_) = splitted_command.next().unwrap().parse::<i32>() {
return Err(RuntimeError::InvalidCommand);
}
},
"POP" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"ADD" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"MUL" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"SUB" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"DIV" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
_ => {
return Err(RuntimeError::InvalidCommand);
}
}
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 let Some(back_operation) = self.back_operations.pop() {
if back_operation.to_instructions != "POP" {
self.stack.pop();
}
self.instructions.push_front(back_operation.to_instructions);
for number in back_operation.to_stack {
self.stack.push(number);
}
Ok(())
}
else {
Err(RuntimeError::NoInstructions)
}
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-bry9yq/solution)
    Finished test [unoptimized + debuginfo] target(s) in 2.45s
     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

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

Васил качи първо решение на 18.01.2021 15:20 (преди 6 месеца)

Васил качи решение на 18.01.2021 18:59 (преди 6 месеца)

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug, Default)]
struct BackOperations {
pub to_instructions: String,
pub to_stack: Vec<i32>
}
impl BackOperations {
pub fn new(to_instructions: String, to_stack: Vec<i32>) -> Self {
BackOperations { to_instructions, to_stack }
}
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
back_operations: Vec<BackOperations>
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
Interpreter { instructions: VecDeque::new(), stack: Vec::new(), back_operations: Vec::new() }
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str]) {
for command in instructions {
self.instructions.push_back(command.to_string());
}
}
/// Връща 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> {
if let Some(command) = self.instructions.pop_front() {
if let Err(e) = Interpreter::validate_command(&command) {
self.instructions.push_front(command);
return Err(e);
}
let mut splitted_command = command.split_whitespace();
match splitted_command.next().unwrap() {
"PUSH" => {
self.stack.push(splitted_command.next().unwrap().parse::<i32>().unwrap());
self.back_operations.push(BackOperations::new(command.to_string(), Vec::new()));
},
"POP" => {
if self.stack.len() == 0 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(number) = self.stack.pop() {
self.back_operations.push(BackOperations::new(command.to_string(), vec![number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"ADD" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
let sum = first_number + second_number;
self.stack.push(sum);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"MUL" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
let product = first_number * second_number;
self.stack.push(product);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"SUB" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
let diff = first_number - second_number;
self.stack.push(diff);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
"DIV" => {
if self.stack.len() < 2 {
self.instructions.push_front(command);
return Err(RuntimeError::StackUnderflow);
}
if let Some(first_number) = self.stack.pop() {
if let Some(second_number) = self.stack.pop() {
if second_number == 0 {
self.stack.push(second_number);
self.stack.push(first_number);
self.instructions.push_front(command);
return Err(RuntimeError::DivideByZero);
}
let quotient = first_number / second_number;
self.stack.push(quotient);
self.back_operations.push(BackOperations::new(command.to_string(), vec![second_number, first_number]));
}
else {
return Err(RuntimeError::StackUnderflow);
}
}
else {
return Err(RuntimeError::StackUnderflow);
}
},
_ => {
return Err(RuntimeError::InvalidCommand);
}
}
Ok(())
}
else {
Err(RuntimeError::NoInstructions)
}
}
fn validate_command(command: &str) -> Result<(), RuntimeError> {
if command.len() == 0 {
return Err(RuntimeError::InvalidCommand);
}
let mut splitted_command = command.split_whitespace();
let count = splitted_command.clone().count();
match splitted_command.next().unwrap() {
"PUSH" => {
if count != 2 {
return Err(RuntimeError::InvalidCommand);
}
if let Err(_) = splitted_command.next().unwrap().parse::<i32>() {
return Err(RuntimeError::InvalidCommand);
}
},
"POP" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"ADD" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"MUL" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"SUB" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
"DIV" => {
if count != 1 {
return Err(RuntimeError::InvalidCommand);
}
},
_ => {
return Err(RuntimeError::InvalidCommand);
}
}
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 let Some(back_operation) = self.back_operations.pop() {
- self.stack.pop();
+ if back_operation.to_instructions != "POP" {
+ self.stack.pop();
+ }
self.instructions.push_front(back_operation.to_instructions);
for number in back_operation.to_stack {
self.stack.push(number);
}
Ok(())
}
else {
Err(RuntimeError::NoInstructions)
}
}
}