Skip to main content

qcode/
error.rs

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    /// Unknown name in an expression
13    UnknownIdentifier(Box<str>, &'static str),
14
15    SizeMismatch {
16        expected: usize,
17        actual: usize,
18    },
19
20    /// Attempted to set a name on a literal value
21    TriedToNameLiteral,
22
23    /// Attempted to set a name that already exists in the current scope
24    NameAlreadyExists(Box<str>),
25
26    UnknownAddress(u64),
27
28    /// A macro argument is not const
29    NonConstArgument,
30
31    MissingArgument(Box<str>),
32
33    /// A name was registered but a value with that name already exists
34    DuplicateName(String),
35
36    /// Two values were assigned to the same machine address
37    DuplicateAddress(u64, ValueId),
38
39    /// A function and it's root block have conflicting addresses
40    FunctionRootAddressMismatch {
41        fn_addr: u64,
42        block_addr: u64,
43    },
44
45    FunctionRootMismatch {
46        expected: BlockId,
47        actual: BlockId,
48    },
49
50    /// A pass was invoked on a function that has no root (entry) block.
51    NoRootBlock,
52}
53
54#[derive(Debug)]
55pub struct Error {
56    pub ty: ErrorTy,
57    /// Byte range `(start, end)` into the prepared source, if available.
58    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}