Files
lysp/src/error.rs
T

170 lines
4.7 KiB
Rust

use std::{fmt, io, ops::RangeInclusive, rc::Rc};
use crate::{
compile::CompileError,
vm::{
Value,
instruction::{Instruction, InstructionDecodeError},
value::{BytecodeFunction, IdentifierValue},
},
};
#[derive(Debug, thiserror::Error)]
#[error("{error}")]
pub struct MachineErrorAt {
// TODO ip where the error occured
pub error: MachineError,
pub location: Option<MachineErrorLocation>,
}
#[derive(Debug)]
pub struct MachineErrorLocation {
pub function: Rc<BytecodeFunction>,
pub offset: usize,
}
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("expected {expected}, got {got}")]
pub struct ValueConversionError {
pub expected: String,
pub got: Value,
}
#[derive(Debug, thiserror::Error)]
pub enum ReadError {
#[error("{0}")]
Lexical(nom::Err<nom::error::Error<String>, nom::error::Error<String>>),
#[error("{0}")]
Io(io::Error),
}
#[derive(Debug, PartialEq)]
pub struct ArgumentCountError {
pub function: Rc<BytecodeFunction>,
pub expected_range: RangeInclusive<usize>,
pub actual: usize,
}
#[derive(Debug, PartialEq, thiserror::Error)]
pub enum MachineError {
// VM itself
#[error("instruction pointer is undefined")]
InstructionPointerUndefined,
#[error("instruction pointer is out of bounds")]
InstructionPointerOutOfBounds,
#[error("instruction fetch failed")]
InstructionFetch,
#[error("instruction decode error: {0}")]
InstructionDecode(#[from] InstructionDecodeError),
#[error("data stack overflowed")]
DataStackOverflow,
#[error("data stack underflowed")]
DataStackUnderflow,
#[error("call stack overflowed")]
CallStackOverflow,
#[error("call stack underflowed")]
CallStackUnderflow,
#[error("undefined upvalue reference")]
UndefinedUpvalueReference,
#[error("undefined local reference")]
UndefinedLocalReference,
#[error("undefined constant reference")]
UndefinedConstantReference,
#[error("unbound identifier reference: {0}")]
UnboundIdentifier(IdentifierValue),
#[error("invalid {0} argument: {1}")]
InvalidInstructionArgument(Instruction, ValueConversionError),
#[error("invalid branch target: {0}{1:+}")]
InvalidBranchTarget(usize, isize),
#[error("GET_TEMP with an empty temp register")]
TempRegisterEmpty,
#[error("{0}")]
ArgumentCount(ArgumentCountError),
// Syntax+evaluation
// #[error("evaluation error: {0}")]
// EvaluationError(EvalError),
#[error("invalid argument count")]
InvalidArgumentCount,
#[error("aborted: {0}")]
Abort(Rc<str>),
#[error("value conversion error: {0}")]
ValueConversion(#[from] ValueConversionError),
#[error("syntax error: {0}")]
Read(ReadError),
#[error("compile error: {0}")]
Compile(#[from] CompileError),
}
impl MachineError {
pub fn at(self, location: Option<MachineErrorLocation>) -> MachineErrorAt {
MachineErrorAt {
error: self,
location,
}
}
pub fn at_unknown(self) -> MachineErrorAt {
self.at(None)
}
}
impl MachineErrorAt {
pub fn at_unknown(error: MachineError) -> Self {
Self::at(error, None)
}
pub fn at(error: MachineError, location: Option<MachineErrorLocation>) -> Self {
Self { error, location }
}
}
impl MachineErrorLocation {
pub fn disassemble_chunk(&self, address: usize, before: usize, after: usize, arrow: bool) {
self.function.disassemble(address, before, after, arrow);
}
}
impl fmt::Display for MachineErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:p}+{}", self.function, self.offset)
}
}
impl PartialEq for ReadError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Lexical(a), Self::Lexical(b)) => a == b,
(Self::Io(a), Self::Io(b)) => a.raw_os_error() == b.raw_os_error(),
_ => false,
}
}
}
impl fmt::Display for ArgumentCountError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let min_arity = *self.expected_range.start();
let max_arity = *self.expected_range.end();
let one_argument = min_arity == max_arity;
let too_few = self.actual < min_arity;
write!(
f,
"too {} arguments for function {}: expected ",
if too_few { "few" } else { "many" },
self.function
)?;
if one_argument {
write!(f, "{min_arity}")?;
write!(f, " argument")?;
if min_arity != 1 {
write!(f, "s")?;
}
} else {
write!(f, "{min_arity}-{max_arity} arguments")?;
}
write!(f, ", got {}", self.actual)
}
}