170 lines
4.7 KiB
Rust
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)
|
|
}
|
|
}
|