Решение на Reversible Interpreter от Йордан Миронски

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

Към профила на Йордан Миронски

Резултати

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

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
use std::str::FromStr;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
pub history: VecDeque<String>,
index: i32,
// + още полета за поддръжка на .back() метода
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
return Interpreter{instructions: VecDeque::new(), stack: Vec::new(), history: VecDeque::new(), index: 0};
}
/// Добавя инструкции от дадения списък към края на `instructions`. Примерно:
///
/// interpreter.add_instructions(&[
/// "PUSH 1",
/// "PUSH 2",
/// "ADD",
/// ]);
///
/// Инструкциите не се интерпретират, само се записват.
///
pub fn add_instructions(&mut self, instructions: &[&str])
{
//todo!();
for line in instructions
{
self.instructions.push_back(line.to_string());
self.history.push_back(line.to_string());
}
}
/// Връща mutable reference към инструкцията, която ще се изпълни при
/// следващия `.forward()` -- първата в списъка/дека.
///
pub fn current_instruction(&mut self) -> Option<&mut String> {
//todo!()
//if self.instructions.front().is_none()
//{
// return None;
//}
//return Some(self.instructions.front_mut().unwrap());
return self.instructions.front_mut();
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
//todo!()
let command_line = self.instructions.pop_front();
//let command_line = self.instructions.front();
if command_line.is_none()
{
return Err(RuntimeError::NoInstructions);
}
self.index += 1;
//self.instructions.pop_front();
let mut iter = command_line.as_ref().unwrap().split_whitespace();
let command = iter.next();
if command.unwrap() == "PUSH".to_string()
{
match iter.next()
{
Some(arg) =>
{
if arg.parse::<i32>().is_ok()
{
self.stack.push(FromStr::from_str(arg).unwrap());
//self.instructions.clone().pop_front();
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
},
None =>
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand)
},
}
match iter.next()
{
Some(_) => {self.stack.pop(); self.instructions.push_front(command_line.unwrap()); return Err(RuntimeError::InvalidCommand)},
None => (),
}
return Ok(());
}
if command.unwrap() == "POP".to_string()
{
//println!("here");
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap()); return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty()
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
self.stack.pop();
//self.instructions.pop_front();
return Ok(());
}
},
}
}
if command.unwrap() == "ADD".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap()); return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
self.stack.push(x.unwrap() + y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
if command.unwrap() == "MUL".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap());return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
self.stack.push(x.unwrap() * y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
if command.unwrap() == "SUB".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap());return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
self.stack.push(x.unwrap() - y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
if command.unwrap() == "DIV".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap());return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
if y.unwrap() == 0
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::DivideByZero);
}
self.stack.push(x.unwrap() / y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
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),
_ => (),
}
}
}
pub fn forward2(&mut self) -> Result<(), RuntimeError> {
let command_line = self.instructions.pop_front();
//let command_line = self.instructions.front();
if command_line.is_none()
{
return Err(RuntimeError::NoInstructions);
}
//self.index += 1;
//self.instructions.pop_front();
let mut iter = command_line.as_ref().unwrap().split_whitespace();
let command = iter.next();
if command.unwrap() == "PUSH".to_string()
{
match iter.next()
{
Some(arg) =>
{
if arg.parse::<i32>().is_ok()
{
self.stack.push(FromStr::from_str(arg).unwrap());
//self.instructions.clone().pop_front();
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
},
None =>
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand)
},
}
match iter.next()
{
Some(_) => {self.stack.pop(); self.instructions.push_front(command_line.unwrap()); return Err(RuntimeError::InvalidCommand)},
None => (),
}
return Ok(());
}
if command.unwrap() == "POP".to_string()
{
//println!("here");
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap()); return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty()
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
self.stack.pop();
//self.instructions.pop_front();
return Ok(());
}
},
}
}
if command.unwrap() == "ADD".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap()); return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
self.stack.push(x.unwrap() + y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
if command.unwrap() == "MUL".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap());return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
self.stack.push(x.unwrap() * y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
if command.unwrap() == "SUB".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap());return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
self.stack.push(x.unwrap() - y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
if command.unwrap() == "DIV".to_string()
{
match iter.next()
{
Some(_) => {self.instructions.push_front(command_line.unwrap());return Err(RuntimeError::InvalidCommand);},
None =>
{
if self.stack.is_empty() || self.stack.len() == 1
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::StackUnderflow);
}
else
{
if iter.next().is_none()
{
let x = self.stack.pop();
let y = self.stack.pop();
if y.unwrap() == 0
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::DivideByZero);
}
self.stack.push(x.unwrap() / y.unwrap());
//self.instructions.pop_front();
return Ok(());
}
else
{
self.instructions.push_front(command_line.unwrap());
return Err(RuntimeError::InvalidCommand);
}
}
},
}
}
return Err(RuntimeError::InvalidCommand);
}
/// "Обръща" последно-изпълнената инструкция с `.forward()`. Това може да се изпълнява отново и
/// отново за всяка инструкция, изпълнена с `.forward()` -- тоест, не пазим само последната
/// инструкция, а списък/стек от всичките досега.
///
/// Ако няма инструкция за връщане, очакваме `RuntimeError::NoInstructions`.
///
pub fn back(&mut self) -> Result<(), RuntimeError>
{
if self.index == 0
{
return Err(RuntimeError::NoInstructions);
}
self.index -= 1;
self.stack.clear();
self.instructions.clear();
for ins in &self.history
{
self.instructions.push_back(ins.to_string());
}
//self.history.pop_back();
let mut i = 0;
while i < self.index
{
self.forward2();
i+=1;
}
Ok(())
}
pub fn pr(&mut self)
{
println!("{:?}", self.instructions);
println!("---stack---");
println!("{:?}", self.stack);
println!("---history---");
println!("{:?}", self.history);
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-vk66ve/solution)
warning: unused `std::result::Result` that must be used
   --> src/lib.rs:503:13
    |
503 |             self.forward2();
    |             ^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unused_must_use)]` on by default
    = note: this `Result` may be an `Err` variant, which should be handled

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 2.83s
     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 ... 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_instructions_after_error stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["PUSH 2", "DIV", "PUSH 3"]`,
 right: `["PUSH 0", "PUSH 2", "DIV", "PUSH 3"]`', tests/solution_test.rs:219:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    solution_test::test_instructions_after_error

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

Йордан качи първо решение на 20.01.2021 00:28 (преди над 4 години)