1use std::{fmt::Display, ops::Range};
2
3use crate::value::{BlockId, ValueId};
4
5#[derive(Debug, PartialEq, Eq)]
6pub enum ErrorTy {
7 RangeOutOfBounds {
8 range: Range<usize>,
9 available: usize,
10 },
11
12 UnknownIdentifier(Box<str>, &'static str),
14
15 SizeMismatch {
16 expected: usize,
17 actual: usize,
18 },
19
20 TriedToNameLiteral,
22
23 NameAlreadyExists(Box<str>),
25
26 UnknownAddress(u64),
27
28 NonConstArgument,
30
31 MissingArgument(Box<str>),
32
33 DuplicateName(String),
35
36 DuplicateAddress(u64, ValueId),
38
39 FunctionRootAddressMismatch {
41 fn_addr: u64,
42 block_addr: u64,
43 },
44
45 FunctionRootMismatch {
46 expected: BlockId,
47 actual: BlockId,
48 },
49
50 NoRootBlock,
52}
53
54#[derive(Debug)]
55pub struct Error {
56 pub ty: ErrorTy,
57 pub span: Option<(usize, usize)>,
59}
60
61pub type Result<T> = std::result::Result<T, Error>;
62
63impl std::error::Error for Error {}
64
65impl PartialEq for Error {
66 fn eq(&self, other: &Self) -> bool {
67 self.ty == other.ty
68 }
69}
70
71impl Eq for Error {}
72
73impl Error {
74 pub fn new(ty: ErrorTy, span: (usize, usize)) -> Self {
75 Self {
76 ty,
77 span: Some(span),
78 }
79 }
80
81 pub fn with_span(mut self, span: (usize, usize)) -> Self {
82 self.span = Some(span);
83 self
84 }
85
86 pub fn spanless(ty: ErrorTy) -> Self {
87 Self { ty, span: None }
88 }
89
90 pub fn range_out_of_bounds(range: Range<usize>, span: (usize, usize)) -> Self {
91 Self::new(
92 ErrorTy::RangeOutOfBounds {
93 range,
94 available: 0,
95 },
96 span,
97 )
98 }
99
100 pub fn unknown_identifier(name: &str, ty: &'static str, span: (usize, usize)) -> Self {
101 Self::new(ErrorTy::UnknownIdentifier(name.into(), ty), span)
102 }
103
104 pub fn size_mismatch(expected: usize, actual: usize, span: (usize, usize)) -> Self {
105 Self::new(ErrorTy::SizeMismatch { expected, actual }, span)
106 }
107
108 pub fn tried_to_name_literal(span: (usize, usize)) -> Self {
109 Self::new(ErrorTy::TriedToNameLiteral, span)
110 }
111
112 pub fn name_already_exists(name: &str, span: (usize, usize)) -> Self {
113 Self::new(ErrorTy::NameAlreadyExists(name.into()), span)
114 }
115
116 pub fn missing_argument(name: &str, span: (usize, usize)) -> Self {
117 Self::new(ErrorTy::MissingArgument(name.into()), span)
118 }
119
120 pub fn non_const_arg(span: (usize, usize)) -> Self {
121 Self::new(ErrorTy::NonConstArgument, span)
122 }
123}
124
125impl Display for Error {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 let message = match &self.ty {
128 ErrorTy::RangeOutOfBounds { range, available } => {
129 format!("Range {range:?} is out of bounds for available size {available}")
130 }
131
132 ErrorTy::UnknownIdentifier(name, ty) => format!("Unknown {ty}: {name}"),
133
134 ErrorTy::SizeMismatch { expected, actual } => {
135 format!("Expected size {expected} but got size {actual}")
136 }
137
138 ErrorTy::TriedToNameLiteral => "Attempted to set a name on a literal value".to_string(),
139
140 ErrorTy::NameAlreadyExists(name) => {
141 format!("A name '{name}' already exists in the current scope")
142 }
143
144 ErrorTy::MissingArgument(name) => format!("Missing argument: {name}"),
145
146 ErrorTy::NonConstArgument => "Argument must be a compile-time constant".to_string(),
147
148 ErrorTy::DuplicateName(name) => format!("duplicate name: {name}"),
149
150 ErrorTy::DuplicateAddress(addr, value) => {
151 format!("address {addr:#x} is already mapped to a value: {value:?}")
152 }
153
154 ErrorTy::UnknownAddress(addr) => {
155 format!("address {addr:#x} is not mapped to any value")
156 }
157
158 ErrorTy::FunctionRootAddressMismatch {
159 fn_addr,
160 block_addr,
161 } => {
162 format!(
163 "Function root address mismatch: function address {fn_addr:#x}, block address {block_addr:#x}"
164 )
165 }
166
167 ErrorTy::FunctionRootMismatch { expected, actual } => {
168 format!("Function root mismatch: expected root block {expected:?}, got {actual:?}")
169 }
170
171 ErrorTy::NoRootBlock => "function has no root (entry) block".to_string(),
172 };
173
174 if let Some((start, end)) = self.span {
175 write!(f, "{message} (bytes {start}..{end})")
176 } else {
177 write!(f, "{message}")
178 }
179 }
180}