Решение на Reversible Interpreter от Цветелин Цецков
Към профила на Цветелин Цецков
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 12 успешни тест(а)
- 0 неуспешни тест(а)
Код
use std::collections::VecDeque;
const SUPPORTED_COMMANDS: &'static [&'static str] = &[
"push", // 1 arg
"pop", // no args
"push_h", // 1 arg -- no history
"pop_h", // no args -- no history
"add", // no args
"mul", // no args
"sub", // no args
"div", // no args
];
#[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>,
// for backing
pub history: Vec<(Command, Vec<Command>)>, // [0] is the command that should be added back in the instructions, all the rest should be executed
}
impl Interpreter {
pub fn new() -> Self {
Interpreter {
instructions: VecDeque::new(),
stack: Vec::new(),
history: Vec::new(),
}
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
for instr in instructions {
let instr = instr.trim();
self.instructions.push_back(instr.into());
}
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
match self.current_instruction() {
Some(instr) => {
let res = match parse_command(instr) {
Ok(cmd) => self.execute_command(&cmd),
Err(err) => Err(err),
};
match res {
Ok(()) => {
self.instructions.pop_front();
Ok(())
}
err => err,
}
}
None => Err(RuntimeError::NoInstructions),
}
}
fn execute_command(&mut self, cmd: &Command) -> Result<(), RuntimeError> {
match cmd.instr.as_str() {
"push" => self.push_on_stack(cmd.clone().args.unwrap()[0]),
"pop" => match self.pop_from_stack() {
Ok(_) => Ok(()),
Err(err) => Err(err),
},
"push_h" => self.push_on_stack_no_hist(cmd.clone().args.unwrap()[0]),
"pop_h" => match self.pop_from_stack_no_hist() {
Ok(_) => Ok(()),
Err(err) => Err(err),
},
"add" => self.attempt_add_in_stack(),
"sub" => self.attempt_sub_in_stack(),
"mul" => self.attempt_mul_in_stack(),
"div" => self.attempt_div_in_stack(),
_ => Err(RuntimeError::InvalidCommand),
}
}
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(cmds) => {
self.instructions.push_front(cmds.0.to_string());
for i in 0..cmds.1.len() {
let res = self.execute_command(&cmds.1[i]);
match res {
Ok(()) => continue,
Err(err) => return Err(err),
}
}
return Ok(());
}
None => Err(RuntimeError::NoInstructions),
}
}
fn push_on_stack(&mut self, val: i32) -> Result<(), RuntimeError> {
self.stack.push(val);
self.history.push((
Command {
instr: "push".into(),
args: Some(vec![val]),
},
vec![Command {
instr: "pop_h".into(),
args: None,
}],
));
Ok(())
}
fn pop_from_stack(&mut self) -> Result<i32, RuntimeError> {
match self.stack.pop() {
Some(val) => {
self.history.push((
Command {
instr: "pop".into(),
args: None,
},
vec![Command {
instr: "push_h".into(),
args: Some(vec![val]),
}],
));
Ok(val)
}
None => Err(RuntimeError::StackUnderflow),
}
}
fn push_on_stack_no_hist(&mut self, val: i32) -> Result<(), RuntimeError> {
self.stack.push(val);
Ok(())
}
fn pop_from_stack_no_hist(&mut self) -> Result<i32, RuntimeError> {
match self.stack.pop() {
Some(val) => Ok(val),
None => Err(RuntimeError::StackUnderflow),
}
}
fn attempt_add_in_stack(&mut self) -> Result<(), RuntimeError> {
let operand1 = self.pop_from_stack_no_hist();
let operand2 = self.pop_from_stack_no_hist();
match operand1 {
Ok(a) => match operand2 {
Ok(b) => {
self.history.push((
Command {
instr: "add".into(),
args: None,
},
vec![
Command {
instr: "pop_h".into(),
args: None,
},
Command {
instr: "push_h".into(),
args: Some(vec![b]),
},
Command {
instr: "push_h".into(),
args: Some(vec![a]),
},
],
));
self.push_on_stack_no_hist(a + b)
}
err => {
self.push_on_stack_no_hist(a).unwrap();
err.map(|_| ())
}
},
err => err.map(|_| ()),
}
}
fn attempt_sub_in_stack(&mut self) -> Result<(), RuntimeError> {
let operand1 = self.pop_from_stack_no_hist();
let operand2 = self.pop_from_stack_no_hist();
match operand1 {
Ok(a) => match operand2 {
Ok(b) => {
self.history.push((
Command {
instr: "sub".into(),
args: None,
},
vec![
Command {
instr: "pop_h".into(),
args: None,
},
Command {
instr: "push_h".into(),
args: Some(vec![b]),
},
Command {
instr: "push_h".into(),
args: Some(vec![a]),
},
],
));
self.push_on_stack_no_hist(a - b)
}
err => {
self.push_on_stack_no_hist(a).unwrap();
err.map(|_| ())
}
},
err => err.map(|_| ()),
}
}
fn attempt_mul_in_stack(&mut self) -> Result<(), RuntimeError> {
let operand1 = self.pop_from_stack_no_hist();
let operand2 = self.pop_from_stack_no_hist();
match operand1 {
Ok(a) => match operand2 {
Ok(b) => {
self.history.push((
Command {
instr: "mul".into(),
args: None,
},
vec![
Command {
instr: "pop_h".into(),
args: None,
},
Command {
instr: "push_h".into(),
args: Some(vec![b]),
},
Command {
instr: "push_h".into(),
args: Some(vec![a]),
},
],
));
self.push_on_stack_no_hist(a * b)
}
err => {
self.push_on_stack_no_hist(a).unwrap();
err.map(|_| ())
}
},
err => err.map(|_| ()),
}
}
fn attempt_div_in_stack(&mut self) -> Result<(), RuntimeError> {
let operand1 = self.pop_from_stack_no_hist();
let operand2 = self.pop_from_stack_no_hist();
match operand1 {
Ok(a) => match operand2 {
Ok(b) => match b {
0 => {
self.push_on_stack_no_hist(a).unwrap();
self.push_on_stack_no_hist(b).unwrap();
Err(RuntimeError::DivideByZero)
}
b => {
self.history.push((
Command {
instr: "div".into(),
args: None,
},
vec![
Command {
instr: "pop_h".into(),
args: None,
},
Command {
instr: "push_h".into(),
args: Some(vec![b]),
},
Command {
instr: "push_h".into(),
args: Some(vec![a]),
},
],
));
self.push_on_stack_no_hist(a / b)
}
},
err => {
self.push_on_stack_no_hist(a).unwrap();
err.map(|_| ())
}
},
err => err.map(|_| ()),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Command {
instr: String,
args: Option<Vec<i32>>,
}
impl ToString for Command {
fn to_string(&self) -> String {
match &self.args {
Some(args) => format!("{} {}", self.instr.to_uppercase(), args[0]),
None => format!("{}", self.instr.to_uppercase()),
}
}
}
fn parse_command(instr: &String) -> Result<Command, RuntimeError> {
let parts: Vec<String> = instr.split(' ').map(|s| String::from(s)).collect();
let instr = parts[0].to_lowercase();
if SUPPORTED_COMMANDS.contains(&instr.as_str()) {
if instr.eq("push") {
// ugly, but can be solved with a map with required args
if parts.len() == 2 {
match parts[1].parse::<i32>() {
Ok(arg) => Ok(Command {
instr,
args: Some(vec![arg]),
}),
Err(_) => Err(RuntimeError::InvalidCommand),
}
} else {
Err(RuntimeError::InvalidCommand)
}
} else {
if parts.len() > 1 {
Err(RuntimeError::InvalidCommand)
} else {
Ok(Command { instr, args: None })
}
}
} else {
Err(RuntimeError::InvalidCommand)
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20210120-1538662-pibkle/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