Решение на Reversible Interpreter от Петър Милев

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

Към профила на Петър Милев

Резултати

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

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
const INSTR_NAMES: [(&'static str, u8); 6] = [
("PUSH", 1),
("POP", 0),
("ADD", 0),
("MUL", 0),
("SUB", 0),
("DIV", 0),
];
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
// + още полета за поддръжка на .back() метода
back_instructions: Vec<String>,
back_stack: Vec<i32>
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
Interpreter {
instructions: VecDeque::<String>::new(),
stack: Vec::<i32>::new(),
back_instructions: Vec::<String>::new(),
back_stack: Vec::<i32>::new(),
}
}
/// Добавя инструкции от дадения списък към края на `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();
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
let instr = match self.current_instruction() {
Some(i) => i.to_string(),
None => return Err(RuntimeError::NoInstructions)
};
let vec: Vec<&str> = instr.split_whitespace().collect();
// check if there are no arguments
if vec.len() == 0 { return Err(RuntimeError::InvalidCommand); }
// check if instruction exists
let args_cnt = match INSTR_NAMES.iter().position(|(x, _)| x == &vec[0]) {
Some(indx) => INSTR_NAMES[indx].1,
_ => return Err(RuntimeError::InvalidCommand)
};
// check if instruction is with right arguments
if vec.len() - 1 != args_cnt as usize {
return Err(RuntimeError::InvalidCommand);
}
// check if arguments are i32
let mut new_args: Vec<i32> = (&vec[1..]).iter().map(|x| {
return match x.parse::<i32>() {
Ok(value) => Ok(value),
Err(_) => Err(RuntimeError::InvalidCommand)
}.unwrap();
}).collect();
match vec[0] {
"PUSH" => {
self.stack.append(&mut new_args);
},
"POP" => {
match self.stack.pop() {
Some(x) => self.back_stack.push(x),
_ => return Err(RuntimeError::StackUnderflow)
};
},
"ADD" => {
let x = match self.stack.pop() {
Some(n) => n,
_ => return Err(RuntimeError::StackUnderflow)
};
let y = match self.stack.pop() {
Some(n) => n,
_ => { self.stack.push(x); return Err(RuntimeError::StackUnderflow)}
};
self.stack.push(x + y);
self.back_stack.push(x);
self.back_stack.push(y);
},
"MUL" => {
let x = match self.stack.pop() {
Some(n) => n,
_ => return Err(RuntimeError::StackUnderflow)
};
let y = match self.stack.pop() {
Some(n) => n,
_ => { self.stack.push(x); return Err(RuntimeError::StackUnderflow)}
};
self.stack.push(x * y);
self.back_stack.push(x);
self.back_stack.push(y);
},
"SUB" => {
let x = match self.stack.pop() {
Some(n) => n,
_ => return Err(RuntimeError::StackUnderflow)
};
let y = match self.stack.pop() {
Some(n) => n,
_ => { self.stack.push(x); return Err(RuntimeError::StackUnderflow)}
};
self.stack.push(x - y);
self.back_stack.push(x);
self.back_stack.push(y);
},
"DIV" => {
let x = match self.stack.pop() {
Some(n) => n,
_ => return Err(RuntimeError::StackUnderflow)
};
let y = match self.stack.pop() {
Some(n) => n,
_ => { self.stack.push(x); return Err(RuntimeError::StackUnderflow)}
};
if y == 0 {
self.stack.push(y);
self.stack.push(x);
return Err(RuntimeError::DivideByZero);
}
self.stack.push(x / y);
self.back_stack.push(x);
self.back_stack.push(y);
},
_ => ()
}
self.instructions.pop_front();
self.back_instructions.push(instr.to_string());
return 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> {
let instr = match self.back_instructions.pop() {
Some(i) => i,
None => return Err(RuntimeError::NoInstructions)
};
let instr_name = instr.split_whitespace().collect::<Vec<&str>>()[0].to_string();
match instr_name.as_str() {
"PUSH" => {
self.stack.pop();
},
"POP" => {
match self.back_stack.pop() {
Some(x) => self.stack.push(x),
_ => return Err(RuntimeError::StackUnderflow)
}
},
"ADD" | "MUL" | "SUB" | "DIV" => {
self.stack.pop();
match self.back_stack.pop() {
Some(x) => self.stack.push(x),
_ => return Err(RuntimeError::StackUnderflow)
}
match self.back_stack.pop() {
Some(x) => self.stack.push(x),
_ => return Err(RuntimeError::StackUnderflow)
}
},
_ => ()
}
self.instructions.push_front(instr.to_string());
return Ok(());
}
}

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

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

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

Петър качи първо решение на 19.01.2021 15:16 (преди над 4 години)