hack-assembler 0.1.0

An assembler for the Hack assembly language
Documentation
use crate::hack_parser::*;
use lazy_static::lazy_static;
use std::collections::HashMap;

lazy_static! {
  static ref COMPUTATION_INSTRUCTIONS: HashMap<&'static str, &'static str> = [
    ("0", "0101010"),
    ("1", "0111111"),
    ("-1", "0111010"),
    ("D", "0001100"),
    ("A", "0110000"),
    ("!D", "0001101"),
    ("!A", "0110001"),
    ("-D", "0001111"),
    ("-A", "0110011"),
    ("D+1", "0011111"),
    ("A+1", "0110111"),
    ("D-1", "0001110"),
    ("A-1", "0110010"),
    ("D+A", "0000010"),
    ("D-A", "0010011"),
    ("A-D", "0000111"),
    ("D&A", "0000000"),
    ("D|A", "0010101"),
    ("M", "1110000"),
    ("!M", "1110001"),
    ("-M", "1110011"),
    ("M+1", "1110111"),
    ("M-1", "1110010"),
    ("D+M", "1000010"),
    ("D-M", "1010011"),
    ("M-D", "1000111"),
    ("D&M", "1000000"),
    ("D|M", "1010101"),
  ]
  .iter()
  .cloned()
  .collect();
}

pub fn emit(instructions: Vec<Instruction>) -> String {
  instructions
    .iter()
    .map(|instruction| match instruction {
      Instruction::A(instruction) =>
        match instruction {
          AInstruction::number(number) => format!("0{:015b}\n", number),
          AInstruction::label(label) => panic!("I found an unconverted label `{}` in the A instruction.\nAll labels should be converted by the parser before emitting binary code.", label.value),
        }
      Instruction::C {
        destinations,
        computation,
        jump,
      } => format!(
        "111{}{}{}\n",
        emit_computation(computation),
        emit_destinations(destinations),
        emit_jump(jump)
      ),
      Instruction::Ignored => "".to_string(),
    })
    .collect::<Vec<String>>()
    .join("")
}

fn emit_destinations(destinations: &Option<Destinations>) -> String {
  match destinations {
    Some(dest) => format!(
      "{}{}{}",
      bool_to_bit(dest.A),
      bool_to_bit(dest.D),
      bool_to_bit(dest.M)
    ),
    None => "000".to_string(),
  }
}

fn emit_computation(computation: &String) -> String {
  COMPUTATION_INSTRUCTIONS
    .get::<str>(&computation)
    .unwrap()
    .to_string()
}

fn emit_jump(jump: &Option<Jump>) -> String {
  match jump {
    Some(j) => format!(
      "{}{}{}",
      bool_to_bit(j.LT),
      bool_to_bit(j.EQ),
      bool_to_bit(j.GT)
    ),
    None => "000".to_string()
  }
}

fn bool_to_bit(boolean: bool) -> String {
  (if boolean { "1" } else { "0" }).to_string()
}