Skip to main content

probl_engine/
error.rs

1//! Runtime errors.
2
3use probl_syntax::{Diagnostic, Span};
4
5/// What kind of problem stopped a run.
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum ErrorKind {
8    /// The program did something the language doesn't allow.
9    Language,
10    /// The program uses a feature that isn't implemented yet.
11    Unsupported,
12    /// The run hit a resource limit, or was cancelled.
13    Limit,
14    /// A bug in Probl.
15    Internal,
16}
17
18/// An error from an operation that doesn't know where in the source it is;
19/// the interpreter attaches the span.
20#[derive(Clone, Debug, PartialEq)]
21pub struct OpError {
22    pub message: String,
23    pub help: Option<String>,
24    pub kind: ErrorKind,
25}
26
27impl OpError {
28    pub fn new(message: impl Into<String>) -> OpError {
29        OpError {
30            message: message.into(),
31            help: None,
32            kind: ErrorKind::Language,
33        }
34    }
35
36    pub fn unsupported(message: impl Into<String>) -> OpError {
37        OpError {
38            kind: ErrorKind::Unsupported,
39            ..OpError::new(message)
40        }
41    }
42
43    pub fn limit(message: impl Into<String>) -> OpError {
44        OpError {
45            kind: ErrorKind::Limit,
46            ..OpError::new(message)
47        }
48    }
49
50    /// A bug in Probl: all the user can do is report it.
51    pub fn internal(message: impl Into<String>) -> OpError {
52        OpError {
53            kind: ErrorKind::Internal,
54            ..OpError::new(message)
55        }
56        .help("this is a bug in Probl; please report it with the program that caused it")
57    }
58
59    pub fn help(mut self, help: impl Into<String>) -> OpError {
60        self.help = Some(help.into());
61        self
62    }
63
64    pub fn at(self, span: Span) -> RuntimeError {
65        RuntimeError {
66            message: self.message,
67            span,
68            notes: Vec::new(),
69            help: self.help,
70            kind: self.kind,
71        }
72    }
73}
74
75pub type OpResult<T> = std::result::Result<T, OpError>;
76
77impl From<probl_number::IntError> for OpError {
78    fn from(error: probl_number::IntError) -> Self {
79        match error {
80            probl_number::IntError::TooLarge => Self::limit(error.to_string()),
81            _ => Self::new(error.to_string()),
82        }
83    }
84}
85
86#[derive(Clone, Debug, PartialEq)]
87pub struct RuntimeError {
88    pub message: String,
89    pub span: Span,
90    pub notes: Vec<String>,
91    pub help: Option<String>,
92    pub kind: ErrorKind,
93}
94
95impl RuntimeError {
96    pub fn new(span: Span, message: impl Into<String>) -> RuntimeError {
97        OpError::new(message).at(span)
98    }
99
100    pub fn limit(span: Span, message: impl Into<String>) -> RuntimeError {
101        OpError::limit(message).at(span)
102    }
103
104    pub fn with_note(mut self, note: impl Into<String>) -> RuntimeError {
105        self.notes.push(note.into());
106        self
107    }
108
109    pub fn with_help(mut self, help: impl Into<String>) -> RuntimeError {
110        self.help = Some(help.into());
111        self
112    }
113
114    pub fn to_diagnostic(&self) -> Diagnostic {
115        let mut d = Diagnostic::error(self.span, &self.message);
116        d.notes = self.notes.clone();
117        d.help = self.help.clone();
118        d
119    }
120}
121
122pub type Result<T> = std::result::Result<T, RuntimeError>;