lithia 1.0.6

Embeddable and simple lisp dialect
Documentation
use std::{error::Error, fmt, rc::Rc};

use crate::object::Object;

pub type LispResult = Result<Rc<Object>, LispError>;
pub type RustFuncResult = Result<Rc<Object>, RustFuncError>;

#[derive(Debug, Clone)]
pub enum LispErrorKind {
    Parser,
    Eval,
    RustFunc,
}

#[derive(Debug)]
pub struct LispError {
    kind: LispErrorKind,
    error: Box<dyn Error>,
}

impl LispError {
    pub fn new<E>(kind: LispErrorKind, error: E) -> Self
    where
        E: Into<Box<dyn Error>>,
    {
        let error = error.into();

        Self { kind, error }
    }
}

impl fmt::Display for LispError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.kind {
            LispErrorKind::Parser => write!(f, "Error parsing code: {}", self.error),
            LispErrorKind::Eval => write!(f, "Error evaluating object: {}", self.error),
            LispErrorKind::RustFunc => write!(f, "{}", self.error),
        }
    }
}

impl Error for LispError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        Some(&*self.error)
    }
}

#[derive(Debug, Clone)]
pub enum ParserError {
    UnmatchedToken(char),
    InvalidToken(String),
    UnparsableAtom(String),
    EmptyQuote,
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnmatchedToken(c) => write!(f, "Unmatched token: '{}'", c),
            Self::UnparsableAtom(a) => write!(f, "Unparsable atom: {}", a),
            Self::InvalidToken(c) => write!(f, "Invalid token: '{}'", c),
            Self::EmptyQuote => write!(f, "Empty quote"),
        }
    }
}

impl Error for ParserError {}

#[derive(Debug, Clone)]
pub enum EvalError {
    UnknownSymbol(String),
    GlobalExists(String),
    NonFunction(Rc<Object>),
}

impl fmt::Display for EvalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnknownSymbol(s) => write!(f, "Unknown symbol: {}", s),
            Self::GlobalExists(s) => write!(f, "Global already exists: {}", s),
            Self::NonFunction(o) => write!(f, "Attempt to call non-function: {}", o),
        }
    }
}

impl Error for EvalError {}

#[derive(Debug)]
pub enum RustFuncError {
    InvalidArguments(ArgumentsError),
    LispError(LispError),
}

impl RustFuncError {
    pub fn new_args_error(error: ArgumentsError) -> Self {
        Self::InvalidArguments(error)
    }
}

impl fmt::Display for RustFuncError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidArguments(e) => {
                write!(f, "Error running function: Invalid arguments: {}", e)
            }
            Self::LispError(e) => write!(f, "{}", e),
        }
    }
}

impl Error for RustFuncError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::InvalidArguments(e) => Some(e),
            Self::LispError(e) => Some(e),
        }
    }
}

impl From<LispError> for RustFuncError {
    fn from(error: LispError) -> Self {
        Self::LispError(error)
    }
}

#[derive(Debug, Clone)]
pub enum ArgumentsError {
    TooMany,
    NotEnough,
    WrongType,
    DottedPair,
}

impl fmt::Display for ArgumentsError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::TooMany => write!(f, "Too many arguments"),
            Self::NotEnough => write!(f, "Not enough arguments"),
            Self::WrongType => write!(f, "Arguments of wrong type"),
            Self::DottedPair => write!(f, "Dotted-pair arguments not accepted"),
        }
    }
}

impl Error for ArgumentsError {}