use crate::lisp::{ expr::Arity, lex::{Span, SpanDisplay}, }; use std::fmt; #[derive(Debug, PartialEq, Clone)] pub enum LispError { Parse(ParseError), Eval(EvalError), } impl fmt::Display for LispError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Parse(p) => write!(f, "parse error: {}", p.kind), Self::Eval(e) => write!(f, "eval error: {}", e), } } } #[derive(Debug, PartialEq, Clone)] pub struct ParseError { pub span: Span, pub kind: ParseErrorKind, } impl ParseError { pub fn new(span: Span, kind: ParseErrorKind) -> Self { Self { span, kind } } pub fn display(&self, text: &str) -> String { let SpanDisplay { line, col, .. } = SpanDisplay::highlight_span(self.span, text); format!("line {}, col {}: {}", line, col, self.kind) } } #[derive(Debug, PartialEq, Clone)] pub enum ParseErrorKind { InvalidLiteral, InvalidToken, InvalidChar(char), LiteralParseError, MissingCloseParen, UnbalancedComma, UnexpectedEof, UnexpectedToken { expected: &'static str, found: &'static str, }, UnmatchedParen, UnterminatedString, } impl fmt::Display for ParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ParseErrorKind::InvalidChar(ch) => write!(f, "invalid character {}", ch), ParseErrorKind::InvalidToken => write!(f, "invalid token"), ParseErrorKind::UnexpectedEof => write!(f, "unexpected end of file"), ParseErrorKind::InvalidLiteral => write!(f, "invalid literal"), ParseErrorKind::UnmatchedParen => write!(f, "unmatched `)`"), ParseErrorKind::UnbalancedComma => write!(f, "unbalanced comma"), ParseErrorKind::UnexpectedToken { expected, found } => { write!( f, "unexpected token, got `{}`, expected `{}`", found, expected ) } ParseErrorKind::LiteralParseError => write!(f, "error parsing literal"), ParseErrorKind::MissingCloseParen => write!(f, "missing `)`"), ParseErrorKind::UnterminatedString => write!(f, "unterminated string literal"), } } } impl From for LispError { fn from(p: ParseError) -> Self { LispError::Parse(p) } } #[derive(Debug, PartialEq, Clone)] pub enum EvalError { ArgumentCount(Arity), BadForm, UnboundVariable(String), DivByZero, TypeMismatch, NoFileName, CustomInternal(&'static str), } impl fmt::Display for EvalError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::ArgumentCount(i) => { write!(f, "invalid number of arguments, expected ")?; match i { Arity::Exact(a) => write!(f, "exactly {} argument(s)", a), Arity::Atleast(a) => write!(f, "atleast {} argument(s)", a), Arity::Atmost(a) => write!(f, "atmost {} argument(s)", a), Arity::Range(low, high) => { write!(f, "between {} and {} argument(s)", low, high) } Arity::None => write!(f, "any number of arguments"), } } Self::BadForm => write!(f, "bad expression form"), Self::UnboundVariable(s) => write!(f, "unbound variable {}", s), Self::TypeMismatch => write!(f, "mismatched types"), Self::DivByZero => write!(f, "attempt to divide by zero"), Self::NoFileName => write!(f, "no file name specified"), Self::CustomInternal(s) => write!(f, "{}", s), } } } impl From for LispError { fn from(e: EvalError) -> Self { LispError::Eval(e) } }