Решение на 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, Default)]
pub struct Interpreter {
pub instructions: VecDeque< String >,
pub stack: Vec< i32 >,
history: Vec< ( String, Vec<i32> ) >,
}
impl Interpreter {
pub fn new() -> Self {
Interpreter { instructions: VecDeque::<String>::new(), stack: vec![], history: vec![] }
}
pub fn add_instructions( &mut self, instructions: &[ &str ] ) {
for instr in instructions {
self.instructions.push_back( instr.to_string() );
}
}
pub fn current_instruction( &mut self ) -> Option< &mut String > {
if self.instructions.is_empty() {
None
} else {
Some( &mut self.instructions[0] )
}
}
pub fn current_instruction_immutable( &self ) -> Option< &String > {
if self.instructions.is_empty() {
None
} else {
Some( &self.instructions[0] )
}
}
fn do_operation( &mut self, op: &str ) -> Result< (), RuntimeError > {
if self.stack.len() < 2 {
Err( RuntimeError::StackUnderflow )
} else {
let num1 = self.stack.pop().unwrap();
let num2 = self.stack.pop().unwrap();
match op {
"ADD" => { self.stack.push( num1 + num2 ); },
"MUL" => { self.stack.push( num1 * num2 ); },
"SUB" => { self.stack.push( num1 - num2 ); },
"DIV" => {
if num2 == 0 {
self.stack.push( num2 );
self.stack.push( num1 );
return Err( RuntimeError::DivideByZero );
}
self.stack.push( num1 / num2 );
},
_ => { return Err( RuntimeError::InvalidCommand ); }
};
self.history.push( ( op.to_string(), vec![ num2, num1 ] ) );
Ok( () )
}
}
pub fn forward( &mut self ) -> Result< (), RuntimeError > {
let curr_instr = self.current_instruction_immutable();
let instruction : String;
match curr_instr {
Some( i ) => { instruction = i.to_string() },
None => { return Err( RuntimeError::NoInstructions ); }
}
let instr_vec = instruction.trim().split(' ').collect::< Vec<_> >();
if instr_vec.is_empty() {
Err( RuntimeError::InvalidCommand )
} else {
let res : Result<(), RuntimeError>;
match instr_vec[0].trim() {
"PUSH" => {
if instr_vec.len() != 2 {
res = Err( RuntimeError::InvalidCommand );
} else {
let parsed_int = instr_vec[1].parse::<i32>();
match parsed_int {
Ok( num ) => {
self.stack.push( num );
self.history.push( ( "PUSH".to_string(), vec![] ) );
res = Ok( () );
},
Err(_) => { res = Err( RuntimeError::InvalidCommand ); }
}
}
},
"POP" => {
if instr_vec.len() != 1 {
res = Err( RuntimeError::InvalidCommand );
} else {
match self.stack.pop() {
Some( num ) => {
self.history.push( ( "POP".to_string(), vec![ num ] ) );
res = Ok( () );
},
None => { res = Err( RuntimeError::StackUnderflow ); }
}
}
},
op if op == "ADD" || op == "MUL" || op == "SUB" || op == "DIV" => {
if instr_vec.len() != 1 {
res = Err( RuntimeError::InvalidCommand )
} else {
res = self.do_operation( op );
}
},
_ => { res = Err( RuntimeError::InvalidCommand ); }
};
if res.is_ok() {
self.instructions.pop_front();
}
res
}
}
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 > {
match self.history.pop() {
Some( ( instr, args ) ) => {
match instr.as_str() {
"PUSH" => {
self.instructions.push_front( instr.clone() + " " + self.stack.pop().unwrap().to_string().as_str() );
Ok( () )
},
"POP" => {
self.instructions.push_front( instr.clone() );
self.stack.push( args[0] );
Ok( () )
},
"ADD" | "MUL" | "SUB" | "DIV" => {
self.instructions.push_front( instr.clone() );
self.stack.pop();
self.stack.push( args[0] );
self.stack.push( args[1] );
Ok( () )
},
_ => Err( RuntimeError::InvalidCommand )
}
},
None => Err( RuntimeError::NoInstructions )
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_commands() {
let mut interpreter = Interpreter::new();
let initial_instructions = &[
"PUSH",
"POP 2",
"POP i am an intruder",
"wrong command",
"ADD 9",
"MUL 0",
"SUB 3",
"DIV 7",
];
interpreter.add_instructions( initial_instructions );
while !interpreter.instructions.is_empty() {
assert_eq!( interpreter.forward(), Err( RuntimeError::InvalidCommand ) );
assert_eq!( interpreter.stack, &[] );
interpreter.instructions.pop_front();
}
assert_eq!( interpreter.stack, &[] );
}
#[test]
fn test_zero_division() {
let mut interpreter = Interpreter::new();
let initial_instructions = &[
"PUSH 5",
"PUSH 13",
"PUSH -13",
"ADD",
"PUSH 10",
"DIV",
];
interpreter.add_instructions( initial_instructions );
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!( interpreter.stack, &[ 5, 0, 10 ] );
assert_eq!( interpreter.forward(), Err( RuntimeError::DivideByZero ) );
assert_eq!( interpreter.stack, &[ 5, 0, 10 ] );
assert_eq!( interpreter.instructions, &[ "DIV" ] );
}
#[test]
fn test_stack_underflow() {
let mut interpreter = Interpreter::new();
let initial_instructions = &[
"PUSH 5",
"PUSH 13",
"PUSH -13",
"ADD",
"SUB",
"MUL",
];
interpreter.add_instructions( initial_instructions );
assert_eq!( interpreter.run(), Err( RuntimeError::StackUnderflow ) );
assert_eq!( interpreter.instructions, &[ "MUL" ] );
assert_eq!( interpreter.stack, &[ -5 ] );
interpreter.back().unwrap();
assert_eq!( interpreter.instructions, &[ "SUB", "MUL" ] );
assert_eq!( interpreter.stack, &[ 5, 0 ] );
interpreter.back().unwrap();
assert_eq!( interpreter.instructions, &[ "ADD", "SUB", "MUL" ] );
assert_eq!( interpreter.stack, &[ 5, 13, -13 ] );
interpreter.back().unwrap();
assert_eq!( interpreter.instructions, &[ "PUSH -13", "ADD", "SUB", "MUL" ] );
assert_eq!( interpreter.stack, &[ 5, 13 ] );
interpreter.back().unwrap();
assert_eq!( interpreter.instructions, &[ "PUSH 13", "PUSH -13", "ADD", "SUB", "MUL" ] );
assert_eq!( interpreter.stack, &[ 5 ] );
interpreter.back().unwrap();
assert_eq!( interpreter.instructions, &[ "PUSH 5", "PUSH 13", "PUSH -13", "ADD", "SUB", "MUL" ] );
assert_eq!( interpreter.stack, &[] );
assert_eq!( interpreter.back(), Err( RuntimeError::NoInstructions ) );
}
#[test]
fn test_run() {
let mut interpreter = Interpreter::new();
let initial_instructions = &[
"PUSH 3",
"PUSH 4",
"PUSH 9",
"DIV",
];
interpreter.add_instructions( initial_instructions );
assert_eq!( interpreter.instructions, initial_instructions );
assert_eq!( interpreter.stack, &[] );
interpreter.run().unwrap();
assert_eq!( interpreter.instructions.len(), 0 );
assert_eq!( interpreter.stack, &[ 3, 2 ] );
interpreter.back().unwrap();
interpreter.back().unwrap();
assert_eq!( interpreter.instructions, &[
"PUSH 9",
"DIV",
] );
assert_eq!( interpreter.stack, &[ 3, 4 ] );
interpreter.add_instructions( &[ "MUL" ] );
interpreter.run().unwrap();
assert_eq!( interpreter.instructions.len(), 0 );
assert_eq!( interpreter.stack, &[ 6 ] );
assert_eq!( interpreter.forward(), Err( RuntimeError::NoInstructions ) );
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.back().unwrap();
let post_initial_instructions = &[
"PUSH 3",
"PUSH 4",
"PUSH 9",
"DIV",
"MUL",
];
assert_eq!( interpreter.instructions, post_initial_instructions );
interpreter.run().unwrap();
assert_eq!( interpreter.instructions.len(), 0 );
assert_eq!( interpreter.stack, &[ 6 ] );
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-yr978m/solution) Finished test [unoptimized + debuginfo] target(s) in 2.40s 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