espy_paws/
error.rs

1use crate::{ComplexType, Value};
2use espy_heart::debug;
3use std::num::NonZero;
4
5#[must_use]
6pub struct Error {
7    /// Program counter relative to the start of the file rather than the start of a block.
8    ///
9    /// This can be used to derive the block, local pc, line number, etc.
10    /// It is "nonzero" to reduce the size of the error structure;
11    /// the first byte of an espy binary is never executable code.
12    absolute_program_counter: Option<NonZero<u32>>,
13    kind: ErrorKind,
14}
15
16impl Error {
17    pub fn kind(&self) -> &ErrorKind {
18        &self.kind
19    }
20
21    pub fn into_kind(self) -> ErrorKind {
22        self.kind
23    }
24
25    pub fn suggest_location(self, absolute_program_counter: usize) -> Self {
26        Self {
27            absolute_program_counter: self.absolute_program_counter.or_else(|| {
28                u32::try_from(absolute_program_counter)
29                    .ok()?
30                    .try_into()
31                    .ok()
32            }),
33            ..self
34        }
35    }
36
37    /// Converts the error's location hint into the source code span it originated from.
38    ///
39    /// This may result in `None`--not all errors have locations.
40    ///
41    /// # Errors
42    ///
43    /// Returns a [`DebugInfoError`] if the debug info is malformed.
44    ///
45    /// The reported position of this error may be trusted and will never be the
46    /// source of an error.
47    pub fn locate(&self, debug_info: &[u8]) -> Result<Option<(usize, usize)>, DebugInfoError> {
48        let Some(absolute_program_counter) = self.absolute_program_counter.map(|x| {
49            u32::from(x)
50                .try_into()
51                .expect("program counter should not exceed usize")
52        }) else {
53            return Ok(None);
54        };
55        let mut this_pc = 0;
56        let mut last_span = None;
57        let mut debug_info = debug_info.iter().copied();
58        while let Some(directive) = debug_info.next() {
59            match directive {
60                debug::SPAN => {
61                    let mut get_u64 = || {
62                        let mut unpacked = [0; size_of::<u64>()];
63                        for dest in unpacked.iter_mut().take(debug_info.next()? as usize) {
64                            *dest = debug_info.next()?;
65                        }
66                        Some(u64::from_le_bytes(unpacked))
67                    };
68                    let start = get_u64().ok_or(DebugInfoError {
69                        kind: DebugInfoErrorKind::UnexpectedEndOfSequence,
70                    })?;
71                    let end = get_u64().ok_or(DebugInfoError {
72                        kind: DebugInfoErrorKind::UnexpectedEndOfSequence,
73                    })?;
74                    last_span = Some((start, end));
75                    this_pc += 1;
76                }
77                debug::DITTO => {
78                    this_pc += debug_info.next().ok_or(DebugInfoError {
79                        kind: DebugInfoErrorKind::UnexpectedEndOfSequence,
80                    })? as usize;
81                }
82                _ => {
83                    return Err(DebugInfoError {
84                        kind: DebugInfoErrorKind::UnknownDirective,
85                    });
86                }
87            }
88            if this_pc > absolute_program_counter {
89                return Ok(last_span.map(|(start, end)| (start as usize, end as usize)));
90            }
91        }
92        Err(DebugInfoError {
93            kind: DebugInfoErrorKind::UnexpectedEndOfSequence,
94        })
95    }
96}
97
98impl std::fmt::Display for Error {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        self.kind.fmt(f)
101    }
102}
103
104impl std::fmt::Debug for Error {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        self.kind.fmt(f)
107    }
108}
109
110impl std::error::Error for Error {}
111
112impl Error {
113    pub fn type_error(value: &Value, ty: &ComplexType) -> Self {
114        ErrorKind::TypeError {
115            value: format!("{value:?}").into_boxed_str(),
116            ty: format!("{:?}", ty).into_boxed_str(),
117        }
118        .into()
119    }
120
121    pub fn index_not_found(container: &Value, index: &Value) -> Self {
122        ErrorKind::IndexNotFound {
123            index: format!("{index:?}").into_boxed_str(),
124            container: format!("{container:?}").into_boxed_str(),
125        }
126        .into()
127    }
128
129    pub fn expected_integers(l: &Value, r: &Value) -> Self {
130        ErrorKind::ExpectedIntegers(
131            format!("{l:?}").into_boxed_str(),
132            format!("{r:?}").into_boxed_str(),
133        )
134        .into()
135    }
136
137    pub fn expected_tuple(i: &Value) -> Self {
138        ErrorKind::ExpectedTuple(format!("{i:?}").into_boxed_str()).into()
139    }
140
141    pub fn expected_named_tuple(i: &Value) -> Self {
142        ErrorKind::ExpectedNamedTuple(format!("{i:?}").into_boxed_str()).into()
143    }
144
145    pub fn expected_function(function: &Value) -> Self {
146        ErrorKind::ExpectedFunction(format!("{function:?}").into_boxed_str()).into()
147    }
148
149    pub fn incomparable_values(l: &Value, r: &Value) -> Self {
150        ErrorKind::IncomparableValues(
151            format!("{l:?}").into_boxed_str(),
152            format!("{r:?}").into_boxed_str(),
153        )
154        .into()
155    }
156
157    pub fn expected_enum_variant(i: &Value) -> Error {
158        ErrorKind::ExpectedEnumVariant(format!("{i:?}").into_boxed_str()).into()
159    }
160
161    pub fn expected_enum_type(i: &Value) -> Error {
162        ErrorKind::ExpectedEnumType(format!("{i:?}").into_boxed_str()).into()
163    }
164
165    pub fn expected_option(i: &Value) -> Error {
166        ErrorKind::ExpectedOption(format!("{i:?}").into_boxed_str()).into()
167    }
168
169    pub fn expected_reference(i: &Value) -> Error {
170        ErrorKind::ExpectedReference(format!("{i:?}").into_boxed_str()).into()
171    }
172
173    pub fn other(e: impl Into<anyhow::Error>) -> Error {
174        ErrorKind::Other(e.into()).into()
175    }
176
177    pub fn custom(message: &'static str) -> Error {
178        ErrorKind::Other(anyhow::Error::msg(message)).into()
179    }
180}
181
182impl From<ErrorKind> for Error {
183    fn from(kind: ErrorKind) -> Self {
184        Self {
185            absolute_program_counter: None,
186            kind,
187        }
188    }
189}
190
191impl From<anyhow::Error> for Error {
192    fn from(e: anyhow::Error) -> Error {
193        Error::other(e)
194    }
195}
196
197#[derive(Debug)]
198#[non_exhaustive]
199#[must_use]
200pub enum ErrorKind {
201    ExpectedIntegers(Box<str>, Box<str>),
202    ExpectedFunction(Box<str>),
203    ExpectedEnumVariant(Box<str>),
204    ExpectedOption(Box<str>),
205    ExpectedEnumType(Box<str>),
206    ExpectedTuple(Box<str>),
207    ExpectedNamedTuple(Box<str>),
208    ExpectedReference(Box<str>),
209
210    IncomparableValues(Box<str>, Box<str>),
211    TypeError {
212        value: Box<str>,
213        ty: Box<str>,
214    },
215    CalledNeverFunction,
216    IndexNotFound {
217        index: Box<str>,
218        container: Box<str>,
219    },
220    OutOfScopeMut,
221    InvalidBytecode(InvalidBytecode),
222
223    Other(anyhow::Error),
224}
225
226impl std::fmt::Display for ErrorKind {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        match self {
229            ErrorKind::ExpectedIntegers(l, r) => {
230                write!(f, "expected two integers, got {l} and {r}")
231            }
232            ErrorKind::ExpectedFunction(i) => write!(f, "expected a function, got {i}"),
233            ErrorKind::ExpectedEnumVariant(i) => write!(f, "expected an enum variant, got {i}"),
234            ErrorKind::ExpectedOption(i) => write!(f, "expected an option, got {i}"),
235            ErrorKind::ExpectedEnumType(i) => write!(f, "expected an enum, got {i}"),
236            ErrorKind::ExpectedTuple(i) => write!(f, "expected a tuple or named tuple, got {i}"),
237            ErrorKind::ExpectedNamedTuple(i) => write!(f, "expected a named tuple, got {i}"),
238            ErrorKind::ExpectedReference(i) => write!(f, "expected a reference type, got {i}"),
239            ErrorKind::IncomparableValues(l, r) => write!(f, "{l} cannot be compared to {r}"),
240            ErrorKind::TypeError { value, ty } => {
241                write!(f, "expected a value of type {ty}, got {value}")
242            }
243            ErrorKind::CalledNeverFunction => write!(f, "attempted to call a \"never\" function"),
244            ErrorKind::IndexNotFound { index, container } => {
245                write!(f, "{container} does not contain the field {index}")
246            }
247            ErrorKind::OutOfScopeMut => write!(
248                f,
249                "attempted to read or write a mutable value which has gone out of scope"
250            ),
251            ErrorKind::InvalidBytecode(invalid_bytecode) => {
252                write!(f, "invalid bytecode: {invalid_bytecode}")
253            }
254            ErrorKind::Other(error) => error.fmt(f),
255        }
256    }
257}
258
259/// Errors that may only be caused due to invalid bytecode.
260///
261/// If this error is returned by an espy program compiled by a compatible
262/// version of the compiler, it is considered an espy bug.
263#[derive(Debug)]
264pub enum InvalidBytecode {
265    /// Caused by an imbalance in stack operations.
266    ///
267    /// Well-behaved bytecode never has any reason to cause this.
268    StackUnderflow,
269    /// An instruction byte had an unexpected value.
270    InvalidInstruction,
271    /// A clone referred to a builtin value that does not exist.
272    InvalidBuiltin,
273    /// An instruction referred to a string id that did not exist.
274    InvalidStringId,
275    /// Occurs when the header is too short or
276    /// describes a program which is longer than the provided slice.
277    MalformedHeader,
278    /// Occurs when the program counter becomes greater than the length of the program.
279    ///
280    /// Note the "*greater*"; a pc of the program's length
281    /// (after the last byte) is considered an intentional return.
282    ProgramOutOfBounds,
283    /// Occurs when a stack access goes beyond the length of the stack.
284    StackOutOfBounds,
285    Utf8Error(std::str::Utf8Error),
286}
287
288impl From<InvalidBytecode> for Error {
289    fn from(e: InvalidBytecode) -> Error {
290        Error {
291            absolute_program_counter: None,
292            kind: ErrorKind::InvalidBytecode(e),
293        }
294    }
295}
296
297impl std::fmt::Display for InvalidBytecode {
298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299        match self {
300            InvalidBytecode::StackUnderflow => write!(f, "stack underflow"),
301            InvalidBytecode::InvalidInstruction => write!(f, "invalid instruction"),
302            InvalidBytecode::InvalidBuiltin => write!(f, "invalid builtin"),
303            InvalidBytecode::InvalidStringId => write!(f, "unexpected string id"),
304            InvalidBytecode::MalformedHeader => write!(f, "malformed header"),
305            InvalidBytecode::ProgramOutOfBounds => write!(f, "program out of bounds"),
306            InvalidBytecode::StackOutOfBounds => write!(f, "stack out of bounds"),
307            InvalidBytecode::Utf8Error(utf8_error) => utf8_error.fmt(f),
308        }
309    }
310}
311
312#[derive(Debug)]
313pub struct DebugInfoError {
314    kind: DebugInfoErrorKind,
315}
316
317impl std::fmt::Display for DebugInfoError {
318    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319        use DebugInfoErrorKind as Kind;
320        match &self.kind {
321            Kind::UnexpectedEndOfSequence => write!(f, "unexpected end of sequence"),
322            Kind::UnknownDirective => write!(f, "unknown directive"),
323        }
324    }
325}
326
327#[derive(Debug)]
328pub enum DebugInfoErrorKind {
329    UnexpectedEndOfSequence,
330    UnknownDirective,
331}