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
18pub use probl_sema::ir::Fault;
19
20/// An error from an operation that doesn't know where in the source it is;
21/// the interpreter attaches the span.
22#[derive(Clone, Debug, PartialEq)]
23pub struct OpError {
24    pub message: String,
25    pub help: Option<String>,
26    pub kind: ErrorKind,
27    pub fault: Option<Fault>,
28}
29
30impl OpError {
31    pub fn new(message: impl Into<String>) -> OpError {
32        OpError {
33            message: message.into(),
34            help: None,
35            kind: ErrorKind::Language,
36            fault: None,
37        }
38    }
39
40    /// A language error that ends only its world in partial mode.
41    pub fn fault(fault: Fault, message: impl Into<String>) -> OpError {
42        OpError {
43            fault: Some(fault),
44            ..OpError::new(message)
45        }
46    }
47
48    /// The same error, as a fault.
49    pub fn as_fault(mut self, fault: Fault) -> OpError {
50        if self.kind == ErrorKind::Language {
51            self.fault = Some(fault);
52        }
53        self
54    }
55
56    pub fn unsupported(message: impl Into<String>) -> OpError {
57        OpError {
58            kind: ErrorKind::Unsupported,
59            ..OpError::new(message)
60        }
61    }
62
63    pub fn limit(message: impl Into<String>) -> OpError {
64        OpError {
65            kind: ErrorKind::Limit,
66            ..OpError::new(message)
67        }
68    }
69
70    /// A bug in Probl: all the user can do is report it.
71    pub fn internal(message: impl Into<String>) -> OpError {
72        OpError {
73            kind: ErrorKind::Internal,
74            ..OpError::new(message)
75        }
76        .help("this is a bug in Probl; please report it with the program that caused it")
77    }
78
79    pub fn help(mut self, help: impl Into<String>) -> OpError {
80        self.help = Some(help.into());
81        self
82    }
83
84    pub fn at(self, span: Span) -> RuntimeError {
85        RuntimeError {
86            message: self.message,
87            span,
88            notes: Vec::new(),
89            help: self.help,
90            kind: self.kind,
91            fault: self.fault,
92        }
93    }
94}
95
96pub type OpResult<T> = std::result::Result<T, OpError>;
97
98impl From<probl_number::IntError> for OpError {
99    fn from(error: probl_number::IntError) -> Self {
100        match error {
101            probl_number::IntError::TooLarge => Self::limit(error.to_string()),
102            probl_number::IntError::DivisionByZero => Self::fault(Fault::DivisionByZero, error.to_string()),
103            _ => Self::new(error.to_string()),
104        }
105    }
106}
107
108#[derive(Clone, Debug, PartialEq)]
109pub struct RuntimeError {
110    pub message: String,
111    pub span: Span,
112    pub notes: Vec<String>,
113    pub help: Option<String>,
114    pub kind: ErrorKind,
115    pub fault: Option<Fault>,
116}
117
118impl RuntimeError {
119    pub fn new(span: Span, message: impl Into<String>) -> RuntimeError {
120        OpError::new(message).at(span)
121    }
122
123    pub fn limit(span: Span, message: impl Into<String>) -> RuntimeError {
124        OpError::limit(message).at(span)
125    }
126
127    pub fn with_note(mut self, note: impl Into<String>) -> RuntimeError {
128        self.notes.push(note.into());
129        self
130    }
131
132    pub fn with_help(mut self, help: impl Into<String>) -> RuntimeError {
133        self.help = Some(help.into());
134        self
135    }
136
137    /// The same error, as a fault.
138    pub fn as_fault(mut self, fault: Fault) -> RuntimeError {
139        if self.kind == ErrorKind::Language {
140            self.fault = Some(fault);
141        }
142        self
143    }
144
145    pub fn to_diagnostic(&self) -> Diagnostic {
146        let mut d = Diagnostic::error(self.span, &self.message);
147        d.notes = self.notes.clone();
148        d.help = self.help.clone();
149        d
150    }
151}
152
153pub type Result<T> = std::result::Result<T, RuntimeError>;