sea_core/parser/
error.rs

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    // E500-E599: Namespace and module errors
48    /// E500: Referenced namespace does not exist
49    NamespaceNotFound {
50        namespace: String,
51        line: usize,
52        column: usize,
53        suggestion: Option<String>,
54    },
55    /// E503: Referenced module file could not be found
56    ModuleNotFound {
57        module_path: String,
58        line: usize,
59        column: usize,
60    },
61    /// E504: Imported symbol is not exported by the target module
62    SymbolNotExported {
63        symbol: String,
64        module: String,
65        line: usize,
66        column: usize,
67        available_exports: Vec<String>,
68    },
69    /// E505: Circular dependency detected between modules
70    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    /// Creates an UndefinedEntity error without location information (uses 0:0)
106    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    /// Creates an UndefinedResource error without location information (uses 0:0)
119    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    /// Creates a DuplicateDeclaration error without location information (uses 0:0)
140    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    /// E500: Namespace not found error
152    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    /// E503: Module not found error
167    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    /// E504: Symbol not exported error
176    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    /// E505: Circular dependency error
193    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}