1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::io;
use std::fmt::{self, Display, Formatter};
use crate::{ParseError, text::InvalidChar};
use std::error::Error as ErrorTrait;

/// An error occurred whilst executing a knight program.
#[derive(Debug)]
pub enum Error {
	/// A division (or modulus) by zero was attempted.
	DivisionByZero {
		/// What kind of error it is---power, modulo, or division.
		kind: &'static str
	},

	Domain {
		message: &'static str
	},

	/// An unknown identifier was attempted to be dereferenced.
	UnknownIdentifier {
		/// The identifier at fault.
		identifier: Box<str>
	},

	/// A function was executed with an invalid operand.
	InvalidOperand {
		/// The function that was attempted.
		func: char,

		/// The type of the operand.
		operand: &'static str
	},

	/// A conversion was attempted for a type which doesn't implement it.
	///
	/// This is only used for [`Value::Variable`](crate::Value::Variable) and [`Value::Function`](
	/// crate::Value::Function), as all other types have well-defined conversion semantics.
	UndefinedConversion {
		/// The resulting type, had the conversion been defined.
		into: &'static str,

		/// The kind that didnt implement the conversion.
		kind: &'static str
	},

	/// A checked operation failed.
	#[cfg(feature = "checked-overflow")]
	Overflow {
		/// Which function overflowed.
		func: char,

		/// The left-hand-side of the function.
		lhs: crate::Number,

		/// The right-hand-side of the function.
		rhs: crate::Number,
	},

	/// Exit with the given status code.
	Quit(i32),

	/// An error occurred whilst parsing (i.e. `EVAL` failed).
	Parse(ParseError),

	/// An invalid string was encountered.
	InvalidString(InvalidChar),

	/// An i/o error occurred (i.e. `` ` `` or `PROMPT` failed).
	Io(io::Error),

	#[cfg(feature="checked-overflow")]
	TextConversionOverflow,

	/// An error class that can be used to raise other, custom errors.
	Custom(Box<dyn ErrorTrait>),
}

impl From<ParseError> for Error {
	#[inline]
	fn from(err: ParseError) -> Self {
		Self::Parse(err)
	}
}

impl From<io::Error> for Error {
	#[inline]
	fn from(err: io::Error) -> Self {
		Self::Io(err)
	}
}

impl From<InvalidChar> for Error {
	#[inline]
	fn from(err: InvalidChar) -> Self {
		Self::InvalidString(err)
	}
}

impl Display for Error {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		match self {
			Self::DivisionByZero { kind } => write!(f, "invalid {} with zero.", kind),
			Self::Domain { message } => write!(f, "{}", message),
			Self::UnknownIdentifier { identifier } => write!(f, "identifier {:?} is undefined.", identifier),
			Self::InvalidOperand { func, operand } => write!(f, "invalid operand kind {:?} for function {:?}.", operand, func),
			Self::UndefinedConversion { kind, into } => write!(f, "invalid conversion into {:?} for kind {:?}.", kind, into),
			#[cfg(feature = "checked-overflow")]
			Self::Overflow { func, lhs, rhs } => write!(f, "Expression '{} {} {}' overflowed", func, lhs, rhs),
			Self::Quit(code) => write!(f, "exit with status {}", code),
			Self::Parse(err) => Display::fmt(err, f),
			Self::InvalidString(err) => Display::fmt(err, f),
			Self::Io(err) => write!(f, "i/o error: {}", err),
			#[cfg(feature="checked-overflow")]
			Self::TextConversionOverflow => write!(f, "text to number conversion overflowed."),
			Self::Custom(err) => Display::fmt(err, f),
		}
	}
}

impl ErrorTrait for Error {
	fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
		match self {
			Self::Parse(err) => Some(err),
			Self::Io(err) => Some(err),
			Self::InvalidString(err) => Some(err),
			Self::Custom(err) => Some(err.as_ref()),
			_ => None
		}
	}
}