1use crate::value::ThunkId;
2use tidepool_repr::{JoinId, PrimOpKind, VarId};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum ValueKind {
7 Literal(&'static str), Constructor,
9 Closure,
10 Thunk,
11 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#[derive(Debug, Clone)]
29pub enum EvalError {
30 UnboundVar(VarId),
32 ArityMismatch {
34 context: &'static str, expected: usize,
36 got: usize,
37 },
38 TypeMismatch {
40 expected: &'static str,
41 got: ValueKind,
42 },
43 NoMatchingAlt,
45 InfiniteLoop(ThunkId),
47 UnsupportedPrimOp(PrimOpKind),
49 HeapExhausted,
51 NotAFunction,
53 UnboundJoin(JoinId),
55 UserError,
57 Undefined,
59 DepthLimit,
61 InternalError(String),
63}
64
65impl std::fmt::Display for EvalError {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 EvalError::UnboundVar(v) => write!(f, "unbound variable: v_{}", v.0),
69 EvalError::ArityMismatch {
70 context,
71 expected,
72 got,
73 } => {
74 write!(
75 f,
76 "arity mismatch: expected {} {}, got {}",
77 expected, context, got
78 )
79 }
80 EvalError::TypeMismatch { expected, got } => {
81 write!(f, "type mismatch: expected {}, got {}", expected, got)
82 }
83 EvalError::NoMatchingAlt => write!(f, "no matching case alternative"),
84 EvalError::InfiniteLoop(id) => write!(f, "infinite loop: thunk {} forced itself", id.0),
85 EvalError::UnsupportedPrimOp(op) => write!(f, "unsupported primop: {:?}", op),
86 EvalError::HeapExhausted => write!(f, "heap exhausted"),
87 EvalError::NotAFunction => write!(f, "application of non-function value"),
88 EvalError::UnboundJoin(id) => write!(f, "jump to unbound join point: j_{}", id.0),
89 EvalError::UserError => write!(f, "Haskell error called"),
90 EvalError::Undefined => write!(f, "Haskell undefined forced"),
91 EvalError::DepthLimit => write!(f, "recursion depth limit exceeded"),
92 EvalError::InternalError(msg) => write!(f, "internal error: {}", msg),
93 }
94 }
95}
96
97impl std::error::Error for EvalError {}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_error_display() {
105 let errs = vec![
106 EvalError::UnboundVar(VarId(42)),
107 EvalError::ArityMismatch {
108 context: "arguments",
109 expected: 2,
110 got: 1,
111 },
112 EvalError::TypeMismatch {
113 expected: "Int#",
114 got: ValueKind::Literal("Char#"),
115 },
116 EvalError::NoMatchingAlt,
117 EvalError::InfiniteLoop(ThunkId(0)),
118 EvalError::UnsupportedPrimOp(PrimOpKind::IntAdd),
119 EvalError::HeapExhausted,
120 EvalError::NotAFunction,
121 EvalError::UnboundJoin(JoinId(7)),
122 ];
123
124 for err in errs {
125 let s = format!("{}", err);
126 assert!(!s.is_empty(), "Display for {:?} should not be empty", err);
127 }
128 }
129}