mirror of
https://github.com/hazemKrimi/hack-assembler.git
synced 2026-05-01 18:20:28 +00:00
With symbols assembler
This commit is contained in:
+125
-40
@@ -1,39 +1,141 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi;
|
use std::ffi;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path;
|
use std::path;
|
||||||
use std::process;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use types::Address;
|
||||||
use types::Instruction;
|
use types::Instruction;
|
||||||
|
|
||||||
mod code;
|
mod code;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
fn process(instruction: String) -> String {
|
fn remove_whitespace_and_comments(content: String) -> String {
|
||||||
match parser::parse(&instruction) {
|
let re = Regex::new(r"\s*\/\/.*").unwrap();
|
||||||
Instruction::AInstruction(parsed_instruction) => {
|
|
||||||
match parsed_instruction.decimal.parse::<i32>() {
|
|
||||||
Ok(decimal) => {
|
|
||||||
let translated = code::decimal_to_fifteen_bits_binary(&decimal);
|
|
||||||
|
|
||||||
String::from(format!("0{}", translated))
|
content
|
||||||
}
|
.lines()
|
||||||
Err(_) => panic!("Failed to parse A instruction {}", instruction),
|
.map(|line| line.trim())
|
||||||
|
.filter(|line| !line.starts_with("//") && !line.is_empty())
|
||||||
|
.map(|line| re.replace_all(line, ""))
|
||||||
|
.map(|line| format!("{}\n", line))
|
||||||
|
.collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_pass(content: String, table: &RefCell<HashMap<String, (i32, Address)>>) -> String {
|
||||||
|
let mut mutable_table = table.borrow_mut();
|
||||||
|
|
||||||
|
content
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, line)| line.starts_with('(') && line.ends_with(')'))
|
||||||
|
.collect::<Vec<(usize, &str)>>()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(index, (line_number, instruction))| {
|
||||||
|
let symbol = instruction[1..instruction.len() - 1].to_string();
|
||||||
|
|
||||||
|
if index > 0 {
|
||||||
|
mutable_table.insert(symbol, ((*line_number - index) as i32, Address::ROM));
|
||||||
|
} else {
|
||||||
|
mutable_table.insert(symbol, (*line_number as i32, Address::ROM));
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
Instruction::CInstruction(parsed_instruction) => {
|
|
||||||
String::from("111")
|
content
|
||||||
+ code::translate_comp(&parsed_instruction.comp.unwrap_or_else(|| "".to_string()))
|
.lines()
|
||||||
.as_str()
|
.filter(|line| !line.starts_with('(') && !line.ends_with(')'))
|
||||||
+ code::translate_dest(&parsed_instruction.dest.unwrap_or_else(|| "".to_string()))
|
.map(|line| format!("{}\n", line))
|
||||||
.as_str()
|
.collect()
|
||||||
+ code::translate_jump(&parsed_instruction.jump.unwrap_or_else(|| "".to_string()))
|
}
|
||||||
.as_str()
|
|
||||||
}
|
fn second_pass(content: String, table: &RefCell<HashMap<String, (i32, Address)>>) -> String {
|
||||||
}
|
content
|
||||||
|
.lines()
|
||||||
|
.map(|instruction| {
|
||||||
|
let mut mutable_table = table.borrow_mut();
|
||||||
|
|
||||||
|
if instruction.is_empty() {
|
||||||
|
return instruction.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
match parser::parse(&instruction.to_string(), &mut mutable_table) {
|
||||||
|
Instruction::AInstruction(parsed_instruction) => {
|
||||||
|
match parsed_instruction.decimal.parse::<i32>() {
|
||||||
|
Ok(decimal) => {
|
||||||
|
let translated = code::decimal_to_fifteen_bits_binary(&decimal);
|
||||||
|
|
||||||
|
String::from(format!("0{}", translated))
|
||||||
|
}
|
||||||
|
Err(_) => panic!("Failed to parse A instruction {}", instruction),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction::CInstruction(parsed_instruction) => {
|
||||||
|
String::from("111")
|
||||||
|
+ code::translate_comp(
|
||||||
|
&parsed_instruction.comp.unwrap_or_else(|| "".to_string()),
|
||||||
|
)
|
||||||
|
.as_str()
|
||||||
|
+ code::translate_dest(
|
||||||
|
&parsed_instruction.dest.unwrap_or_else(|| "".to_string()),
|
||||||
|
)
|
||||||
|
.as_str()
|
||||||
|
+ code::translate_jump(
|
||||||
|
&parsed_instruction.jump.unwrap_or_else(|| "".to_string()),
|
||||||
|
)
|
||||||
|
.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|line| !line.is_empty())
|
||||||
|
.map(|line| format!("{}\n", line))
|
||||||
|
.collect::<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(path: &PathBuf) {
|
||||||
|
let filepath = path
|
||||||
|
.canonicalize()
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.replace("asm", "hack");
|
||||||
|
let mut file = fs::File::create(filepath).unwrap();
|
||||||
|
let content = fs::read_to_string(&path).expect("You must provide a correct filepath!");
|
||||||
|
let symbol_table = RefCell::new(HashMap::from([
|
||||||
|
(String::from("SP"), (0, Address::ROM)),
|
||||||
|
(String::from("LCL"), (1, Address::ROM)),
|
||||||
|
(String::from("ARG"), (2, Address::ROM)),
|
||||||
|
(String::from("THIS"), (3, Address::ROM)),
|
||||||
|
(String::from("THAT"), (4, Address::ROM)),
|
||||||
|
(String::from("R0"), (0, Address::RAM)),
|
||||||
|
(String::from("R1"), (1, Address::RAM)),
|
||||||
|
(String::from("R2"), (2, Address::RAM)),
|
||||||
|
(String::from("R3"), (3, Address::RAM)),
|
||||||
|
(String::from("R4"), (4, Address::RAM)),
|
||||||
|
(String::from("R5"), (5, Address::RAM)),
|
||||||
|
(String::from("R6"), (6, Address::RAM)),
|
||||||
|
(String::from("R7"), (7, Address::RAM)),
|
||||||
|
(String::from("R8"), (8, Address::RAM)),
|
||||||
|
(String::from("R9"), (9, Address::RAM)),
|
||||||
|
(String::from("R10"), (10, Address::RAM)),
|
||||||
|
(String::from("R11"), (11, Address::RAM)),
|
||||||
|
(String::from("R12"), (12, Address::RAM)),
|
||||||
|
(String::from("R13"), (13, Address::RAM)),
|
||||||
|
(String::from("R14"), (14, Address::RAM)),
|
||||||
|
(String::from("R15"), (15, Address::RAM)),
|
||||||
|
(String::from("SCREEN"), (16384, Address::RAM)),
|
||||||
|
(String::from("KBD"), (24576, Address::RAM)),
|
||||||
|
]));
|
||||||
|
let without_whitespace_and_comments = remove_whitespace_and_comments(content);
|
||||||
|
let ran_through_first_pass = first_pass(without_whitespace_and_comments, &symbol_table);
|
||||||
|
let ran_through_second_pass = second_pass(ran_through_first_pass, &symbol_table);
|
||||||
|
|
||||||
|
file.write_all(ran_through_second_pass.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -48,24 +150,7 @@ fn main() {
|
|||||||
.to_str()
|
.to_str()
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
{
|
{
|
||||||
"asm" => {
|
"asm" => process(&path),
|
||||||
let filepath = path.canonicalize().unwrap().to_str().unwrap().replace("asm", "hack");
|
_ => panic!("The file extension must be asm!"),
|
||||||
let mut file = fs::File::create(filepath).unwrap();
|
|
||||||
let content = fs::read_to_string(&path).expect("You must provide a correct filepath!");
|
|
||||||
let re = Regex::new(r"\s*\/\/.*").unwrap();
|
|
||||||
let processed: String = content
|
|
||||||
.lines()
|
|
||||||
.filter(|line| !line.starts_with("//") && !line.is_empty())
|
|
||||||
.map(|line| re.replace_all(line, ""))
|
|
||||||
.map(|line| process(line.to_string()))
|
|
||||||
.map(|line| format!("{}\n", line))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
file.write_all(processed.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("The file extension must be asm!");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-5
@@ -1,15 +1,42 @@
|
|||||||
use crate::types::{AInstruction, CInstruction, Instruction};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::types::{AInstruction, Address, CInstruction, Instruction};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
pub fn parse(instruction: &String) -> Instruction {
|
pub fn parse(instruction: &String, table: &mut HashMap<String, (i32, Address)>) -> Instruction {
|
||||||
let mut cloned = instruction.clone();
|
let mut cloned = instruction.clone();
|
||||||
|
|
||||||
if cloned.starts_with('@') {
|
if cloned.starts_with('@') {
|
||||||
cloned.remove(0);
|
cloned.remove(0);
|
||||||
|
|
||||||
return Instruction::AInstruction(AInstruction {
|
if cloned.parse::<i32>().is_ok() {
|
||||||
decimal: cloned.trim().to_string(),
|
return Instruction::AInstruction(AInstruction { decimal: cloned });
|
||||||
});
|
} else {
|
||||||
|
if table.contains_key(&cloned) {
|
||||||
|
return Instruction::AInstruction(AInstruction {
|
||||||
|
decimal: table.get(&cloned).copied().unwrap().0.to_string(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let mut temp_table = table.clone();
|
||||||
|
|
||||||
|
temp_table.remove_entry(&String::from("SCREEN"));
|
||||||
|
temp_table.remove_entry(&String::from("KBD"));
|
||||||
|
|
||||||
|
let address = temp_table
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, (_, address))| matches!(address, Address::RAM))
|
||||||
|
.fold(
|
||||||
|
0,
|
||||||
|
|acc, (_, (addr, _))| if *addr > acc { *addr } else { acc },
|
||||||
|
)
|
||||||
|
+ 1;
|
||||||
|
|
||||||
|
table.insert(cloned, (address, Address::RAM));
|
||||||
|
return Instruction::AInstruction(AInstruction {
|
||||||
|
decimal: address.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let re_dest = Regex::new(r"=").unwrap();
|
let re_dest = Regex::new(r"=").unwrap();
|
||||||
let re_jump = Regex::new(r";").unwrap();
|
let re_jump = Regex::new(r";").unwrap();
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ pub struct CInstruction {
|
|||||||
pub jump: Option<String>,
|
pub jump: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Address {
|
||||||
|
RAM,
|
||||||
|
ROM
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
AInstruction(AInstruction),
|
AInstruction(AInstruction),
|
||||||
CInstruction(CInstruction),
|
CInstruction(CInstruction),
|
||||||
|
|||||||
Reference in New Issue
Block a user