Решение на Reversible Interpreter от Теодор Тошков
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 12 успешни тест(а)
- 0 неуспешни тест(а)
Код
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug)]
enum Instruction {
PUSH(i32),
POP,
ADD,
MUL,
SUB,
DIV,
}
#[derive(Debug)]
enum ReversedInstruction {
PUSH(i32),
POP(i32),
ADD(i32, i32),
MUL(i32, i32),
SUB(i32, i32),
DIV(i32, i32),
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<ReversedInstruction>,
}
impl Interpreter {
pub fn new() -> Self {
Self::default()
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for instruction in instructions {
self.instructions.push_back(instruction.to_string())
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn parse_instruction(instruction: &str) -> Result<Instruction, RuntimeError> {
let mut parts = instruction.trim().split_whitespace();
match parts.next() {
Some("PUSH") => match parts.next() {
Some(maybe_number) => match maybe_number.parse::<i32>() {
Err(_) => Err(RuntimeError::InvalidCommand),
Ok(number) => match parts.next() {
None => Ok(Instruction::PUSH(number)),
_ => Err(RuntimeError::InvalidCommand),
},
},
None => Err(RuntimeError::InvalidCommand),
},
Some("POP") => match parts.next() {
None => Ok(Instruction::POP),
_ => Err(RuntimeError::InvalidCommand),
},
Some("ADD") => match parts.next() {
None => Ok(Instruction::ADD),
_ => Err(RuntimeError::InvalidCommand),
},
Some("SUB") => match parts.next() {
None => Ok(Instruction::SUB),
_ => Err(RuntimeError::InvalidCommand),
},
Some("MUL") => match parts.next() {
None => Ok(Instruction::MUL),
_ => Err(RuntimeError::InvalidCommand),
},
Some("DIV") => match parts.next() {
None => Ok(Instruction::DIV),
_ => Err(RuntimeError::InvalidCommand),
},
_ => Err(RuntimeError::InvalidCommand),
}
}
fn execute_instruction(&mut self, instruction: Instruction) -> Result<(), RuntimeError> {
match instruction {
Instruction::PUSH(number) => {
self.stack.push(number);
self.history.push(ReversedInstruction::PUSH(number));
}
Instruction::POP => match self.stack.pop() {
None => {
return Err(RuntimeError::StackUnderflow);
}
Some(number) => {
self.history.push(ReversedInstruction::POP(number));
}
},
Instruction::ADD => {
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
} else {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.history.push(ReversedInstruction::ADD(lhs, rhs));
self.stack.push(lhs + rhs);
}
}
Instruction::SUB => {
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
} else {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.history.push(ReversedInstruction::SUB(lhs, rhs));
self.stack.push(lhs - rhs);
}
}
Instruction::MUL => {
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
} else {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
self.history.push(ReversedInstruction::MUL(lhs, rhs));
self.stack.push(lhs * rhs);
}
}
Instruction::DIV => {
if self.stack.len() < 2 {
return Err(RuntimeError::StackUnderflow);
} else {
let lhs = self.stack.pop().unwrap();
let rhs = self.stack.pop().unwrap();
if rhs == 0 {
self.stack.push(rhs);
self.stack.push(lhs);
return Err(RuntimeError::DivideByZero);
}
self.history.push(ReversedInstruction::DIV(lhs, rhs));
self.stack.push(lhs / rhs);
}
}
};
Ok(())
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
match self.instructions.front() {
None => Err(RuntimeError::NoInstructions),
Some(maybe_instruction) => {
let instruction = Self::parse_instruction(&maybe_instruction)?;
self.execute_instruction(instruction)?;
self.instructions.pop_front();
Ok(())
}
}
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
fn restore_instruction(instruction: ReversedInstruction) -> String {
match instruction {
ReversedInstruction::PUSH(number) => format!("PUSH {}", number),
ReversedInstruction::POP(_) => "POP".to_string(),
ReversedInstruction::ADD(_, _) => "ADD".to_string(),
ReversedInstruction::SUB(_, _) => "SUB".to_string(),
ReversedInstruction::MUL(_, _) => "MUL".to_string(),
ReversedInstruction::DIV(_, _) => "DIV".to_string(),
}
}
fn reverse_instruction(&mut self, instruction: ReversedInstruction) {
match instruction {
ReversedInstruction::PUSH(_) => {
self.stack.pop();
}
ReversedInstruction::POP(number) => {
self.stack.push(number);
}
ReversedInstruction::ADD(lhs, rhs)
| ReversedInstruction::SUB(lhs, rhs)
| ReversedInstruction::MUL(lhs, rhs)
| ReversedInstruction::DIV(lhs, rhs) => {
self.stack.pop();
self.stack.push(rhs);
self.stack.push(lhs);
}
};
self.instructions
.push_front(Self::restore_instruction(instruction));
}
pub fn back(&mut self) -> Result<(), RuntimeError> {
match self.history.pop() {
None => Err(RuntimeError::NoInstructions),
Some(reversed_instruction) => {
self.reverse_instruction(reversed_instruction);
Ok(())
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_basic() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2", "PUSH 3", "ADD"]);
assert_eq!(
interpreter.instructions,
&["PUSH 1", "PUSH 2", "PUSH 3", "ADD",]
);
assert_eq!(interpreter.stack, &[]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.instructions, &["ADD"]);
assert_eq!(interpreter.stack, &[1, 2, 3]);
interpreter.run().unwrap();
assert_eq!(interpreter.instructions.len(), 0);
assert_eq!(interpreter.stack, &[1, 5]);
interpreter.back().unwrap();
interpreter.back().unwrap();
assert_eq!(interpreter.instructions, &["PUSH 3", "ADD",]);
assert_eq!(interpreter.stack, &[1, 2]);
interpreter.add_instructions(&["ADD", "ADD"]);
assert_eq!(interpreter.run(), Err(RuntimeError::StackUnderflow));
assert_eq!(interpreter.current_instruction().unwrap(), "ADD");
assert_eq!(interpreter.stack, &[6]);
interpreter
.current_instruction()
.map(|i| *i = String::from("PUSH 6"));
interpreter.add_instructions(&["SUB"]);
interpreter.run().unwrap();
assert_eq!(interpreter.instructions.len(), 0);
assert_eq!(interpreter.stack, &[0]);
interpreter.add_instructions(&["PUSH 36", "DIV"]);
assert_eq!(interpreter.run(), Err(RuntimeError::DivideByZero));
assert_eq!(interpreter.stack, &[0, 36]);
assert_eq!(interpreter.instructions, &["DIV"]);
interpreter.back().unwrap();
assert_eq!(interpreter.stack, &[0]);
assert_eq!(interpreter.instructions, &["PUSH 36", "DIV"]);
interpreter.back().unwrap();
assert_eq!(interpreter.stack, &[6, 6]);
assert_eq!(interpreter.instructions, &["SUB", "PUSH 36", "DIV"]);
interpreter
.current_instruction()
.map(|i| *i = String::from("ADD"));
interpreter.run().unwrap();
assert_eq!(interpreter.instructions.len(), 0);
assert_eq!(interpreter.stack, &[3]);
interpreter.add_instructions(&["POP", "PUSH 4"]);
interpreter.run().unwrap();
assert_eq!(interpreter.stack, &[4]);
interpreter.back().unwrap();
assert_eq!(interpreter.stack, &[]);
assert_eq!(interpreter.instructions, &["PUSH 4"]);
interpreter.back().unwrap();
assert_eq!(interpreter.stack, &[3]);
assert_eq!(interpreter.instructions, &["POP", "PUSH 4"]);
interpreter.add_instructions(&["ADD 1"]);
assert_eq!(interpreter.run(), Err(RuntimeError::InvalidCommand));
interpreter
.current_instruction()
.map(|i| *i = String::from("PUSH"));
assert_eq!(interpreter.run(), Err(RuntimeError::InvalidCommand));
interpreter
.current_instruction()
.map(|i| *i = String::from("PUSH 1 2"));
assert_eq!(interpreter.run(), Err(RuntimeError::InvalidCommand));
interpreter
.current_instruction()
.map(|i| *i = String::from("PUSH Z"));
assert_eq!(interpreter.run(), Err(RuntimeError::InvalidCommand));
interpreter
.current_instruction()
.map(|i| *i = String::from("PagChomp"));
assert_eq!(interpreter.run(), Err(RuntimeError::InvalidCommand));
assert_eq!(interpreter.stack, &[4]);
assert_eq!(interpreter.instructions, &["PagChomp"]);
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-6d6nds/solution) Finished test [unoptimized + debuginfo] target(s) in 2.50s 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