1
2use pyo3::PyErr;
3use thiserror::Error as E;
4
5use crate::{BinOp, BoolOp, Compare, Expr, ExprType, StatementType, UnaryOp, PositionInfo};
6
7#[derive(Debug, Clone, PartialEq)]
9pub struct SourceLocation {
10 pub filename: String,
11 pub line: Option<usize>,
12 pub column: Option<usize>,
13 pub end_line: Option<usize>,
14 pub end_column: Option<usize>,
15}
16
17impl SourceLocation {
18 pub fn new(filename: impl Into<String>) -> Self {
19 Self {
20 filename: filename.into(),
21 line: None,
22 column: None,
23 end_line: None,
24 end_column: None,
25 }
26 }
27
28 pub fn with_position(
29 filename: impl Into<String>,
30 line: Option<usize>,
31 column: Option<usize>,
32 ) -> Self {
33 Self {
34 filename: filename.into(),
35 line,
36 column,
37 end_line: None,
38 end_column: None,
39 }
40 }
41
42 pub fn with_span(
43 filename: impl Into<String>,
44 line: Option<usize>,
45 column: Option<usize>,
46 end_line: Option<usize>,
47 end_column: Option<usize>,
48 ) -> Self {
49 Self {
50 filename: filename.into(),
51 line,
52 column,
53 end_line,
54 end_column,
55 }
56 }
57
58 pub fn from_node(filename: impl Into<String>, node: &dyn PositionInfo) -> Self {
60 let (line, column, end_line, end_column) = node.position_info();
61 Self {
62 filename: filename.into(),
63 line,
64 column,
65 end_line,
66 end_column,
67 }
68 }
69}
70
71impl std::fmt::Display for SourceLocation {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 match (self.line, self.column) {
74 (Some(line), Some(col)) => {
75 if let (Some(end_line), Some(end_col)) = (self.end_line, self.end_column) {
76 if line == end_line {
77 write!(f, "{}:{}:{}-{}", self.filename, line, col, end_col)
78 } else {
79 write!(f, "{}:{}:{}-{}:{}", self.filename, line, col, end_line, end_col)
80 }
81 } else {
82 write!(f, "{}:{}:{}", self.filename, line, col)
83 }
84 }
85 (Some(line), None) => write!(f, "{}:{}", self.filename, line),
86 _ => write!(f, "{}", self.filename),
87 }
88 }
89}
90
91#[derive(E, Debug)]
92pub enum Error {
93 #[error("Parsing error at {location}: {message}\nHelp: {help}")]
94 ParseError {
95 location: SourceLocation,
96 message: String,
97 help: String,
98 },
99
100 #[error("Code generation error at {location}: {message}\nHelp: {help}")]
101 CodeGenError {
102 location: SourceLocation,
103 message: String,
104 help: String,
105 },
106
107 #[error("Unsupported feature at {location}: {feature} is not yet implemented\nHelp: {help}")]
108 UnsupportedFeature {
109 location: SourceLocation,
110 feature: String,
111 help: String,
112 },
113
114 #[error("Type error at {location}: {message}\nExpected: {expected}\nFound: {found}\nHelp: {help}")]
115 TypeError {
116 location: SourceLocation,
117 message: String,
118 expected: String,
119 found: String,
120 help: String,
121 },
122
123 #[error("Invalid syntax at {location}: {message}\nHelp: {help}")]
124 SyntaxError {
125 location: SourceLocation,
126 message: String,
127 help: String,
128 },
129
130 #[error("BinOp type not yet implemented: {:?}", .0)]
132 BinOpNotYetImplemented(BinOp),
133
134 #[error("BoolOp type not yet implemented: {:?}", .0)]
135 BoolOpNotYetImplemented(BoolOp),
136
137 #[error("Compare type not yet implemented: {:?}", .0)]
138 CompareNotYetImplemented(Compare),
139
140 #[error("Expr type not yet implemented: {:?}", .0)]
141 ExprNotYetImplemented(Expr),
142 #[error("ExprType type not yet implemented: {:?}", .0)]
143 ExprTypeNotYetImplemented(ExprType),
144
145 #[error("Unknown type {0}")]
146 UnknownType(String),
147
148 #[error("PyO3 Error: {0}")]
149 #[from(PyErr)]
150 Pyo3Error(PyErr),
151
152 #[error("Statement type not yet implemented: {:?}", .0)]
153 StatementNotYetImplemented(StatementType),
154 #[error("UnaryOp type not yet implemented: {:?}", .0)]
155 UnaryOpNotYetImplemented(UnaryOp),
156
157 #[error("Unknown Error: {0}")]
158 #[from(Box<dyn std::error::Error>)]
159 UnknownError(Box<dyn std::error::Error>),
160}
161
162impl From<Error> for PyErr {
163 fn from(err: Error) -> PyErr {
164 match err {
165 Error::ParseError { message, .. }
166 | Error::SyntaxError { message, .. } => {
167 pyo3::exceptions::PySyntaxError::new_err(message)
168 },
169 Error::TypeError { message, .. } => {
170 pyo3::exceptions::PyTypeError::new_err(message)
171 },
172 Error::UnsupportedFeature { feature, .. } => {
173 pyo3::exceptions::PyNotImplementedError::new_err(format!("Unsupported feature: {}", feature))
174 },
175 Error::CodeGenError { message, .. } => {
176 pyo3::exceptions::PyRuntimeError::new_err(message)
177 },
178 Error::Pyo3Error(py_err) => py_err,
179 _ => pyo3::exceptions::PyRuntimeError::new_err(format!("{}", err)),
180 }
181 }
182}
183
184impl Error {
185 pub fn parsing_error(
187 location: SourceLocation,
188 message: impl Into<String>,
189 help: impl Into<String>,
190 ) -> Self {
191 Error::ParseError {
192 location,
193 message: message.into(),
194 help: help.into(),
195 }
196 }
197
198 pub fn codegen_error(
200 location: SourceLocation,
201 message: impl Into<String>,
202 help: impl Into<String>,
203 ) -> Self {
204 Error::CodeGenError {
205 location,
206 message: message.into(),
207 help: help.into(),
208 }
209 }
210
211 pub fn unsupported_feature(
213 location: SourceLocation,
214 feature: impl Into<String>,
215 help: impl Into<String>,
216 ) -> Self {
217 Error::UnsupportedFeature {
218 location,
219 feature: feature.into(),
220 help: help.into(),
221 }
222 }
223
224 pub fn type_error(
226 location: SourceLocation,
227 message: impl Into<String>,
228 expected: impl Into<String>,
229 found: impl Into<String>,
230 help: impl Into<String>,
231 ) -> Self {
232 Error::TypeError {
233 location,
234 message: message.into(),
235 expected: expected.into(),
236 found: found.into(),
237 help: help.into(),
238 }
239 }
240
241 pub fn syntax_error(
243 location: SourceLocation,
244 message: impl Into<String>,
245 help: impl Into<String>,
246 ) -> Self {
247 Error::SyntaxError {
248 location,
249 message: message.into(),
250 help: help.into(),
251 }
252 }
253}
254
255pub type Result<T> = std::result::Result<T, Error>;
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn test_error_display_unknown_type() {
263 let error = Error::UnknownType("SomeUnknownType".to_string());
264 let display = format!("{}", error);
265 assert_eq!(display, "Unknown type SomeUnknownType");
266 }
267
268 #[test]
269 fn test_error_display_unknown_error() {
270 let custom_error: Box<dyn std::error::Error> = Box::new(std::io::Error::new(
271 std::io::ErrorKind::NotFound,
272 "Test error"
273 ));
274 let error = Error::UnknownError(custom_error);
275 let display = format!("{}", error);
276 assert!(display.contains("Unknown Error"));
277 assert!(display.contains("Test error"));
278 }
279
280 #[test]
281 fn test_error_debug() {
282 let error = Error::UnknownType("TestType".to_string());
283 let debug = format!("{:?}", error);
284 assert!(debug.contains("UnknownType"));
285 assert!(debug.contains("TestType"));
286 }
287
288 #[test]
289 fn test_result_type_ok() {
290 let result: Result<i32> = Ok(42);
291 assert!(result.is_ok());
292 assert_eq!(result.unwrap(), 42);
293 }
294
295 #[test]
296 fn test_result_type_err() {
297 let result: Result<i32> = Err(Error::UnknownType("TestError".to_string()));
298 assert!(result.is_err());
299
300 match result {
301 Err(Error::UnknownType(msg)) => assert_eq!(msg, "TestError"),
302 _ => panic!("Expected UnknownType error"),
303 }
304 }
305
306 #[test]
307 fn test_error_chaining() {
308 let result: Result<i32> = Err(Error::UnknownType("ChainTest".to_string()));
309
310 let chained_result = result.map_err(|e| Error::UnknownError(Box::new(e)));
311
312 assert!(chained_result.is_err());
313 match chained_result {
314 Err(Error::UnknownError(_)) => (),
315 _ => panic!("Expected chained error"),
316 }
317 }
318}