Решение на Reversible Interpreter от Георги Катевски
Към профила на Георги Катевски
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 12 успешни тест(а)
- 0 неуспешни тест(а)
Код
#[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>,
// + още полета за поддръжка на .back() метода
pub back_vector: Vec<String>,
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
let vector: VecDeque<String> = VecDeque::new();
let vec=Vec::new();
let back_v=Vec::new();
Self{instructions:vector,stack:vec,back_vector:back_v}
}
/// Добавя инструкции от дадения списък към края на `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> {
return self.instructions.front_mut();
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
{
if self.instructions.is_empty()
{
return Err(RuntimeError::NoInstructions);
}
let slice = &self.instructions[self.instructions.len()-1][..3];
if(slice=="POP" || slice=="MUL" || slice==" SUB" || slice=="ADD" || slice=="DIV") && self.instructions[self.instructions.len()-1].len() !=3
{
return Err(RuntimeError::InvalidCommand);
}
let str=self.instructions.front();
let mut str2=String::new();
let mut flag=true;
let mut number:i32=0;
let mut sign=1;
let mut count=0;
for i in str.unwrap().chars()
{
if i==' '
{
count+=1;
flag=false;
}
if flag
{
str2.push(i);
}
if i=='-'
{
sign=0;
}
else
{
if i != ' ' && !flag
{
if str2=="POP" || str2=="DIV" || str2=="MUL" || str2=="ADD" || str2=="SUB"
{
return Err(RuntimeError::InvalidCommand);
}
if(i as i32) > 57 ||( i as i32) < 48
{
return Err(RuntimeError::InvalidCommand);
}
number*=10;
number+= i as i32-'0' as i32;
if count==2
{
return Err(RuntimeError::InvalidCommand);
}
}
}
}
if sign==0
{
number-= number*2;
}
if str2=="PUSH"
{
if &str2==str.unwrap()
{
return Err(RuntimeError::InvalidCommand);
}
self.stack.push(number);
self.back_vector.push(str.unwrap().to_string());
}
else if str2=="POP"
{
if self.stack.len()==0
{
return Err(RuntimeError::StackUnderflow) ;
}
self.back_vector.push(self.stack[self.stack.len()-1].to_string());
self.back_vector.push(str2);
self.stack.pop();
}
else if str2=="ADD"
{
if self.stack.len()<2
{
return Err(RuntimeError::StackUnderflow) ;
}
let number1=self.stack[self.stack.len()-1];
let number2=self.stack[self.stack.len()-2];
self.stack.pop();
self.stack.pop();
let number3=number1+number2;
self.stack.push(number3);
self.back_vector.push(number1.to_string());
self.back_vector.push(number2.to_string());
self.back_vector.push(str2.to_string());
}
else if str2=="MUL"
{
if self.stack.len()<2
{
return Err(RuntimeError::StackUnderflow) ;
}
let number1=self.stack[self.stack.len()-1];
let number2=self.stack[self.stack.len()-2];
self.stack.pop();
self.stack.pop();
let number3=number1*number2;
self.stack.push(number3);
self.back_vector.push(number1.to_string());
self.back_vector.push(number2.to_string());
self.back_vector.push(str2.to_string());
}
else if str2=="SUB"
{
if self.stack.len()<2
{
return Err(RuntimeError::StackUnderflow) ;
}
let number1=self.stack[self.stack.len()-1];
let number2=self.stack[self.stack.len()-2];
self.stack.pop();
self.stack.pop();
let number3=number1-number2;
self.stack.push(number3);
self.back_vector.push(number1.to_string());
self.back_vector.push(number2.to_string());
self.back_vector.push(str2.to_string());
}
else if str2=="DIV"
{
if self.stack.len()<2
{
return Err(RuntimeError::StackUnderflow) ;
}
let number1=self.stack[self.stack.len()-1];
let number2=self.stack[self.stack.len()-2];
if number2 ==0
{
return Err(RuntimeError::DivideByZero);
}
self.stack.pop();
self.stack.pop();
let number3=number1/number2;
self.stack.push(number3);
self.back_vector.push(number1.to_string());
self.back_vector.push(number2.to_string());
self.back_vector.push(str2.to_string());
}
else
{
return Err(RuntimeError::InvalidCommand)
}
}
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.back_vector.len()==0
{
return Err(RuntimeError::NoInstructions);
}
if self.back_vector[self.back_vector.len()-1 as usize] == "ADD"
|| self.back_vector[self.back_vector.len()-1 as usize] == "SUB"
|| self.back_vector[self.back_vector.len()-1 as usize] == "MUL"
|| self.back_vector[self.back_vector.len()-1 as usize] == "DIV"
{
self.stack.pop();
self.instructions.push_front(self.back_vector[self.back_vector.len()-1 as usize].clone() );
self.back_vector.pop();
self.stack.push(self.back_vector[self.back_vector.len()-1 as usize].parse::<i32>().unwrap());
self.back_vector.pop();
self.stack.push(self.back_vector[self.back_vector.len()-1 as usize].parse::<i32>().unwrap());
self.back_vector.pop();
}
else if self.back_vector[self.back_vector.len()-1 as usize] == "POP"
{
self.instructions.push_front(self.back_vector[self.back_vector.len()-1 as usize].clone() );
self.back_vector.pop();
self.stack.push(self.back_vector[self.back_vector.len()-1 as usize].parse::<i32>().unwrap());
self.back_vector.pop();
}
else {
self.instructions.push_front(self.back_vector[self.back_vector.len()-1 as usize].clone() );
self.back_vector.pop();
self.stack.pop();
}
Ok(())
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-b4mt60/solution) Finished test [unoptimized + debuginfo] target(s) in 3.91s 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