diff --git a/assembler/src/assembler/assembler.rs b/assembler/src/assembler/assembler.rs index 917caab..5ba9e1c 100644 --- a/assembler/src/assembler/assembler.rs +++ b/assembler/src/assembler/assembler.rs @@ -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, program: ProgramRef) -> Self { + #[must_use] + pub const fn new( + path: PathBuf, + hash: u64, + nodes: Vec, + 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 { + // 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 { + fn lex(&self) -> Result, 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) { + fn parse(&mut self, tokens: Vec) -> 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() } } diff --git a/assembler/src/assembler/codegen.rs b/assembler/src/assembler/codegen.rs index a114a44..5c101a4 100644 --- a/assembler/src/assembler/codegen.rs +++ b/assembler/src/assembler/codegen.rs @@ -12,8 +12,8 @@ pub fn codegen(nodes: Vec) -> Result, 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) -> Result, AssembleError> { Ok(instructions) } -fn build_instruction(node: Node) -> Result { +fn build_instruction(node: &Node) -> Result { 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 { | 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 { | 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 { | Opcode::Popa => Err(AssembleError::InvalidArg), } } + +fn build_mov_instruction( + opcode: Opcode, + args: &[crate::assembler::model::Token], +) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + let Some(immediate_token) = args.first() else { + return Err(AssembleError::MissingArgument(0)); + }; + + let immediate = expect_token!(immediate_token, Immediate)?; + Ok(Instruction::Segment(immediate)) +} diff --git a/assembler/src/assembler/expand.rs b/assembler/src/assembler/expand.rs index 993a6ad..ed25bd3 100644 --- a/assembler/src/assembler/expand.rs +++ b/assembler/src/assembler/expand.rs @@ -9,7 +9,7 @@ pub fn expand_pseudo_ops( ) -> Result, AssembleError> { let mut result = Vec::::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) -> Result<(), AssembleError> { +fn expand_push(current: &Node, nodes: &mut Vec) -> 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) -> Result<(), AssembleError Ok(()) } -fn expand_pusha(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { +fn expand_pusha(current: &Node, nodes: &mut Vec) -> 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::general(); @@ -70,44 +78,39 @@ fn expand_pusha(current: Node, nodes: &mut Vec) -> 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::>(), - ); + 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) -> Result<(), AssembleError> { +fn expand_popa(current: &Node, nodes: &mut Vec) -> 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::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::>(), - ); + 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) -> Result<(), AssembleError Ok(()) } -fn expand_call(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { +fn expand_call(current: &Node, nodes: &mut Vec) -> 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) -> Result<(), AssembleError Ok(()) } -fn expand_return(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { +fn expand_return(current: &Node, nodes: &mut Vec) { 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) -> Result<(), AssembleErr node!(None, Opcode::AddI, spr, 4, spr), node!(None, Opcode::Jmp, 4, ret), ]); - - Ok(()) } -fn expand_pop(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { +fn expand_pop(current: &Node, nodes: &mut Vec) -> 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) -> Result<(), AssembleError> Ok(()) } -fn expand_ldx(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { +fn expand_ldx(current: &Node, nodes: &mut Vec) -> 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 = 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) -> Result<(), AssembleError> Ok(()) } -fn expand_stx(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { +fn expand_stx(current: &Node, nodes: &mut Vec) -> 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 = 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) -> Result<(), AssembleError> Ok(()) } -fn expand_lwi(current: Node, nodes: &mut Vec) -> 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) -> 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) -> Result<(), AssembleError> Ok(()) } -fn expand_resx(current: Node, nodes: &mut Vec) -> 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) -> 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) -> Result<(), AssembleError Ok(()) } -fn expand_dx(current: Node, nodes: &mut Vec) -> Result<(), AssembleError> { - let region_label = expect_token!(current.arg(0).unwrap(), Symbol)?; +fn expand_dx(current: &Node, nodes: &mut Vec) -> 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, diff --git a/assembler/src/assembler/lexer.rs b/assembler/src/assembler/lexer.rs index ad783c9..0150309 100644 --- a/assembler/src/assembler/lexer.rs +++ b/assembler/src/assembler/lexer.rs @@ -7,7 +7,7 @@ use common::prelude::Register; pub fn lexer(mut program: String, module: u64) -> Result, 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, AssembleError> { pub fn parse_opcode(token: &str) -> Result, 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, 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, AssembleError> { @@ -89,10 +94,13 @@ pub fn parse_octal(token: &str) -> Result, 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, AssembleError> { @@ -100,38 +108,48 @@ pub fn parse_binary(token: &str) -> Result, 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, AssembleError> { - if token.parse::().is_err() { - Ok(None) - } else { - Ok(Some(Token::Immediate(token.parse().unwrap()))) - } + let Ok(tok) = token.parse::() else { + return Ok(None); + }; + + Ok(Some(Token::Immediate(tok))) } pub fn parse_label(token: &str, module: u64) -> Result, 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, 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 { diff --git a/assembler/src/assembler/mod.rs b/assembler/src/assembler/mod.rs index 4218c04..26fd801 100644 --- a/assembler/src/assembler/mod.rs +++ b/assembler/src/assembler/mod.rs @@ -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, 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() } diff --git a/assembler/src/assembler/model.rs b/assembler/src/assembler/model.rs index d9c041c..97ad939 100644 --- a/assembler/src/assembler/model.rs +++ b/assembler/src/assembler/model.rs @@ -12,22 +12,26 @@ pub struct Node { } impl Node { - pub fn new(symbol: Option, opcode: Opcode, tokens: Vec) -> Node { - Node { + #[must_use] + pub const fn new(symbol: Option, opcode: Opcode, tokens: Vec) -> Self { + Self { symbol, opcode, tokens, } } + #[must_use] pub fn label(&self) -> Option { 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 { 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 { + #[must_use] + pub const fn to_opcode_value(&self) -> Option { 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 diff --git a/assembler/src/assembler/parser.rs b/assembler/src/assembler/parser.rs index 73c23fa..7909058 100644 --- a/assembler/src/assembler/parser.rs +++ b/assembler/src/assembler/parser.rs @@ -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) { @@ -40,8 +41,8 @@ impl Default for Program { impl Parser { pub fn parse_nodes(tokens: Vec) -> Result, 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 { 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 { + fn peek_next(&self) -> Result { 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()) } } } diff --git a/assembler/src/assembler/resolver.rs b/assembler/src/assembler/resolver.rs index cb6d602..626a045 100644 --- a/assembler/src/assembler/resolver.rs +++ b/assembler/src/assembler/resolver.rs @@ -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, AssembleError> { +fn generate_symbol_table(nodes: &[Node]) -> HashMap { let mut table = HashMap::new(); for (i, node) in nodes.iter().enumerate() { @@ -67,7 +54,7 @@ fn generate_symbol_table(nodes: &[Node]) -> Result, 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); diff --git a/assembler/src/brainf.rs b/assembler/src/brainf.rs index 163c936..b8a0f77 100644 --- a/assembler/src/brainf.rs +++ b/assembler/src/brainf.rs @@ -10,25 +10,28 @@ use crate::{ node, }; +#[must_use] pub fn build(src: &Path) -> Vec { - 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 { +#[must_use] +#[expect(clippy::too_many_lines)] +pub fn parse(src: &str) -> Vec { 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 { let tokens = lex(src); - let _id = 0; + // let _id = 0; let mut idstack = Vec::::new(); nodes.extend(vec![ @@ -115,8 +118,8 @@ pub fn parse(src: String) -> Vec { } 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 { 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 { ]); } 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) { ), // 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 { +fn lex(src: &str) -> Vec { src.chars() .filter_map(|c| match c { '+' => Some(BfToken::Inc), @@ -289,7 +274,7 @@ fn lex(src: String) -> Vec { fn _create_symbol(id: u32) -> Symbol { Symbol { - name: format!("label_{}", id), + name: format!("label_{id}"), module: Module::Resolved(0), } } diff --git a/assembler/src/lib.rs b/assembler/src/lib.rs index 2c74cb3..bdc132f 100644 --- a/assembler/src/lib.rs +++ b/assembler/src/lib.rs @@ -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; diff --git a/assembler/src/util/logging.rs b/assembler/src/util/logging.rs index 16cff31..93c37e1 100644 --- a/assembler/src/util/logging.rs +++ b/assembler/src/util/logging.rs @@ -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", } ) } diff --git a/assembler/src/util/mod.rs b/assembler/src/util/mod.rs index 01f628d..c8746e4 100644 --- a/assembler/src/util/mod.rs +++ b/assembler/src/util/mod.rs @@ -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() }