1use crate::parser::Rule;
2use pest::error::Error as PestError;
3use std::fmt;
4
5pub type ParseResult<T> = Result<T, ParseError>;
6
7#[derive(Debug, Clone)]
8pub enum ParseError {
9 SyntaxError {
10 message: String,
11 line: usize,
12 column: usize,
13 },
14 UnsupportedExpression {
15 kind: String,
16 span: Option<String>,
17 },
18 GrammarError(String),
19 UndefinedEntity {
20 name: String,
21 line: usize,
22 column: usize,
23 },
24 UndefinedResource {
25 name: String,
26 line: usize,
27 column: usize,
28 },
29 UndefinedVariable {
30 name: String,
31 line: usize,
32 column: usize,
33 },
34 DuplicateDeclaration {
35 name: String,
36 line: usize,
37 column: usize,
38 },
39 TypeError {
40 message: String,
41 location: String,
42 },
43 InvalidExpression(String),
44 InvalidQuantity(String),
45 Validation(String),
46
47 NamespaceNotFound {
50 namespace: String,
51 line: usize,
52 column: usize,
53 suggestion: Option<String>,
54 },
55 ModuleNotFound {
57 module_path: String,
58 line: usize,
59 column: usize,
60 },
61 SymbolNotExported {
63 symbol: String,
64 module: String,
65 line: usize,
66 column: usize,
67 available_exports: Vec<String>,
68 },
69 CircularDependency {
71 cycle: Vec<String>,
72 },
73}
74
75impl ParseError {
76 pub fn from_pest(err: PestError<Rule>) -> Self {
77 let (line, column) = match err.line_col {
78 pest::error::LineColLocation::Pos((l, c)) => (l, c),
79 pest::error::LineColLocation::Span((l, c), _) => (l, c),
80 };
81
82 ParseError::SyntaxError {
83 message: err.variant.message().to_string(),
84 line,
85 column,
86 }
87 }
88
89 pub fn syntax_error(message: impl Into<String>, line: usize, column: usize) -> Self {
90 ParseError::SyntaxError {
91 message: message.into(),
92 line,
93 column,
94 }
95 }
96
97 pub fn undefined_entity(name: impl Into<String>, line: usize, column: usize) -> Self {
98 ParseError::UndefinedEntity {
99 name: name.into(),
100 line,
101 column,
102 }
103 }
104
105 pub fn undefined_entity_no_loc(name: impl Into<String>) -> Self {
107 Self::undefined_entity(name, 0, 0)
108 }
109
110 pub fn undefined_resource(name: impl Into<String>, line: usize, column: usize) -> Self {
111 ParseError::UndefinedResource {
112 name: name.into(),
113 line,
114 column,
115 }
116 }
117
118 pub fn undefined_resource_no_loc(name: impl Into<String>) -> Self {
120 Self::undefined_resource(name, 0, 0)
121 }
122
123 pub fn undefined_variable(name: impl Into<String>, line: usize, column: usize) -> Self {
124 ParseError::UndefinedVariable {
125 name: name.into(),
126 line,
127 column,
128 }
129 }
130
131 pub fn duplicate_declaration(name: impl Into<String>, line: usize, column: usize) -> Self {
132 ParseError::DuplicateDeclaration {
133 name: name.into(),
134 line,
135 column,
136 }
137 }
138
139 pub fn duplicate_declaration_no_loc(name: impl Into<String>) -> Self {
141 Self::duplicate_declaration(name, 0, 0)
142 }
143
144 pub fn type_error(message: impl Into<String>, location: impl Into<String>) -> Self {
145 ParseError::TypeError {
146 message: message.into(),
147 location: location.into(),
148 }
149 }
150
151 pub fn namespace_not_found(
153 namespace: impl Into<String>,
154 line: usize,
155 column: usize,
156 suggestion: Option<String>,
157 ) -> Self {
158 ParseError::NamespaceNotFound {
159 namespace: namespace.into(),
160 line,
161 column,
162 suggestion,
163 }
164 }
165
166 pub fn module_not_found(module_path: impl Into<String>, line: usize, column: usize) -> Self {
168 ParseError::ModuleNotFound {
169 module_path: module_path.into(),
170 line,
171 column,
172 }
173 }
174
175 pub fn symbol_not_exported(
177 symbol: impl Into<String>,
178 module: impl Into<String>,
179 line: usize,
180 column: usize,
181 available_exports: Vec<String>,
182 ) -> Self {
183 ParseError::SymbolNotExported {
184 symbol: symbol.into(),
185 module: module.into(),
186 line,
187 column,
188 available_exports,
189 }
190 }
191
192 pub fn circular_dependency(cycle: Vec<String>) -> Self {
194 ParseError::CircularDependency { cycle }
195 }
196}
197
198impl fmt::Display for ParseError {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 ParseError::SyntaxError {
202 message,
203 line,
204 column,
205 } => {
206 write!(f, "Syntax error at {}:{}: {}", line, column, message)
207 }
208 ParseError::UnsupportedExpression { kind, span } => {
209 if let Some(span) = span {
210 write!(f, "Unsupported expression '{}' at {}", kind, span)
211 } else {
212 write!(f, "Unsupported expression '{}'", kind)
213 }
214 }
215 ParseError::GrammarError(msg) => write!(f, "Grammar error: {}", msg),
216 ParseError::UndefinedEntity { name, line, column } => {
217 write!(f, "Undefined entity: {} at {}:{}", name, line, column)
218 }
219 ParseError::UndefinedResource { name, line, column } => {
220 write!(f, "Undefined resource: {} at {}:{}", name, line, column)
221 }
222 ParseError::UndefinedVariable { name, line, column } => {
223 write!(f, "Undefined variable: {} at {}:{}", name, line, column)
224 }
225 ParseError::DuplicateDeclaration { name, line, column } => {
226 write!(f, "Duplicate declaration: {} at {}:{}", name, line, column)
227 }
228 ParseError::TypeError { message, location } => {
229 write!(f, "Type error at {}: {}", location, message)
230 }
231 ParseError::InvalidExpression(msg) => write!(f, "Invalid expression: {}", msg),
232 ParseError::InvalidQuantity(msg) => write!(f, "Invalid quantity: {}", msg),
233 ParseError::Validation(msg) => write!(f, "Validation error: {}", msg),
234 ParseError::NamespaceNotFound {
235 namespace,
236 line,
237 column,
238 suggestion,
239 } => {
240 write!(
241 f,
242 "Namespace '{}' not found at {}:{}",
243 namespace, line, column
244 )?;
245 if let Some(sug) = suggestion {
246 write!(f, ". Did you mean '{}'?", sug)?;
247 }
248 Ok(())
249 }
250 ParseError::ModuleNotFound {
251 module_path,
252 line,
253 column,
254 } => {
255 write!(
256 f,
257 "Module '{}' not found at {}:{}",
258 module_path, line, column
259 )
260 }
261 ParseError::SymbolNotExported {
262 symbol,
263 module,
264 line,
265 column,
266 available_exports,
267 } => {
268 write!(
269 f,
270 "Symbol '{}' is not exported by module '{}' at {}:{}",
271 symbol, module, line, column
272 )?;
273 if !available_exports.is_empty() {
274 write!(f, ". Available exports: {}", available_exports.join(", "))?;
275 }
276 Ok(())
277 }
278 ParseError::CircularDependency { cycle } => {
279 write!(f, "Circular dependency detected: {}", cycle.join(" -> "))
280 }
281 }
282 }
283}
284
285impl std::error::Error for ParseError {}
286
287impl From<PestError<Rule>> for ParseError {
288 fn from(err: PestError<Rule>) -> Self {
289 ParseError::from_pest(err)
290 }
291}