From c245507df9120f3d7ad079ee7bc059660d216fd1 Mon Sep 17 00:00:00 2001 From: Murad Date: Mon, 9 May 2022 22:41:35 +0300 Subject: [PATCH] more meaningful errors in assembler library code --- src/assembler.rs | 272 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 225 insertions(+), 47 deletions(-) diff --git a/src/assembler.rs b/src/assembler.rs index f46d9c6..60e8400 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -3,10 +3,15 @@ use crate::instruction::{decode, encode, Instruction, Register, ShiftMode}; use regex::Regex; -use std::error::Error; +use std::{ + error::Error, + fmt::{Display, Formatter}, + num, + ops::RangeInclusive, +}; use ux::{u2, u3, u4}; -type InstructionDecoder = fn(&str, &Regex) -> Result>; +type InstructionDecoder = fn(&str, &Regex) -> Result; const REGEX_REG: &str = r"(R[01])"; const REGEX_REG_HALF: &str = r"(R[01]\.[lh])"; @@ -22,6 +27,170 @@ macro_rules! asm_entry { }; } +#[derive(Debug, Clone)] +enum AssemblerErrorKind { + TooMuchInstructions, + UnknownInstruction { + instruction: String, + }, + InvalidRegister { + register: String, + }, + InvalidRegisterHalf { + register_half: String, + }, + InvalidCarry { + carry: String, + }, + NumberOutOfRange { + number: i128, + range: RangeInclusive, + }, + InvalidInteger { + parse_int_error: num::ParseIntError, + }, + InvalidRelativeJumpOffset { + jump_offset: i8, + }, + MalformedRegisterToRegisterCopy, +} + +impl AssemblerErrorKind { + fn unknown_instruction(instruction: &str) -> AssemblerErrorKind { + AssemblerErrorKind::UnknownInstruction { + instruction: instruction.to_string(), + } + } + + fn invalid_register(register: &str) -> AssemblerErrorKind { + AssemblerErrorKind::InvalidRegister { + register: register.to_string(), + } + } + + fn invalid_register_half(register_half: &str) -> AssemblerErrorKind { + AssemblerErrorKind::InvalidRegisterHalf { + register_half: register_half.to_string(), + } + } + + fn invalid_carry(carry: &str) -> AssemblerErrorKind { + AssemblerErrorKind::InvalidCarry { + carry: carry.to_string(), + } + } + + fn number_out_of_range + Copy>( + number: T, + range: RangeInclusive, + ) -> AssemblerErrorKind { + AssemblerErrorKind::NumberOutOfRange { + number: number.into(), + // am I doing this right? + range: (*range.start()).into()..=(*range.end()).into(), + } + } + + fn invalid_relative_jump_offset(jump_offset: i8) -> AssemblerErrorKind { + AssemblerErrorKind::InvalidRelativeJumpOffset { jump_offset } + } + + fn malformed_register_to_register_copy() -> AssemblerErrorKind { + AssemblerErrorKind::MalformedRegisterToRegisterCopy + } + + fn to_assembler_error(&self, line: usize) -> AssemblerError { + AssemblerError { + line, + error: self.clone(), + } + } +} + +impl From for AssemblerErrorKind { + fn from(parse_int_error: num::ParseIntError) -> AssemblerErrorKind { + AssemblerErrorKind::InvalidInteger { parse_int_error } + } +} + +impl Display for AssemblerErrorKind { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "{}", + match &self { + AssemblerErrorKind::TooMuchInstructions => String::from("too much instructions"), + AssemblerErrorKind::UnknownInstruction { instruction } => + format!("unknown instruction: '{}'", instruction), + AssemblerErrorKind::InvalidRegister { register } => + format!("invalid register: '{}'", register), + AssemblerErrorKind::InvalidRegisterHalf { register_half } => + format!("invalid register half: '{}'", register_half), + AssemblerErrorKind::InvalidCarry { carry } => format!("invalid carry: '{}'", carry), + AssemblerErrorKind::NumberOutOfRange { number, range } => format!( + "number {} is out of range [{}, {}]", + number, + range.start(), + range.end() + ), + AssemblerErrorKind::InvalidInteger { parse_int_error } => + format!("failed to parse integer: {}", parse_int_error), + AssemblerErrorKind::InvalidRelativeJumpOffset { jump_offset } => + format!("{} is not a valid relative jump offset", jump_offset), + AssemblerErrorKind::MalformedRegisterToRegisterCopy => + String::from("malformed register to register copy"), + } + ) + } +} + +impl Error for AssemblerErrorKind { + fn description(&self) -> &str { + match self { + AssemblerErrorKind::TooMuchInstructions => "Too much instructions", + AssemblerErrorKind::UnknownInstruction { .. } => "Unknown instruction", + AssemblerErrorKind::InvalidRegister { .. } => "Invalid register", + AssemblerErrorKind::InvalidRegisterHalf { .. } => "Invalid register half", + AssemblerErrorKind::InvalidCarry { .. } => "Invalid carry", + AssemblerErrorKind::NumberOutOfRange { .. } => "Number out of range", + AssemblerErrorKind::InvalidInteger { .. } => "Failed to parse integer", + AssemblerErrorKind::InvalidRelativeJumpOffset { .. } => "Invalid relative jump offset", + AssemblerErrorKind::MalformedRegisterToRegisterCopy => { + "Malformed register to register copy" + } + } + } +} + +#[derive(Debug)] +pub struct AssemblerError { + line: usize, + error: AssemblerErrorKind, +} + +type AssemblerResult = Result; + +impl Display for AssemblerError { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "Failed to assemble, line {}: {}", self.line, self.error) + } +} + +impl Error for AssemblerError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.error) + } +} + +impl AssemblerError { + fn too_much_instructions(line: usize) -> AssemblerError { + AssemblerError { + line, + error: AssemblerErrorKind::TooMuchInstructions, + } + } +} + pub struct Assembler { table: Vec<(Regex, InstructionDecoder)>, } @@ -111,17 +280,23 @@ impl Assembler { } } - pub fn assemble(&self, input: &str) -> Result<[u8; 0x100], Box> { + pub fn assemble(&self, input: &str) -> AssemblerResult<[u8; 0x100]> { let mut ret = [0u8; 0x100]; let mut i: usize = 0; - for line in input.lines().map(|line| strip_comment(line).trim()) { + for (line_num, line) in input + .lines() + .map(|line| strip_comment(line).trim()) + .enumerate() + { match line { "" => (), line => { if i > 0xFF { - return Err("Too much instructions".into()); + return Err(AssemblerError::too_much_instructions(line_num)); } - let insn = self.line_to_insn(line)?; + let insn = self + .line_to_insn(line) + .map_err(|e| e.to_assembler_error(line_num))?; ret[i] = encode(insn); i += 1; } @@ -130,13 +305,13 @@ impl Assembler { Ok(ret) } - fn line_to_insn(&self, line: &str) -> Result> { + fn line_to_insn(&self, line: &str) -> Result { for (regex, handler) in &self.table { if regex.is_match(line) { return handler(line, regex); } } - Err("Unknown instruction".into()) + Err(AssemblerErrorKind::unknown_instruction(line)) } } @@ -153,7 +328,7 @@ fn strip_comment(input: &str) -> &str { } } -fn parse_u128(src: &str) -> Result> { +fn parse_u128(src: &str) -> Result { if let Some(s) = src.strip_prefix("0b") { Ok(u128::from_str_radix(s, 2)?) } else if let Some(s) = src.strip_prefix("0o") { @@ -165,14 +340,14 @@ fn parse_u128(src: &str) -> Result> { } } -fn parse_u8(src: &str) -> Result> { +fn parse_u8(src: &str) -> Result { match parse_u128(src)? { x @ 0..=255 => Ok(x as u8), - x => Err(format!("{} is not in range [0, 255]", x).into()), + x => Err(AssemblerErrorKind::number_out_of_range(x as i128, 0..=255)), } } -fn parse_i8(src: &str) -> Result> { +fn parse_i8(src: &str) -> Result { let (sign, num): (i128, u128) = match src.strip_prefix('-') { Some(s) => (-1, parse_u128(s)?), None => (1, parse_u128(src)?), @@ -180,78 +355,81 @@ fn parse_i8(src: &str) -> Result> { match (sign, num) { (1, x) if x <= 127 => Ok((x as i128 * sign) as i8), (-1, x) if x <= 128 => Ok((x as i128 * sign) as i8), - (_, x) => Err(format!("{} is not in range [-128, 127]", x).into()), + (_, x) => Err(AssemblerErrorKind::number_out_of_range( + x as i128, + -128..=127, + )), } } -fn parse_u4(src: &str) -> Result> { +fn parse_u4(src: &str) -> Result { match parse_u8(src)? { x @ 0..=15 => Ok(u4::new(x)), - x => Err(format!("{} is not in range [0, 15]", x).into()), + x => Err(AssemblerErrorKind::number_out_of_range(x as i128, 0..=15)), } } -fn parse_u3(src: &str) -> Result> { +fn parse_u3(src: &str) -> Result { match parse_u8(src)? { x @ 0..=7 => Ok(u3::new(x)), - x => Err(format!("{} is not in range [0, 7]", x).into()), + x => Err(AssemblerErrorKind::number_out_of_range(x, 0..=7)), } } -fn parse_reg(src: &str) -> Result> { +fn parse_reg(src: &str) -> Result { match src { "R0" => Ok(Register::R0), "R1" => Ok(Register::R1), - x => Err(format!("'{}' is not a valid register", x).into()), + x => Err(AssemblerErrorKind::invalid_register(x)), } } -fn parse_reg_half(src: &str) -> Result<(Register, bool), Box> { +fn parse_reg_half(src: &str) -> Result<(Register, bool), AssemblerErrorKind> { match src { "R0.l" => Ok((Register::R0, true)), "R0.h" => Ok((Register::R0, false)), "R1.l" => Ok((Register::R1, true)), "R1.h" => Ok((Register::R1, false)), - x => Err(format!("'{}' is not a valid register half", x).into()), + x => Err(AssemblerErrorKind::invalid_register_half(x)), } } -fn parse_carry(src: &str) -> Result> { +fn parse_carry(src: &str) -> Result { match src { "0" => Ok(false), "1" => Ok(true), - x => Err(format!("'{}' is an invalid carry value", x).into()), + x => Err(AssemblerErrorKind::invalid_carry(x)), } } -fn parse_byte(line: &str, regex: &Regex) -> Result> { +fn parse_byte(line: &str, regex: &Regex) -> Result { Ok(decode(parse_u8( regex.captures(line).unwrap().get(1).unwrap().as_str(), )?)) } -fn parse_mov_reg_mem(line: &str, regex: &Regex) -> Result> { +fn parse_mov_reg_mem(line: &str, regex: &Regex) -> Result { let captures = regex.captures(line).unwrap(); let reg = parse_reg(captures.get(1).unwrap().as_str())?; let addr = parse_u4(captures.get(2).unwrap().as_str())?; Ok(Instruction::Load { reg, addr }) } -fn parse_mov_mem_reg(line: &str, regex: &Regex) -> Result> { +fn parse_mov_mem_reg(line: &str, regex: &Regex) -> Result { let captures = regex.captures(line).unwrap(); let addr = parse_u4(captures.get(1).unwrap().as_str())?; let reg = parse_reg(captures.get(2).unwrap().as_str())?; Ok(Instruction::Store { reg, addr }) } -fn parse_mov_reg_half_imm(line: &str, regex: &Regex) -> Result> { +fn parse_mov_reg_half_imm(line: &str, regex: &Regex) -> Result { let captures = regex.captures(line).unwrap(); let (reg, low) = parse_reg_half(captures.get(1).unwrap().as_str())?; let val = parse_u4(captures.get(2).unwrap().as_str())?; Ok(Instruction::LoadImmediate { low, reg, val }) } -fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result> { +fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result { let captures = regex.captures(line).unwrap(); let reg_to = parse_reg(captures.get(1).unwrap().as_str())?; let reg_from = parse_reg(captures.get(2).unwrap().as_str())?; @@ -262,7 +440,7 @@ fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result Ok(Instruction::Copy { reg_from: Register::R0, }), - _ => Err("Malformed register to register copy".into()), + _ => Err(AssemblerErrorKind::malformed_register_to_register_copy()), } } @@ -270,7 +448,7 @@ fn parse_jmp_impl( line: &str, regex: &Regex, use_carry: bool, -) -> Result> { +) -> Result { match parse_i8(regex.captures(line).unwrap().get(1).unwrap().as_str())? { x @ -3..=-1 => Ok(Instruction::NearJumpBackward { use_carry, @@ -280,15 +458,15 @@ fn parse_jmp_impl( use_carry, offset: u3::new(x as u8), }), - x => Err(format!("{} is not a valid relative jump offset", x).into()), + x => Err(AssemblerErrorKind::invalid_relative_jump_offset(x)), } } -fn parse_jmp(line: &str, regex: &Regex) -> Result> { +fn parse_jmp(line: &str, regex: &Regex) -> Result { parse_jmp_impl(line, regex, false) } -fn parse_jmpc(line: &str, regex: &Regex) -> Result> { +fn parse_jmpc(line: &str, regex: &Regex) -> Result { parse_jmp_impl(line, regex, true) } @@ -296,16 +474,16 @@ fn parse_ajmp_impl( line: &str, regex: &Regex, use_carry: bool, -) -> Result> { +) -> Result { let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::FarJump { reg, use_carry }) } -fn parse_ajmp(line: &str, regex: &Regex) -> Result> { +fn parse_ajmp(line: &str, regex: &Regex) -> Result { parse_ajmp_impl(line, regex, false) } -fn parse_ajmpc(line: &str, regex: &Regex) -> Result> { +fn parse_ajmpc(line: &str, regex: &Regex) -> Result { parse_ajmp_impl(line, regex, true) } @@ -314,7 +492,7 @@ fn parse_shift( regex: &Regex, right: bool, mode: ShiftMode, -) -> Result> { +) -> Result { let captures = regex.captures(line).unwrap(); let reg = parse_reg(captures.get(1).unwrap().as_str())?; let len = parse_u3(captures.get(2).unwrap().as_str())?; @@ -326,48 +504,48 @@ fn parse_shift( }) } -fn parse_shr(line: &str, regex: &Regex) -> Result> { +fn parse_shr(line: &str, regex: &Regex) -> Result { parse_shift(line, regex, true, ShiftMode::Logical) } -fn parse_shl(line: &str, regex: &Regex) -> Result> { +fn parse_shl(line: &str, regex: &Regex) -> Result { parse_shift(line, regex, false, ShiftMode::Logical) } -fn parse_rotr(line: &str, regex: &Regex) -> Result> { +fn parse_rotr(line: &str, regex: &Regex) -> Result { parse_shift(line, regex, true, ShiftMode::Circular) } -fn parse_rotl(line: &str, regex: &Regex) -> Result> { +fn parse_rotl(line: &str, regex: &Regex) -> Result { parse_shift(line, regex, false, ShiftMode::Circular) } -fn parse_inc(line: &str, regex: &Regex) -> Result> { +fn parse_inc(line: &str, regex: &Regex) -> Result { let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::Inc { reg }) } -fn parse_dec(line: &str, regex: &Regex) -> Result> { +fn parse_dec(line: &str, regex: &Regex) -> Result { let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::Dec { reg }) } -fn parse_compl(line: &str, regex: &Regex) -> Result> { +fn parse_compl(line: &str, regex: &Regex) -> Result { let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::Complement { reg }) } -fn parse_setc(line: &str, regex: &Regex) -> Result> { +fn parse_setc(line: &str, regex: &Regex) -> Result { let carry = parse_carry(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::Setc { carry }) } -fn parse_cload(line: &str, regex: &Regex) -> Result> { +fn parse_cload(line: &str, regex: &Regex) -> Result { let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::Cload { reg }) } -fn parse_zero(line: &str, regex: &Regex) -> Result> { +fn parse_zero(line: &str, regex: &Regex) -> Result { let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?; Ok(Instruction::Zero { reg }) }