use std::{fmt::Display, ops::Range};
use crate::value::{BlockId, ValueId};
#[derive(Debug, PartialEq, Eq)]
pub enum ErrorTy {
RangeOutOfBounds {
range: Range<usize>,
available: usize,
},
UnknownIdentifier(Box<str>, &'static str),
SizeMismatch {
expected: usize,
actual: usize,
},
TriedToNameLiteral,
NameAlreadyExists(Box<str>),
UnknownAddress(u64),
NonConstArgument,
MissingArgument(Box<str>),
DuplicateName(String),
DuplicateAddress(u64, ValueId),
FunctionRootAddressMismatch {
fn_addr: u64,
block_addr: u64,
},
FunctionRootMismatch {
expected: BlockId,
actual: BlockId,
},
NoRootBlock,
}
#[derive(Debug)]
pub struct Error {
pub ty: ErrorTy,
pub span: Option<(usize, usize)>,
}
pub type Result<T> = std::result::Result<T, Error>;
impl std::error::Error for Error {}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
self.ty == other.ty
}
}
impl Eq for Error {}
impl Error {
pub fn new(ty: ErrorTy, span: (usize, usize)) -> Self {
Self {
ty,
span: Some(span),
}
}
pub fn with_span(mut self, span: (usize, usize)) -> Self {
self.span = Some(span);
self
}
pub fn spanless(ty: ErrorTy) -> Self {
Self { ty, span: None }
}
pub fn range_out_of_bounds(range: Range<usize>, span: (usize, usize)) -> Self {
Self::new(
ErrorTy::RangeOutOfBounds {
range,
available: 0,
},
span,
)
}
pub fn unknown_identifier(name: &str, ty: &'static str, span: (usize, usize)) -> Self {
Self::new(ErrorTy::UnknownIdentifier(name.into(), ty), span)
}
pub fn size_mismatch(expected: usize, actual: usize, span: (usize, usize)) -> Self {
Self::new(ErrorTy::SizeMismatch { expected, actual }, span)
}
pub fn tried_to_name_literal(span: (usize, usize)) -> Self {
Self::new(ErrorTy::TriedToNameLiteral, span)
}
pub fn name_already_exists(name: &str, span: (usize, usize)) -> Self {
Self::new(ErrorTy::NameAlreadyExists(name.into()), span)
}
pub fn missing_argument(name: &str, span: (usize, usize)) -> Self {
Self::new(ErrorTy::MissingArgument(name.into()), span)
}
pub fn non_const_arg(span: (usize, usize)) -> Self {
Self::new(ErrorTy::NonConstArgument, span)
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match &self.ty {
ErrorTy::RangeOutOfBounds { range, available } => {
format!("Range {range:?} is out of bounds for available size {available}")
}
ErrorTy::UnknownIdentifier(name, ty) => format!("Unknown {ty}: {name}"),
ErrorTy::SizeMismatch { expected, actual } => {
format!("Expected size {expected} but got size {actual}")
}
ErrorTy::TriedToNameLiteral => "Attempted to set a name on a literal value".to_string(),
ErrorTy::NameAlreadyExists(name) => {
format!("A name '{name}' already exists in the current scope")
}
ErrorTy::MissingArgument(name) => format!("Missing argument: {name}"),
ErrorTy::NonConstArgument => "Argument must be a compile-time constant".to_string(),
ErrorTy::DuplicateName(name) => format!("duplicate name: {name}"),
ErrorTy::DuplicateAddress(addr, value) => {
format!("address {addr:#x} is already mapped to a value: {value:?}")
}
ErrorTy::UnknownAddress(addr) => {
format!("address {addr:#x} is not mapped to any value")
}
ErrorTy::FunctionRootAddressMismatch {
fn_addr,
block_addr,
} => {
format!(
"Function root address mismatch: function address {fn_addr:#x}, block address {block_addr:#x}"
)
}
ErrorTy::FunctionRootMismatch { expected, actual } => {
format!("Function root mismatch: expected root block {expected:?}, got {actual:?}")
}
ErrorTy::NoRootBlock => "function has no root (entry) block".to_string(),
};
if let Some((start, end)) = self.span {
write!(f, "{message} (bytes {start}..{end})")
} else {
write!(f, "{message}")
}
}
}