Решение на 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: VecDeque<(String,Option<i32>,Option<i32>)>,
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
//_instructions = VecDeque::new();
//_stack = Vec::new();
//_reverse = Vec::new();
return Interpreter{instructions: VecDeque::new(),stack: Vec::new(),reverse: VecDeque::new()};
//more stuff needed for back
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str]) {
for i in instructions
{
self.instructions.push_back(i.to_string());
}
}
/// Връща mutable reference към инструкцията, която ще се изпълни при
/// следващия `.forward()` -- първата в списъка/дека.
///
pub fn current_instruction(&mut self) -> Option<&mut String> {
if !self.instructions.is_empty()
{
return Some(self.instructions.front_mut().unwrap());
}
else
{
return None;
}
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
let instruction = self.instructions.front();
let instr: String;
match instruction{
Some(com) => instr = com.to_string(),
None => return Err(RuntimeError::NoInstructions),
}
let mut iter_instr = instr.split_whitespace();
let command = iter_instr.next();
match command
{
Some("PUSH") => {
let arg = iter_instr.next();
match arg{
None => return Err(RuntimeError::InvalidCommand),
_ =>{
match iter_instr.next(){
None => {
self.stack.push(arg.unwrap().parse().unwrap());
self.reverse.push_back(("PUSH".to_string(),None,None));
self.instructions.pop_front();
return Ok(());
},
_ => return Err(RuntimeError::InvalidCommand),
}
},
}
},
Some("POP") =>{
match iter_instr.next()
{
None => {
let num = self.stack.pop();
match num{
None => return Err(RuntimeError::StackUnderflow),
_ => {
self.reverse.push_back(("POP".to_string(),Some(num.unwrap()),None));
self.instructions.pop_front();
return Ok(());
},
}
}
_ => return Err(RuntimeError::InvalidCommand),
}
}
Some("ADD") =>{
match iter_instr.next(){
None =>{
let arg1 = self.stack.pop();
match arg1
{
None => return Err(RuntimeError::StackUnderflow),
_ => {
let arg2 = self.stack.pop();
match arg2{
None => return Err(RuntimeError::StackUnderflow),
_ => {
self.stack.push(arg1.unwrap() + arg2.unwrap());
self.reverse.push_back(("ADD".to_string(),Some(arg1.unwrap()),Some(arg2.unwrap())));
self.instructions.pop_front();
Ok(())
},
}
},
}
}
_ => return Err(RuntimeError::InvalidCommand),
}
}
Some("MUL") =>{
match iter_instr.next(){
None =>{
let arg1 = self.stack.pop();
match arg1
{
None => return Err(RuntimeError::StackUnderflow),
_ => {
let arg2 = self.stack.pop();
match arg2{
None => return Err(RuntimeError::StackUnderflow),
_ => {
self.stack.push(arg1.unwrap() * arg2.unwrap());
self.reverse.push_back(("MUL".to_string(),Some(arg1.unwrap()),Some(arg2.unwrap())));
self.instructions.pop_front();
Ok(())
},
}
},
}
}
_ => return Err(RuntimeError::InvalidCommand),
}
}
Some("SUB") =>{
match iter_instr.next(){
None =>{
let arg1 = self.stack.pop();
match arg1
{
None => return Err(RuntimeError::StackUnderflow),
_ => {
let arg2 = self.stack.pop();
match arg2{
None => return Err(RuntimeError::StackUnderflow),
_ => {
self.stack.push(arg1.unwrap() - arg2.unwrap());
self.reverse.push_back(("SUB".to_string(),Some(arg1.unwrap()),Some(arg2.unwrap())));
self.instructions.pop_front();
Ok(())
},
}
},
}
}
_ => return Err(RuntimeError::InvalidCommand),
}
}
Some("DIV") =>{
match iter_instr.next(){
None =>{
let arg1 = self.stack.pop();
match arg1
{
None => return Err(RuntimeError::StackUnderflow),
_ => {
let arg2 = self.stack.pop();
match arg2{
None => return Err(RuntimeError::StackUnderflow),
Some(0) => {
return Err(RuntimeError::DivideByZero);
},
_ => {
self.stack.push(arg1.unwrap() / arg2.unwrap());
self.reverse.push_back(("DIV".to_string(),Some(arg1.unwrap()),Some(arg2.unwrap())));
self.instructions.pop_front();
Ok(())
},
}
},
}
}
_ => return Err(RuntimeError::InvalidCommand),
}
}
_ =>{
return Err(RuntimeError::InvalidCommand);
},
}
}
/// Вика `.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 mut current_command = self.reverse.pop_back();
match current_command{
Some((command,arg1,arg2)) =>{
println!("{}", command);
if command == "PUSH".to_string()
{
let num = self.stack.pop();
self.instructions.push_front("PUSH".to_string() + " " + &num.unwrap().to_string());
return Ok(());
}
if command == "ADD".to_string()
{
self.stack.pop();
self.stack.push(arg2.unwrap());
self.stack.push(arg1.unwrap());
self.instructions.push_front("ADD".to_string());
return Ok(());
}
if command == "SUB".to_string()
{
self.stack.pop();
self.stack.push(arg2.unwrap());
self.stack.push(arg1.unwrap());
self.instructions.push_front("SUB".to_string());
return Ok(());
}
if command == "DIV".to_string()
{
self.stack.pop();
self.stack.push(arg2.unwrap());
self.stack.push(arg1.unwrap());
self.instructions.push_front("DIV".to_string());
return Ok(());
}
if command == "MULT".to_string()
{
self.stack.pop();
self.stack.push(arg2.unwrap());
self.stack.push(arg1.unwrap());
self.instructions.push_front("MULT".to_string());
return Ok(());
}
return Ok(());
},
None => Err(RuntimeError::NoInstructions),
}
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-12jod71/solution)
warning: variable does not need to be mutable
   --> src/lib.rs:235:13
    |
235 |         let mut current_command = self.reverse.pop_back();
    |             ----^^^^^^^^^^^^^^^
    |             |
    |             help: remove this `mut`
    |
    = note: `#[warn(unused_mut)]` on by default

warning: 1 warning emitted

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

running 12 tests
test solution_test::test_arg_number ... ok
test solution_test::test_arithmetic_back ... FAILED
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 ... FAILED
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_arithmetic_back stdout ----
DIV
PUSH
SUB
PUSH
MUL
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[9]`,
 right: `[3, 3]`', tests/solution_test.rs:113:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_instructions_after_error stdout ----
PUSH
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:243:82


failures:
    solution_test::test_arithmetic_back
    solution_test::test_instructions_after_error

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 коментара)

Костадин качи първо решение на 19.01.2021 22:53 (преди над 4 години)