Решение на Reversible Interpreter от Тихомир Каменов

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

Към профила на Тихомир Каменов

Резултати

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

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
pub enum Command {
Push(i32),
Pop,
Add,
Mul,
Sub,
Div,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
back_instructions: Vec<String>,
back_stack: VecDeque<i32>
}
impl Interpreter {
pub fn new() -> Self {
Self {
instructions: VecDeque::<String>::new(),
stack: Vec::<i32>::new(),
back_instructions: Vec::<String>::new(),
back_stack: VecDeque::<i32>::new()
}
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for elem in instructions {
self.instructions.push_back(elem.to_string());
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn parse_command(&mut self) -> Result<Command, RuntimeError> {
if let Some(command) = self.current_instruction() {
let mut iter = command.split_whitespace();
match iter.next() {
Some("PUSH") => {
let value = iter.next().ok_or(RuntimeError::InvalidCommand)?.parse::<i32>().map_err(|_| RuntimeError::InvalidCommand)?;
if None != iter.next() {
return Err(RuntimeError::InvalidCommand);
}
Ok(Command::Push(value))
},
Some("POP") => {
if None != iter.next() {
return Err(RuntimeError::InvalidCommand);
}
Ok(Command::Pop)
},
Some("ADD") => {
if None != iter.next() {
return Err(RuntimeError::InvalidCommand);
}
Ok(Command::Add)
},
Some("MUL") => {
if None != iter.next() {
return Err(RuntimeError::InvalidCommand);
}
Ok(Command::Mul)
},
Some("SUB") => {
if None != iter.next() {
return Err(RuntimeError::InvalidCommand);
}
Ok(Command::Sub)
},
Some("DIV") => {
if None != iter.next() {
return Err(RuntimeError::InvalidCommand);
}
Ok(Command::Div)
},
_ => return Err(RuntimeError::InvalidCommand),
}
} else {
Err(RuntimeError::NoInstructions)
}
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
match self.parse_command() {
Ok(Command::Push(v)) => {
self.stack.push(v);
},
Ok(Command::Pop) => {
self.stack.pop();
},
Ok(Command::Add) => {
let (v1, v2) = self.stack_pop()?;
self.back_stack_push(vec![v1, v2]);
self.stack.push(v1 + v2);
},
Ok(Command::Mul) => {
let (v1, v2) = self.stack_pop()?;
self.back_stack_push(vec![v1, v2]);
self.stack.push(v1 * v2);
},
Ok(Command::Sub) => {
let (v1, v2) = self.stack_pop()?;
self.back_stack_push(vec![v1, v2]);
self.stack.push(v1 - v2);
},
Ok(Command::Div) => {
let (v1, v2) = self.stack_pop()?;
if v2 == 0 {
return Err(RuntimeError::DivideByZero);
}
self.back_stack_push(vec![v1, v2]);
self.stack.push(v1/v2);
},
Err(e) => return Err(e),
}
if !self.instructions.is_empty() {
let val = self.instructions.front_mut().unwrap();
self.back_instructions.push(val.clone());
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),
_ => (),
}
}
}
pub fn back(&mut self) -> Result<(), RuntimeError> {
let command = self.back_instructions.pop().ok_or(RuntimeError::NoInstructions)?;
self.instructions.push_front(command.clone());
match command {
i if i == "POP" => {
let val = self.stack.pop();
self.back_stack.push_back(val.unwrap());
},
i if i == "ADD" || i == "MUL" || i == "SUB" || i == "DIV" => {
// extract the result from the operator
self.stack.pop();
// return the arguments of the operator
let v1 = self.back_stack.pop_back();
let v2 = self.back_stack.pop_back();
self.stack.push(v1.unwrap());
self.stack.push(v2.unwrap());
}
// "PUSH [number]" in this case we just remove the last element on the stack
_ => {self.stack.pop(); ()},
};
Ok(())
}
fn back_stack_push(&mut self, values: Vec<i32>) {
for val in values.iter() {
self.back_stack.push_back(*val);
}
}
fn stack_pop(&mut self) -> Result<(i32, i32), RuntimeError> {
let v1 = self.stack.pop();
let v2 = self.stack.pop();
if v1.is_none() || v2.is_none() {
return Err(RuntimeError::StackUnderflow);
}
Ok((v1.unwrap(), v2.unwrap()))
}
}
#[cfg(test)]
mod tests {
use super::Interpreter;
#[test]
fn test_new() {
let interpreter = Interpreter::new();
assert_eq!(interpreter.instructions.len(), 0);
assert_eq!(interpreter.stack.len(), 0);
}
#[test]
fn test_instructions() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2", "ADD"]);
assert_eq!(interpreter.instructions, &["PUSH 1", "PUSH 2", "ADD"]);
assert_eq!(interpreter.current_instruction(), Some(&mut "PUSH 1".to_string()));
interpreter.forward().unwrap();
assert_eq!(interpreter.stack, &[1]);
}
#[test]
#[should_panic]
fn test_forward_stackunderfow() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["ADD"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_forward_no_instructions() {
let mut interpreter = Interpreter::new();
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_value() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1.5"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_push() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_push_with_arguments() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1 3"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_pop() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["POP 15"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_add() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["ADD 15"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_mul() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["MUL 15"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_sub() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["SUB 15"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_div() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["DIV 15"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_invalid_command() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["HELLO"]);
interpreter.forward().unwrap();
}
#[test]
#[should_panic]
fn test_division_by_zero() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 0", "PUSH 10", "DIV"]);
interpreter.run().unwrap();
}
#[test]
fn test_example() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 5", "PUSH 2", "PUSH 10", "DIV", "MUL"]);
interpreter.run().unwrap();
assert_eq!(interpreter.stack, &[25]);
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.current_instruction().map(|i| *i = String::from("PUSH 20"));
interpreter.run().unwrap();
assert_eq!(interpreter.stack, &[50]);
}
}

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

Compiling solution v0.1.0 (/tmp/d20210120-1538662-gt7nau/solution)
    Finished test [unoptimized + debuginfo] target(s) in 2.48s
     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 ... FAILED
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

failures:

---- solution_test::test_errors_1 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Ok(())`,
 right: `Err(StackUnderflow)`', tests/solution_test.rs:159:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    solution_test::test_errors_1

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 13:03 (преди над 4 години)