use crate::Assembler;
use crate::tree::AsmNode::*;
use crate::tree::*;
use std::collections::HashMap;
pub fn register_labels(asm: &mut Assembler) {
fn registering_labels(node: &AsmNode) -> Option<AsmNode> {
match node {
Source{code, meta} => {
match code[0].as_str() {
"@label" | "@labref" => {
if code.len() == 2 {
Some(Label{name: code[1].clone(), is_definition: &code[0] == "@label", meta: meta.clone()})
} else {
Some(Error{msg: format!("Error, directive {} takes a label as its only argument.\n", &code[0]), meta: meta.clone()})
}
},
_ => {
if code.len() == 1 {
let chars = code[0].chars().collect::<Vec<char>>();
if chars[chars.len() - 1] == ':' {
let label_name = chars.clone().into_iter().take(chars.len()-1).collect::<String>();
Some(Label{name: label_name, is_definition: true, meta: meta.clone()})
} else {
None
}
} else {
None
}
},
}
},
_ => None,
}
}
asm.root.traverse_tree(&mut registering_labels);
}
pub fn expand_labels(asm: &mut Assembler) {
let mut label_table: HashMap<String, usize> = HashMap::new();
let mut current_offset = asm.start_address;
let mut reading_labels_offsets = | node: &AsmNode | -> Option<AsmNode> {
match node {
Label{name, is_definition, meta} => {
if *is_definition {
if label_table.contains_key(name) {
Some(Error{msg: format!("Error, label {} is defined multiple times.\n", &name), meta: meta.clone()})
} else {
label_table.insert(name.clone(), current_offset);
None
}
} else {
current_offset += asm.wordsize;
None
}
},
Raw(data) => {
current_offset += data.len();
None
},
Error{msg: _, meta: _} => None,
x => {
Some(Error{msg: format!("There is a bug in the assembler. A node should not have been left while in expand_labels: {x}."), meta: Metadata{raw: "!!!".to_string(), source_file: "!!!".to_string(), line: !0}})
},
}
};
asm.root.traverse_tree(&mut reading_labels_offsets);
let mut computed_label_references: HashMap<String, Result<Vec<u8>, String>> = HashMap::new();
for label in label_table.keys() {
computed_label_references.insert(label.clone(), match asm.format_number(*label_table.get(&label.clone()).unwrap() as u128) { Some(num) => Ok(num),
None => Err(format!("Error, unable to compute offset for label {} which is {:X}. It's probably because the offset is larger that the valid address range.", &label, label_table.get(&label.clone()).unwrap())),
});
}
let mut expanding_label_references = | node: &AsmNode | -> Option<AsmNode> {
match node {
Label{name, is_definition, meta} => {
if *is_definition {
return None
}
match computed_label_references.get(&name.clone()) {
Some(offset_result) => match offset_result {
Ok(num) => Some(Raw(num.clone())),
Err(msg) => Some(Error{msg: msg.to_string(), meta: meta.clone()}),
},
None => Some(Error{msg: format!("Error, label {} is used but never defined.", &name), meta: meta.clone()}),
}
},
_ => None, }
};
asm.root.traverse_tree(&mut expanding_label_references);
}
pub fn label_dump(asm: &mut Assembler) -> String {
let mut ret = String::new();
let mut current_offset = asm.start_address;
let mut dumping_labels = | node: &AsmNode | -> Option<AsmNode> {
match node {
Label{name, is_definition, meta: _} => {
if *is_definition {
ret.push_str(&format!("{}: 0x{:X}\n", &name, current_offset));
} else {
current_offset += asm.wordsize;
}
},
Raw(data) => {
current_offset += data.len();
},
Error{msg: _, meta: _} => {
return None;
},
x => {
ret.push_str(&format!("The size of the node {} is not known. Thus, we can't make a label dump.", &x.to_string()));
},
}
None
};
asm.root.traverse_tree(&mut dumping_labels);
ret
}
#[test]
fn test_register_labels() {
let mut asm = Assembler::from_text("@labref lab1\n@label lab1\n@labref lab2\nlab2:");
register_labels(&mut asm);
assert_eq!(asm.root.to_string(), "Reference to label lab1\nDefinition of label lab1\nReference to label lab2\nDefinition of label lab2\n");
}
#[test]
fn test_raw_bytes() {
let mut asm = Assembler::from_text("@label start\n@labref end\n@labref start\n@label end");
assert_eq!(asm.set_word_size(16), None);
register_labels(&mut asm);
expand_labels(&mut asm);
assert_eq!(asm.root.to_string(), "Definition of label start\n0x04 0x00 \n0x00 0x00 \nDefinition of label end\n");
}