aboutsummaryrefslogtreecommitdiff
path: root/src/lisp/error.rs
blob: a360eb2a79409abc5b87f2a8c753a26eaeadc8f0 (plain)
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
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 fmt(&self, f: &mut fmt::Formatter<'_>, text: &str) -> fmt::Result {
        let SpanDisplay { line, col, .. } = SpanDisplay::highlight_span(self.span, text);
        write!(f, "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<ParseError> 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<EvalError> for LispError {
    fn from(e: EvalError) -> Self {
        LispError::Eval(e)
    }
}