Browse Source

more meaningful errors in assembler library code

pull/7/head
Murad 2 years ago
parent
commit
c245507df9
Signed by: foxpy GPG Key ID: 78BE32418B0C8450
  1. 272
      src/assembler.rs

272
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<Instruction, Box<dyn Error>>;
type InstructionDecoder = fn(&str, &Regex) -> Result<Instruction, AssemblerErrorKind>;
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<i128>,
},
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<T: Into<i128> + Copy>(
number: T,
range: RangeInclusive<T>,
) -> 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<num::ParseIntError> 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<T> = Result<T, AssemblerError>;
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<dyn Error>> {
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<Instruction, Box<dyn Error>> {
fn line_to_insn(&self, line: &str) -> Result<Instruction, AssemblerErrorKind> {
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<u128, Box<dyn Error>> {
fn parse_u128(src: &str) -> Result<u128, num::ParseIntError> {
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<u128, Box<dyn Error>> {
}
}
fn parse_u8(src: &str) -> Result<u8, Box<dyn Error>> {
fn parse_u8(src: &str) -> Result<u8, AssemblerErrorKind> {
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<i8, Box<dyn Error>> {
fn parse_i8(src: &str) -> Result<i8, AssemblerErrorKind> {
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<i8, Box<dyn Error>> {
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<u4, Box<dyn Error>> {
fn parse_u4(src: &str) -> Result<u4, AssemblerErrorKind> {
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<u3, Box<dyn Error>> {
fn parse_u3(src: &str) -> Result<u3, AssemblerErrorKind> {
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<Register, Box<dyn Error>> {
fn parse_reg(src: &str) -> Result<Register, AssemblerErrorKind> {
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<dyn Error>> {
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<bool, Box<dyn Error>> {
fn parse_carry(src: &str) -> Result<bool, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_byte(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
Ok(decode(parse_u8(
regex.captures(line).unwrap().get(1).unwrap().as_str(),
)?))
}
fn parse_mov_reg_mem(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_mov_reg_mem(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_mov_mem_reg(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_mov_reg_half_imm(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_mov_reg_reg(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn E
(Register::R1, Register::R0) => 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<Instruction, Box<dyn Error>> {
) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_jmp(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_jmp_impl(line, regex, false)
}
fn parse_jmpc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_jmpc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_jmp_impl(line, regex, true)
}
@ -296,16 +474,16 @@ fn parse_ajmp_impl(
line: &str,
regex: &Regex,
use_carry: bool,
) -> Result<Instruction, Box<dyn Error>> {
) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_ajmp(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_ajmp_impl(line, regex, false)
}
fn parse_ajmpc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_ajmpc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_ajmp_impl(line, regex, true)
}
@ -314,7 +492,7 @@ fn parse_shift(
regex: &Regex,
right: bool,
mode: ShiftMode,
) -> Result<Instruction, Box<dyn Error>> {
) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_shr(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_shift(line, regex, true, ShiftMode::Logical)
}
fn parse_shl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_shl(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_shift(line, regex, false, ShiftMode::Logical)
}
fn parse_rotr(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_rotr(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_shift(line, regex, true, ShiftMode::Circular)
}
fn parse_rotl(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_rotl(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
parse_shift(line, regex, false, ShiftMode::Circular)
}
fn parse_inc(line: &str, regex: &Regex) -> Result<Instruction, Box<dyn Error>> {
fn parse_inc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_dec(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_compl(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_setc(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_cload(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
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<Instruction, Box<dyn Error>> {
fn parse_zero(line: &str, regex: &Regex) -> Result<Instruction, AssemblerErrorKind> {
let reg = parse_reg(regex.captures(line).unwrap().get(1).unwrap().as_str())?;
Ok(Instruction::Zero { reg })
}

Loading…
Cancel
Save