Решение на 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::collections::HashMap;
type Operation = Box<dyn Fn(i32, i32) -> Result<i32, RuntimeError>>;
use std::fmt::{self, Debug, Formatter};
#[derive(Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: VecDeque<String>,
commands: HashMap< String, Option<Operation>>
}
impl Debug for Interpreter {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write! {f, "Interpreter {{ instructions: {:?}, stack: {:?} }}", self.instructions, self.stack}
}
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
let mut commands = HashMap::<String, Option<Operation>>::new();
commands.insert("POP".to_string(), None);
commands.insert("PUSH".to_string(), None);
commands.insert("ADD".to_string(), Some(Box::new(|a, b| Ok(a + b))));
commands.insert("SUB".to_string(), Some(Box::new(|a, b| Ok(a - b))));
commands.insert("MUL".to_string(), Some(Box::new(|a, b| Ok(a * b))));
commands.insert("DIV".to_string(), Some(Box::new(|a, b| {
if b == 0 {
return Err(RuntimeError::DivideByZero);
}
Ok(a / b)
})));
Interpreter{ instructions: VecDeque::new(),
stack: Vec::new(),
history: VecDeque::new(), commands }
}
/// Добавя инструкции от дадения списък към края на `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> {
self.instructions.front_mut()
}
fn safe_pop(stack: &mut Vec<i32>) -> Result<i32, RuntimeError> {
match stack.pop() {
None => Err(RuntimeError::StackUnderflow),
Some(elem) => Ok(elem),
}
}
fn exec_instruction(&mut self, instr: &str) -> Result<(), RuntimeError > {
let split: Vec<&str> = instr.split_whitespace().collect();
if split[0] == "PUSH" {
if split.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
} else {
if split.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
}
match &self.commands[split[0]] {
None => {
if split.len() == 2 {
let x = match split[1].parse::<i32>() {
Ok(n) => n,
Err(_) => return Err(RuntimeError::InvalidCommand),
};
self.stack.push(x)
} else {
Self::safe_pop(&mut self.stack)?;
}
},
Some(op) => {
let x = Self::safe_pop(&mut self.stack)?;
self.history.push_back(format!("PUSH {}", x));
let y = Self::safe_pop(&mut self.stack)?;
if split[0] == "DIV" && y == 0 {
self.stack.push(y);
self.stack.push(x);
self.history.pop_back();
return Err(RuntimeError::DivideByZero);
}
self.history.push_back(format!("PUSH {}", y));
self.stack.push((*op)(x, y)?);
}
}
Ok(())
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
match self.instructions.pop_front() {
None => Err(RuntimeError::NoInstructions),
Some(instr) => match self.exec_instruction(instr.as_str()) {
Err(e) => {
self.instructions.push_front(instr);
Err(e)
},
Ok(_) => {
self.history.push_back(instr);
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> {
match self.history.pop_back() {
None => return Err(RuntimeError::NoInstructions),
Some(instr) => {
let split: Vec<&str> = instr.split_whitespace().collect();
if split[0] == "PUSH" {
if split.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
} else {
if split.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
}
match &self.commands[split[0]] {
None => {
if split.len() == 2 {
Self::safe_pop(&mut self.stack)?;
self.instructions.push_front(instr)
} else {
self.instructions.push_front(instr);
let value = self.history.pop_back().unwrap();
let value1 = value.split_whitespace().last();
self.stack.push(value1.unwrap().parse::<i32>().unwrap());
self.history.push_back(value);
}
},
Some(_) => {
let _ = Self::safe_pop(&mut self.stack);
let value = self.history.pop_back().unwrap();
let value = value.split_whitespace().last();
self.stack.push(value.unwrap().parse::<i32>().unwrap());
let value = self.history.pop_back().unwrap();
let value = value.split_whitespace().last();
self.stack.push(value.unwrap().parse::<i32>().unwrap());
self.instructions.push_front(instr);
}
}
Ok(())
}
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-kxdsg0/solution) Finished test [unoptimized + debuginfo] target(s) in 3.35s 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 ... FAILED test solution_test::test_pop ... ok test solution_test::test_push ... ok test solution_test::test_restoring_instructions ... ok failures: ---- solution_test::test_invalid_args stdout ---- thread 'main' panicked at 'no entry found for key', src/lib.rs:94:16 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: solution_test::test_invalid_args test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--test solution_test'
История (2 версии и 0 коментара)
Лъчезар качи решение на 20.01.2021 16:48 (преди над 4 години)
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
use std::collections::HashMap;
type Operation = Box<dyn Fn(i32, i32) -> Result<i32, RuntimeError>>;
use std::fmt::{self, Debug, Formatter};
#[derive(Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: VecDeque<String>,
commands: HashMap< String, Option<Operation>>
}
impl Debug for Interpreter {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write! {f, "Interpreter {{ instructions: {:?}, stack: {:?} }}", self.instructions, self.stack}
}
}
impl Interpreter {
/// Конструира нов интерпретатор с празен стек и никакви инструкции.
pub fn new() -> Self {
let mut commands = HashMap::<String, Option<Operation>>::new();
commands.insert("POP".to_string(), None);
commands.insert("PUSH".to_string(), None);
commands.insert("ADD".to_string(), Some(Box::new(|a, b| Ok(a + b))));
commands.insert("SUB".to_string(), Some(Box::new(|a, b| Ok(a - b))));
commands.insert("MUL".to_string(), Some(Box::new(|a, b| Ok(a * b))));
commands.insert("DIV".to_string(), Some(Box::new(|a, b| {
if b == 0 {
return Err(RuntimeError::DivideByZero);
}
Ok(a / b)
})));
Interpreter{ instructions: VecDeque::new(),
stack: Vec::new(),
history: VecDeque::new(), commands }
}
/// Добавя инструкции от дадения списък към края на `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> {
self.instructions.front_mut()
}
fn safe_pop(stack: &mut Vec<i32>) -> Result<i32, RuntimeError> {
match stack.pop() {
None => Err(RuntimeError::StackUnderflow),
Some(elem) => Ok(elem),
}
}
fn exec_instruction(&mut self, instr: &str) -> Result<(), RuntimeError > {
let split: Vec<&str> = instr.split_whitespace().collect();
if split[0] == "PUSH" {
if split.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
} else {
if split.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
}
match &self.commands[split[0]] {
None => {
if split.len() == 2 {
let x = match split[1].parse::<i32>() {
Ok(n) => n,
Err(_) => return Err(RuntimeError::InvalidCommand),
};
self.stack.push(x)
} else {
Self::safe_pop(&mut self.stack)?;
}
},
Some(op) => {
let x = Self::safe_pop(&mut self.stack)?;
self.history.push_back(format!("PUSH {}", x));
let y = Self::safe_pop(&mut self.stack)?;
+ if split[0] == "DIV" && y == 0 {
+ self.stack.push(y);
+ self.stack.push(x);
+ self.history.pop_back();
+ return Err(RuntimeError::DivideByZero);
+ }
self.history.push_back(format!("PUSH {}", y));
self.stack.push((*op)(x, y)?);
}
}
Ok(())
}
/// Интерпретира първата инструкция в `self.instructions` по правилата описани по-горе. Записва
/// някаква информация за да може успешно да се изпълни `.back()` в по-нататъшен момент.
///
/// Ако няма инструкции, връща `RuntimeError::NoInstructions`. Другите грешки идват от
/// обясненията по-горе.
///
pub fn forward(&mut self) -> Result<(), RuntimeError> {
match self.instructions.pop_front() {
None => Err(RuntimeError::NoInstructions),
Some(instr) => match self.exec_instruction(instr.as_str()) {
Err(e) => {
self.instructions.push_front(instr);
Err(e)
},
Ok(_) => {
self.history.push_back(instr);
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> {
match self.history.pop_back() {
None => return Err(RuntimeError::NoInstructions),
Some(instr) => {
let split: Vec<&str> = instr.split_whitespace().collect();
if split[0] == "PUSH" {
if split.len() != 2 {
return Err(RuntimeError::InvalidCommand);
}
} else {
if split.len() != 1 {
return Err(RuntimeError::InvalidCommand);
}
}
match &self.commands[split[0]] {
None => {
if split.len() == 2 {
Self::safe_pop(&mut self.stack)?;
self.instructions.push_front(instr)
} else {
self.instructions.push_front(instr);
let value = self.history.pop_back().unwrap();
- let value = value.split_whitespace().last();
+ let value1 = value.split_whitespace().last();
- self.stack.push(value.unwrap().parse::<i32>().unwrap());
+ self.stack.push(value1.unwrap().parse::<i32>().unwrap());
+ self.history.push_back(value);
}
},
Some(_) => {
let _ = Self::safe_pop(&mut self.stack);
let value = self.history.pop_back().unwrap();
let value = value.split_whitespace().last();
self.stack.push(value.unwrap().parse::<i32>().unwrap());
let value = self.history.pop_back().unwrap();
let value = value.split_whitespace().last();
self.stack.push(value.unwrap().parse::<i32>().unwrap());
self.instructions.push_front(instr);
}
}
Ok(())
}
}
}
}