Решение на Reversible Interpreter от Тодор Димов

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

Към профила на Тодор Димов

Резултати

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

Код

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<Op>,
}
#[derive(Debug)]
enum Op {
Push( i32 ), // what to push on the stack
Pop( i32 ), // what to restore on the stack (same for the following)
Add( i32, i32 ),
Mul( i32, i32 ),
Sub( i32, i32 ),
Div( i32, i32 ),
}
use std::str::SplitWhitespace;
use std::ops::{Add, Mul, Sub, Div};
impl Interpreter {
pub fn new() -> Self {
Self{ instructions: VecDeque::<String>::new(), stack: Vec::<i32>::new(), history: Vec::<Op>::new() }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
self.instructions.extend( instructions.iter().map( |s| s.to_string() ) )
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn validate_instruction(mut instruction: SplitWhitespace) -> Result<Op, RuntimeError> {
match instruction.next() {
Some( "PUSH" ) =>
match instruction.map( str::parse::<i32> ).collect::<Result<Vec<i32>,_>>() {
Ok( args ) if args.len() == 1 => Ok( Op::Push( args[ 0 ] ) ),
_ => Err( RuntimeError::InvalidCommand ),
},
Some( "POP" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "ADD" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "MUL" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Mul( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "SUB" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Sub( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "DIV" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Div( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
_ => Err( RuntimeError::InvalidCommand ),
}
}
fn exec_helper(&mut self, op: &(dyn Fn(i32, i32) -> i32 + 'static)) -> Result<(i32, i32), RuntimeError> {
let mut args = ( 0_i32, 0_i32 );
let err = RuntimeError::StackUnderflow;
self.stack.pop().ok_or( err.clone() ).and_then( |i| { args.0 = i; self.stack.pop().ok_or( err.clone() ) } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).map_err( |_| { self.stack.push( args.0 ); err.clone() } )
}
fn execute_instruction(&mut self, instruction: Op) -> Result<(), RuntimeError> {
match instruction {
Op::Div( _, _ ) if self.stack.len() >= 2 && self.stack[ self.stack.len() - 2 ] == 0 => Err( RuntimeError::DivideByZero ),
Op::Push( arg ) => { self.stack.push( arg ); Ok( self.history.push( instruction ) ) },
Op::Pop( _ ) => self.stack.pop().map( |i| self.history.push( Op::Pop( i ) ) ).ok_or( RuntimeError::StackUnderflow ),
Op::Add( _, _ ) => self.exec_helper( &Add::add ).map( |t| self.history.push( Op::Add( t.0, t.1 ) ) ),
Op::Mul( _, _ ) => self.exec_helper( &Mul::mul ).map( |t| self.history.push( Op::Mul( t.0, t.1 ) ) ),
Op::Sub( _, _ ) => self.exec_helper( &Sub::sub ).map( |t| self.history.push( Op::Sub( t.0, t.1 ) ) ),
Op::Div( _, _ ) => self.exec_helper( &Div::div ).map( |t| self.history.push( Op::Div( t.0, t.1 ) ) ),
}
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions ).and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).map( |_| { self.instructions.pop_front(); } )
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
fn inverse_instruction(&mut self, instruction: Op) {
match instruction {
Op::Push( arg ) => { self.instructions.push_front( "PUSH ".to_owned() + &arg.to_string() ); self.stack.pop(); },
Op::Pop( arg ) => { self.instructions.push_front( "POP".to_string() ); self.stack.push( arg ) },
Op::Add( x, y ) => { self.instructions.push_front( "ADD".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Mul( x, y ) => { self.instructions.push_front( "MUL".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Sub( x, y ) => { self.instructions.push_front( "SUB".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Div( x, y ) => { self.instructions.push_front( "DIV".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
}
}
pub fn back(&mut self) -> Result<(), RuntimeError> {
self.history.pop().map( |op| self.inverse_instruction( op ) ).ok_or( RuntimeError::NoInstructions )
}
}

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

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

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

Тодор качи първо решение на 15.01.2021 01:33 (преди над 4 години)

Тодор качи решение на 15.01.2021 01:38 (преди над 4 години)

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<Op>,
}
#[derive(Debug)]
enum Op {
Noop,
Push( i32 ), // what to push on the stack
Pop( i32 ), // what to restore on the stack (same for the following)
Add( i32, i32 ),
Mul( i32, i32 ),
Sub( i32, i32 ),
Div( i32, i32 ),
}
use std::str::SplitWhitespace;
use std::ops::{Add, Mul, Sub, Div};
impl Interpreter {
pub fn new() -> Self {
Self{ instructions: VecDeque::<String>::new(), stack: Vec::<i32>::new(), history: Vec::<Op>::new() }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
self.instructions.extend( instructions.iter().map( |s| s.to_string() ) )
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn validate_instruction(mut instruction: SplitWhitespace) -> Result<Op, RuntimeError> {
match instruction.next() {
Some( "PUSH" ) =>
match instruction.map( str::parse::<i32> ).collect::<Result<Vec<i32>,_>>() {
Ok( args ) if args.len() == 1 => Ok( Op::Push( args[ 0 ] ) ),
_ => Err( RuntimeError::InvalidCommand ),
},
Some( "POP" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "ADD" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "MUL" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Mul( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "SUB" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Sub( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "DIV" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Div( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
_ => Err( RuntimeError::InvalidCommand ),
}
}
fn exec_helper(&mut self, op: &(dyn Fn(i32, i32) -> i32 + 'static)) -> Result<(i32, i32), RuntimeError> {
- let mut x = 0_i32;
- let mut y = 0_i32;
+ let mut args = ( 0_i32, 0_i32 );
- self.stack.pop().and_then( |i| { x = i; self.stack.pop() } ).map( |i| { y = i; self.stack.push( op( x, y ) ) } ).ok_or( RuntimeError::StackUnderflow )?;
-
- Ok( ( x, y ) )
+ self.stack.pop().and_then( |i| { args.0 = i; self.stack.pop() } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).ok_or( RuntimeError::StackUnderflow )
}
fn execute_instruction(&mut self, instruction: Op) -> Result<(), RuntimeError> {
match instruction {
Op::Div( _, _ ) if self.stack.len() >= 2 && self.stack[ self.stack.len() - 2 ] == 0 => Err( RuntimeError::DivideByZero ),
Op::Push( arg ) => { self.stack.push( arg ); Ok( self.history.push( instruction ) ) },
Op::Pop( _ ) => self.stack.pop().map( |i| self.history.push( Op::Pop( i ) ) ).ok_or( RuntimeError::StackUnderflow ),
Op::Add( _, _ ) => self.exec_helper( &Add::add ).map( |t| self.history.push( Op::Add( t.0, t.1 ) ) ),
Op::Mul( _, _ ) => self.exec_helper( &Mul::mul ).map( |t| self.history.push( Op::Mul( t.0, t.1 ) ) ),
Op::Sub( _, _ ) => self.exec_helper( &Sub::sub ).map( |t| self.history.push( Op::Sub( t.0, t.1 ) ) ),
Op::Div( _, _ ) => self.exec_helper( &Div::div ).map( |t| self.history.push( Op::Div( t.0, t.1 ) ) ),
_ => Err( RuntimeError::StackUnderflow ),
}
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions )
.and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).and_then( |_| { 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),
_ => (),
}
}
}
/// "Обръща" последно-изпълнената инструкция с `.forward()`. Това може да се изпълнява отново и
/// отново за всяка инструкция, изпълнена с `.forward()` -- тоест, не пазим само последната
/// инструкция, а списък/стек от всичките досега.
///
/// Ако няма инструкция за връщане, очакваме `RuntimeError::NoInstructions`.
///
pub fn back(&mut self) -> Result<(), RuntimeError> {
todo!()
}
}
fn main(){
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 0",
"PUSH 42",
"DIV",
]);
interpreter.forward().unwrap(); // PUSH 0
interpreter.forward().unwrap(); // PUSH 42
// Дотук добре:
println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
// Instructions: ["DIV"]
// Stack: [0, 42]
println!("{:?}", interpreter.forward());
// => Err(DivideByZero)... Опа, объркахме първата команда, дай назад
interpreter.back().unwrap();
interpreter.back().unwrap();
interpreter.current_instruction().map(|i| *i = String::from("PUSH 2"));
interpreter.run().unwrap();
println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
// Instructions: []
// Stack: [21]
}
#[test]
fn test_basic() {
let mut interpreter = Interpreter::new();
interpreter.add_instructions(&[
"PUSH 1",
"PUSH 2",
"PUSH 3",
"ADD",
]);
assert_eq!(interpreter.instructions, &[
"PUSH 1",
"PUSH 2",
"PUSH 3",
"ADD",
]);
assert_eq!(interpreter.stack, &[]);
interpreter.forward().unwrap();
interpreter.forward().unwrap();
interpreter.forward().unwrap();
assert_eq!(interpreter.instructions, &["ADD"]);
assert_eq!(interpreter.stack, &[1, 2, 3]);
interpreter.run().unwrap();
assert_eq!(interpreter.instructions.len(), 0);
assert_eq!(interpreter.stack, &[1, 5]);
interpreter.back().unwrap();
interpreter.back().unwrap();
assert_eq!(interpreter.instructions, &[
"PUSH 3",
"ADD",
]);
assert_eq!(interpreter.stack, &[1, 2]);
interpreter.add_instructions(&["ADD", "ADD"]);
assert_eq!(interpreter.run(), Err(RuntimeError::StackUnderflow));
assert_eq!(interpreter.current_instruction().unwrap(), "ADD");
}

Тодор качи решение на 15.01.2021 13:17 (преди над 4 години)

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<Op>,
}
#[derive(Debug)]
enum Op {
- Noop,
Push( i32 ), // what to push on the stack
Pop( i32 ), // what to restore on the stack (same for the following)
Add( i32, i32 ),
Mul( i32, i32 ),
Sub( i32, i32 ),
Div( i32, i32 ),
}
use std::str::SplitWhitespace;
use std::ops::{Add, Mul, Sub, Div};
impl Interpreter {
pub fn new() -> Self {
Self{ instructions: VecDeque::<String>::new(), stack: Vec::<i32>::new(), history: Vec::<Op>::new() }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
self.instructions.extend( instructions.iter().map( |s| s.to_string() ) )
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn validate_instruction(mut instruction: SplitWhitespace) -> Result<Op, RuntimeError> {
match instruction.next() {
Some( "PUSH" ) =>
match instruction.map( str::parse::<i32> ).collect::<Result<Vec<i32>,_>>() {
Ok( args ) if args.len() == 1 => Ok( Op::Push( args[ 0 ] ) ),
_ => Err( RuntimeError::InvalidCommand ),
},
- Some( "POP" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
- Some( "ADD" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
- Some( "MUL" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Mul( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
- Some( "SUB" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Sub( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
- Some( "DIV" ) => instruction.next().map( |_| Op::Noop ).xor( Some( Op::Div( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
+ Some( "POP" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
+ Some( "ADD" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
+ Some( "MUL" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Mul( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
+ Some( "SUB" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Sub( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
+ Some( "DIV" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Div( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
_ => Err( RuntimeError::InvalidCommand ),
}
}
fn exec_helper(&mut self, op: &(dyn Fn(i32, i32) -> i32 + 'static)) -> Result<(i32, i32), RuntimeError> {
let mut args = ( 0_i32, 0_i32 );
-
- self.stack.pop().and_then( |i| { args.0 = i; self.stack.pop() } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).ok_or( RuntimeError::StackUnderflow )
+ let err = RuntimeError::StackUnderflow;
+ self.stack.pop().ok_or( err.clone() ).and_then( |i| { args.0 = i; self.stack.pop().ok_or( err.clone() ) } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).or_else( |_| { self.stack.push( args.0 ); Err( err.clone() ) } )
}
fn execute_instruction(&mut self, instruction: Op) -> Result<(), RuntimeError> {
match instruction {
Op::Div( _, _ ) if self.stack.len() >= 2 && self.stack[ self.stack.len() - 2 ] == 0 => Err( RuntimeError::DivideByZero ),
Op::Push( arg ) => { self.stack.push( arg ); Ok( self.history.push( instruction ) ) },
Op::Pop( _ ) => self.stack.pop().map( |i| self.history.push( Op::Pop( i ) ) ).ok_or( RuntimeError::StackUnderflow ),
Op::Add( _, _ ) => self.exec_helper( &Add::add ).map( |t| self.history.push( Op::Add( t.0, t.1 ) ) ),
Op::Mul( _, _ ) => self.exec_helper( &Mul::mul ).map( |t| self.history.push( Op::Mul( t.0, t.1 ) ) ),
Op::Sub( _, _ ) => self.exec_helper( &Sub::sub ).map( |t| self.history.push( Op::Sub( t.0, t.1 ) ) ),
Op::Div( _, _ ) => self.exec_helper( &Div::div ).map( |t| self.history.push( Op::Div( t.0, t.1 ) ) ),
- _ => Err( RuntimeError::StackUnderflow ),
}
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions )
- .and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).and_then( |_| { self.instructions.pop_front(); Ok( () ) } )
+ .and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).map( |_| { self.instructions.pop_front(); () } )
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
- /// "Обръща" последно-изпълнената инструкция с `.forward()`. Това може да се изпълнява отново и
- /// отново за всяка инструкция, изпълнена с `.forward()` -- тоест, не пазим само последната
- /// инструкция, а списък/стек от всичките досега.
- ///
- /// Ако няма инструкция за връщане, очакваме `RuntimeError::NoInstructions`.
- ///
- pub fn back(&mut self) -> Result<(), RuntimeError> {
- todo!()
+ fn inverse_instruction(&mut self, instruction: Op) {
+ match instruction {
+ Op::Push( arg ) => { self.instructions.push_front( "PUSH ".to_owned() + &arg.to_string() ); self.stack.pop(); () },
+ Op::Pop( arg ) => { self.instructions.push_front( "POP".to_string() ); self.stack.push( arg ) },
+ Op::Add( x, y ) => { self.instructions.push_front( "ADD".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
+ Op::Mul( x, y ) => { self.instructions.push_front( "MUL".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
+ Op::Sub( x, y ) => { self.instructions.push_front( "SUB".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
+ Op::Div( x, y ) => { self.instructions.push_front( "DIV".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
+ }
}
-}
-fn main(){
- let mut interpreter = Interpreter::new();
+ pub fn back(&mut self) -> Result<(), RuntimeError> {
- interpreter.add_instructions(&[
+ self.history.pop().map( |op| self.inverse_instruction( op ) ).ok_or( RuntimeError::NoInstructions )
- "PUSH 0",
+ }
- "PUSH 42",
+}
- "DIV",
- ]);
-
- interpreter.forward().unwrap(); // PUSH 0
- interpreter.forward().unwrap(); // PUSH 42
-
- // Дотук добре:
- println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
- // Instructions: ["DIV"]
- // Stack: [0, 42]
-
- println!("{:?}", interpreter.forward());
- // => Err(DivideByZero)... Опа, объркахме първата команда, дай назад
-
- interpreter.back().unwrap();
- interpreter.back().unwrap();
- interpreter.current_instruction().map(|i| *i = String::from("PUSH 2"));
-
- interpreter.run().unwrap();
- println!("Instructions: {:?}\nStack: {:?}", interpreter.instructions, interpreter.stack);
- // Instructions: []
- // Stack: [21]
-}
-
-#[test]
-fn test_basic() {
- let mut interpreter = Interpreter::new();
- interpreter.add_instructions(&[
- "PUSH 1",
- "PUSH 2",
- "PUSH 3",
- "ADD",
- ]);
-
- assert_eq!(interpreter.instructions, &[
- "PUSH 1",
- "PUSH 2",
- "PUSH 3",
- "ADD",
- ]);
- assert_eq!(interpreter.stack, &[]);
-
- interpreter.forward().unwrap();
- interpreter.forward().unwrap();
- interpreter.forward().unwrap();
-
- assert_eq!(interpreter.instructions, &["ADD"]);
- assert_eq!(interpreter.stack, &[1, 2, 3]);
-
- interpreter.run().unwrap();
-
- assert_eq!(interpreter.instructions.len(), 0);
- assert_eq!(interpreter.stack, &[1, 5]);
-
- interpreter.back().unwrap();
- interpreter.back().unwrap();
-
- assert_eq!(interpreter.instructions, &[
- "PUSH 3",
- "ADD",
- ]);
- assert_eq!(interpreter.stack, &[1, 2]);
-
- interpreter.add_instructions(&["ADD", "ADD"]);
-
- assert_eq!(interpreter.run(), Err(RuntimeError::StackUnderflow));
- assert_eq!(interpreter.current_instruction().unwrap(), "ADD");
-}

Тодор качи решение на 15.01.2021 14:24 (преди над 4 години)

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<Op>,
}
#[derive(Debug)]
enum Op {
Push( i32 ), // what to push on the stack
Pop( i32 ), // what to restore on the stack (same for the following)
Add( i32, i32 ),
Mul( i32, i32 ),
Sub( i32, i32 ),
Div( i32, i32 ),
}
use std::str::SplitWhitespace;
use std::ops::{Add, Mul, Sub, Div};
impl Interpreter {
pub fn new() -> Self {
Self{ instructions: VecDeque::<String>::new(), stack: Vec::<i32>::new(), history: Vec::<Op>::new() }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
self.instructions.extend( instructions.iter().map( |s| s.to_string() ) )
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn validate_instruction(mut instruction: SplitWhitespace) -> Result<Op, RuntimeError> {
match instruction.next() {
Some( "PUSH" ) =>
match instruction.map( str::parse::<i32> ).collect::<Result<Vec<i32>,_>>() {
Ok( args ) if args.len() == 1 => Ok( Op::Push( args[ 0 ] ) ),
_ => Err( RuntimeError::InvalidCommand ),
},
Some( "POP" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "ADD" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "MUL" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Mul( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "SUB" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Sub( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "DIV" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Div( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
_ => Err( RuntimeError::InvalidCommand ),
}
}
fn exec_helper(&mut self, op: &(dyn Fn(i32, i32) -> i32 + 'static)) -> Result<(i32, i32), RuntimeError> {
let mut args = ( 0_i32, 0_i32 );
let err = RuntimeError::StackUnderflow;
self.stack.pop().ok_or( err.clone() ).and_then( |i| { args.0 = i; self.stack.pop().ok_or( err.clone() ) } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).or_else( |_| { self.stack.push( args.0 ); Err( err.clone() ) } )
}
fn execute_instruction(&mut self, instruction: Op) -> Result<(), RuntimeError> {
match instruction {
Op::Div( _, _ ) if self.stack.len() >= 2 && self.stack[ self.stack.len() - 2 ] == 0 => Err( RuntimeError::DivideByZero ),
Op::Push( arg ) => { self.stack.push( arg ); Ok( self.history.push( instruction ) ) },
Op::Pop( _ ) => self.stack.pop().map( |i| self.history.push( Op::Pop( i ) ) ).ok_or( RuntimeError::StackUnderflow ),
Op::Add( _, _ ) => self.exec_helper( &Add::add ).map( |t| self.history.push( Op::Add( t.0, t.1 ) ) ),
Op::Mul( _, _ ) => self.exec_helper( &Mul::mul ).map( |t| self.history.push( Op::Mul( t.0, t.1 ) ) ),
Op::Sub( _, _ ) => self.exec_helper( &Sub::sub ).map( |t| self.history.push( Op::Sub( t.0, t.1 ) ) ),
Op::Div( _, _ ) => self.exec_helper( &Div::div ).map( |t| self.history.push( Op::Div( t.0, t.1 ) ) ),
}
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
- self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions )
- .and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).map( |_| { self.instructions.pop_front(); () } )
+ self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions ).and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).map( |_| { self.instructions.pop_front(); () } )
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
fn inverse_instruction(&mut self, instruction: Op) {
match instruction {
Op::Push( arg ) => { self.instructions.push_front( "PUSH ".to_owned() + &arg.to_string() ); self.stack.pop(); () },
Op::Pop( arg ) => { self.instructions.push_front( "POP".to_string() ); self.stack.push( arg ) },
Op::Add( x, y ) => { self.instructions.push_front( "ADD".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Mul( x, y ) => { self.instructions.push_front( "MUL".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Sub( x, y ) => { self.instructions.push_front( "SUB".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Div( x, y ) => { self.instructions.push_front( "DIV".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
}
}
pub fn back(&mut self) -> Result<(), RuntimeError> {
self.history.pop().map( |op| self.inverse_instruction( op ) ).ok_or( RuntimeError::NoInstructions )
}
}

Тодор качи решение на 16.01.2021 17:29 (преди над 4 години)

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RuntimeError {
DivideByZero,
StackUnderflow,
InvalidCommand,
NoInstructions,
}
use std::collections::VecDeque;
#[derive(Debug, Default)]
pub struct Interpreter {
pub instructions: VecDeque<String>,
pub stack: Vec<i32>,
history: Vec<Op>,
}
#[derive(Debug)]
enum Op {
Push( i32 ), // what to push on the stack
Pop( i32 ), // what to restore on the stack (same for the following)
Add( i32, i32 ),
Mul( i32, i32 ),
Sub( i32, i32 ),
Div( i32, i32 ),
}
use std::str::SplitWhitespace;
use std::ops::{Add, Mul, Sub, Div};
impl Interpreter {
pub fn new() -> Self {
Self{ instructions: VecDeque::<String>::new(), stack: Vec::<i32>::new(), history: Vec::<Op>::new() }
}
pub fn add_instructions(&mut self, instructions: &[&str]) {
self.instructions.extend( instructions.iter().map( |s| s.to_string() ) )
}
pub fn current_instruction(&mut self) -> Option<&mut String> {
self.instructions.front_mut()
}
fn validate_instruction(mut instruction: SplitWhitespace) -> Result<Op, RuntimeError> {
match instruction.next() {
Some( "PUSH" ) =>
match instruction.map( str::parse::<i32> ).collect::<Result<Vec<i32>,_>>() {
Ok( args ) if args.len() == 1 => Ok( Op::Push( args[ 0 ] ) ),
_ => Err( RuntimeError::InvalidCommand ),
},
Some( "POP" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "ADD" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "MUL" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Mul( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "SUB" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Sub( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
Some( "DIV" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Div( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
_ => Err( RuntimeError::InvalidCommand ),
}
}
fn exec_helper(&mut self, op: &(dyn Fn(i32, i32) -> i32 + 'static)) -> Result<(i32, i32), RuntimeError> {
let mut args = ( 0_i32, 0_i32 );
let err = RuntimeError::StackUnderflow;
- self.stack.pop().ok_or( err.clone() ).and_then( |i| { args.0 = i; self.stack.pop().ok_or( err.clone() ) } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).or_else( |_| { self.stack.push( args.0 ); Err( err.clone() ) } )
+ self.stack.pop().ok_or( err.clone() ).and_then( |i| { args.0 = i; self.stack.pop().ok_or( err.clone() ) } ).map( |i| { args.1 = i; self.stack.push( op( args.0, args.1 ) ); args } ).map_err( |_| { self.stack.push( args.0 ); err.clone() } )
}
fn execute_instruction(&mut self, instruction: Op) -> Result<(), RuntimeError> {
match instruction {
Op::Div( _, _ ) if self.stack.len() >= 2 && self.stack[ self.stack.len() - 2 ] == 0 => Err( RuntimeError::DivideByZero ),
Op::Push( arg ) => { self.stack.push( arg ); Ok( self.history.push( instruction ) ) },
Op::Pop( _ ) => self.stack.pop().map( |i| self.history.push( Op::Pop( i ) ) ).ok_or( RuntimeError::StackUnderflow ),
Op::Add( _, _ ) => self.exec_helper( &Add::add ).map( |t| self.history.push( Op::Add( t.0, t.1 ) ) ),
Op::Mul( _, _ ) => self.exec_helper( &Mul::mul ).map( |t| self.history.push( Op::Mul( t.0, t.1 ) ) ),
Op::Sub( _, _ ) => self.exec_helper( &Sub::sub ).map( |t| self.history.push( Op::Sub( t.0, t.1 ) ) ),
Op::Div( _, _ ) => self.exec_helper( &Div::div ).map( |t| self.history.push( Op::Div( t.0, t.1 ) ) ),
}
}
pub fn forward(&mut self) -> Result<(), RuntimeError> {
- self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions ).and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).map( |_| { self.instructions.pop_front(); () } )
+ self.instructions.front().map( |s| s.split_whitespace() ).ok_or( RuntimeError::NoInstructions ).and_then( Self::validate_instruction ).and_then( |op| self.execute_instruction( op ) ).map( |_| { self.instructions.pop_front(); } )
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
loop {
match self.forward() {
Err(RuntimeError::NoInstructions) => return Ok(()),
Err(e) => return Err(e),
_ => (),
}
}
}
fn inverse_instruction(&mut self, instruction: Op) {
match instruction {
- Op::Push( arg ) => { self.instructions.push_front( "PUSH ".to_owned() + &arg.to_string() ); self.stack.pop(); () },
+ Op::Push( arg ) => { self.instructions.push_front( "PUSH ".to_owned() + &arg.to_string() ); self.stack.pop(); },
Op::Pop( arg ) => { self.instructions.push_front( "POP".to_string() ); self.stack.push( arg ) },
Op::Add( x, y ) => { self.instructions.push_front( "ADD".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Mul( x, y ) => { self.instructions.push_front( "MUL".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Sub( x, y ) => { self.instructions.push_front( "SUB".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
Op::Div( x, y ) => { self.instructions.push_front( "DIV".to_string() ); self.stack.pop(); self.stack.push( y ); self.stack.push( x ) },
}
}
pub fn back(&mut self) -> Result<(), RuntimeError> {
self.history.pop().map( |op| self.inverse_instruction( op ) ).ok_or( RuntimeError::NoInstructions )
}
}

Сори, че чак сега пиша. Предполагам, че говориш за тоя блок?

match instruction.next() {
    Some( "PUSH" ) =>
        match instruction.map( str::parse::<i32> ).collect::<Result<Vec<i32>,_>>() {
            Ok( args ) if args.len() == 1 => Ok( Op::Push( args[ 0 ] ) ),
            _ => Err( RuntimeError::InvalidCommand ),
        },
    Some( "POP" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Pop( 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
    Some( "ADD" ) => instruction.next().map( |_| Op::Pop( 0 ) ).xor( Some( Op::Add( 0, 0 ) ) ).ok_or( RuntimeError::InvalidCommand ),
    ...

Винаги можеш поне част от общата логика да я побереш синтактично в макрос. Примерно нещо като:

Some("ADD") => build_instruction!(Op::Add(0))

С дефиниция на макроса над match-а:

macro_rules! build_instruction {
    ($expr:expr) => {
        instruction.next().map(|_| Op::Pop( 0 )).xor(Some($expr)).ok_or(RuntimeError::InvalidCommand)
    }
}

Честно казано, обаче, не съм сигурен какво точно се опитваш да постигнеш с pop-а и xor-овете. Може да пробваш да match-неш (instruction.next(), instruction.next()) и така валидния клон на PUSH ще е Some("PUSH"), Some(arg), валидните на останалите ще са Some("ADD"), None, и default-ния ще си остане InvalidCommand. Във всеки от клоновете просто ще си върнеш Ok(Op::<whatever>(...)). Или даже може да пробваш да pop-неш неща от стека тук, но виждам, че си сложил тази логика другаде.

Не съм го пробвал, но поне на мен ми изглежда, че би сработило нещо такова.