use lua_tokenizer::TokenizeError;
use crate::LuaString;
use crate::{LuaEnv, LuaValue};
#[derive(Debug, Clone)]
pub enum RuntimeError {
Custom(LuaValue),
BadArgument(usize, Box<RuntimeError>),
TableIndexNil,
TableIndexNan,
IndexOutOfRange,
PositionOutOfBounds,
Expected(&'static str, Option<&'static str>),
ValueOutOfRange,
CloseRunningThread,
CloseNormalThread,
YieldOutsideCoroutine,
AttemptToGetLengthOf(&'static str),
AttemptToArithmeticOn(&'static str),
AttemptToBitwiseOn(&'static str),
AttemptToConcatenate(&'static str),
NoIntegerRepresentation,
TokenizeError(TokenizeError),
BaseOutOfRange,
AttemptTo(&'static str, &'static str, &'static str),
Error,
}
impl RuntimeError {
pub(crate) fn new_empty_argument(idx: usize, expected: &'static str) -> Self {
RuntimeError::BadArgument(idx, Box::new(RuntimeError::Expected(expected, None)))
}
pub fn into_lua_value(self, env: &mut LuaEnv) -> LuaValue {
match self {
RuntimeError::Custom(val) => return val,
_ => {
let string = RuntimeErrorEnvPair(&self, env).to_string();
LuaValue::String(LuaString::from_string(string))
}
}
}
pub fn to_error_message(&self, env: &LuaEnv) -> String {
RuntimeErrorEnvPair(&self, env).to_string()
}
}
struct RuntimeErrorEnvPair<'a>(&'a RuntimeError, &'a LuaEnv);
impl<'a> std::fmt::Display for RuntimeErrorEnvPair<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
RuntimeError::BadArgument(arg_idx, err) => write!(
f,
"bad argument #{} to '{}' ({})",
arg_idx,
self.1.last_op,
RuntimeErrorEnvPair(err, self.1)
),
RuntimeError::IndexOutOfRange => "index out of range".fmt(f),
RuntimeError::PositionOutOfBounds => "position out of bounds".fmt(f),
RuntimeError::TableIndexNil => "table index is nil".fmt(f),
RuntimeError::TableIndexNan => "table index is NaN".fmt(f),
RuntimeError::Expected(expected, got) => write!(
f,
"{} expected, got {}",
expected,
got.unwrap_or("no value")
),
RuntimeError::ValueOutOfRange => "value out of range".fmt(f),
RuntimeError::CloseRunningThread => "cannot close a running coroutine".fmt(f),
RuntimeError::CloseNormalThread => "cannot close a normal coroutine".fmt(f),
RuntimeError::YieldOutsideCoroutine => {
"attempt to yield from outside a coroutine".fmt(f)
}
RuntimeError::AttemptToGetLengthOf(type_str) => {
write!(f, "attempt to get length of a {} value", type_str)
}
RuntimeError::AttemptToArithmeticOn(type_str) => {
write!(f, "attempt to perform arithmetic on a {} value", type_str)
}
RuntimeError::AttemptToBitwiseOn(type_str) => {
write!(
f,
"attempt to perform bitwise operation on a {} value",
type_str
)
}
RuntimeError::AttemptToConcatenate(type_str) => {
write!(f, "attempt to concatenate a {} value", type_str)
}
RuntimeError::NoIntegerRepresentation => "number has no integer representation".fmt(f),
RuntimeError::TokenizeError(err) => write!(f, "{}", err),
RuntimeError::BaseOutOfRange => "base out of range".fmt(f),
RuntimeError::AttemptTo(method, lhs, rhs) => {
write!(f, "attempt to {} a '{}' with a '{}'", method, lhs, rhs)
}
RuntimeError::Custom(val) => write!(f, "{}", val),
_ => write!(f, "{:?}", self.0),
}
}
}