use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT};
#[cfg(feature = "no_std")]
use core_error::Error;
#[cfg(not(feature = "no_std"))]
use std::error::Error;
use std::fmt;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
#[derive(Debug)]
#[non_exhaustive]
pub enum EvalAltResult {
#[cfg(not(feature = "sync"))]
ErrorSystem(String, Box<dyn Error>),
#[cfg(feature = "sync")]
ErrorSystem(String, Box<dyn Error + Send + Sync>),
ErrorParsing(ParseErrorType, Position),
ErrorVariableExists(String, Position),
ErrorForbiddenVariable(String, Position),
ErrorVariableNotFound(String, Position),
ErrorPropertyNotFound(String, Position),
ErrorIndexNotFound(Dynamic, Position),
ErrorFunctionNotFound(String, Position),
ErrorModuleNotFound(String, Position),
ErrorInFunctionCall(String, String, Box<Self>, Position),
ErrorInModule(String, Box<Self>, Position),
ErrorUnboundThis(Position),
ErrorMismatchDataType(String, String, Position),
ErrorMismatchOutputType(String, String, Position),
ErrorIndexingType(String, Position),
ErrorArrayBounds(usize, INT, Position),
ErrorStringBounds(usize, INT, Position),
ErrorBitFieldBounds(usize, INT, Position),
ErrorFor(Position),
ErrorDataRace(String, Position),
ErrorAssignmentToConstant(String, Position),
ErrorDotExpr(String, Position),
ErrorArithmetic(String, Position),
ErrorTooManyOperations(Position),
ErrorTooManyModules(Position),
ErrorStackOverflow(Position),
ErrorDataTooLarge(String, Position),
ErrorTerminated(Dynamic, Position),
ErrorCustomSyntax(String, Vec<String>, Position),
ErrorRuntime(Dynamic, Position),
LoopBreak(bool, Position),
Return(Dynamic, Position),
}
impl Error for EvalAltResult {}
impl fmt::Display for EvalAltResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ErrorSystem(s, err) => match s.as_str() {
"" => write!(f, "{}", err),
s => write!(f, "{}: {}", s, err),
}?,
Self::ErrorParsing(p, ..) => write!(f, "Syntax error: {}", p)?,
#[cfg(not(feature = "no_function"))]
Self::ErrorInFunctionCall(s, src, err, ..) if crate::parser::is_anonymous_fn(s) => {
write!(f, "{} in call to closure", err)?;
if !src.is_empty() {
write!(f, " @ '{}'", src)?;
}
}
Self::ErrorInFunctionCall(s, src, err, ..) => {
write!(f, "{} in call to function {}", err, s)?;
if !src.is_empty() {
write!(f, " @ '{}'", src)?;
}
}
Self::ErrorInModule(s, err, ..) if s.is_empty() => {
write!(f, "Error in module > {}", err)?
}
Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{}' > {}", s, err)?,
Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {}", s)?,
Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {}", s)?,
Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {}", s)?,
Self::ErrorPropertyNotFound(s, ..) => write!(f, "Property not found: {}", s)?,
Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {}", s)?,
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {}", s)?,
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {}", s)?,
Self::ErrorDataRace(s, ..) => {
write!(f, "Data race detected when accessing variable: {}", s)?
}
Self::ErrorDotExpr(s, ..) => match s.as_str() {
"" => f.write_str("Malformed dot expression"),
s => f.write_str(s),
}?,
Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {}", s)?,
Self::ErrorUnboundThis(..) => f.write_str("'this' not bound")?,
Self::ErrorFor(..) => f.write_str("For loop expects an iterable type")?,
Self::ErrorTooManyOperations(..) => f.write_str("Too many operations")?,
Self::ErrorTooManyModules(..) => f.write_str("Too many modules imported")?,
Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
Self::ErrorTerminated(..) => f.write_str("Script terminated")?,
Self::ErrorRuntime(d, ..) if d.is::<()>() => f.write_str("Runtime error")?,
Self::ErrorRuntime(d, ..)
if d.read_lock::<ImmutableString>()
.map_or(false, |v| v.is_empty()) =>
{
write!(f, "Runtime error")?
}
Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {}", d)?,
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?,
Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) {
("", e) => write!(f, "Output type incorrect, expecting {}", e),
(a, "") => write!(f, "Output type incorrect: {}", a),
(a, e) => write!(f, "Output type incorrect: {} (expecting {})", a, e),
}?,
Self::ErrorMismatchDataType(e, a, ..) => match (a.as_str(), e.as_str()) {
("", e) => write!(f, "Data type incorrect, expecting {}", e),
(a, "") => write!(f, "Data type incorrect: {}", a),
(a, e) => write!(f, "Data type incorrect: {} (expecting {})", a, e),
}?,
Self::ErrorArithmetic(s, ..) => match s.as_str() {
"" => f.write_str("Arithmetic error"),
s => f.write_str(s),
}?,
Self::LoopBreak(true, ..) => f.write_str("'break' not inside a loop")?,
Self::LoopBreak(false, ..) => f.write_str("'continue' not inside a loop")?,
Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
Self::ErrorArrayBounds(max, index, ..) => match max {
0 => write!(f, "Array index {} out of bounds: array is empty", index),
1 => write!(
f,
"Array index {} out of bounds: only 1 element in array",
index
),
_ => write!(
f,
"Array index {} out of bounds: only {} elements in array",
index, max
),
}?,
Self::ErrorStringBounds(max, index, ..) => match max {
0 => write!(f, "String index {} out of bounds: string is empty", index),
1 => write!(
f,
"String index {} out of bounds: only 1 character in string",
index
),
_ => write!(
f,
"String index {} out of bounds: only {} characters in string",
index, max
),
}?,
Self::ErrorBitFieldBounds(max, index, ..) => write!(
f,
"Bit-field index {} out of bounds: only {} bits in bit-field",
index, max
)?,
Self::ErrorDataTooLarge(typ, ..) => write!(f, "{} exceeds maximum limit", typ)?,
Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{}: {}", s, tokens.join(" "))?,
}
if !self.position().is_none() {
write!(f, " ({})", self.position())?;
}
Ok(())
}
}
impl<T: AsRef<str>> From<T> for EvalAltResult {
#[cold]
#[inline(never)]
fn from(err: T) -> Self {
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE)
}
}
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
#[cold]
#[inline(never)]
fn from(err: T) -> Self {
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
}
}
impl EvalAltResult {
#[cold]
#[inline(never)]
#[must_use]
pub const fn is_pseudo_error(&self) -> bool {
match self {
Self::LoopBreak(..) | Self::Return(..) => true,
_ => false,
}
}
#[cold]
#[inline(never)]
#[must_use]
pub const fn is_catchable(&self) -> bool {
match self {
Self::ErrorSystem(..) => false,
Self::ErrorParsing(..) => false,
Self::ErrorFunctionNotFound(..)
| Self::ErrorInFunctionCall(..)
| Self::ErrorInModule(..)
| Self::ErrorUnboundThis(..)
| Self::ErrorMismatchDataType(..)
| Self::ErrorArrayBounds(..)
| Self::ErrorStringBounds(..)
| Self::ErrorBitFieldBounds(..)
| Self::ErrorIndexingType(..)
| Self::ErrorFor(..)
| Self::ErrorVariableExists(..)
| Self::ErrorForbiddenVariable(..)
| Self::ErrorVariableNotFound(..)
| Self::ErrorPropertyNotFound(..)
| Self::ErrorIndexNotFound(..)
| Self::ErrorModuleNotFound(..)
| Self::ErrorDataRace(..)
| Self::ErrorAssignmentToConstant(..)
| Self::ErrorMismatchOutputType(..)
| Self::ErrorDotExpr(..)
| Self::ErrorArithmetic(..)
| Self::ErrorRuntime(..) => true,
Self::ErrorCustomSyntax(..) => false,
Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..)
| Self::ErrorTerminated(..) => false,
Self::LoopBreak(..) | Self::Return(..) => false,
}
}
#[cold]
#[inline(never)]
#[must_use]
pub const fn is_system_exception(&self) -> bool {
match self {
Self::ErrorSystem(..) => true,
Self::ErrorParsing(..) => true,
Self::ErrorCustomSyntax(..)
| Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..) => true,
Self::ErrorTerminated(..) => true,
_ => false,
}
}
#[cfg(not(feature = "no_object"))]
#[cold]
#[inline(never)]
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
map.insert(
"error".into(),
format!("{self:?}")
.split('(')
.next()
.expect("`ErrorXXX(...)`")
.into(),
);
match self {
Self::LoopBreak(..) | Self::Return(..) => (),
Self::ErrorSystem(..)
| Self::ErrorParsing(..)
| Self::ErrorUnboundThis(..)
| Self::ErrorFor(..)
| Self::ErrorArithmetic(..)
| Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorRuntime(..) => (),
Self::ErrorFunctionNotFound(f, ..) => {
map.insert("function".into(), f.into());
}
Self::ErrorInFunctionCall(f, s, ..) => {
map.insert("function".into(), f.into());
map.insert("source".into(), s.into());
}
Self::ErrorMismatchDataType(r, a, ..) | Self::ErrorMismatchOutputType(r, a, ..) => {
map.insert("requested".into(), r.into());
map.insert("actual".into(), a.into());
}
Self::ErrorArrayBounds(n, i, ..)
| Self::ErrorStringBounds(n, i, ..)
| Self::ErrorBitFieldBounds(n, i, ..) => {
map.insert("length".into(), (*n as INT).into());
map.insert("index".into(), (*i as INT).into());
}
Self::ErrorVariableExists(v, ..)
| Self::ErrorForbiddenVariable(v, ..)
| Self::ErrorVariableNotFound(v, ..)
| Self::ErrorPropertyNotFound(v, ..)
| Self::ErrorDataRace(v, ..)
| Self::ErrorAssignmentToConstant(v, ..) => {
map.insert("variable".into(), v.into());
}
Self::ErrorIndexNotFound(v, ..) => {
map.insert("index".into(), v.clone());
}
Self::ErrorInModule(m, ..) | Self::ErrorModuleNotFound(m, ..) => {
map.insert("module".into(), m.into());
}
Self::ErrorDotExpr(p, ..) => {
map.insert("property".into(), p.into());
}
Self::ErrorIndexingType(t, ..) | Self::ErrorDataTooLarge(t, ..) => {
map.insert("type".into(), t.into());
}
Self::ErrorTerminated(t, ..) => {
map.insert("token".into(), t.clone());
}
Self::ErrorCustomSyntax(_, tokens, _) => {
map.insert(
"tokens".into(),
#[cfg(not(feature = "no_index"))]
Dynamic::from_array(tokens.iter().map(Into::into).collect()),
#[cfg(feature = "no_index")]
tokens
.iter()
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join(" ")
.into(),
);
}
};
}
#[cold]
#[inline(never)]
#[must_use]
pub fn unwrap_inner(&self) -> &Self {
match self {
Self::ErrorInFunctionCall(.., err, _) | Self::ErrorInModule(.., err, _) => {
err.unwrap_inner()
}
_ => self,
}
}
#[cold]
#[inline(never)]
#[must_use]
pub const fn position(&self) -> Position {
match self {
Self::ErrorSystem(..) => Position::NONE,
Self::ErrorParsing(.., pos)
| Self::ErrorFunctionNotFound(.., pos)
| Self::ErrorInFunctionCall(.., pos)
| Self::ErrorInModule(.., pos)
| Self::ErrorUnboundThis(pos)
| Self::ErrorMismatchDataType(.., pos)
| Self::ErrorArrayBounds(.., pos)
| Self::ErrorStringBounds(.., pos)
| Self::ErrorBitFieldBounds(.., pos)
| Self::ErrorIndexingType(.., pos)
| Self::ErrorFor(pos)
| Self::ErrorVariableExists(.., pos)
| Self::ErrorForbiddenVariable(.., pos)
| Self::ErrorVariableNotFound(.., pos)
| Self::ErrorPropertyNotFound(.., pos)
| Self::ErrorIndexNotFound(.., pos)
| Self::ErrorModuleNotFound(.., pos)
| Self::ErrorDataRace(.., pos)
| Self::ErrorAssignmentToConstant(.., pos)
| Self::ErrorMismatchOutputType(.., pos)
| Self::ErrorDotExpr(.., pos)
| Self::ErrorArithmetic(.., pos)
| Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(.., pos)
| Self::ErrorTerminated(.., pos)
| Self::ErrorCustomSyntax(.., pos)
| Self::ErrorRuntime(.., pos)
| Self::LoopBreak(.., pos)
| Self::Return(.., pos) => *pos,
}
}
#[cold]
#[inline(never)]
pub fn clear_position(&mut self) -> &mut Self {
self.set_position(Position::NONE)
}
#[cold]
#[inline(never)]
pub fn take_position(&mut self) -> Position {
let pos = self.position();
self.set_position(Position::NONE);
pos
}
#[cold]
#[inline(never)]
pub fn set_position(&mut self, new_position: Position) -> &mut Self {
match self {
Self::ErrorSystem(..) => (),
Self::ErrorParsing(.., pos)
| Self::ErrorFunctionNotFound(.., pos)
| Self::ErrorInFunctionCall(.., pos)
| Self::ErrorInModule(.., pos)
| Self::ErrorUnboundThis(pos)
| Self::ErrorMismatchDataType(.., pos)
| Self::ErrorArrayBounds(.., pos)
| Self::ErrorStringBounds(.., pos)
| Self::ErrorBitFieldBounds(.., pos)
| Self::ErrorIndexingType(.., pos)
| Self::ErrorFor(pos)
| Self::ErrorVariableExists(.., pos)
| Self::ErrorForbiddenVariable(.., pos)
| Self::ErrorVariableNotFound(.., pos)
| Self::ErrorPropertyNotFound(.., pos)
| Self::ErrorIndexNotFound(.., pos)
| Self::ErrorModuleNotFound(.., pos)
| Self::ErrorDataRace(.., pos)
| Self::ErrorAssignmentToConstant(.., pos)
| Self::ErrorMismatchOutputType(.., pos)
| Self::ErrorDotExpr(.., pos)
| Self::ErrorArithmetic(.., pos)
| Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(.., pos)
| Self::ErrorTerminated(.., pos)
| Self::ErrorCustomSyntax(.., pos)
| Self::ErrorRuntime(.., pos)
| Self::LoopBreak(.., pos)
| Self::Return(.., pos) => *pos = new_position,
}
self
}
#[cold]
#[inline(never)]
#[must_use]
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
if self.position().is_none() {
self.set_position(new_position);
}
self
}
}