turing_lib/
warnings.rs

1use std::fmt::Display;
2
3use log::error;
4use pest::{iterators::Pair, Span};
5
6use crate::Rule;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum CompilerWarning {
10    /// Warning for when an instruction overwrites another
11    StateOverwrite {
12        position: ErrorPosition,
13        /// The state that is being overwritten
14        state: String,
15        value_from: bool,
16    },
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum CompilerError {
21    /// A generic syntax error
22    SyntaxError {
23        position: ErrorPosition,
24        /// The error message
25        message: String,
26        /// The line of code that caused the error
27        code: String,
28        expected: Rule,
29        found: Option<Rule>,
30    },
31
32    /// An error when parsing the file rule
33    FileRuleError {
34        error: Box<pest::error::Error<Rule>>,
35    },
36}
37
38impl CompilerError {
39    /// Log the error to the console
40    /// with the format `Syntax error At position {position}: {message} - Expected {expected:?}, got {found:?}` or `Syntax error: {error}` if the error is a `FileRuleError`
41    pub fn log_error(&self) {
42        match self {
43            CompilerError::SyntaxError {
44                position,
45                message,
46                expected,
47                found,
48                ..
49            } => {
50                error!(
51                    "Syntax error At position {position}: {message} - Expected {expected:?}, got {:?}",
52                    found.unwrap_or(Rule::EOI)
53                );
54            }
55            CompilerError::FileRuleError { error, .. } => {
56                error!("Syntax error: {}", error);
57            }
58        }
59    }
60
61    /// Get the expected message. If the error is a `FileRuleError`, the message will be extracted from `pest::error::Error`, otherwise it will be `Expected {expected:?}, got {found:?}`
62    pub fn get_message_expected(&self) -> String {
63        match &self {
64            CompilerError::SyntaxError {
65                expected, found, ..
66            } => format!("Expected {:?}, found {:?}", expected, found),
67            CompilerError::FileRuleError { error } => String::from(error.variant.message()),
68        }
69    }
70
71    /// Get the code that caused the error. If the error is a `FileRuleError`, the code will be extracted from `pest::error::Error`
72    pub fn code(&self) -> String {
73        match self {
74            CompilerError::SyntaxError { code, .. } => code.clone(),
75            CompilerError::FileRuleError { error, .. } => String::from(error.line()),
76        }
77    }
78
79    /// Get the error message
80    pub fn message(&self) -> String {
81        match self {
82            CompilerError::SyntaxError { message, .. } => String::from(message),
83            CompilerError::FileRuleError { error, .. } => error.variant.message().to_string(),
84        }
85    }
86
87    /// Get the line of the error. If the error is a `FileRuleError`, the line will be `0`
88    pub fn line(&self) -> usize {
89        match self {
90            CompilerError::SyntaxError { position, .. } => position.start.0,
91            CompilerError::FileRuleError { .. } => 0,
92        }
93    }
94
95    /// Get the position of the error. It extracts the position from the `pest::error::Error` if the error is a `FileRuleError`
96    pub fn position(&self) -> ErrorPosition {
97        match self {
98            CompilerError::SyntaxError { position, .. } => *position,
99            CompilerError::FileRuleError { error, .. } => match error.line_col {
100                pest::error::LineColLocation::Pos((line, col)) => ErrorPosition {
101                    start: (line, col),
102                    end: None,
103                },
104                pest::error::LineColLocation::Span((line1, col1), (line2, col2)) => ErrorPosition {
105                    start: (line1 - 1, col1),
106                    end: Some((line2 - 1, col2)),
107                },
108            },
109        }
110    }
111
112    /// Get the expected rule
113    pub fn expected(&self) -> Rule {
114        match self {
115            CompilerError::SyntaxError { expected, .. } => *expected,
116            CompilerError::FileRuleError { error, .. } => match &error.variant {
117                pest::error::ErrorVariant::ParsingError { positives, .. } => {
118                    *positives.first().unwrap()
119                }
120                _ => Rule::EOI,
121            },
122        }
123    }
124
125    /// Get the found rule
126    pub fn found(&self) -> Option<Rule> {
127        match self {
128            CompilerError::SyntaxError { found, .. } => *found,
129            CompilerError::FileRuleError { error, .. } => match &error.variant {
130                pest::error::ErrorVariant::ParsingError { positives, .. } => {
131                    Some(*positives.first().unwrap())
132                }
133                _ => None,
134            },
135        }
136    }
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140/// A struct to store the position of an error
141pub struct ErrorPosition {
142    /// The start position of the error. The first value is the line, the second is the column
143    pub start: (usize, usize),
144
145    /// The end position of the error. The first value is the line, the second is the column.
146    pub end: Option<(usize, usize)>,
147}
148
149impl ErrorPosition {
150    pub fn new(start: (usize, usize), end: Option<(usize, usize)>) -> Self {
151        ErrorPosition { start, end }
152    }
153}
154
155impl Display for ErrorPosition {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        match self.end {
158            Some(end) => write!(
159                f,
160                "{}:{} to {}:{}",
161                self.start.0, self.start.1, end.0, end.1
162            ),
163            None => write!(f, "{}:{}", self.start.0, self.start.1),
164        }
165    }
166}
167
168impl From<pest::error::LineColLocation> for ErrorPosition {
169    /// Convert a `pest::error::LineColLocation` to an `ErrorPosition`
170    fn from(e: pest::error::LineColLocation) -> Self {
171        match e {
172            pest::error::LineColLocation::Pos((line, col)) => ErrorPosition {
173                start: (line - 1, col),
174                end: None,
175            },
176            pest::error::LineColLocation::Span((line1, col1), (line2, col2)) => ErrorPosition {
177                start: (line1 - 1, col1),
178                end: Some((line2 - 1, col2)),
179            },
180        }
181    }
182}
183
184impl From<pest::error::Error<Rule>> for ErrorPosition {
185    /// Convert a `pest::error::Error` to an `ErrorPosition`
186    /// Only a `pest::error::LineColLocation` has an end position, so the end position will be `None` otherwise
187    fn from(e: pest::error::Error<Rule>) -> Self {
188        match e.line_col {
189            pest::error::LineColLocation::Pos((line, col)) => ErrorPosition {
190                start: (line - 1, col),
191                end: None,
192            },
193            pest::error::LineColLocation::Span((line1, col1), (line2, col2)) => ErrorPosition {
194                start: (line1 - 1, col1),
195                end: Some((line2 - 1, col2)),
196            },
197        }
198    }
199}
200
201impl From<Span<'_>> for ErrorPosition {
202    /// Convert a `pest::Span` to an `ErrorPosition`
203    /// This operation on a `pest::Span` is `O(n)`, so you better use pair.line_col() instead if it has no end position
204    fn from(e: Span) -> Self {
205        ErrorPosition {
206            start: (e.start_pos().line_col().0 - 1, e.start_pos().line_col().1),
207            end: Some((e.end_pos().line_col().0 - 1, e.end_pos().line_col().1)),
208        }
209    }
210}
211
212impl From<&Span<'_>> for ErrorPosition {
213    /// Convert a `&pest::Span` to an `ErrorPosition`
214    /// This operation on a `pest::Span` is `O(n)`, so you better use pair.line_col() instead if it has no end position
215    fn from(e: &Span) -> Self {
216        ErrorPosition {
217            start: (e.start_pos().line_col().0 - 1, e.start_pos().line_col().1),
218            end: Some((e.end_pos().line_col().0 - 1, e.end_pos().line_col().1)),
219        }
220    }
221}
222
223impl From<&Pair<'_, Rule>> for ErrorPosition {
224    /// Convert a `pest::Pair` to an `ErrorPosition`.
225    /// Note that a `pest::Pair` has no end position, so the end position will be `None`
226    fn from(e: &Pair<Rule>) -> Self {
227        ErrorPosition {
228            start: (e.line_col().0 - 1, e.line_col().1),
229            end: None,
230        }
231    }
232}
233
234impl From<(usize, usize)> for ErrorPosition {
235    /// Convert a `(usize, usize)` to an `ErrorPosition`.
236    /// Note that a `(usize, usize)` has no end position, so the end position will be `None`
237    fn from(e: (usize, usize)) -> Self {
238        ErrorPosition {
239            start: (e.0 - 1, e.1),
240            end: None,
241        }
242    }
243}