Решение на Reversible Interpreter от Мартин Тодоров

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

Към профила на Мартин Тодоров

Резултати

  • 14 точки от тестове
  • 0 бонус точки
  • 14 точки общо
  • 11 успешни тест(а)
  • 1 неуспешни тест(а)

Код

use std::collections::VecDeque;
use std::collections::HashMap;
#[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>,
operations_map: HashMap<String, String>,
traceback_instructions: VecDeque<String>,
traceback_stack: Vec<i32>,
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
let mut map:HashMap<String, String> = HashMap::new();
map.insert(String::from("pop"),String::from("push"));
map.insert(String::from("push"),String::from("pop"));
map.insert(String::from("add"),String::from("sub"));
map.insert(String::from("sub"),String::from("add"));
map.insert(String::from("mul"),String::from("div"));
map.insert(String::from("div"),String::from("mul"));
Self{instructions: VecDeque::new(),
stack: Vec::new(),
operations_map: map,
traceback_instructions: VecDeque::new(),
traceback_stack: Vec::new()}
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
// let mut instruction;
// let mut operation;
// let mut value;
// for inst in instructions{
// instruction = inst.trim().split_whitespace();
// operation = instruction.next().unwrap();
// self.instructions.push_back(operation.to_string());
// if operation == "push"{
// value = instruction.next().unwrap().parse::<i32>().unwrap();
// self.stack.push(value);
// }
pub fn add_instructions(&mut self, instructions: &[&str]) {
for inst in instructions{
self.instructions.push_back(inst.to_string());
}
}
/// Връща mutable reference към инструкцията, която ще се изпълни при
/// следващия `.forward()` -- първата в списъка/дека.
///
pub fn current_instruction(&mut self) -> Option<&mut String> {
Some(&mut self.instructions[0])
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
if self.instructions.is_empty(){
return Err(RuntimeError::NoInstructions);
}
let mut instruction = self.instructions[0].clone().to_lowercase();
let mut operation = instruction.trim().split_whitespace().collect::<Vec<&str>>();
let mut operation_value = operation[0];
if !self.operations_map.contains_key(operation_value){
return Err(RuntimeError::InvalidCommand);
}
let reverse_value = self.operations_map.get(operation_value).unwrap().to_string();
if operation_value == "push"{
if operation.len() != 2 || !operation[1].parse::<i32>().is_ok(){
return Err(RuntimeError::InvalidCommand);
}
else {
self.pop_values();
self.traceback_stack.push(operation[1].parse::<i32>().unwrap());
self.stack.push(operation[1].parse::<i32>().unwrap());
self.traceback_instructions.push_back(reverse_value);
}
}
else if operation_value == "pop" {
if self.stack.is_empty(){
return Err(RuntimeError::StackUnderflow);
}
if operation.len() != 1{
return Err(RuntimeError::InvalidCommand);
}
else {
let mut poped_value = self.stack[self.stack.len()-1];
self.pop_values();
self.traceback_stack.push(poped_value);
self.traceback_instructions.push_back(reverse_value);
}
}
else if vec!["add","sub","div","mul"].iter().any(|&i| i==operation_value){
if self.stack.len() < 2{
return Err(RuntimeError::StackUnderflow);
}
let first_value = self.stack[self.stack.len()-1];
let second_value = self.stack[self.stack.len()-2];
if operation_value == "div"{
if second_value == 0 {
return Err(RuntimeError::DivideByZero);
}
else {
self.pop_values();
self.stack.push(first_value/second_value);
self.push_values(first_value,second_value,&reverse_value);
}
}
else if operation_value == "mul"{
self.pop_values();
self.stack.push(first_value*second_value);
self.push_values(first_value,second_value,&reverse_value);
}
else if operation_value == "add"{
self.pop_values();
self.stack.push(first_value+second_value);
self.push_values(first_value,second_value,&reverse_value);
}
else if operation_value == "sub"{
self.pop_values();
self.stack.push(first_value-second_value);
self.push_values(first_value,second_value,&reverse_value);
}
}
Ok(())
}
fn push_values(&mut self,first_value:i32, second_value:i32, reverse_value:&String) {
self.traceback_stack.push(second_value);
self.traceback_stack.push(first_value);
self.traceback_instructions.push_back(reverse_value.to_string());
}
fn pop_values(&mut self) {
let instruction = self.instructions[0].clone().to_lowercase();
let operation = instruction.trim().split_whitespace().collect::<Vec<&str>>()[0];
self.instructions.pop_front();
if operation != "push"{
self.stack.pop();
if operation != "pop"{
self.stack.pop();
}
}
}
/// Вика `.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> {
println!("{:?}", self.traceback_stack);
if self.traceback_instructions.is_empty(){
return Err(RuntimeError::NoInstructions);
}
let mut operation = self.traceback_instructions.pop_back().unwrap().clone();
if operation == "push"{
let push_value = self.traceback_stack.pop().unwrap();
self.stack.push(push_value);
self.instructions.push_front("pop".to_string().to_uppercase());
}
else if operation == "pop"{
let value = self.traceback_stack.pop().unwrap();
self.stack.pop();
self.instructions.push_front(format!(r#"push {}"#, value).to_uppercase());
}
else if vec!["add","sub","div","mul"].iter().any(|&i| i==operation){
let first_value = self.traceback_stack.pop().unwrap();
let second_value = self.traceback_stack.pop().unwrap();
self.stack.pop();
self.stack.push(second_value);
self.stack.push(first_value);
let mut rev_value = self.operations_map.get(&operation).unwrap().to_string();
self.instructions.push_front(rev_value.to_string().to_uppercase());
}
Ok(())
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-5yw2t6/solution)
warning: variable does not need to be mutable
  --> src/lib.rs:84:13
   |
84 |         let mut instruction = self.instructions[0].clone().to_lowercase();
   |             ----^^^^^^^^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

warning: variable does not need to be mutable
  --> src/lib.rs:85:13
   |
85 |         let mut operation = instruction.trim().split_whitespace().collect::<Vec<&str>>();
   |             ----^^^^^^^^^
   |             |
   |             help: remove this `mut`

warning: variable does not need to be mutable
  --> src/lib.rs:86:13
   |
86 |         let mut operation_value = operation[0];
   |             ----^^^^^^^^^^^^^^^
   |             |
   |             help: remove this `mut`

warning: variable does not need to be mutable
   --> src/lib.rs:110:21
    |
110 |                 let mut poped_value = self.stack[self.stack.len()-1];
    |                     ----^^^^^^^^^^^
    |                     |
    |                     help: remove this `mut`

warning: variable does not need to be mutable
   --> src/lib.rs:191:13
    |
191 |         let mut operation = self.traceback_instructions.pop_back().unwrap().clone();
    |             ----^^^^^^^^^
    |             |
    |             help: remove this `mut`

warning: variable does not need to be mutable
   --> src/lib.rs:208:17
    |
208 |             let mut rev_value = self.operations_map.get(&operation).unwrap().to_string();
    |                 ----^^^^^^^^^
    |                 |
    |                 help: remove this `mut`

warning: 6 warnings emitted

    Finished test [unoptimized + debuginfo] target(s) in 3.40s
     Running target/debug/deps/solution_test-8916805fc40a2dab

running 12 tests
test solution_test::test_arg_number ... FAILED
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

failures:

---- solution_test::test_arg_number stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Err(StackUnderflow)`,
 right: `Err(InvalidCommand)`: Should have been invalid: ADD 1 2', tests/solution_test.rs:242:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    solution_test::test_arg_number

test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

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

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

Мартин качи първо решение на 17.01.2021 22:38 (преди над 4 години)