assembler: purge unwrap and use more AssembleErrors
This commit is contained in:
@@ -33,6 +33,7 @@ pub struct Program {
|
||||
}
|
||||
|
||||
impl Program {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
registry: HashSet::new(),
|
||||
@@ -59,6 +60,7 @@ pub struct ProgramRef {
|
||||
}
|
||||
|
||||
impl ProgramRef {
|
||||
#[must_use]
|
||||
pub fn new(program: Program) -> Self {
|
||||
Self {
|
||||
program: Arc::new(Mutex::new(program)),
|
||||
@@ -68,15 +70,16 @@ impl ProgramRef {
|
||||
pub fn register(&self, path: &Path) {
|
||||
self.program
|
||||
.lock()
|
||||
.unwrap()
|
||||
.expect("Failed to acquire program lock")
|
||||
.registry
|
||||
.insert(quick_hash(path));
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_registered(&self, path: &Path) -> bool {
|
||||
self.program
|
||||
.lock()
|
||||
.unwrap()
|
||||
.expect("Failed to acquire program lock")
|
||||
.registry
|
||||
.contains(&quick_hash(path))
|
||||
}
|
||||
@@ -86,15 +89,26 @@ impl ProgramRef {
|
||||
// }
|
||||
|
||||
pub fn add_task(&self, task: Task) {
|
||||
self.program.lock().unwrap().add_task(task);
|
||||
self.program
|
||||
.lock()
|
||||
.expect("Failed to acquire program lock")
|
||||
.add_task(task);
|
||||
}
|
||||
|
||||
pub fn add_module(&self, module: Module) {
|
||||
self.program.lock().unwrap().modules.push(module);
|
||||
self.program
|
||||
.lock()
|
||||
.expect("Failed to acquire program lock")
|
||||
.modules
|
||||
.push(module);
|
||||
}
|
||||
|
||||
pub fn log(&self, message: &str) {
|
||||
self.program.lock().unwrap().logger.log(message);
|
||||
self.program
|
||||
.lock()
|
||||
.expect("Failed to acquire program lock")
|
||||
.logger
|
||||
.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +128,13 @@ pub struct Module {
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(path: PathBuf, hash: u64, nodes: Vec<Node>, program: ProgramRef) -> Self {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
path: PathBuf,
|
||||
hash: u64,
|
||||
nodes: Vec<Node>,
|
||||
program: ProgramRef,
|
||||
) -> Self {
|
||||
Self {
|
||||
path,
|
||||
hash,
|
||||
@@ -123,23 +143,35 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(path: PathBuf, program: ProgramRef) -> Task {
|
||||
// spawn a thread that creates the main function and executes the lexer and parser.
|
||||
pub fn build(path: PathBuf, program: ProgramRef) -> Result<Task, AssembleError> {
|
||||
// Spawn a thread that creates the main function and executes the lexer and parser.
|
||||
let handle = thread::spawn(move || {
|
||||
let mut module =
|
||||
Module::new(path.clone(), quick_hash(&path), Vec::new(), program.clone());
|
||||
Self::new(path.clone(), quick_hash(&path), Vec::new(), program.clone());
|
||||
|
||||
let tokens = module.lex();
|
||||
module.parse(tokens);
|
||||
module.expand();
|
||||
module.prepare_dependencies();
|
||||
module
|
||||
match module.lex() {
|
||||
Ok(tokens) => {
|
||||
module.parse(tokens);
|
||||
module.expand();
|
||||
module.prepare_dependencies();
|
||||
module
|
||||
}
|
||||
Err(why) => {
|
||||
eprintln!(
|
||||
"Error building program at path `{}`: {why}",
|
||||
path.display()
|
||||
);
|
||||
|
||||
// TODO: Find a way to make this work without panicking.
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Task { module: handle }
|
||||
Ok(Task { module: handle })
|
||||
}
|
||||
|
||||
fn lex(&mut self) -> Vec<Token> {
|
||||
fn lex(&self) -> Result<Vec<Token>, AssembleError> {
|
||||
if let Ok(path) = self.path.canonicalize() {
|
||||
self.program.log(&format!(
|
||||
"{:20} {:20} [{}]",
|
||||
@@ -150,34 +182,43 @@ impl Module {
|
||||
}
|
||||
|
||||
let src = fs::read_to_string(&self.path)
|
||||
.map_err(|_| AssembleError::InvalidFile(self.path.to_path_buf()))
|
||||
.unwrap();
|
||||
.map_err(|_| AssembleError::InvalidFile(self.path.clone()))?;
|
||||
|
||||
let file_hash = quick_hash(&self.path);
|
||||
|
||||
self.program
|
||||
.log(&format!("{:20} {:20}", "Tokenising", self.get_filename()));
|
||||
lexer::lexer(src, file_hash).unwrap()
|
||||
|
||||
lexer::lexer(src, file_hash)
|
||||
}
|
||||
|
||||
fn parse(&mut self, tokens: Vec<Token>) {
|
||||
fn parse(&mut self, tokens: Vec<Token>) -> Result<(), AssembleError> {
|
||||
self.program
|
||||
.log(&format!("{:20} {:20}", "Parsing", self.get_filename()));
|
||||
let parsed = Parser::parse_nodes(tokens).unwrap();
|
||||
|
||||
let parsed = Parser::parse_nodes(tokens)?;
|
||||
self.nodes = parsed;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand(&mut self) {
|
||||
fn expand(&mut self) -> Result<(), AssembleError> {
|
||||
self.program
|
||||
.log(&format!("{:20} {:20}", "Expanding", self.get_filename()));
|
||||
let expanded = expand_pseudo_ops(self.nodes.clone(), self.hash).unwrap();
|
||||
|
||||
let expanded = expand_pseudo_ops(self.nodes.clone(), self.hash)?;
|
||||
self.nodes = expanded;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_dependencies(&mut self) {
|
||||
let nodes = resolve_dependencies(self.nodes.clone(), self.path.parent().unwrap())
|
||||
.unwrap();
|
||||
fn prepare_dependencies(&self) -> Result<(), AssembleError> {
|
||||
let nodes = resolve_dependencies(
|
||||
self.nodes.clone(),
|
||||
self.path.parent().expect("File should have a parent path!"),
|
||||
)?;
|
||||
|
||||
let dependencies = Parser::get_dependencies(&nodes, &self.path).unwrap();
|
||||
let dependencies = Parser::get_dependencies(&nodes, &self.path)?;
|
||||
|
||||
for dep in dependencies {
|
||||
if self.program.is_registered(&dep) {
|
||||
@@ -189,14 +230,32 @@ impl Module {
|
||||
|
||||
// create new module
|
||||
// add the task to the program
|
||||
let task = Module::build(dep, self.program.clone());
|
||||
self.program.add_task(task);
|
||||
|
||||
match Self::build(dep, self.program.clone()) {
|
||||
Ok(task) => self.program.add_task(task),
|
||||
Err(why) => {
|
||||
eprintln!("Error building program: {why}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// helper functions
|
||||
/// Gets the filename from a [`PathBuf`].
|
||||
fn get_filename(&self) -> &str {
|
||||
self.path.file_name().unwrap().to_str().unwrap()
|
||||
self.path
|
||||
.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Gets the parent filepath from a [`PathBuf`].
|
||||
fn get_parent(&self) -> &str {
|
||||
self.path
|
||||
.parent()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+282
-131
@@ -12,8 +12,8 @@ pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||
|
||||
for node in nodes {
|
||||
instructions.push(
|
||||
build_instruction(node.clone())
|
||||
.unwrap_or_else(|_| panic!("Failed to build instruction: {:?}", node)),
|
||||
build_instruction(&node)
|
||||
.unwrap_or_else(|_| panic!("Failed to build instruction: {node:?}")),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,22 +23,13 @@ pub fn codegen(nodes: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
|
||||
let opcode = node.opcode();
|
||||
let args = node.args();
|
||||
|
||||
match opcode {
|
||||
Opcode::Nop => Ok(Instruction::Nop),
|
||||
Opcode::Mov => {
|
||||
let src = expect_token!(args.first().unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::Mov(args!(R, sr1: src, dr: dest)))
|
||||
}
|
||||
Opcode::Movs => {
|
||||
let src = expect_token!(args.first().unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::MovSigned(args!(R, sr1: src, dr: dest)))
|
||||
}
|
||||
Opcode::Mov | Opcode::Movs => build_mov_instruction(opcode, &args),
|
||||
Opcode::Ldb
|
||||
| Opcode::Ldw
|
||||
| Opcode::Ldh
|
||||
@@ -46,83 +37,18 @@ fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
| Opcode::Ldhs
|
||||
| Opcode::Stb
|
||||
| Opcode::Stw
|
||||
| Opcode::Sth => {
|
||||
let base = expect_token!(args.first().unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let offset = expect_token!(args.get(2).unwrap(), Immediate)?;
|
||||
let args = args!(I, immediate: offset as u16, r1: base, r2: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Ldb => Ok(Instruction::LoadByte(args)),
|
||||
Opcode::Ldw => Ok(Instruction::LoadWord(args)),
|
||||
Opcode::Ldh => Ok(Instruction::LoadHalfword(args)),
|
||||
Opcode::Ldbs => Ok(Instruction::LoadByteSigned(args)),
|
||||
Opcode::Ldhs => Ok(Instruction::LoadHalfwordSigned(args)),
|
||||
Opcode::Stb => Ok(Instruction::StoreByte(args)),
|
||||
Opcode::Stw => Ok(Instruction::StoreWord(args)),
|
||||
Opcode::Sth => Ok(Instruction::StoreHalfword(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Lli => {
|
||||
let value = expect_token!(args.first().unwrap(), Immediate)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: value as u16, r1: dest);
|
||||
|
||||
Ok(Instruction::LoadLowerImmediate(args))
|
||||
}
|
||||
Opcode::Lui => {
|
||||
let value = expect_token!(args.first().unwrap(), Immediate)? >> 16;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: value as u16, r1: dest);
|
||||
|
||||
Ok(Instruction::LoadUpperImmediate(args))
|
||||
}
|
||||
| Opcode::Sth => build_memory_instruction(opcode, &args),
|
||||
Opcode::Lli | Opcode::Lui => build_load_immediate_instruction(opcode, &args),
|
||||
Opcode::Jmp
|
||||
| Opcode::Jeq
|
||||
| Opcode::Jne
|
||||
| Opcode::Jgt
|
||||
| Opcode::Jge
|
||||
| Opcode::Jlt
|
||||
| Opcode::Jle => {
|
||||
let address = expect_token!(args.first().unwrap(), Immediate)?;
|
||||
let offset = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: address as u16, r1: offset);
|
||||
|
||||
match opcode {
|
||||
Opcode::Jmp => Ok(Instruction::Jump(args)),
|
||||
Opcode::Jeq => Ok(Instruction::JumpEq(args)),
|
||||
Opcode::Jne => Ok(Instruction::JumpNeq(args)),
|
||||
Opcode::Jgt => Ok(Instruction::JumpGt(args)),
|
||||
Opcode::Jge => Ok(Instruction::JumpGe(args)),
|
||||
Opcode::Jlt => Ok(Instruction::JumpLt(args)),
|
||||
Opcode::Jle => Ok(Instruction::JumpLe(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Cmp => {
|
||||
let left = expect_token!(args.first().unwrap(), Register)?;
|
||||
let right = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::Compare(args!(R, sr1: left, sr2: right)))
|
||||
}
|
||||
Opcode::Inc => {
|
||||
let reg = expect_token!(args.first().unwrap(), Register)?;
|
||||
Ok(Instruction::Increment(args!(R, sr1: reg)))
|
||||
}
|
||||
Opcode::Dec => {
|
||||
let reg = expect_token!(args.first().unwrap(), Register)?;
|
||||
Ok(Instruction::Decrement(args!(R, sr1: reg)))
|
||||
}
|
||||
Opcode::Shl => {
|
||||
let reg = expect_token!(args.first().unwrap(), Register)?;
|
||||
let amount = expect_token!(args.get(1).unwrap(), Immediate)? as u8;
|
||||
Ok(Instruction::ShiftLeft(args!(R, sr1: reg, shamt: amount)))
|
||||
}
|
||||
Opcode::Shr => {
|
||||
let reg = expect_token!(args.first().unwrap(), Register)?;
|
||||
let amount = expect_token!(args.get(1).unwrap(), Immediate)? as u8;
|
||||
Ok(Instruction::ShiftRight(args!(R, sr1: reg, shamt: amount)))
|
||||
}
|
||||
| Opcode::Jle => build_jump_instruction(opcode, &args),
|
||||
Opcode::Cmp => build_compare_instruction(&args),
|
||||
Opcode::Inc | Opcode::Dec => build_inc_dec_instruction(opcode, &args),
|
||||
Opcode::Shl | Opcode::Shr => build_shift_instruction(opcode, &args),
|
||||
Opcode::Add
|
||||
| Opcode::Sub
|
||||
| Opcode::And
|
||||
@@ -130,58 +56,17 @@ fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
| Opcode::Xor
|
||||
| Opcode::Nand
|
||||
| Opcode::Nor
|
||||
| Opcode::Xnor => {
|
||||
let left = expect_token!(args.first().unwrap(), Register)?;
|
||||
let right = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(2).unwrap(), Register)?;
|
||||
let args = args!(R, sr1: left, sr2: right, dr: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Add => Ok(Instruction::Add(args)),
|
||||
Opcode::Sub => Ok(Instruction::Sub(args)),
|
||||
Opcode::And => Ok(Instruction::And(args)),
|
||||
Opcode::Or => Ok(Instruction::Or(args)),
|
||||
Opcode::Xor => Ok(Instruction::Xor(args)),
|
||||
Opcode::Nand => Ok(Instruction::Nand(args)),
|
||||
Opcode::Nor => Ok(Instruction::Nor(args)),
|
||||
Opcode::Xnor => Ok(Instruction::Xnor(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
| Opcode::Xnor => build_arithmetic_instruction(opcode, &args),
|
||||
Opcode::AddI | Opcode::SubI => {
|
||||
let reg = expect_token!(args.first().unwrap(), Register)?;
|
||||
let immediate = expect_token!(args.get(1).unwrap(), Immediate)? as u16;
|
||||
let dest = expect_token!(args.get(2).unwrap(), Register)?;
|
||||
let args = args!(I, immediate: immediate, r1: reg, r2: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::AddI => Ok(Instruction::AddImmediate(args)),
|
||||
Opcode::SubI => Ok(Instruction::SubImmediate(args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Opcode::Not => {
|
||||
let reg = expect_token!(args.first().unwrap(), Register)?;
|
||||
let dest = expect_token!(args.get(1).unwrap(), Register)?;
|
||||
Ok(Instruction::Not(args!(R, sr1: reg, dr: dest)))
|
||||
}
|
||||
Opcode::Int => {
|
||||
let code = expect_token!(args.first().unwrap(), Immediate)? as u8;
|
||||
Ok(Instruction::Interrupt(Interrupt::Software(code)))
|
||||
build_arithmetic_immediate_instruction(opcode, &args)
|
||||
}
|
||||
Opcode::Not => build_not_instruction(&args),
|
||||
Opcode::Int => build_interrupt_instruction(&args),
|
||||
Opcode::Irt => Ok(Instruction::IntReturn),
|
||||
Opcode::Hlt => Ok(Instruction::Halt),
|
||||
Opcode::Data => {
|
||||
let immediate = expect_token!(args.first().unwrap(), Immediate)?;
|
||||
Ok(Instruction::Data(immediate))
|
||||
}
|
||||
Opcode::Segment => {
|
||||
let immediate = expect_token!(args.first().unwrap(), Immediate)?;
|
||||
Ok(Instruction::Segment(immediate))
|
||||
}
|
||||
|
||||
Opcode::Data => build_data_instruction(&args),
|
||||
Opcode::Segment => build_segment_instruction(&args),
|
||||
// These pseudo-instructions should have been expanded!
|
||||
// this case being activated suggests the wrong syntax was given, and indicates a bug in the parser
|
||||
Opcode::Db
|
||||
| Opcode::Dh
|
||||
| Opcode::Dw
|
||||
@@ -198,3 +83,269 @@ fn build_instruction(node: Node) -> Result<Instruction, AssembleError> {
|
||||
| Opcode::Popa => Err(AssembleError::InvalidArg),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_mov_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(src_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(dest_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let src = expect_token!(src_token, Register)?;
|
||||
let dest = expect_token!(dest_token, Register)?;
|
||||
|
||||
match opcode {
|
||||
Opcode::Mov => Ok(Instruction::Mov(args!(R, sr1: src, dr: dest))),
|
||||
Opcode::Movs => Ok(Instruction::MovSigned(args!(R, sr1: src, dr: dest))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_memory_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(base_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(dest_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
let Some(offset_token) = args.get(2) else {
|
||||
return Err(AssembleError::MissingArgument(2));
|
||||
};
|
||||
|
||||
let base = expect_token!(base_token, Register)?;
|
||||
let dest = expect_token!(dest_token, Register)?;
|
||||
let offset = expect_token!(offset_token, Immediate)?;
|
||||
let instruction_args = args!(I, immediate: offset as u16, r1: base, r2: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Ldb => Ok(Instruction::LoadByte(instruction_args)),
|
||||
Opcode::Ldw => Ok(Instruction::LoadWord(instruction_args)),
|
||||
Opcode::Ldh => Ok(Instruction::LoadHalfword(instruction_args)),
|
||||
Opcode::Ldbs => Ok(Instruction::LoadByteSigned(instruction_args)),
|
||||
Opcode::Ldhs => Ok(Instruction::LoadHalfwordSigned(instruction_args)),
|
||||
Opcode::Stb => Ok(Instruction::StoreByte(instruction_args)),
|
||||
Opcode::Stw => Ok(Instruction::StoreWord(instruction_args)),
|
||||
Opcode::Sth => Ok(Instruction::StoreHalfword(instruction_args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_load_immediate_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(value_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(dest_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let value = expect_token!(value_token, Immediate)?;
|
||||
let dest = expect_token!(dest_token, Register)?;
|
||||
|
||||
match opcode {
|
||||
Opcode::Lli => {
|
||||
let instruction_args = args!(I, immediate: value as u16, r1: dest);
|
||||
Ok(Instruction::LoadLowerImmediate(instruction_args))
|
||||
}
|
||||
Opcode::Lui => {
|
||||
let upper_value = value >> 16;
|
||||
let instruction_args = args!(I, immediate: upper_value as u16, r1: dest);
|
||||
Ok(Instruction::LoadUpperImmediate(instruction_args))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_jump_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(address_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(offset_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let address = expect_token!(address_token, Immediate)?;
|
||||
let offset = expect_token!(offset_token, Register)?;
|
||||
let instruction_args = args!(I, immediate: address as u16, r1: offset);
|
||||
|
||||
match opcode {
|
||||
Opcode::Jmp => Ok(Instruction::Jump(instruction_args)),
|
||||
Opcode::Jeq => Ok(Instruction::JumpEq(instruction_args)),
|
||||
Opcode::Jne => Ok(Instruction::JumpNeq(instruction_args)),
|
||||
Opcode::Jgt => Ok(Instruction::JumpGt(instruction_args)),
|
||||
Opcode::Jge => Ok(Instruction::JumpGe(instruction_args)),
|
||||
Opcode::Jlt => Ok(Instruction::JumpLt(instruction_args)),
|
||||
Opcode::Jle => Ok(Instruction::JumpLe(instruction_args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_compare_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(left_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(right_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let left = expect_token!(left_token, Register)?;
|
||||
let right = expect_token!(right_token, Register)?;
|
||||
Ok(Instruction::Compare(args!(R, sr1: left, sr2: right)))
|
||||
}
|
||||
|
||||
fn build_inc_dec_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let reg = expect_token!(reg_token, Register)?;
|
||||
match opcode {
|
||||
Opcode::Inc => Ok(Instruction::Increment(args!(R, sr1: reg))),
|
||||
Opcode::Dec => Ok(Instruction::Decrement(args!(R, sr1: reg))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_shift_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(amount_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let reg = expect_token!(reg_token, Register)?;
|
||||
let amount = expect_token!(amount_token, Immediate)? as u8;
|
||||
|
||||
match opcode {
|
||||
Opcode::Shl => Ok(Instruction::ShiftLeft(args!(R, sr1: reg, shamt: amount))),
|
||||
Opcode::Shr => Ok(Instruction::ShiftRight(args!(R, sr1: reg, shamt: amount))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_arithmetic_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(left_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(right_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
let Some(dest_token) = args.get(2) else {
|
||||
return Err(AssembleError::MissingArgument(2));
|
||||
};
|
||||
|
||||
let left = expect_token!(left_token, Register)?;
|
||||
let right = expect_token!(right_token, Register)?;
|
||||
let dest = expect_token!(dest_token, Register)?;
|
||||
let instruction_args = args!(R, sr1: left, sr2: right, dr: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::Add => Ok(Instruction::Add(instruction_args)),
|
||||
Opcode::Sub => Ok(Instruction::Sub(instruction_args)),
|
||||
Opcode::And => Ok(Instruction::And(instruction_args)),
|
||||
Opcode::Or => Ok(Instruction::Or(instruction_args)),
|
||||
Opcode::Xor => Ok(Instruction::Xor(instruction_args)),
|
||||
Opcode::Nand => Ok(Instruction::Nand(instruction_args)),
|
||||
Opcode::Nor => Ok(Instruction::Nor(instruction_args)),
|
||||
Opcode::Xnor => Ok(Instruction::Xnor(instruction_args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_arithmetic_immediate_instruction(
|
||||
opcode: Opcode,
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(immediate_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
let Some(dest_token) = args.get(2) else {
|
||||
return Err(AssembleError::MissingArgument(2));
|
||||
};
|
||||
|
||||
let reg = expect_token!(reg_token, Register)?;
|
||||
let immediate = expect_token!(immediate_token, Immediate)? as u16;
|
||||
let dest = expect_token!(dest_token, Register)?;
|
||||
let instruction_args = args!(I, immediate: immediate, r1: reg, r2: dest);
|
||||
|
||||
match opcode {
|
||||
Opcode::AddI => Ok(Instruction::AddImmediate(instruction_args)),
|
||||
Opcode::SubI => Ok(Instruction::SubImmediate(instruction_args)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_not_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(reg_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
let Some(dest_token) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let reg = expect_token!(reg_token, Register)?;
|
||||
let dest = expect_token!(dest_token, Register)?;
|
||||
Ok(Instruction::Not(args!(R, sr1: reg, dr: dest)))
|
||||
}
|
||||
|
||||
fn build_interrupt_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(code_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let code = expect_token!(code_token, Immediate)? as u8;
|
||||
Ok(Instruction::Interrupt(Interrupt::Software(code)))
|
||||
}
|
||||
|
||||
fn build_data_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(immediate_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let immediate = expect_token!(immediate_token, Immediate)?;
|
||||
Ok(Instruction::Data(immediate))
|
||||
}
|
||||
|
||||
fn build_segment_instruction(
|
||||
args: &[crate::assembler::model::Token],
|
||||
) -> Result<Instruction, AssembleError> {
|
||||
let Some(immediate_token) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let immediate = expect_token!(immediate_token, Immediate)?;
|
||||
Ok(Instruction::Segment(immediate))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn expand_pseudo_ops(
|
||||
) -> Result<Vec<Node>, AssembleError> {
|
||||
let mut result = Vec::<Node>::with_capacity(nodes.len());
|
||||
|
||||
for node in nodes.iter_mut() {
|
||||
for node in &mut nodes {
|
||||
if try_expand(node.clone(), &mut result, module).is_err() {
|
||||
result.push(node.clone());
|
||||
}
|
||||
@@ -24,28 +24,32 @@ fn try_expand(
|
||||
_module: u64,
|
||||
) -> Result<(), AssembleError> {
|
||||
match node.opcode() {
|
||||
Opcode::Push => expand_push(node.clone(), result)?,
|
||||
Opcode::Pop => expand_pop(node.clone(), result)?,
|
||||
Opcode::Pusha => expand_pusha(node.clone(), result)?,
|
||||
Opcode::Popa => expand_popa(node.clone(), result)?,
|
||||
Opcode::Call => expand_call(node.clone(), result)?,
|
||||
Opcode::Return => expand_return(node.clone(), result)?,
|
||||
Opcode::Push => expand_push(&node, result)?,
|
||||
Opcode::Pop => expand_pop(&node, result)?,
|
||||
Opcode::Pusha => expand_pusha(&node, result)?,
|
||||
Opcode::Popa => expand_popa(&node, result)?,
|
||||
Opcode::Call => expand_call(&node, result)?,
|
||||
Opcode::Return => expand_return(&node, result),
|
||||
Opcode::Ldb | Opcode::Ldbs | Opcode::Ldh | Opcode::Ldhs | Opcode::Ldw => {
|
||||
expand_ldx(node.clone(), result)?
|
||||
expand_ldx(&node, result)?;
|
||||
}
|
||||
Opcode::Stb | Opcode::Sth | Opcode::Stw => expand_stx(node.clone(), result)?,
|
||||
Opcode::Stb | Opcode::Sth | Opcode::Stw => expand_stx(&node, result)?,
|
||||
|
||||
Opcode::Lwi => expand_lwi(node.clone(), result)?,
|
||||
Opcode::Resb | Opcode::Resh | Opcode::Resw => expand_resx(node.clone(), result)?,
|
||||
Opcode::Db | Opcode::Dh | Opcode::Dw => expand_dx(node.clone(), result)?,
|
||||
_ => result.push(node.clone()),
|
||||
};
|
||||
Opcode::Lwi => expand_lwi(&node, result)?,
|
||||
Opcode::Resb | Opcode::Resh | Opcode::Resw => expand_resx(&node, result)?,
|
||||
Opcode::Db | Opcode::Dh | Opcode::Dw => expand_dx(&node, result)?,
|
||||
_ => result.push(node),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_push(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_push(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
let Ok(arg0) = current.arg(0) else {
|
||||
return Err(AssembleError::Generic);
|
||||
};
|
||||
|
||||
let reg = expect_type!(arg0, Register)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
|
||||
nodes.extend(vec![
|
||||
@@ -56,9 +60,13 @@ fn expand_push(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_pusha(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_pusha(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let count = expect_token!(current.arg(0).unwrap(), Immediate)?;
|
||||
let Ok(arg0) = current.arg(0) else {
|
||||
return Err(AssembleError::Generic);
|
||||
};
|
||||
|
||||
let count = expect_token!(arg0, Immediate)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let registers: Vec<Register> = Register::general();
|
||||
|
||||
@@ -70,44 +78,39 @@ fn expand_pusha(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleErro
|
||||
spr
|
||||
));
|
||||
|
||||
nodes.extend(
|
||||
(0..count)
|
||||
.rev()
|
||||
.map(|i| {
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
Token::Register(registers[i as usize]),
|
||||
spr,
|
||||
Token::Immediate(i * 4)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Node>>(),
|
||||
);
|
||||
nodes.extend((0..count).rev().map(|i| {
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
Token::Register(registers[i as usize]),
|
||||
spr,
|
||||
Token::Immediate(i * 4)
|
||||
)
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_popa(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_popa(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let count = expect_token!(current.arg(0).unwrap(), Immediate)?;
|
||||
|
||||
let Ok(arg0) = current.arg(0) else {
|
||||
return Err(AssembleError::Generic);
|
||||
};
|
||||
|
||||
let count = expect_token!(arg0, Immediate)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let registers: Vec<Register> = Register::general();
|
||||
|
||||
nodes.extend(
|
||||
(0..count)
|
||||
.rev()
|
||||
.map(|i| {
|
||||
node!(
|
||||
{ if i == 0 { label.clone() } else { None } },
|
||||
Opcode::Ldw,
|
||||
spr,
|
||||
Token::Register(registers[i as usize]),
|
||||
Token::Immediate(i * 4)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Node>>(),
|
||||
);
|
||||
nodes.extend((0..count).rev().map(|i| {
|
||||
node!(
|
||||
{ if i == 0 { label.clone() } else { None } },
|
||||
Opcode::Ldw,
|
||||
spr,
|
||||
Token::Register(registers[i as usize]),
|
||||
Token::Immediate(i * 4)
|
||||
)
|
||||
}));
|
||||
|
||||
nodes.push(node!(
|
||||
None,
|
||||
@@ -120,9 +123,14 @@ fn expand_popa(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_call(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_call(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let addr = expect_type!(current.arg(0).unwrap(), Symbol)?;
|
||||
|
||||
let Ok(arg0) = current.arg(0) else {
|
||||
return Err(AssembleError::Generic);
|
||||
};
|
||||
|
||||
let addr = expect_type!(arg0, Symbol)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let pcx = Token::Register(Register::Pcx);
|
||||
let zero = Token::Register(Register::Zero);
|
||||
@@ -136,7 +144,7 @@ fn expand_call(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_return(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_return(current: &Node, nodes: &mut Vec<Node>) {
|
||||
let label = current.label();
|
||||
let spr = Token::Register(Register::Spr);
|
||||
let ret = Token::Register(Register::Ret);
|
||||
@@ -146,13 +154,16 @@ fn expand_return(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleErr
|
||||
node!(None, Opcode::AddI, spr, 4, spr),
|
||||
node!(None, Opcode::Jmp, 4, ret),
|
||||
]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_pop(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_pop(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let label = current.label();
|
||||
let reg = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
|
||||
let Ok(arg0) = current.arg(0) else {
|
||||
return Err(AssembleError::Generic);
|
||||
};
|
||||
|
||||
let reg = expect_type!(arg0, Register)?;
|
||||
let spr = Token::Register(Register::Spr);
|
||||
|
||||
nodes.extend(vec![
|
||||
@@ -163,11 +174,25 @@ fn expand_pop(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_ldx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_ldx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let opcode = current.opcode();
|
||||
let name = expect_type!(current.arg(0).unwrap(), Symbol)?;
|
||||
let reg = expect_type!(current.arg(1).unwrap(), Register)?;
|
||||
let offset = expect_type!(current.arg(2).unwrap(), Immediate)?;
|
||||
let args: Vec<Token> = current.args().into_iter().take(3).collect();
|
||||
|
||||
let Some(name) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let Some(reg) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let Some(offset) = args.get(2) else {
|
||||
return Err(AssembleError::MissingArgument(2));
|
||||
};
|
||||
|
||||
let name = expect_type!(name, Symbol)?;
|
||||
let reg = expect_type!(reg, Register)?;
|
||||
let offset = expect_type!(offset, Immediate)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(current.label(), Opcode::Lli, name, reg),
|
||||
@@ -178,11 +203,26 @@ fn expand_ldx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_stx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
fn expand_stx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let opcode = current.opcode();
|
||||
let base = expect_type!(current.arg(0).unwrap(), Register)?;
|
||||
let dest = expect_type!(current.arg(1).unwrap(), Symbol)?;
|
||||
let offset = expect_type!(current.arg(2).unwrap(), Immediate)?;
|
||||
|
||||
let args: Vec<Token> = current.args().into_iter().take(3).collect();
|
||||
|
||||
let Some(base) = args.first() else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let Some(dest) = args.get(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let Some(offset) = args.get(2) else {
|
||||
return Err(AssembleError::MissingArgument(2));
|
||||
};
|
||||
|
||||
let base = expect_type!(base, Register)?;
|
||||
let dest = expect_type!(dest, Symbol)?;
|
||||
let offset = expect_type!(offset, Immediate)?;
|
||||
let temp = Token::Register(Register::Acc);
|
||||
|
||||
nodes.extend(vec![
|
||||
@@ -194,9 +234,17 @@ fn expand_stx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_lwi(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let val = expect_type!(current.arg(0).unwrap(), Symbol, Immediate)?;
|
||||
let reg = expect_type!(current.arg(1).unwrap(), Register)?;
|
||||
fn expand_lwi(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let Ok(val) = current.arg(0) else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let Ok(reg) = current.arg(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let val = expect_type!(val, Symbol, Immediate)?;
|
||||
let reg = expect_type!(reg, Register)?;
|
||||
|
||||
nodes.extend(vec![
|
||||
node!(current.label(), Opcode::Lli, val, reg),
|
||||
@@ -206,9 +254,17 @@ fn expand_lwi(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_resx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let region_label = expect_token!(current.arg(0).unwrap(), Symbol)?;
|
||||
let size = expect_token!(current.arg(1).unwrap(), Immediate)?;
|
||||
fn expand_resx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let Ok(region_label) = current.arg(0) else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let Ok(size) = current.arg(1) else {
|
||||
return Err(AssembleError::MissingArgument(1));
|
||||
};
|
||||
|
||||
let region_label = expect_token!(region_label, Symbol)?;
|
||||
let size = expect_token!(size, Immediate)?;
|
||||
|
||||
let units_per = match current.opcode() {
|
||||
Opcode::Resb => 4,
|
||||
@@ -229,8 +285,12 @@ fn expand_resx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_dx(current: Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let region_label = expect_token!(current.arg(0).unwrap(), Symbol)?;
|
||||
fn expand_dx(current: &Node, nodes: &mut Vec<Node>) -> Result<(), AssembleError> {
|
||||
let Ok(region_label) = current.arg(0) else {
|
||||
return Err(AssembleError::MissingArgument(0));
|
||||
};
|
||||
|
||||
let region_label = expect_token!(region_label, Symbol)?;
|
||||
let size = match current.opcode() {
|
||||
Opcode::Db => 4,
|
||||
Opcode::Dh => 2,
|
||||
|
||||
@@ -7,7 +7,7 @@ use common::prelude::Register;
|
||||
pub fn lexer(mut program: String, module: u64) -> Result<Vec<Token>, AssembleError> {
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
program = program.replace(",", "");
|
||||
program = program.replace(',', "");
|
||||
let lines = program.lines();
|
||||
let mut literal = String::new();
|
||||
|
||||
@@ -67,7 +67,9 @@ pub fn parse_register(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
|
||||
pub fn parse_opcode(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
if Opcode::OPCODES.contains(&token) {
|
||||
Ok(Some(Token::Opcode(Opcode::from_str(token).unwrap())))
|
||||
Ok(Some(Token::Opcode(Opcode::from_str(token).expect(
|
||||
"Opcode::from_str failed for a valid opcode token",
|
||||
))))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@@ -78,10 +80,13 @@ pub fn parse_hex(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
match u32::from_str_radix(&token[2..], 16) {
|
||||
Ok(value) => Ok(Some(Token::Immediate(value))),
|
||||
Err(_) => Err(AssembleError::Generic),
|
||||
}
|
||||
let Some(lit) = &token.get(2..) else {
|
||||
return Err(AssembleError::InvalidArg);
|
||||
};
|
||||
|
||||
u32::from_str_radix(lit, 16).map_or(Err(AssembleError::Generic), |value| {
|
||||
Ok(Some(Token::Immediate(value)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_octal(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
@@ -89,10 +94,13 @@ pub fn parse_octal(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
match u32::from_str_radix(&token[2..], 8) {
|
||||
Ok(value) => Ok(Some(Token::Immediate(value))),
|
||||
Err(_) => Err(AssembleError::Generic),
|
||||
}
|
||||
let Some(lit) = &token.get(2..) else {
|
||||
return Err(AssembleError::InvalidArg);
|
||||
};
|
||||
|
||||
u32::from_str_radix(lit, 8).map_or(Err(AssembleError::Generic), |value| {
|
||||
Ok(Some(Token::Immediate(value)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_binary(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
@@ -100,38 +108,48 @@ pub fn parse_binary(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
match u32::from_str_radix(&token[2..], 2) {
|
||||
Ok(value) => Ok(Some(Token::Immediate(value))),
|
||||
Err(_) => Err(AssembleError::Generic),
|
||||
}
|
||||
let Some(lit) = &token.get(2..) else {
|
||||
return Err(AssembleError::InvalidArg);
|
||||
};
|
||||
|
||||
u32::from_str_radix(lit, 2).map_or(Err(AssembleError::Generic), |value| {
|
||||
Ok(Some(Token::Immediate(value)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_decimal(token: &str) -> Result<Option<Token>, AssembleError> {
|
||||
if token.parse::<u32>().is_err() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Token::Immediate(token.parse().unwrap())))
|
||||
}
|
||||
let Ok(tok) = token.parse::<u32>() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
Ok(Some(Token::Immediate(tok)))
|
||||
}
|
||||
|
||||
pub fn parse_label(token: &str, module: u64) -> Result<Option<Token>, AssembleError> {
|
||||
if !token.ends_with(":") {
|
||||
Ok(None)
|
||||
} else {
|
||||
if token.ends_with(':') {
|
||||
Ok(Some(Token::Symbol(Symbol {
|
||||
name: token[0..token.len() - 1].to_string(),
|
||||
module: Module::Resolved(module),
|
||||
})))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_symbol(token: &str, module: u64) -> Result<Option<Token>, AssembleError> {
|
||||
if token.chars().next().unwrap().is_numeric() {
|
||||
let Some(tokc) = token.chars().next() else {
|
||||
return Err(AssembleError::Generic); // TODO: What is this error?
|
||||
};
|
||||
|
||||
if tokc.is_numeric() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut split = token.splitn(2, "::");
|
||||
let symbol1 = split.next().unwrap().to_string();
|
||||
let Some(symbol1) = split.next() else {
|
||||
return Err(AssembleError::InvalidArg);
|
||||
};
|
||||
let symbol1 = symbol1.to_string();
|
||||
|
||||
if let Some(symbol2) = split.next() {
|
||||
Ok(Some(Token::Symbol(Symbol {
|
||||
|
||||
@@ -48,6 +48,7 @@ pub struct CompilerEngine {
|
||||
}
|
||||
|
||||
impl CompilerEngine {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
Self {
|
||||
@@ -68,7 +69,8 @@ impl CompilerEngine {
|
||||
|
||||
thread::spawn(move || {
|
||||
let result = assemble(&src);
|
||||
tx.send(result).unwrap();
|
||||
tx.send(result)
|
||||
.expect("Failed to send compilation result from worker thread");
|
||||
});
|
||||
|
||||
self.is_running = true;
|
||||
@@ -80,7 +82,12 @@ impl CompilerEngine {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self.result_rx.as_ref().unwrap().try_recv() {
|
||||
match self
|
||||
.result_rx
|
||||
.as_ref()
|
||||
.expect("result_rx should be Some while compilation is running")
|
||||
.try_recv()
|
||||
{
|
||||
Ok(result) => {
|
||||
self.is_running = false;
|
||||
Some(result)
|
||||
@@ -99,15 +106,17 @@ impl CompilerEngine {
|
||||
return Err(AssembleError::Generic);
|
||||
}
|
||||
|
||||
match self.result_rx.take().unwrap().recv() {
|
||||
Ok(result) => {
|
||||
self.is_running = false;
|
||||
result
|
||||
}
|
||||
Err(_) => {
|
||||
self.is_running = false;
|
||||
Err(AssembleError::Generic)
|
||||
}
|
||||
if let Ok(result) = self
|
||||
.result_rx
|
||||
.take()
|
||||
.expect("result_rx should be Some while waiting for compilation result")
|
||||
.recv()
|
||||
{
|
||||
self.is_running = false;
|
||||
result
|
||||
} else {
|
||||
self.is_running = false;
|
||||
Err(AssembleError::Generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,7 +153,11 @@ fn prepare_dependency(
|
||||
modules: &mut HashSet<u64>,
|
||||
program: &mut Program,
|
||||
) -> Result<(), AssembleError> {
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
let filename = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.expect("Failed to get file name from path");
|
||||
|
||||
if let Ok(path) = path.canonicalize() {
|
||||
log(&format!(
|
||||
"{:20} {:20} [{}]",
|
||||
@@ -185,7 +198,7 @@ fn prepare_dependency(
|
||||
node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)),
|
||||
);
|
||||
|
||||
for n in nodes.iter() {
|
||||
for n in &nodes {
|
||||
println!("{n}");
|
||||
}
|
||||
|
||||
@@ -195,12 +208,14 @@ fn prepare_dependency(
|
||||
log(&format!(
|
||||
"{:20} {:20}",
|
||||
"Including",
|
||||
dep.file_name().unwrap().to_str().unwrap()
|
||||
dep.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.expect("Dependency path has no file name or is not valid UTF-8")
|
||||
));
|
||||
|
||||
if !modules.contains(&quick_hash(&dep)) {
|
||||
modules.insert(quick_hash(&dep));
|
||||
prepare_dependency(dep.as_path(), modules, program)?
|
||||
let dep_hash = quick_hash(&dep);
|
||||
if modules.insert(dep_hash) {
|
||||
prepare_dependency(dep.as_path(), modules, program)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,27 +230,37 @@ pub enum AssembleError {
|
||||
UnexpectedToken(Token, TokenType),
|
||||
InvalidArg,
|
||||
UndefinedSymbol(Symbol),
|
||||
/// Contains the nth element missing from the instruction.
|
||||
MissingArgument(u8),
|
||||
}
|
||||
|
||||
impl fmt::Display for AssembleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AssembleError::Generic => write!(f, "Generic error"),
|
||||
AssembleError::UnexpectedToken(tok, expected) => {
|
||||
Self::Generic => write!(f, "Generic error"),
|
||||
Self::UnexpectedToken(tok, expected) => {
|
||||
write!(f, "Unexpected token {tok:?}, expected {expected:?}")
|
||||
}
|
||||
AssembleError::UnexpectedEof => write!(f, "Unexpected end of file"),
|
||||
AssembleError::InvalidFile(path) => write!(f, "Invalid file {path:?}"),
|
||||
AssembleError::InvalidArg => write!(f, "Invalid argument"),
|
||||
AssembleError::UndefinedSymbol(symbol) => {
|
||||
Self::UnexpectedEof => write!(f, "Unexpected end of file"),
|
||||
Self::InvalidFile(path) => write!(f, "Invalid file `{}`", path.display()),
|
||||
Self::InvalidArg => write!(f, "Invalid argument"),
|
||||
Self::UndefinedSymbol(symbol) => {
|
||||
write!(f, "Undefined symbol {symbol}")
|
||||
}
|
||||
Self::MissingArgument(n) => {
|
||||
write!(f, "Missing argument #{n} from instruction arguments.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn quick_hash(value: &Path) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
value.canonicalize().unwrap().to_str().hash(&mut hasher);
|
||||
value
|
||||
.canonicalize()
|
||||
.expect("Failed to canonicalize path for quick_hash")
|
||||
.to_str()
|
||||
.hash(&mut hasher);
|
||||
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
@@ -12,22 +12,26 @@ pub struct Node {
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(symbol: Option<Symbol>, opcode: Opcode, tokens: Vec<Token>) -> Node {
|
||||
Node {
|
||||
#[must_use]
|
||||
pub const fn new(symbol: Option<Symbol>, opcode: Opcode, tokens: Vec<Token>) -> Self {
|
||||
Self {
|
||||
symbol,
|
||||
opcode,
|
||||
tokens,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn label(&self) -> Option<Symbol> {
|
||||
self.symbol.clone()
|
||||
}
|
||||
|
||||
pub fn opcode(&self) -> Opcode {
|
||||
#[must_use]
|
||||
pub const fn opcode(&self) -> Opcode {
|
||||
self.opcode
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn args(&self) -> Vec<Token> {
|
||||
self.tokens.clone()
|
||||
}
|
||||
@@ -42,10 +46,10 @@ impl Node {
|
||||
|
||||
impl fmt::Display for Node {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let symbol = match &self.label() {
|
||||
Some(symbol) => format!("{symbol}:\n"),
|
||||
None => "".to_string(),
|
||||
};
|
||||
let symbol = self
|
||||
.label()
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |symbol| format!("{symbol}:\n"));
|
||||
|
||||
write!(
|
||||
f,
|
||||
@@ -66,8 +70,8 @@ impl fmt::Display for Symbol {
|
||||
impl fmt::Display for Module {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Module::Unresolved(name) => write!(f, "{name}"),
|
||||
Module::Resolved(name) => write!(f, "{name}"),
|
||||
Self::Unresolved(name) => write!(f, "{name}"),
|
||||
Self::Resolved(name) => write!(f, "{name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,63 +79,63 @@ impl fmt::Display for Module {
|
||||
impl fmt::Display for Opcode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Opcode::Nop => write!(f, "nop"),
|
||||
Opcode::Mov => write!(f, "mov"),
|
||||
Opcode::Movs => write!(f, "movs"),
|
||||
Opcode::Ldb => write!(f, "ldb"),
|
||||
Opcode::Ldbs => write!(f, "ldbs"),
|
||||
Opcode::Ldh => write!(f, "ldh"),
|
||||
Opcode::Ldhs => write!(f, "ldhs"),
|
||||
Opcode::Ldw => write!(f, "ldw"),
|
||||
Opcode::Stb => write!(f, "stb"),
|
||||
Opcode::Sth => write!(f, "sth"),
|
||||
Opcode::Stw => write!(f, "stw"),
|
||||
Opcode::Lli => write!(f, "lli"),
|
||||
Opcode::Lui => write!(f, "lui"),
|
||||
Opcode::Jmp => write!(f, "jmp"),
|
||||
Opcode::Jeq => write!(f, "jeq"),
|
||||
Opcode::Jne => write!(f, "jne"),
|
||||
Opcode::Jgt => write!(f, "jgt"),
|
||||
Opcode::Jge => write!(f, "jge"),
|
||||
Opcode::Jlt => write!(f, "jlt"),
|
||||
Opcode::Jle => write!(f, "jle"),
|
||||
Opcode::Cmp => write!(f, "cmp"),
|
||||
Opcode::Inc => write!(f, "inc"),
|
||||
Opcode::Dec => write!(f, "dec"),
|
||||
Opcode::Shl => write!(f, "shl"),
|
||||
Opcode::Shr => write!(f, "shr"),
|
||||
Opcode::Add => write!(f, "add"),
|
||||
Opcode::Sub => write!(f, "sub"),
|
||||
Opcode::And => write!(f, "and"),
|
||||
Opcode::Or => write!(f, "or"),
|
||||
Opcode::Not => write!(f, "not"),
|
||||
Opcode::Xor => write!(f, "xor"),
|
||||
Opcode::Nand => write!(f, "nand"),
|
||||
Opcode::Nor => write!(f, "nor"),
|
||||
Opcode::Xnor => write!(f, "xnor"),
|
||||
Opcode::Int => write!(f, "int"),
|
||||
Opcode::Irt => write!(f, "irt"),
|
||||
Opcode::Hlt => write!(f, "hlt"),
|
||||
Opcode::AddI => write!(f, "addi"),
|
||||
Opcode::SubI => write!(f, "subi"),
|
||||
Opcode::Db => write!(f, "db"),
|
||||
Opcode::Dh => write!(f, "dh"),
|
||||
Opcode::Dw => write!(f, "dw"),
|
||||
Opcode::Resb => write!(f, "resb"),
|
||||
Opcode::Resh => write!(f, "resh"),
|
||||
Opcode::Resw => write!(f, "resw"),
|
||||
Opcode::Push => write!(f, "push"),
|
||||
Opcode::Pop => write!(f, "pop"),
|
||||
Opcode::Lwi => write!(f, "lwi"),
|
||||
Opcode::Call => write!(f, "call"),
|
||||
Opcode::Return => write!(f, "return"),
|
||||
Opcode::Pusha => write!(f, "pusha"),
|
||||
Opcode::Popa => write!(f, "popa"),
|
||||
Self::Nop => write!(f, "nop"),
|
||||
Self::Mov => write!(f, "mov"),
|
||||
Self::Movs => write!(f, "movs"),
|
||||
Self::Ldb => write!(f, "ldb"),
|
||||
Self::Ldbs => write!(f, "ldbs"),
|
||||
Self::Ldh => write!(f, "ldh"),
|
||||
Self::Ldhs => write!(f, "ldhs"),
|
||||
Self::Ldw => write!(f, "ldw"),
|
||||
Self::Stb => write!(f, "stb"),
|
||||
Self::Sth => write!(f, "sth"),
|
||||
Self::Stw => write!(f, "stw"),
|
||||
Self::Lli => write!(f, "lli"),
|
||||
Self::Lui => write!(f, "lui"),
|
||||
Self::Jmp => write!(f, "jmp"),
|
||||
Self::Jeq => write!(f, "jeq"),
|
||||
Self::Jne => write!(f, "jne"),
|
||||
Self::Jgt => write!(f, "jgt"),
|
||||
Self::Jge => write!(f, "jge"),
|
||||
Self::Jlt => write!(f, "jlt"),
|
||||
Self::Jle => write!(f, "jle"),
|
||||
Self::Cmp => write!(f, "cmp"),
|
||||
Self::Inc => write!(f, "inc"),
|
||||
Self::Dec => write!(f, "dec"),
|
||||
Self::Shl => write!(f, "shl"),
|
||||
Self::Shr => write!(f, "shr"),
|
||||
Self::Add => write!(f, "add"),
|
||||
Self::Sub => write!(f, "sub"),
|
||||
Self::And => write!(f, "and"),
|
||||
Self::Or => write!(f, "or"),
|
||||
Self::Not => write!(f, "not"),
|
||||
Self::Xor => write!(f, "xor"),
|
||||
Self::Nand => write!(f, "nand"),
|
||||
Self::Nor => write!(f, "nor"),
|
||||
Self::Xnor => write!(f, "xnor"),
|
||||
Self::Int => write!(f, "int"),
|
||||
Self::Irt => write!(f, "irt"),
|
||||
Self::Hlt => write!(f, "hlt"),
|
||||
Self::AddI => write!(f, "addi"),
|
||||
Self::SubI => write!(f, "subi"),
|
||||
Self::Db => write!(f, "db"),
|
||||
Self::Dh => write!(f, "dh"),
|
||||
Self::Dw => write!(f, "dw"),
|
||||
Self::Resb => write!(f, "resb"),
|
||||
Self::Resh => write!(f, "resh"),
|
||||
Self::Resw => write!(f, "resw"),
|
||||
Self::Push => write!(f, "push"),
|
||||
Self::Pop => write!(f, "pop"),
|
||||
Self::Lwi => write!(f, "lwi"),
|
||||
Self::Call => write!(f, "call"),
|
||||
Self::Return => write!(f, "return"),
|
||||
Self::Pusha => write!(f, "pusha"),
|
||||
Self::Popa => write!(f, "popa"),
|
||||
|
||||
// meta instructions
|
||||
Opcode::Include => write!(f, "include"),
|
||||
Opcode::Data => write!(f, "data"),
|
||||
Opcode::Segment => write!(f, "[SEGMENT]"),
|
||||
Self::Include => write!(f, "include"),
|
||||
Self::Data => write!(f, "data"),
|
||||
Self::Segment => write!(f, "[SEGMENT]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +174,7 @@ pub enum Token {
|
||||
Opcode(Opcode),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum TokenType {
|
||||
Symbol,
|
||||
Register,
|
||||
@@ -180,18 +184,19 @@ pub enum TokenType {
|
||||
}
|
||||
|
||||
impl TokenType {
|
||||
pub fn from_token(token: &Token) -> TokenType {
|
||||
#[must_use]
|
||||
pub const fn from_token(token: &Token) -> Self {
|
||||
match token {
|
||||
Token::Symbol(_) => TokenType::Symbol,
|
||||
Token::Register(_) => TokenType::Register,
|
||||
Token::Immediate(_) => TokenType::Immediate,
|
||||
Token::StringLit(_) => TokenType::StringLit,
|
||||
Token::Opcode(_) => TokenType::Opcode,
|
||||
Token::Symbol(_) => Self::Symbol,
|
||||
Token::Register(_) => Self::Register,
|
||||
Token::Immediate(_) => Self::Immediate,
|
||||
Token::StringLit(_) => Self::StringLit,
|
||||
Token::Opcode(_) => Self::Opcode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Opcode {
|
||||
// Real instructions (0x00-0x26)
|
||||
Nop,
|
||||
@@ -258,12 +263,14 @@ pub enum Opcode {
|
||||
#[derive(Debug)]
|
||||
pub enum OpcodeFromStrError {
|
||||
InvalidRegister(&'static str),
|
||||
InvalidOpcode(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for OpcodeFromStrError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidRegister(reg) => write!(f, "register does not exist: {reg}"),
|
||||
Self::InvalidOpcode(op) => write!(f, "instruction does not exist: {op}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,7 +335,7 @@ impl FromStr for Opcode {
|
||||
"return" => Ok(Self::Return),
|
||||
"pusha" => Ok(Self::Pusha),
|
||||
"popa" => Ok(Self::Popa),
|
||||
_ => Err(OpcodeFromStrError::InvalidRegister("unknown opcode")),
|
||||
_ => Err(OpcodeFromStrError::InvalidOpcode(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,7 +352,8 @@ impl Opcode {
|
||||
"include",
|
||||
];
|
||||
|
||||
pub fn to_opcode_value(&self) -> Option<u8> {
|
||||
#[must_use]
|
||||
pub const fn to_opcode_value(&self) -> Option<u8> {
|
||||
match self {
|
||||
Self::Nop => Some(0x00),
|
||||
Self::Mov => Some(0x01),
|
||||
@@ -393,7 +401,8 @@ impl Opcode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pseudo_instruction(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn is_pseudo_instruction(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Db
|
||||
|
||||
@@ -16,8 +16,9 @@ pub struct Program {
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new() -> Program {
|
||||
Program { nodes: vec![] }
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self { nodes: vec![] }
|
||||
}
|
||||
|
||||
pub fn add_module(&mut self, module: Vec<Node>) {
|
||||
@@ -40,8 +41,8 @@ impl Default for Program {
|
||||
|
||||
impl Parser {
|
||||
pub fn parse_nodes(tokens: Vec<Token>) -> Result<Vec<Node>, AssembleError> {
|
||||
let mut self_ = Parser {
|
||||
tokens: tokens.clone().into_iter().rev().collect(),
|
||||
let mut self_ = Self {
|
||||
tokens: tokens.into_iter().rev().collect(),
|
||||
nodes: vec![],
|
||||
};
|
||||
|
||||
@@ -64,8 +65,11 @@ impl Parser {
|
||||
.ok_or_else(|| AssembleError::InvalidFile(source_path.to_path_buf()))?;
|
||||
|
||||
for node in nodes {
|
||||
if let Opcode::Include = node.opcode() {
|
||||
let path_str = expect_token!(node.args().get(1).unwrap(), StringLit)?;
|
||||
if node.opcode() == Opcode::Include {
|
||||
let path_str = expect_token!(
|
||||
node.args().get(1).ok_or(AssembleError::Generic)?,
|
||||
StringLit
|
||||
)?;
|
||||
let path = PathBuf::from(path_str);
|
||||
|
||||
// If the path is not absolute, make it relative to the base directory
|
||||
@@ -81,6 +85,7 @@ impl Parser {
|
||||
Ok(dependencies)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
fn parse_instruction(&mut self) -> Result<Node, AssembleError> {
|
||||
if self.tokens.is_empty() {
|
||||
unreachable!();
|
||||
@@ -266,12 +271,21 @@ impl Parser {
|
||||
Opcode::Db => {
|
||||
// db can take string literals or u8 immediates
|
||||
while !self.tokens.is_empty() {
|
||||
match self.tokens.last().unwrap() {
|
||||
let token = self
|
||||
.tokens
|
||||
.last()
|
||||
.expect("Expected a token for data definition, but found none");
|
||||
|
||||
match token {
|
||||
Token::StringLit(_) => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
values.push(self.tokens.pop().expect(
|
||||
"Expected a token for data definition, but found none",
|
||||
));
|
||||
}
|
||||
Token::Immediate(val) if *val <= u8::MAX as u32 => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
Token::Immediate(val) if u8::try_from(*val).is_ok() => {
|
||||
values.push(self.tokens.pop().expect(
|
||||
"Expected a token for data definition, but found none",
|
||||
));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
@@ -281,12 +295,21 @@ impl Parser {
|
||||
Opcode::Dh => {
|
||||
// dh can take u16 immediates
|
||||
while !self.tokens.is_empty() {
|
||||
match self.tokens.last().unwrap() {
|
||||
let token = self
|
||||
.tokens
|
||||
.last()
|
||||
.expect("Expected a token for data definition, but found none");
|
||||
|
||||
match token {
|
||||
Token::StringLit(_) => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
values.push(self.tokens.pop().expect(
|
||||
"Expected a token for data definition, but found none",
|
||||
));
|
||||
}
|
||||
Token::Immediate(val) if *val <= u16::MAX as u32 => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
Token::Immediate(val) if u16::try_from(*val).is_ok() => {
|
||||
values.push(self.tokens.pop().expect(
|
||||
"Expected a token for data definition, but found none",
|
||||
));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
@@ -296,12 +319,20 @@ impl Parser {
|
||||
Opcode::Dw => {
|
||||
// dw can take u32 immediates
|
||||
while !self.tokens.is_empty() {
|
||||
match self.tokens.last().unwrap() {
|
||||
match self
|
||||
.tokens
|
||||
.last()
|
||||
.expect("Expected a token for data definition, but found none")
|
||||
{
|
||||
Token::StringLit(_) => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
values.push(self.tokens.pop().expect(
|
||||
"Expected a token for data definition, but found none",
|
||||
));
|
||||
}
|
||||
Token::Immediate(_) => {
|
||||
values.push(self.tokens.pop().unwrap());
|
||||
Token::Immediate(val) => {
|
||||
values.push(self.tokens.pop().expect(
|
||||
"Expected a token for data definition, but found none",
|
||||
));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
@@ -318,15 +349,22 @@ impl Parser {
|
||||
if self.tokens.is_empty() {
|
||||
Err(AssembleError::UnexpectedEof)
|
||||
} else {
|
||||
Ok(self.tokens.pop().unwrap())
|
||||
Ok(self
|
||||
.tokens
|
||||
.pop()
|
||||
.expect("tokens vector was unexpectedly empty in next()"))
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_next(&mut self) -> Result<Token, AssembleError> {
|
||||
fn peek_next(&self) -> Result<Token, AssembleError> {
|
||||
if self.tokens.is_empty() {
|
||||
Err(AssembleError::UnexpectedEof)
|
||||
} else {
|
||||
Ok(self.tokens.last().unwrap().clone())
|
||||
Ok(self
|
||||
.tokens
|
||||
.last()
|
||||
.expect("peek_next called on empty tokens vector")
|
||||
.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,40 +14,27 @@ use crate::assembler::{
|
||||
use crate::{assembler::AssembleError, node};
|
||||
|
||||
pub fn resolve_symbols(nodes: &mut [Node]) -> Result<(), AssembleError> {
|
||||
let symbol_table = generate_symbol_table(nodes)?;
|
||||
let symbol_table = generate_symbol_table(nodes);
|
||||
|
||||
for node in nodes.iter_mut() {
|
||||
match node.opcode() {
|
||||
Opcode::Lli => {
|
||||
if let Token::Symbol(symbol) = node.arg(0).unwrap() {
|
||||
if let Some(address) = symbol_table.get(&symbol) {
|
||||
node.tokens[0] = Token::Immediate(*address);
|
||||
} else {
|
||||
return Err(AssembleError::UndefinedSymbol(symbol.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Opcode::Lui => {
|
||||
if let Token::Symbol(symbol) = node.arg(0).unwrap() {
|
||||
if let Some(address) = symbol_table.get(&symbol) {
|
||||
node.tokens[0] = Token::Immediate(*address);
|
||||
} else {
|
||||
return Err(AssembleError::UndefinedSymbol(symbol.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Opcode::Jmp
|
||||
| Opcode::Jeq
|
||||
| Opcode::Jne
|
||||
| Opcode::Jgt
|
||||
| Opcode::Jge
|
||||
| Opcode::Jlt
|
||||
| Opcode::Jle => {
|
||||
if let Token::Symbol(symbol) = node.arg(0).unwrap() {
|
||||
| Opcode::Jle
|
||||
| Opcode::Lli
|
||||
| Opcode::Lui => {
|
||||
if let Token::Symbol(symbol) = node
|
||||
.arg(0)
|
||||
.expect("Expected argument 0 for jump-like opcode")
|
||||
{
|
||||
if let Some(address) = symbol_table.get(&symbol) {
|
||||
node.tokens[0] = Token::Immediate(*address);
|
||||
} else {
|
||||
return Err(AssembleError::UndefinedSymbol(symbol.clone()));
|
||||
return Err(AssembleError::UndefinedSymbol(symbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +45,7 @@ pub fn resolve_symbols(nodes: &mut [Node]) -> Result<(), AssembleError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_symbol_table(nodes: &[Node]) -> Result<HashMap<Symbol, u32>, AssembleError> {
|
||||
fn generate_symbol_table(nodes: &[Node]) -> HashMap<Symbol, u32> {
|
||||
let mut table = HashMap::new();
|
||||
|
||||
for (i, node) in nodes.iter().enumerate() {
|
||||
@@ -67,7 +54,7 @@ fn generate_symbol_table(nodes: &[Node]) -> Result<HashMap<Symbol, u32>, Assembl
|
||||
}
|
||||
}
|
||||
|
||||
Ok(table)
|
||||
table
|
||||
}
|
||||
|
||||
pub fn resolve_dependencies(
|
||||
@@ -77,23 +64,25 @@ pub fn resolve_dependencies(
|
||||
// First we get a list of imports.
|
||||
let mut dependencies = Vec::new();
|
||||
for node in &nodes {
|
||||
if let Opcode::Include = node.opcode() {
|
||||
if node.opcode() == Opcode::Include {
|
||||
// we want the path, and the name
|
||||
let name = if let Token::Symbol(name) = node.arg(0).unwrap() {
|
||||
let name = if let Token::Symbol(name) = node
|
||||
.arg(0)
|
||||
.expect("Expected argument #0 for Include directive.")
|
||||
{
|
||||
name.name.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
}; //node.2.get(0).unwrap()
|
||||
let path = if let Token::StringLit(path) = node.arg(1).unwrap() {
|
||||
path
|
||||
} else {
|
||||
}; //node.2.get(0).unwrap()
|
||||
|
||||
let Ok(Token::StringLit(path)) = node.arg(1) else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let full_path = base_dir.join(path);
|
||||
let canonical_path = full_path
|
||||
.canonicalize()
|
||||
.map_err(|_| AssembleError::InvalidFile(full_path.to_path_buf()))?;
|
||||
.map_err(|_| AssembleError::InvalidFile(full_path.clone()))?;
|
||||
|
||||
let hash = quick_hash(&canonical_path);
|
||||
|
||||
|
||||
+30
-45
@@ -10,25 +10,28 @@ use crate::{
|
||||
node,
|
||||
};
|
||||
|
||||
#[must_use]
|
||||
pub fn build(src: &Path) -> Vec<Instruction> {
|
||||
let src = fs::read_to_string(src).unwrap();
|
||||
let mut nodes = parse(src);
|
||||
let src = fs::read_to_string(src).expect("Failed to read source file");
|
||||
let mut nodes = parse(&src);
|
||||
|
||||
// we need to expand pseudoinstructions etc now
|
||||
nodes = expand_pseudo_ops(nodes, 0).unwrap();
|
||||
nodes = expand_pseudo_ops(nodes, 0).expect("Failed to expand pseudo-operations");
|
||||
|
||||
create_sections(&mut nodes).unwrap();
|
||||
create_sections(&mut nodes).expect("Failed to create sections");
|
||||
|
||||
for n in nodes.iter() {
|
||||
println!("{}", n);
|
||||
for n in &nodes {
|
||||
println!("{n}");
|
||||
}
|
||||
|
||||
resolve_symbols(&mut nodes).unwrap();
|
||||
resolve_symbols(&mut nodes).expect("Failed to resolve symbols");
|
||||
|
||||
codegen(nodes).unwrap()
|
||||
codegen(nodes).expect("Failed to generate code from nodes")
|
||||
}
|
||||
|
||||
pub fn parse(src: String) -> Vec<Node> {
|
||||
#[must_use]
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn parse(src: &str) -> Vec<Node> {
|
||||
let stack = Token::Immediate(0x10000);
|
||||
let acc = Token::Register(Register::Acc);
|
||||
let rga = Token::Register(Register::Rga);
|
||||
@@ -45,7 +48,7 @@ pub fn parse(src: String) -> Vec<Node> {
|
||||
|
||||
let tokens = lex(src);
|
||||
|
||||
let _id = 0;
|
||||
// let _id = 0;
|
||||
let mut idstack = Vec::<u32>::new();
|
||||
|
||||
nodes.extend(vec![
|
||||
@@ -115,8 +118,8 @@ pub fn parse(src: String) -> Vec<Node> {
|
||||
}
|
||||
BfToken::Forward => {
|
||||
// Start of loop [
|
||||
let loop_start = format!("loop_start_{}", id);
|
||||
let loop_end = format!("loop_end_{}", id);
|
||||
let loop_start = format!("loop_start_{id}");
|
||||
let loop_end = format!("loop_end_{id}");
|
||||
|
||||
// Push the current position for the matching ]
|
||||
idstack.push(id as u32);
|
||||
@@ -149,8 +152,8 @@ pub fn parse(src: String) -> Vec<Node> {
|
||||
BfToken::Back => {
|
||||
// End of loop ]
|
||||
if let Some(start_id) = idstack.pop() {
|
||||
let loop_start = format!("loop_start_{}", start_id);
|
||||
let loop_end = format!("loop_end_{}", start_id);
|
||||
let loop_start = format!("loop_start_{start_id}");
|
||||
let loop_end = format!("loop_end_{start_id}");
|
||||
|
||||
// Jump back to the start of the loop
|
||||
nodes.extend(vec![
|
||||
@@ -177,7 +180,7 @@ pub fn parse(src: String) -> Vec<Node> {
|
||||
]);
|
||||
} else {
|
||||
// Unmatched closing bracket - could add error handling here
|
||||
eprintln!("Warning: Unmatched ']' at position {}", id);
|
||||
eprintln!("Warning: Unmatched ']' at position {id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,47 +218,29 @@ fn insert_lib(nodes: &mut Vec<Node>) {
|
||||
),
|
||||
// print function
|
||||
// initialisation
|
||||
node!(Some(print_start.clone()), Opcode::Push, bpr.clone()),
|
||||
node!(None, Opcode::Mov, spr.clone(), bpr.clone()),
|
||||
node!(Some(print_start), Opcode::Push, bpr),
|
||||
node!(None, Opcode::Mov, spr, bpr),
|
||||
// function body
|
||||
node!(
|
||||
None,
|
||||
Opcode::Ldw,
|
||||
bpr.clone(),
|
||||
rg0.clone(),
|
||||
Token::Immediate(8)
|
||||
),
|
||||
node!(None, Opcode::Ldw, bpr, rg0, Token::Immediate(8)),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Ldw,
|
||||
Token::Symbol(current.clone()), // Load address of current
|
||||
rg1.clone(),
|
||||
rg1,
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stb,
|
||||
rg0.clone(),
|
||||
rg1.clone(),
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(
|
||||
None,
|
||||
Opcode::AddI,
|
||||
rg1.clone(),
|
||||
Token::Immediate(1),
|
||||
rg1.clone()
|
||||
),
|
||||
node!(None, Opcode::Stb, rg0, rg1, Token::Immediate(0)),
|
||||
node!(None, Opcode::AddI, rg1, Token::Immediate(1), rg1),
|
||||
// function return according to spec.
|
||||
node!(
|
||||
None,
|
||||
Opcode::Stw,
|
||||
rg1.clone(),
|
||||
Token::Symbol(current.clone()), // Store back to current
|
||||
rg1,
|
||||
Token::Symbol(current), // Store back to current
|
||||
Token::Immediate(0)
|
||||
),
|
||||
node!(None, Opcode::Mov, bpr.clone(), spr.clone()),
|
||||
node!(None, Opcode::Pop, bpr.clone()),
|
||||
node!(None, Opcode::Mov, bpr, spr),
|
||||
node!(None, Opcode::Pop, bpr),
|
||||
node!(None, Opcode::Return),
|
||||
]);
|
||||
}
|
||||
@@ -271,7 +256,7 @@ enum BfToken {
|
||||
Back,
|
||||
}
|
||||
|
||||
fn lex(src: String) -> Vec<BfToken> {
|
||||
fn lex(src: &str) -> Vec<BfToken> {
|
||||
src.chars()
|
||||
.filter_map(|c| match c {
|
||||
'+' => Some(BfToken::Inc),
|
||||
@@ -289,7 +274,7 @@ fn lex(src: String) -> Vec<BfToken> {
|
||||
|
||||
fn _create_symbol(id: u32) -> Symbol {
|
||||
Symbol {
|
||||
name: format!("label_{}", id),
|
||||
name: format!("label_{id}"),
|
||||
module: Module::Resolved(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
#![deny(
|
||||
clippy::unwrap_used,
|
||||
clippy::nursery,
|
||||
clippy::perf,
|
||||
clippy::pedantic,
|
||||
clippy::complexity
|
||||
)]
|
||||
#![allow(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::match_wildcard_for_single_variants
|
||||
)]
|
||||
|
||||
pub mod assembler;
|
||||
pub mod brainf;
|
||||
pub mod tooling;
|
||||
|
||||
@@ -5,11 +5,12 @@ use std::{fmt, sync::mpsc::Sender};
|
||||
pub struct Logger {}
|
||||
|
||||
impl Logger {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn log(&self, message: &str) {
|
||||
_ = self;
|
||||
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||
}
|
||||
}
|
||||
@@ -90,11 +91,11 @@ impl fmt::Display for EntryType {
|
||||
f,
|
||||
"{:<5}",
|
||||
match self {
|
||||
EntryType::Debug => "DEBUG",
|
||||
EntryType::Info => "INFO",
|
||||
EntryType::Warn => "WARN",
|
||||
EntryType::Error => "ERROR",
|
||||
EntryType::Fatal => "FATAL",
|
||||
Self::Debug => "DEBUG",
|
||||
Self::Info => "INFO",
|
||||
Self::Warn => "WARN",
|
||||
Self::Error => "ERROR",
|
||||
Self::Fatal => "FATAL",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ use std::io::Write;
|
||||
|
||||
pub fn input(prompt: &str) -> String {
|
||||
print!("{prompt}\n > ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
std::io::stdout().flush().expect("Failed to flush stdout");
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
std::io::stdin()
|
||||
.read_line(&mut input)
|
||||
.expect("Failed to read line from stdin");
|
||||
input.trim().to_string()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user