hrm_interpreter 0.2.0

a small interpreter for the HRM language, used in Human Resource Machine
Documentation
use operators::Operator;
use state::InternalState;
use Value;
use Location;

enum Error {
	PointerCellContainsChar,
	NoValue{cell: Location},
	BumpChar{value: char}
}

struct BumpOp {}

impl BumpOp {
	fn explain_error(error: Error) -> String {
		match error {
			Error::NoValue{cell: Location::Cell(_cell)} => format!("There is no value at cell {:?}", _cell),
			Error::NoValue{cell: Location::Address(_cell)} => format!("There is no value at cell {:?}", _cell),
			Error::BumpChar{value: _char} => format!("Cannot bump char {:?}", _char),
			Error::PointerCellContainsChar => String::from("The selected cell should contain a number, not a char")
		}
	}
}

pub struct BumpPlusOp {
	pub cell: Location
}

pub struct BumpMinusOp {
	pub cell: Location
}

impl Operator for BumpPlusOp {
	fn changes_instruction_counter(&self) -> bool { false }

	fn apply_to(&self, s: &mut InternalState) -> Result<(), String> {
		let memory_position = match self.cell {
			Location::Cell(mempos) => Ok(mempos),
			Location::Address(mempos) => {
				let value_from_memory = s.memory[mempos].clone();
				match value_from_memory {
					Some(Value::Number{value: pointed_cell}) => Ok(pointed_cell as usize),
					Some(Value::Character{value: _}) => Err(BumpOp::explain_error(Error::PointerCellContainsChar)),
					None => Err(BumpOp::explain_error(Error::NoValue{cell: Location::Cell(mempos)}))
				}
			}
		};
		if let Err(error_message) = memory_position {
			return Err(error_message);
		}

		let mempos = memory_position.unwrap();
		let data = s.memory[mempos];
		match data {
			None => Err(BumpOp::explain_error(Error::NoValue{cell: Location::Cell(mempos)})),
			Some(Value::Number{value: _num}) => {
				s.memory[mempos] = Some(Value::Number{value: _num+1});
				s.register = s.memory[mempos];
				Ok(())
			},
			Some(Value::Character{value: _char}) =>
				Err(BumpOp::explain_error(Error::BumpChar{value: _char}))
		}

	}
}

impl Operator for BumpMinusOp {
	fn changes_instruction_counter(&self) -> bool { false }

	fn apply_to(&self, s: &mut InternalState) -> Result<(), String> {
		let memory_position = match self.cell {
			Location::Cell(mempos) => Ok(mempos),
			Location::Address(mempos) => {
				let value_from_memory = s.memory[mempos].clone();
				match value_from_memory {
					Some(Value::Number{value: pointed_cell}) => Ok(pointed_cell as usize),
					Some(Value::Character{value: _}) => Err(BumpOp::explain_error(Error::PointerCellContainsChar)),
					None => Err(BumpOp::explain_error(Error::NoValue{cell: Location::Cell(mempos)}))
				}
			}
		};
		if let Err(error_message) = memory_position {
			return Err(error_message);
		}

		let mempos = memory_position.unwrap();
		let data = s.memory[mempos];
		match data {
			None => Err(BumpOp::explain_error(Error::NoValue{cell: Location::Cell(mempos)})),
			Some(Value::Number{value: _num}) => {
				s.memory[mempos] = Some(Value::Number{value: _num-1});
				s.register = s.memory[mempos];
				Ok(())
			},
			Some(Value::Character{value: _char}) =>
				Err(BumpOp::explain_error(Error::BumpChar{value: _char}))
		}

	}
}


#[cfg(test)]
mod test {
	use Value;
	use Location;
	use state::InternalState;
	use operators::Operator;
	use operators::bump::BumpPlusOp;
	use operators::bump::BumpMinusOp;

	#[test]
	fn bumpplus_number() {
		let mut state = InternalState::new(None, 0);
		state.memory = vec!(Some(Value::Number{value: 0}));

		let result = BumpPlusOp{cell: Location::Cell(0)}.apply_to(&mut state);

		assert!(result.is_ok());
		assert!(match state.memory[0] {
			Some(Value::Number{value: 1}) => true,
			_ => false
		});
		assert!(match state.register {
			Some(Value::Number{value: 1}) => true,
			_ => false
		});
	}
	
	#[test]
	fn bumpplus_char() {
		let mut state = InternalState::new(None, 0);
		state.memory = vec!(Some(Value::Character{value: 'a'}));

		let result = BumpPlusOp{cell: Location::Cell(0)}.apply_to(&mut state);

		assert!(result.is_err());
		assert!(match state.register {
			None => true,
			_ => false
		});
	}

	#[test]
	fn bumpplus_none() {
		let mut state = InternalState::new(None, 0);
		state.memory = vec!(None);

		let result = BumpPlusOp{cell: Location::Cell(0)}.apply_to(&mut state);

		assert!(result.is_err());
		assert!(match state.register {
			None => true,
			_ => false
		});
	}

	#[test]
	fn bumpminus_number() {
		let mut state = InternalState::new(None, 0);
		state.memory = vec!(Some(Value::Number{value: 0}));

		let result = BumpMinusOp{cell: Location::Cell(0)}.apply_to(&mut state);

		assert!(result.is_ok());
		assert!(match state.memory[0] {
			Some(Value::Number{value: -1}) => true,
			_ => false
		});
		assert!(match state.register {
			Some(Value::Number{value: -1}) => true,
			_ => false
		});
	}

	#[test]
	fn bumpminus_char() {
		let mut state = InternalState::new(None, 0);
		state.memory = vec!(Some(Value::Character{value: 'a'}));

		let result = BumpMinusOp{cell: Location::Cell(0)}.apply_to(&mut state);

		assert!(result.is_err());
		assert!(match state.register {
			None => true,
			_ => false
		});
	}

	#[test]
	fn bumpminus_none() {
		let mut state = InternalState::new(None, 0);
		state.memory = vec!(None);

		let result = BumpMinusOp{cell: Location::Cell(0)}.apply_to(&mut state);

		assert!(result.is_err());
		assert!(match state.register {
			None => true,
			_ => false
		});
	}

}