[2016][rust][12.1] Break out Assembunny into a module
diff --git a/2016/rust/src/assembunny.rs b/2016/rust/src/assembunny.rs
new file mode 100644
index 0000000..0013bb3
--- /dev/null
+++ b/2016/rust/src/assembunny.rs
@@ -0,0 +1,243 @@
+use std::collections::HashMap;
+use std::str;
+
+use errors::*;
+
+pub struct Assembunny {
+ pub registers: HashMap<Register, isize>,
+ pub instructions: Instructions,
+}
+
+impl Assembunny {
+ fn value<V: Into<Variable>>(&self, v: V) -> isize {
+ let v: Variable = v.into();
+ match v {
+ Variable::Register(r) => {
+ self.registers.get(&r).cloned().unwrap_or(0)
+ }
+ Variable::Value(i) => i,
+ }
+ }
+}
+
+impl Iterator for Assembunny {
+ type Item = HashMap<Register, isize>;
+ fn next(&mut self) -> Option<HashMap<Register, isize>> {
+ let pc = self.value(Register::PC) as usize;
+ let instruction = match self.instructions.0.get(pc) {
+ Some(i) => i,
+ None => {
+ return None;
+ }
+ };
+
+ match *instruction {
+ Instruction::Cpy(v, r) => {
+ let value = self.value(v);
+ self.registers.insert(r, value);
+ *self.registers.entry(Register::PC).or_insert(0) += 1;
+ }
+ Instruction::Inc(r) => {
+ *self.registers.entry(r).or_insert(0) += 1;
+ *self.registers.entry(Register::PC).or_insert(0) += 1;
+ }
+ Instruction::Dec(r) => {
+ *self.registers.entry(r).or_insert(0) -= 1;
+ *self.registers.entry(Register::PC).or_insert(0) += 1;
+ }
+ Instruction::Jnz(v, i) => {
+ let delta = if self.value(v) == 0 { 1 } else { i };
+ let pc = self.value(Register::PC) + delta;
+ self.registers.insert(Register::PC, pc);
+ }
+ }
+
+ Some(self.registers.clone())
+ }
+}
+
+pub struct Instructions(Vec<Instruction>);
+
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub enum Register {
+ PC,
+ A,
+ B,
+ C,
+ D,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum Instruction {
+ Cpy(Variable, Register),
+ Inc(Register),
+ Dec(Register),
+ Jnz(Variable, isize),
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum Variable {
+ Register(Register),
+ Value(isize),
+}
+
+impl From<Register> for Variable {
+ fn from(r: Register) -> Self {
+ Variable::Register(r)
+ }
+}
+
+// Parsing
+
+impl str::FromStr for Instructions {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self> {
+ s.lines()
+ .map(|line| line.parse())
+ .collect::<Result<Vec<_>>>()
+ .map(Instructions)
+ }
+}
+
+impl str::FromStr for Register {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self> {
+ match s {
+ "a" => Ok(Register::A),
+ "b" => Ok(Register::B),
+ "c" => Ok(Register::C),
+ "d" => Ok(Register::D),
+ _ => Err(format!("invalid register '{}'", s).into()),
+ }
+ }
+}
+
+impl str::FromStr for Instruction {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self> {
+ let mut tokens = s.split_whitespace();
+ match tokens.next() {
+ Some("cpy") => {
+ let v = tokens.read_variable()?;
+ let r = tokens.read_register()?;
+ Ok(Instruction::Cpy(v, r))
+ }
+ Some("inc") => {
+ let r = tokens.read_register()?;
+ Ok(Instruction::Inc(r))
+ }
+ Some("dec") => {
+ let r = tokens.read_register()?;
+ Ok(Instruction::Dec(r))
+ }
+ Some("jnz") => {
+ let var = tokens.read_variable()?;
+ let val = tokens.read_value()?;
+ Ok(Instruction::Jnz(var, val))
+ }
+ Some(inst) => Err(format!("invalid instruction '{}'", inst).into()),
+ None => Err("no instruction".into()),
+ }
+ }
+}
+
+impl str::FromStr for Variable {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self> {
+ s.parse::<Register>()
+ .map(Variable::Register)
+ .or_else(|_| s.parse::<isize>().map(Variable::Value))
+ .map_err(|_| format!("invalid variable '{}'", s).into())
+ }
+}
+
+trait SplitWhitespaceExt {
+ fn read_variable(&mut self) -> Result<Variable>;
+ fn read_register(&mut self) -> Result<Register>;
+ fn read_value(&mut self) -> Result<isize>;
+}
+
+impl<'a> SplitWhitespaceExt for str::SplitWhitespace<'a> {
+ fn read_variable(&mut self) -> Result<Variable> {
+ self.next()
+ .ok_or("missing variable".into())
+ .and_then(|v| v.parse::<Variable>())
+ }
+
+ fn read_register(&mut self) -> Result<Register> {
+ self.next()
+ .ok_or("missing register".into())
+ .and_then(|v| v.parse::<Register>())
+ }
+
+ fn read_value(&mut self) -> Result<isize> {
+ self.next()
+ .ok_or("missing value".into())
+ .and_then(|v| v.parse::<isize>().chain_err(|| ""))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{Assembunny, Instructions, Instruction, Register, Variable};
+ use std::collections::HashMap;
+ use std::str::FromStr;
+
+ #[test]
+ fn test_assembunny() {
+ let instructions: Instructions = "cpy 41 a
+inc a
+inc a
+dec a
+jnz a 2
+dec a"
+ .parse()
+ .unwrap();
+ let mut assembunny = Assembunny {
+ registers: HashMap::new(),
+ instructions: instructions,
+ };
+
+ let registers = assembunny.next().unwrap();
+ assert_eq!(registers.get(&Register::A), Some(&41));
+ assert_eq!(registers.get(&Register::B), None);
+
+ let registers = assembunny.next().unwrap();
+ assert_eq!(registers.get(&Register::A), Some(&42));
+ assert_eq!(registers.get(&Register::C), None);
+
+ let registers = assembunny.last().unwrap();
+ assert_eq!(registers.get(&Register::A), Some(&42));
+ assert_eq!(registers.get(&Register::PC), Some(&6));
+ }
+
+ #[test]
+ fn test_instructions_from_str() {
+ let i: Instructions = "cpy 41 a
+inc a
+inc a
+dec a
+jnz a 2
+dec a"
+ .parse()
+ .unwrap();
+ assert_eq!(i.0.len(), 6);
+ assert_eq!(i.0[0], Instruction::from_str("cpy 41 a").unwrap());
+ }
+
+ #[test]
+ fn test_instruction_from_str() {
+ assert!(Instruction::from_str("").is_err());
+ assert!(Instruction::from_str("omg").is_err());
+ assert!(Instruction::from_str("inc 5").is_err());
+
+ assert_eq!(Instruction::from_str("cpy 41 a").unwrap(),
+ Instruction::Cpy(Variable::Value(41), Register::A));
+ assert_eq!(Instruction::from_str("inc a").unwrap(),
+ Instruction::Inc(Register::A));
+ assert_eq!(Instruction::from_str("dec b").unwrap(),
+ Instruction::Dec(Register::B));
+ assert_eq!(Instruction::from_str("jnz c 2").unwrap(),
+ Instruction::Jnz(Variable::Register(Register::C), 2));
+ }
+}
diff --git a/2016/rust/src/day_12.rs b/2016/rust/src/day_12.rs
index b14f9e0..080ab4d 100644
--- a/2016/rust/src/day_12.rs
+++ b/2016/rust/src/day_12.rs
@@ -1,6 +1,4 @@
-use std::collections::HashMap;
-use std::str;
-
+use assembunny::*;
use errors::*;
pub fn solve(input: &str) -> Result<String> {
@@ -12,243 +10,3 @@ pub fn solve(input: &str) -> Result<String> {
let a = registers.get(&Register::A).ok_or("")?;
Ok(a.to_string())
}
-
-struct Assembunny {
- registers: HashMap<Register, isize>,
- instructions: Instructions,
-}
-
-impl Assembunny {
- fn new(instructions: Instructions) -> Self {
- Assembunny {
- registers: HashMap::new(),
- instructions: instructions,
- }
- }
-
- fn value<V: Into<Variable>>(&self, v: V) -> isize {
- let v: Variable = v.into();
- match v {
- Variable::Register(r) => self.registers.get(&r).cloned().unwrap_or(0),
- Variable::Value(i) => i,
- }
- }
-}
-
-impl Iterator for Assembunny {
- type Item = HashMap<Register, isize>;
- fn next(&mut self) -> Option<HashMap<Register, isize>> {
- let pc = self.value(Register::PC) as usize;
- let instruction = match self.instructions.0.get(pc) {
- Some(i) => i,
- None => {
- return None;
- }
- };
-
- match *instruction {
- Instruction::Cpy(v, r) => {
- let value = self.value(v);
- self.registers.insert(r, value);
- *self.registers.entry(Register::PC).or_insert(0) += 1;
- }
- Instruction::Inc(r) => {
- *self.registers.entry(r).or_insert(0) += 1;
- *self.registers.entry(Register::PC).or_insert(0) += 1;
- }
- Instruction::Dec(r) => {
- *self.registers.entry(r).or_insert(0) -= 1;
- *self.registers.entry(Register::PC).or_insert(0) += 1;
- }
- Instruction::Jnz(v, i) => {
- let delta = if self.value(v) == 0 { 1 } else { i };
- let pc = self.value(Register::PC) + delta;
- self.registers.insert(Register::PC, pc);
- }
- }
-
- Some(self.registers.clone())
- }
-}
-
-struct Instructions(Vec<Instruction>);
-
-#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
-enum Register {
- PC,
- A,
- B,
- C,
- D,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum Instruction {
- Cpy(Variable, Register),
- Inc(Register),
- Dec(Register),
- Jnz(Variable, isize),
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum Variable {
- Register(Register),
- Value(isize),
-}
-
-impl From<Register> for Variable {
- fn from(r: Register) -> Self {
- Variable::Register(r)
- }
-}
-
-// Parsing
-
-impl str::FromStr for Instructions {
- type Err = Error;
- fn from_str(s: &str) -> Result<Self> {
- s.lines()
- .map(|line| line.parse())
- .collect::<Result<Vec<_>>>()
- .map(Instructions)
- }
-}
-
-impl str::FromStr for Register {
- type Err = Error;
- fn from_str(s: &str) -> Result<Self> {
- match s {
- "a" => Ok(Register::A),
- "b" => Ok(Register::B),
- "c" => Ok(Register::C),
- "d" => Ok(Register::D),
- _ => Err(format!("invalid register '{}'", s).into()),
- }
- }
-}
-
-impl str::FromStr for Instruction {
- type Err = Error;
- fn from_str(s: &str) -> Result<Self> {
- let mut tokens = s.split_whitespace();
- match tokens.next() {
- Some("cpy") => {
- let v = tokens.read_variable()?;
- let r = tokens.read_register()?;
- Ok(Instruction::Cpy(v, r))
- }
- Some("inc") => {
- let r = tokens.read_register()?;
- Ok(Instruction::Inc(r))
- }
- Some("dec") => {
- let r = tokens.read_register()?;
- Ok(Instruction::Dec(r))
- }
- Some("jnz") => {
- let var = tokens.read_variable()?;
- let val = tokens.read_value()?;
- Ok(Instruction::Jnz(var, val))
- }
- Some(inst) => Err(format!("invalid instruction '{}'", inst).into()),
- None => Err("no instruction".into()),
- }
- }
-}
-
-impl str::FromStr for Variable {
- type Err = Error;
- fn from_str(s: &str) -> Result<Self> {
- s.parse::<Register>()
- .map(Variable::Register)
- .or_else(|_| s.parse::<isize>().map(Variable::Value))
- .map_err(|_| format!("invalid variable '{}'", s).into())
- }
-}
-
-trait SplitWhitespaceExt {
- fn read_variable(&mut self) -> Result<Variable>;
- fn read_register(&mut self) -> Result<Register>;
- fn read_value(&mut self) -> Result<isize>;
-}
-
-impl<'a> SplitWhitespaceExt for str::SplitWhitespace<'a> {
- fn read_variable(&mut self) -> Result<Variable> {
- self.next()
- .ok_or("missing variable".into())
- .and_then(|v| v.parse::<Variable>())
- }
-
- fn read_register(&mut self) -> Result<Register> {
- self.next()
- .ok_or("missing register".into())
- .and_then(|v| v.parse::<Register>())
- }
-
- fn read_value(&mut self) -> Result<isize> {
- self.next()
- .ok_or("missing value".into())
- .and_then(|v| v.parse::<isize>().chain_err(|| ""))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{Assembunny, Instructions, Instruction, Register, Variable};
- use std::str::FromStr;
-
- #[test]
- fn test_assembunny() {
- let instructions: Instructions = "cpy 41 a
-inc a
-inc a
-dec a
-jnz a 2
-dec a"
- .parse()
- .unwrap();
- let mut assembunny = Assembunny::new(instructions);
-
- let registers = assembunny.next().unwrap();
- assert_eq!(registers.get(&Register::A), Some(&41));
- assert_eq!(registers.get(&Register::B), None);
-
- let registers = assembunny.next().unwrap();
- assert_eq!(registers.get(&Register::A), Some(&42));
- assert_eq!(registers.get(&Register::C), None);
-
- let registers = assembunny.last().unwrap();
- assert_eq!(registers.get(&Register::A), Some(&42));
- assert_eq!(registers.get(&Register::PC), Some(&6));
- }
-
- #[test]
- fn test_instructions_from_str() {
- let i: Instructions = "cpy 41 a
-inc a
-inc a
-dec a
-jnz a 2
-dec a"
- .parse()
- .unwrap();
- assert_eq!(i.0.len(), 6);
- assert_eq!(i.0[0], Instruction::from_str("cpy 41 a").unwrap());
- }
-
- #[test]
- fn test_instruction_from_str() {
- assert!(Instruction::from_str("").is_err());
- assert!(Instruction::from_str("omg").is_err());
- assert!(Instruction::from_str("inc 5").is_err());
-
- assert_eq!(Instruction::from_str("cpy 41 a").unwrap(),
- Instruction::Cpy(Variable::Value(41), Register::A));
- assert_eq!(Instruction::from_str("inc a").unwrap(),
- Instruction::Inc(Register::A));
- assert_eq!(Instruction::from_str("dec b").unwrap(),
- Instruction::Dec(Register::B));
- assert_eq!(Instruction::from_str("jnz c 2").unwrap(),
- Instruction::Jnz(Variable::Register(Register::C), 2));
- }
-}
diff --git a/2016/rust/src/lib.rs b/2016/rust/src/lib.rs
index 8963715..1130314 100644
--- a/2016/rust/src/lib.rs
+++ b/2016/rust/src/lib.rs
@@ -6,6 +6,7 @@ extern crate error_chain;
extern crate regex;
extern crate crypto;
+mod assembunny;
pub mod errors;
pub mod day_01;
pub mod day_02;