Решение на Reversible Interpreter от Ана Иванова

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

Към профила на Ана Иванова

Резултати

  • 13 точки от тестове
  • 0 бонус точки
  • 13 точки общо
  • 10 успешни тест(а)
  • 2 неуспешни тест(а)

Код

#[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 reverse_commands: Vec<(String, String, Option<i32>)>
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
Interpreter {
instructions : VecDeque::new(),
stack: Vec::new(),
reverse_commands: Vec::new(),
}
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str]) {
let added_instructions : Vec<String> = instructions.into_iter().map(|x| x.to_string()).collect();
self.instructions.extend(added_instructions);
}
/// Връща 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 current_instruction = self.current_instruction();
if current_instruction.is_none() {
return Err(RuntimeError::NoInstructions)
}
let current_instruction = current_instruction.unwrap();
let orig_instruction = current_instruction.to_owned();
let instruction_parts : Vec<String> = current_instruction.split_whitespace().map(|x| x.to_string()).collect();
match instruction_parts[0].as_ref() {
"PUSH" => {
if instruction_parts.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
let arg = instruction_parts[1].parse::<i32>().unwrap();
self.stack.push(arg);
self.reverse_commands.push((orig_instruction, String::from("POP"), None));
},
"POP" => {
if instruction_parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.is_empty() {
return Err(RuntimeError::StackUnderflow);
}
let arg = self.stack.pop();
self.reverse_commands.push((orig_instruction, String::from("PUSH"), arg));
}
_ => {
if instruction_parts.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
self.interpret_fd_arithmetic_instruction(instruction_parts[0].as_ref())?;
}
}
self.instructions.pop_front();
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.reverse_commands.is_empty() {
return Err(RuntimeError::NoInstructions);
}
let (orig_instruction, command, arg) = self.reverse_commands.pop().unwrap();
self.instructions.push_front(orig_instruction);
match command.as_ref() {
"PUSH" => {
self.stack.push(arg.unwrap());
},
"POP" => {
self.stack.pop();
},
_ => {
self.interpret_bk_arithmetic_instruction(&command, arg.unwrap())?;
},
}
Ok(())
}
fn interpret_fd_arithmetic_instruction(&mut self, instruction : &str) -> Result<(), RuntimeError> {
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
}
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
match instruction.to_uppercase().as_ref() {
"ADD" => {
self.stack.push(lhs+rhs);
self.reverse_commands.push((instruction.to_owned(), String::from("SUB_BY"), Some(rhs)));
},
"SUB" =>{
self.stack.push(lhs-rhs);
self.reverse_commands.push((instruction.to_owned(), String::from("ADD_TO"), Some(rhs)));
},
"MUL" => {
self.stack.push(lhs*rhs);
self.reverse_commands.push((instruction.to_owned(), String::from("DIV_BY"), Some(rhs)));
},
"DIV" => {
if rhs == 0 {
return Err(RuntimeError::DivideByZero);
}
self.stack.push(lhs/rhs);
self.reverse_commands.push((instruction.to_owned(), String::from("MUL_BY"), Some(rhs)));
}
_ => {
return Err(RuntimeError::InvalidCommand);
}
}
Ok(())
}
fn interpret_bk_arithmetic_instruction(&mut self, instruction : &str, arg : i32) -> Result<(), RuntimeError> {
if self.stack.len() < 1 {
return Err(RuntimeError::StackUnderflow);
}
let num = self.stack.pop().unwrap();
match instruction.to_uppercase().as_ref() {
"ADD_TO" => {
self.stack.push(arg);
self.stack.push(num+arg);
},
"SUB_BY" =>{
self.stack.push(arg);
self.stack.push(num-arg);
},
"MUL_BY" => {
self.stack.push(arg);
self.stack.push(num*arg);
},
"DIV_BY" => {
if arg == 0 {
return Err(RuntimeError::DivideByZero);
}
self.stack.push(arg);
self.stack.push(num/arg)
},
_ => {
return Err(RuntimeError::InvalidCommand);
}
}
Ok(())
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-peq4z3/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.27s
     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 ... FAILED
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 ... FAILED
test solution_test::test_pop ... ok
test solution_test::test_push ... ok
test solution_test::test_restoring_instructions ... ok

failures:

---- solution_test::test_div_2 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[5, 5]`,
 right: `[5, 7]`', tests/solution_test.rs:152:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_invalid_args stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Err(StackUnderflow)`,
 right: `Err(InvalidCommand)`: Should have been invalid: POSH', tests/solution_test.rs:265:9


failures:
    solution_test::test_div_2
    solution_test::test_invalid_args

test result: FAILED. 10 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

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

Ана качи първо решение на 20.01.2021 12:49 (преди над 4 години)