Skip to main content

tidepool_eval/
error.rs

1use crate::value::ThunkId;
2use tidepool_repr::{JoinId, PrimOpKind, VarId};
3
4/// Describes the kind of a Value for error reporting.
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum ValueKind {
7    Literal(&'static str), // "Int#", "Word#", "Double#", "Char#", "String"
8    Constructor,
9    Closure,
10    Thunk,
11    /// Fallback for complex values — stores Debug output
12    Other(String),
13}
14
15impl std::fmt::Display for ValueKind {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            ValueKind::Literal(name) => write!(f, "{}", name),
19            ValueKind::Constructor => write!(f, "constructor"),
20            ValueKind::Closure => write!(f, "closure"),
21            ValueKind::Thunk => write!(f, "thunk"),
22            ValueKind::Other(s) => write!(f, "{}", s),
23        }
24    }
25}
26
27/// Evaluation error.
28#[derive(Debug, Clone)]
29pub enum EvalError {
30    /// Variable not found in environment
31    UnboundVar(VarId),
32    /// Arity mismatch (wrong number of arguments or fields)
33    ArityMismatch {
34        context: &'static str, // "arguments", "fields", "case binders"
35        expected: usize,
36        got: usize,
37    },
38    /// Type mismatch during evaluation
39    TypeMismatch {
40        expected: &'static str,
41        got: ValueKind,
42    },
43    /// No matching alternative in case expression
44    NoMatchingAlt,
45    /// Infinite loop detected (thunk forced itself)
46    InfiniteLoop(ThunkId),
47    /// Unsupported primop
48    UnsupportedPrimOp(PrimOpKind),
49    /// Heap exhausted
50    HeapExhausted,
51    /// Application of non-function value
52    NotAFunction,
53    /// Jump to unknown join point
54    UnboundJoin(JoinId),
55}
56
57impl std::fmt::Display for EvalError {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        match self {
60            EvalError::UnboundVar(v) => write!(f, "unbound variable: v_{}", v.0),
61            EvalError::ArityMismatch {
62                context,
63                expected,
64                got,
65            } => {
66                write!(
67                    f,
68                    "arity mismatch: expected {} {}, got {}",
69                    expected, context, got
70                )
71            }
72            EvalError::TypeMismatch { expected, got } => {
73                write!(f, "type mismatch: expected {}, got {}", expected, got)
74            }
75            EvalError::NoMatchingAlt => write!(f, "no matching case alternative"),
76            EvalError::InfiniteLoop(id) => write!(f, "infinite loop: thunk {} forced itself", id.0),
77            EvalError::UnsupportedPrimOp(op) => write!(f, "unsupported primop: {:?}", op),
78            EvalError::HeapExhausted => write!(f, "heap exhausted"),
79            EvalError::NotAFunction => write!(f, "application of non-function value"),
80            EvalError::UnboundJoin(id) => write!(f, "jump to unbound join point: j_{}", id.0),
81        }
82    }
83}
84
85impl std::error::Error for EvalError {}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_error_display() {
93        let errs = vec![
94            EvalError::UnboundVar(VarId(42)),
95            EvalError::ArityMismatch {
96                context: "arguments",
97                expected: 2,
98                got: 1,
99            },
100            EvalError::TypeMismatch {
101                expected: "Int#",
102                got: ValueKind::Literal("Char#"),
103            },
104            EvalError::NoMatchingAlt,
105            EvalError::InfiniteLoop(ThunkId(0)),
106            EvalError::UnsupportedPrimOp(PrimOpKind::IntAdd),
107            EvalError::HeapExhausted,
108            EvalError::NotAFunction,
109            EvalError::UnboundJoin(JoinId(7)),
110        ];
111
112        for err in errs {
113            let s = format!("{}", err);
114            assert!(!s.is_empty(), "Display for {:?} should not be empty", err);
115        }
116    }
117}