Решение на Reversible Interpreter от Пламен Николов

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

Към профила на Пламен Николов

Резултати

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

Код

use std::collections::{HashMap, VecDeque};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
#[derive(Debug)]
struct ExecutionLog {
instruction: String,
stack_args: Vec<i32>,
output_size: usize,
}
struct InterpreterAction {
action: Box<dyn Fn(&[i32], &[i32]) -> Result<Vec<i32>, RuntimeError>>,
arg_count: usize,
stack_arg_count: usize,
}
impl std::fmt::Debug for InterpreterAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Instruction {{ arg_count: {}, stack_args_count: {} }}", self.arg_count, self.stack_arg_count)
}
}
impl InterpreterAction {
fn execute(&self, args: &[i32], stack_args: &[i32]) -> Result<Vec<i32>, RuntimeError> {
(*self.action)(args, stack_args)
}
}
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<ExecutionLog>,
action_map: HashMap<String, InterpreterAction>,
}
impl Interpreter {
pub fn new() -> Self {
let mut interpreter = Interpreter {
instructions: VecDeque::new(),
stack: Vec::new(),
history: Vec::new(),
action_map: HashMap::new(),
};
interpreter.init_action_map();
interpreter
}
fn add_action<F>(&mut self, name: &str, action: F, arg_count: usize, stack_arg_count: usize)
where F: Fn(&[i32], &[i32]) -> Result<Vec<i32>, RuntimeError> + 'static
{
self.action_map.insert(String::from(name),
InterpreterAction {
action: Box::new(action),
arg_count,
stack_arg_count,
}
);
}
fn init_action_map(&mut self) {
self.add_action("PUSH", |args, _stack_args| Ok(vec![args[0]]), 1, 0);
self.add_action("POP", |_args, _stack_args| Ok(vec![]), 0, 1);
self.add_action("ADD", |_args, stack_args| Ok(vec![stack_args[1] + stack_args[0]]), 0, 2);
self.add_action("SUB", |_args, stack_args| Ok(vec![stack_args[1] - stack_args[0]]), 0, 2);
self.add_action("MUL", |_args, stack_args| Ok(vec![stack_args[1] * stack_args[0]]), 0, 2);
self.add_action("DIV", |_args, stack_args| {
if stack_args[0] == 0 {
return Err(RuntimeError::DivideByZero);
}
Ok(vec![stack_args[1] / stack_args[0]])
}, 0, 2);
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
self.instructions.append(&mut instructions.iter().map(|s| String::from(*s)).collect::<VecDeque<_>>());
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn get_current_instruction_words(& self) -> Result<Vec<&str>, RuntimeError> {
match self.instructions.front() {
None => Err(RuntimeError::NoInstructions),
Some(instruction) => Ok(instruction.split_whitespace().map(str::trim).collect::<Vec<_>>()),
}
}
fn parse_args(args: &[&str]) -> Result<Vec<i32>, RuntimeError> {
let mut parsed = Vec::with_capacity(args.len());
for arg in args.iter() {
parsed.push(match arg.parse::<i32>() {
Err(_) => return Err(RuntimeError::InvalidCommand),
Ok(arg) => arg,
});
}
Ok(parsed)
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
let instruction_words = self.get_current_instruction_words()?;
if instruction_words.len() == 0 {
return Err(RuntimeError::InvalidCommand);
}
let instruction_info = match self.action_map.get(&instruction_words[0].to_ascii_uppercase()) {
None => return Err(RuntimeError::InvalidCommand),
Some(info) => info,
};
if instruction_words.len() - 1 != instruction_info.arg_count {
return Err(RuntimeError::InvalidCommand);
}
if self.stack.len() < instruction_info.stack_arg_count {
return Err(RuntimeError::StackUnderflow);
}
let args = Interpreter::parse_args(&instruction_words[1..])?;
let stack_args = &self.stack[(self.stack.len() - instruction_info.stack_arg_count)..];
let mut output = instruction_info.execute(&args, stack_args)?;
self.history.push(ExecutionLog {
instruction: self.instructions.pop_front().unwrap(),
stack_args: Vec::from(stack_args),
output_size: output.len(),
});
self.stack.drain((self.stack.len() - instruction_info.stack_arg_count)..);
self.stack.append(&mut output);
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 mut log = match self.history.pop() {
None => return Err(RuntimeError::NoInstructions),
Some(log) => log,
};
self.instructions.push_front(log.instruction);
self.stack.drain((self.stack.len() - log.output_size)..);
self.stack.append(&mut log.stack_args);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_state(interpreter: &mut Interpreter, instructions: &[&str], stack: &[i32], current_instruction: Option<&str>) {
assert_eq!(interpreter.instructions, instructions);
assert_eq!(interpreter.stack, stack);
assert_eq!(interpreter.current_instruction().map(|current| &current[..]), current_instruction);
}
#[test]
fn test_new_interpreter() {
let mut interpreter = Interpreter::new();
assert_state(
&mut interpreter,
&[],
&[],
None,
);
}
#[test]
fn test_add_instructions() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2", "MUL"]);
assert_state(
&mut interpreter,
&["PUSH 1", "PUSH 2", "MUL"],
&[],
Some("PUSH 1"),
);
interpreter.add_instructions(&["ADD", "SOME", "MORE"]);
assert_state(
&mut interpreter,
&["PUSH 1", "PUSH 2", "MUL", "ADD", "SOME", "MORE"],
&[],
Some("PUSH 1"),
);
}
#[test]
fn test_push() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2"]);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["PUSH 2"],
&[1],
Some("PUSH 2"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&[],
&[1, 2],
None,
);
}
#[test]
fn test_pop() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "POP"]);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["POP"],
&[1],
Some("POP"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&[],
&[],
None,
);
}
#[test]
fn test_add() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2", "PUSH -3", "ADD", "ADD"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["ADD", "ADD"],
&[1, 2, -3],
Some("ADD"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["ADD"],
&[1, -1],
Some("ADD"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&[],
&[0],
None,
);
}
#[test]
fn test_sub() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH -3", "PUSH 2", "PUSH -1", "SUB", "SUB"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["SUB", "SUB"],
&[-3, 2, -1],
Some("SUB"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["SUB"],
&[-3, -3],
Some("SUB"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&[],
&[0],
None,
);
}
#[test]
fn test_mul() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH -3", "PUSH 2", "PUSH 5", "MUL", "MUL"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["MUL", "MUL"],
&[-3, 2, 5],
Some("MUL"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["MUL"],
&[-3, 10],
Some("MUL"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&[],
&[-30],
None,
);
}
#[test]
fn test_div() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH -3", "PUSH 2", "PUSH 31", "DIV", "DIV"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["DIV", "DIV"],
&[-3, 2, 31],
Some("DIV"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&["DIV"],
&[-3, 15],
Some("DIV"),
);
interpreter.forward().unwrap();
assert_state(
&mut interpreter,
&[],
&[-5],
None,
);
}
#[test]
fn test_div_zero() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 0", "PUSH 2", "DIV"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.forward(), Err(RuntimeError::DivideByZero));
assert_state(
&mut interpreter,
&["DIV"],
&[0, 2],
Some("DIV"),
);
}
#[test]
fn test_instruction_modification() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 0", "PUSH 2", "DIV"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
*interpreter.current_instruction().unwrap() = String::from("ADD");
assert_eq!(interpreter.forward(), Ok(()));
assert_state(
&mut interpreter,
&[],
&[2],
None,
);
}
#[test]
fn test_underflow() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "ADD"]);
interpreter.forward().unwrap();
assert_eq!(interpreter.forward(), Err(RuntimeError::StackUnderflow));
assert_state(
&mut interpreter,
&["ADD"],
&[1],
Some("ADD"),
);
*interpreter.current_instruction().unwrap() = String::from("SUB");
assert_eq!(interpreter.forward(), Err(RuntimeError::StackUnderflow));
assert_state(
&mut interpreter,
&["SUB"],
&[1],
Some("SUB"),
);
*interpreter.current_instruction().unwrap() = String::from("MUL");
assert_eq!(interpreter.forward(), Err(RuntimeError::StackUnderflow));
assert_state(
&mut interpreter,
&["MUL"],
&[1],
Some("MUL"),
);
*interpreter.current_instruction().unwrap() = String::from("DIV");
assert_eq!(interpreter.forward(), Err(RuntimeError::StackUnderflow));
assert_state(
&mut interpreter,
&["DIV"],
&[1],
Some("DIV"),
);
*interpreter.current_instruction().unwrap() = String::from("POP");
assert_eq!(interpreter.forward(), Ok(()));
interpreter.add_instructions(&["POP"]);
assert_eq!(interpreter.forward(), Err(RuntimeError::StackUnderflow));
assert_state(
&mut interpreter,
&["POP"],
&[],
Some("POP"),
);
}
#[test]
fn test_forward_no_instructions() {
let mut interpreter = Interpreter::new();
assert_eq!(interpreter.forward(), Err(RuntimeError::NoInstructions));
interpreter.add_instructions(&["PUSH 1", "PUSH 2"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.forward(), Err(RuntimeError::NoInstructions));
}
#[test]
fn test_run() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 4", "PUSH 2", "PUSH 2", "SUB", "POP", "PUSH 4", "DIV"]);
assert_eq!(interpreter.run(), Ok(()));
assert_state(
&mut interpreter,
&[],
&[1],
None,
);
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 4", "PUSH 2", "PUSH 2", "SUB", "PUSH 4", "DIV"]);
assert_eq!(interpreter.run(), Err(RuntimeError::DivideByZero));
assert_state(
&mut interpreter,
&["DIV"],
&[4, 0, 4],
Some("DIV"),
);
}
#[test]
fn test_back() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2", "ADD", "POP"]);
interpreter.run().unwrap();
assert_state(
&mut interpreter,
&[],
&[],
None,
);
interpreter.back().unwrap();
assert_state(
&mut interpreter,
&["POP"],
&[3],
Some("POP"),
);
interpreter.back().unwrap();
assert_state(
&mut interpreter,
&["ADD", "POP"],
&[1, 2],
Some("ADD"),
);
interpreter.back().unwrap();
assert_state(
&mut interpreter,
&["PUSH 2", "ADD", "POP"],
&[1],
Some("PUSH 2"),
);
}
#[test]
fn test_back_no_instructions() {
let mut interpreter = Interpreter::new();
assert_eq!(interpreter.back(), Err(RuntimeError::NoInstructions));
interpreter.add_instructions(&["PUSH 1"]);
interpreter.forward().unwrap();
interpreter.back().unwrap();
assert_eq!(interpreter.back(), Err(RuntimeError::NoInstructions));
}
#[test]
fn test_case_insensitivity() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "Push 2", "add", "PuSh 3", "sUb", "PoP"]);
assert_eq!(interpreter.run(), Ok(()));
}
#[test]
fn test_whitespace_insensitivity() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 1", "PUSH 2", " ADD ", " PUSH 3 ", " SUB", "POP "]);
assert_eq!(interpreter.run(), Ok(()));
}
#[test]
fn test_invalid_arg_counts() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH"]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("PUSH 1 2");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("POP 0");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("ADD 1");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("SUB 2");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("MUL 3");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("DIV 4");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH 0", "PUSH 1", "ADD 5"]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("DIV 6");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
}
#[test]
fn test_invalid_arg_format() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUSH ONE"]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("PUSH 1.0");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
*interpreter.current_instruction().unwrap() = String::from("PUSH 1A");
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
}
#[test]
fn test_unknow_command() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&["PUT 1"]);
assert_eq!(interpreter.forward(), Err(RuntimeError::InvalidCommand));
}
}

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

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

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

Пламен качи първо решение на 15.01.2021 00:23 (преди 6 месеца)