use std::fmt::Display;
use crate::collections::handle_table::Handle;
use crate::prelude::Trace;
use crate::traits::VmFunction;
use thiserror::Error;
pub type ExecutionResult<T = ()> = Result<T, ExecutionError>;
#[derive(Debug, Clone, Error)]
pub enum ExecutionErrorPayload {
#[error("The program has overflown its call stack")]
CallStackOverflow,
#[error("Input ended unexpectedly")]
UnexpectedEndOfInput,
#[error("Program exited with status code: {0}")]
ExitCode(i32),
#[error("Got an invalid instruction code {0}")]
InvalidInstruction(u8),
#[error("Got an invalid argument: {}",
.context.as_ref().map(|x|x.as_str()).unwrap_or_else(|| ""))]
InvalidArgument { context: Option<String> },
#[error("Variable {0} was not found!")]
VarNotFound(String),
#[error("Procedure by the hash {0:?} could not be found")]
ProcedureNotFound(Handle),
#[error("Unimplemented")]
Unimplemented,
#[error("The program ran out of memory")]
OutOfMemory,
#[error("Missing argument to function call")]
MissingArgument,
#[error("Program timed out")]
Timeout,
#[error("Subtask [{name}] failed {error}")]
TaskFailure {
name: String,
error: Box<ExecutionErrorPayload>,
},
#[error("The program has overflowns its stack")]
Stackoverflow,
#[error("Failed to return from a function {reason}")]
BadReturn { reason: String },
#[error("Trying to hash an unhashable object")]
Unhashable,
#[error("Assertion failed: {0}")]
AssertionError(String),
#[error("Closure requested a non-existent upvalue")]
InvalidUpvalue,
#[error("Expected to be in the context of a closure")]
NotClosure,
}
#[derive(Debug, Clone, Error)]
pub struct ExecutionError {
pub payload: ExecutionErrorPayload,
pub trace: Vec<Trace>,
}
impl ExecutionError {
pub fn new(payload: ExecutionErrorPayload, trace: Vec<Trace>) -> Self {
Self { payload, trace }
}
}
impl Display for ExecutionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ExecutionError: {}", self.payload)
}
}
impl ExecutionErrorPayload {
pub fn invalid_argument<S>(reason: S) -> Self
where
S: Into<String>,
{
Self::InvalidArgument {
context: Some(reason.into()),
}
}
}
pub(crate) struct Procedure<Aux> {
pub fun: std::rc::Rc<dyn VmFunction<Aux>>,
pub name: String,
}
impl<Aux> Clone for Procedure<Aux> {
fn clone(&self) -> Self {
Self {
fun: self.fun.clone(),
name: self.name.clone(),
}
}
}
impl<Aux> Procedure<Aux> {
pub fn name(&self) -> &str {
self.name.as_str()
}
}
impl<Aux> std::fmt::Debug for Procedure<Aux> {
fn fmt(&self, writer: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(writer, "Procedure '{}'", self.name)
}
}