Решение на Reversible Interpreter от Андрей

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

Към профила на Андрей

Резултати

  • 15 точки от тестове
  • 0 бонус точки
  • 15 точки общо
  • 12 успешни тест(а)
  • 0 неуспешни тест(а)

Код

use std::collections::VecDeque;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug)]
enum UndoOp {
Add(i32, i32),
Sub(i32, i32),
Mul(i32, i32),
Div(i32, i32),
Pop(i32),
Push,
}
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<(String, UndoOp)>,
}
impl Interpreter {
pub fn new() -> Self {
let instructions = VecDeque::new();
let stack = Vec::new();
let history = Vec::new();
Self { instructions, stack, history }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for new_instruction in instructions {
self.instructions.push_back(new_instruction.trim().to_owned());
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
let undo_op = self.eval_next_line()?;
// [Unwrap] eval_next_line already accesses the first line, it must be there
let line = self.instructions.pop_front().unwrap();
self.history.push((line, undo_op));
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 (line, undo_op) = self.history.pop().ok_or(RuntimeError::NoInstructions)?;
match undo_op {
UndoOp::Add(arg1, arg2) |
UndoOp::Sub(arg1, arg2) |
UndoOp::Mul(arg1, arg2) |
UndoOp::Div(arg1, arg2) => {
self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
self.stack.push(arg2);
self.stack.push(arg1);
},
UndoOp::Pop(arg) => self.stack.push(arg),
UndoOp::Push => {
self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
},
};
self.instructions.push_front(line);
Ok(())
}
fn eval_next_line(&mut self) -> Result<UndoOp, RuntimeError> {
let line = self.instructions.front().ok_or(RuntimeError::NoInstructions)?;
let mut parts = line.split_whitespace();
let command = parts.next().unwrap();
let mut args = parts.collect::<Vec<_>>();
macro_rules! check_no_args {
() => { if args.len() != 0 { return Err(RuntimeError::InvalidCommand) } }
}
match command {
"ADD" => {
check_no_args!();
let (x, y) = self.eval_binary_op(|x, y| x + y)?;
Ok(UndoOp::Add(x, y))
},
"SUB" => {
check_no_args!();
let (x, y) = self.eval_binary_op(|x, y| x - y)?;
Ok(UndoOp::Sub(x, y))
},
"MUL" => {
check_no_args!();
let (x, y) = self.eval_binary_op(|x, y| x * y)?;
Ok(UndoOp::Mul(x, y))
},
"DIV" => {
check_no_args!();
let arg1 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
let arg2 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
if arg2 == 0 {
// Restore the stack:
self.stack.push(arg2);
self.stack.push(arg1);
return Err(RuntimeError::DivideByZero);
}
let res = arg1 / arg2;
self.stack.push(res);
Ok(UndoOp::Div(arg1, arg2))
},
"POP" => {
check_no_args!();
let value = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
Ok(UndoOp::Pop(value))
},
"PUSH" => {
let value = args.pop().ok_or(RuntimeError::InvalidCommand)?;
check_no_args!();
let value = value.parse().map_err(|_| RuntimeError::InvalidCommand)?;
self.stack.push(value);
Ok(UndoOp::Push)
},
_ => return Err(RuntimeError::InvalidCommand),
}
}
fn eval_binary_op(&mut self, op: impl Fn(i32, i32) -> i32) -> Result<(i32, i32), RuntimeError> {
let arg1 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
let arg2 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
self.stack.push(op(arg1, arg2));
Ok((arg1, arg2))
}
}

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

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

История (2 версии и 0 коментара)

Андрей качи първо решение на 12.01.2021 10:16 (преди 6 месеца)

Андрей качи решение на 16.01.2021 17:58 (преди 6 месеца)

use std::collections::VecDeque;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug)]
enum UndoOp {
Add(i32, i32),
Sub(i32, i32),
Mul(i32, i32),
Div(i32, i32),
Pop(i32),
Push,
}
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<(String, UndoOp)>,
}
impl Interpreter {
pub fn new() -> Self {
let instructions = VecDeque::new();
let stack = Vec::new();
let history = Vec::new();
Self { instructions, stack, history }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for new_instruction in instructions {
self.instructions.push_back(new_instruction.trim().to_owned());
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
let undo_op = self.eval_next_line()?;
// [Unwrap] eval_next_line already accesses the first line, it must be there
let line = self.instructions.pop_front().unwrap();
self.history.push((line, undo_op));
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 (line, undo_op) = self.history.pop().ok_or(RuntimeError::NoInstructions)?;
match undo_op {
UndoOp::Add(arg1, arg2) |
UndoOp::Sub(arg1, arg2) |
UndoOp::Mul(arg1, arg2) |
UndoOp::Div(arg1, arg2) => {
self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
self.stack.push(arg2);
self.stack.push(arg1);
},
UndoOp::Pop(arg) => self.stack.push(arg),
UndoOp::Push => {
self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
},
};
self.instructions.push_front(line);
Ok(())
}
fn eval_next_line(&mut self) -> Result<UndoOp, RuntimeError> {
let line = self.instructions.front().ok_or(RuntimeError::NoInstructions)?;
let mut parts = line.split_whitespace();
- let command = parts.next().unwrap();
- let args = parts.collect::<Vec<_>>();
+ let command = parts.next().unwrap();
+ let mut args = parts.collect::<Vec<_>>();
+ macro_rules! check_no_args {
+ () => { if args.len() != 0 { return Err(RuntimeError::InvalidCommand) } }
+ }
+
match command {
"ADD" => {
+ check_no_args!();
let (x, y) = self.eval_binary_op(|x, y| x + y)?;
Ok(UndoOp::Add(x, y))
},
"SUB" => {
+ check_no_args!();
let (x, y) = self.eval_binary_op(|x, y| x - y)?;
Ok(UndoOp::Sub(x, y))
},
"MUL" => {
+ check_no_args!();
let (x, y) = self.eval_binary_op(|x, y| x * y)?;
Ok(UndoOp::Mul(x, y))
},
"DIV" => {
+ check_no_args!();
let arg1 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
let arg2 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
if arg2 == 0 {
// Restore the stack:
self.stack.push(arg2);
self.stack.push(arg1);
return Err(RuntimeError::DivideByZero);
}
let res = arg1 / arg2;
self.stack.push(res);
Ok(UndoOp::Div(arg1, arg2))
},
"POP" => {
+ check_no_args!();
let value = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
Ok(UndoOp::Pop(value))
},
"PUSH" => {
- let value = args.get(0).ok_or(RuntimeError::InvalidCommand)?;
+ let value = args.pop().ok_or(RuntimeError::InvalidCommand)?;
+ check_no_args!();
let value = value.parse().map_err(|_| RuntimeError::InvalidCommand)?;
self.stack.push(value);
Ok(UndoOp::Push)
},
_ => return Err(RuntimeError::InvalidCommand),
}
}
fn eval_binary_op(&mut self, op: impl Fn(i32, i32) -> i32) -> Result<(i32, i32), RuntimeError> {
let arg1 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
let arg2 = self.stack.pop().ok_or(RuntimeError::StackUnderflow)?;
self.stack.push(op(arg1, arg2));
Ok((arg1, arg2))
}
-}
+}