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
131
132
133
use std::fmt;

use super::SExp;

#[derive(Debug)]
pub enum SyntaxError {
    UnmatchedQuote(String),
    UnmatchedParen {
        exp: String,
        expected: char,
        given: Option<char>,
    },
    InvalidCond(SExp),
    NotANumber(String),
    NotAPrimitive(String),
    NotAToken(String),
}

impl fmt::Display for SyntaxError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SyntaxError::UnmatchedQuote(s) => write!(f, "Unmatched quote: {}", s),
            SyntaxError::UnmatchedParen {
                exp,
                expected,
                given: Some(g),
            } => write!(
                f,
                "Paren mismatch: expected {}, given {} in expression {}",
                expected, g, exp
            ),
            SyntaxError::UnmatchedParen { exp, expected, .. } => write!(
                f,
                "Paren mismatch: expected {} and no match found in expression {}",
                expected, exp
            ),
            SyntaxError::InvalidCond(e) => write!(f, "Invalid `cond` clause: {}", e),
            SyntaxError::NotANumber(s) => write!(f, "Could not parse as a number: {}", s),
            SyntaxError::NotAPrimitive(s) => {
                write!(f, "Could not parse as a primitive value: {}", s)
            }
            SyntaxError::NotAToken(s) => write!(f, "Unrecognized token: {}", s),
        }
    }
}

/// Multipurpose error type.
#[derive(Debug)]
pub enum Error {
    Syntax(SyntaxError),
    Type {
        expected: &'static str,
        given: String,
    },
    UndefinedSymbol {
        sym: String,
    },
    Arity {
        expected: usize,
        given: usize,
    },
    ArityMin {
        expected: usize,
        given: usize,
    },
    ArityMax {
        expected: usize,
        given: usize,
    },
    NotAList {
        atom: String,
    },
    NullList,
    NotAProcedure {
        exp: String,
    },
    Index {
        i: usize,
    },
    IO(String),
}

impl ::std::error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::Syntax(err) => write!(f, "{}", err),
            Error::Type { expected, given } => {
                write!(f, "Type error: expected {}, got {}", expected, given)
            }
            Error::UndefinedSymbol { sym } => write!(f, "Undefined symbol: {}", sym),
            Error::Arity { expected, given } => write!(
                f,
                "Arity mismatch: expected {} parameters, got {}.",
                expected, given
            ),
            Error::ArityMin { expected, given } => write!(
                f,
                "Arity mismatch: expected at least {} parameters, got {}.",
                expected, given
            ),
            Error::ArityMax { expected, given } => write!(
                f,
                "Arity mismatch: expected at most {} parameters, got {}.",
                expected, given
            ),
            Error::NotAList { atom } => write!(f, "Expected a list, got {}", atom),
            Error::NullList => write!(f, "Expected a pair, got null."),
            Error::NotAProcedure { exp } => write!(f, "{} is not a procedure.", exp),
            Error::Index { i } => write!(f, "Tried to access invalid index: [{}]", i),
            Error::IO(err) => write!(f, "I/O error: {}", err),
        }
    }
}

impl From<SyntaxError> for Error {
    fn from(e: SyntaxError) -> Self {
        Error::Syntax(e)
    }
}

impl From<std::fmt::Error> for Error {
    fn from(e: std::fmt::Error) -> Self {
        Error::IO(format!("{}", e))
    }
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Error::IO(format!("{}", e))
    }
}