99 lines
3.5 KiB
Rust
99 lines
3.5 KiB
Rust
use std::{
|
|
cmp::Ordering,
|
|
fmt::{self, Display, Formatter},
|
|
};
|
|
|
|
use itertools::Itertools;
|
|
|
|
use crate::instructions::instruction;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum SyntaxErr {
|
|
/// false for '(' true for ')'
|
|
UnmatchedParen(bool),
|
|
UnexpectedChar,
|
|
OutsideOp(String),
|
|
InvalidRegister,
|
|
}
|
|
|
|
impl Display for SyntaxErr {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
|
|
SyntaxErr::UnexpectedChar => write!(f, "unexpected character"),
|
|
SyntaxErr::OutsideOp(kind) => write!(f, "'{kind}' before opcode"),
|
|
SyntaxErr::InvalidRegister => write!(f, "invalid register"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SyntaxErr {
|
|
pub fn note(&self) -> String {
|
|
match self {
|
|
SyntaxErr::UnmatchedParen(false) => "add `)` after the register name".to_string(),
|
|
SyntaxErr::UnmatchedParen(true) => "add `(` before the register name".to_string(),
|
|
SyntaxErr::UnexpectedChar => "ensure the input is well-formed".to_string(),
|
|
SyntaxErr::OutsideOp(_) => format!("only add arguments after the opcode"),
|
|
SyntaxErr::InvalidRegister => {
|
|
"registers are either xN (N < 32 with no leading 0) or the standard aliases"
|
|
.to_string()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum RuntimeErr {
|
|
InvalidOp,
|
|
/// op, actual, expected
|
|
InvalidOpArity(String, usize, usize),
|
|
/// actual, expected
|
|
InvalidType(String, String),
|
|
LabelNotFound,
|
|
}
|
|
|
|
impl Display for RuntimeErr {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
RuntimeErr::InvalidOp => write!(f, "invalid opcode"),
|
|
RuntimeErr::InvalidOpArity(_, actual, expected) => {
|
|
write!(f, "expected {} args, got {}", expected, actual)
|
|
}
|
|
RuntimeErr::InvalidType(actual, expected) => {
|
|
write!(f, "expected '{}', got '{}'", expected, actual)
|
|
}
|
|
RuntimeErr::LabelNotFound => write!(f, "label not found"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RuntimeErr {
|
|
pub fn note(&self) -> String {
|
|
match self {
|
|
RuntimeErr::InvalidOp => "check the ref sheet for the avaliable opcodes".to_string(),
|
|
RuntimeErr::InvalidOpArity(op, actual, expected) => {
|
|
let args = instruction(op).unwrap().1;
|
|
match actual.cmp(expected) {
|
|
Ordering::Equal => unreachable!(),
|
|
Ordering::Greater if actual - expected == 1 => {
|
|
"remove the extra argument".to_string()
|
|
}
|
|
Ordering::Greater => "remove the extra arguments".to_string(),
|
|
Ordering::Less if expected - actual == 1 => {
|
|
format!("add the extra '{}' argument", args.last().unwrap().kind())
|
|
}
|
|
Ordering::Less => format!(
|
|
"add the extra `{}` arguments",
|
|
args.get((actual - 1)..)
|
|
.unwrap()
|
|
.iter()
|
|
.map(|arg| arg.kind())
|
|
.join("', '")
|
|
),
|
|
}
|
|
}
|
|
RuntimeErr::InvalidType(_, _) => "ensure the operation is valid".to_string(),
|
|
RuntimeErr::LabelNotFound => "ensure the label is spelled correctly".to_string(),
|
|
}
|
|
}
|
|
}
|