lemma/
error.rs

1use crate::ast::Span;
2use std::fmt;
3use std::sync::Arc;
4
5/// Detailed error information with source location
6#[derive(Debug, Clone)]
7pub struct ErrorDetails {
8    pub message: String,
9    pub span: Span,
10    pub source_id: String,
11    pub source_text: Arc<str>,
12    pub doc_name: String,
13    pub doc_start_line: usize,
14    pub suggestion: Option<String>,
15}
16
17/// Error types for the Lemma system with source location tracking
18#[derive(Debug, Clone)]
19pub enum LemmaError {
20    /// Parse error with source location
21    Parse(Box<ErrorDetails>),
22
23    /// Semantic validation error with source location
24    Semantic(Box<ErrorDetails>),
25
26    /// Runtime error during evaluation with source location
27    Runtime(Box<ErrorDetails>),
28
29    /// Engine error without specific source location
30    Engine(String),
31
32    /// Circular dependency error
33    CircularDependency(String),
34
35    /// Resource limit exceeded
36    ResourceLimitExceeded {
37        limit_name: String,
38        limit_value: String,
39        actual_value: String,
40        suggestion: String,
41    },
42
43    /// Multiple errors collected together
44    MultipleErrors(Vec<LemmaError>),
45}
46
47impl LemmaError {
48    /// Create a parse error with source information
49    pub fn parse(
50        message: impl Into<String>,
51        span: Span,
52        source_id: impl Into<String>,
53        source_text: Arc<str>,
54        doc_name: impl Into<String>,
55        doc_start_line: usize,
56    ) -> Self {
57        Self::Parse(Box::new(ErrorDetails {
58            message: message.into(),
59            span,
60            source_id: source_id.into(),
61            source_text,
62            doc_name: doc_name.into(),
63            doc_start_line,
64            suggestion: None,
65        }))
66    }
67
68    /// Create a parse error with suggestion
69    pub fn parse_with_suggestion(
70        message: impl Into<String>,
71        span: Span,
72        source_id: impl Into<String>,
73        source_text: Arc<str>,
74        doc_name: impl Into<String>,
75        doc_start_line: usize,
76        suggestion: impl Into<String>,
77    ) -> Self {
78        Self::Parse(Box::new(ErrorDetails {
79            message: message.into(),
80            span,
81            source_id: source_id.into(),
82            source_text,
83            doc_name: doc_name.into(),
84            doc_start_line,
85            suggestion: Some(suggestion.into()),
86        }))
87    }
88
89    /// Create a semantic error with source information
90    pub fn semantic(
91        message: impl Into<String>,
92        span: Span,
93        source_id: impl Into<String>,
94        source_text: Arc<str>,
95        doc_name: impl Into<String>,
96        doc_start_line: usize,
97    ) -> Self {
98        Self::Semantic(Box::new(ErrorDetails {
99            message: message.into(),
100            span,
101            source_id: source_id.into(),
102            source_text,
103            doc_name: doc_name.into(),
104            doc_start_line,
105            suggestion: None,
106        }))
107    }
108
109    /// Create a semantic error with suggestion
110    pub fn semantic_with_suggestion(
111        message: impl Into<String>,
112        span: Span,
113        source_id: impl Into<String>,
114        source_text: Arc<str>,
115        doc_name: impl Into<String>,
116        doc_start_line: usize,
117        suggestion: impl Into<String>,
118    ) -> Self {
119        Self::Semantic(Box::new(ErrorDetails {
120            message: message.into(),
121            span,
122            source_id: source_id.into(),
123            source_text,
124            doc_name: doc_name.into(),
125            doc_start_line,
126            suggestion: Some(suggestion.into()),
127        }))
128    }
129}
130
131impl fmt::Display for LemmaError {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        match self {
134            LemmaError::Parse(details) => {
135                write!(f, "Parse error: {}", details.message)?;
136                if let Some(suggestion) = &details.suggestion {
137                    write!(f, " (suggestion: {})", suggestion)?;
138                }
139                write!(
140                    f,
141                    " at {}:{}:{}",
142                    details.source_id, details.span.line, details.span.col
143                )
144            }
145            LemmaError::Semantic(details) => {
146                write!(f, "Semantic error: {}", details.message)?;
147                if let Some(suggestion) = &details.suggestion {
148                    write!(f, " (suggestion: {})", suggestion)?;
149                }
150                write!(
151                    f,
152                    " at {}:{}:{}",
153                    details.source_id, details.span.line, details.span.col
154                )
155            }
156            LemmaError::Runtime(details) => {
157                write!(f, "Runtime error: {}", details.message)?;
158                if let Some(suggestion) = &details.suggestion {
159                    write!(f, " (suggestion: {})", suggestion)?;
160                }
161                write!(
162                    f,
163                    " at {}:{}:{}",
164                    details.source_id, details.span.line, details.span.col
165                )
166            }
167            LemmaError::Engine(msg) => write!(f, "Engine error: {}", msg),
168            LemmaError::CircularDependency(msg) => write!(f, "Circular dependency: {}", msg),
169            LemmaError::ResourceLimitExceeded {
170                limit_name,
171                limit_value,
172                actual_value,
173                suggestion,
174            } => {
175                write!(
176                    f,
177                    "Resource limit exceeded: {} (limit: {}, actual: {}). {}",
178                    limit_name, limit_value, actual_value, suggestion
179                )
180            }
181            LemmaError::MultipleErrors(errors) => {
182                writeln!(f, "Multiple errors:")?;
183                for (i, error) in errors.iter().enumerate() {
184                    write!(f, "  {}. {}", i + 1, error)?;
185                    if i < errors.len() - 1 {
186                        writeln!(f)?;
187                    }
188                }
189                Ok(())
190            }
191        }
192    }
193}
194
195impl std::error::Error for LemmaError {}
196
197impl From<std::fmt::Error> for LemmaError {
198    fn from(err: std::fmt::Error) -> Self {
199        LemmaError::Engine(format!("Format error: {}", err))
200    }
201}