1#![allow(dead_code)]
6
7use anyhow::Result;
8use std::fmt;
9
10#[derive(Debug)]
12pub struct ErrorWithSuggestions {
13 pub error: String,
14 pub suggestions: Vec<String>,
15 pub examples: Vec<String>,
16}
17
18impl fmt::Display for ErrorWithSuggestions {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 writeln!(f, "Error: {}", self.error)?;
21
22 if !self.suggestions.is_empty() {
23 writeln!(f, "\nSuggestions:")?;
24 for suggestion in &self.suggestions {
25 writeln!(f, " • {}", suggestion)?;
26 }
27 }
28
29 if !self.examples.is_empty() {
30 writeln!(f, "\nExamples:")?;
31 for example in &self.examples {
32 writeln!(f, " {}", example)?;
33 }
34 }
35
36 Ok(())
37 }
38}
39
40pub fn enhance_compilation_error(error: &str) -> ErrorWithSuggestions {
42 let error_lower = error.to_lowercase();
43
44 if error_lower.contains("free variable") || error_lower.contains("unbound") {
46 ErrorWithSuggestions {
47 error: error.to_string(),
48 suggestions: vec![
49 "Add a quantifier (EXISTS or FORALL) for unbound variables".to_string(),
50 "Define a domain for the variable using --domains".to_string(),
51 "Check variable names for typos".to_string(),
52 ],
53 examples: vec![
54 "EXISTS x IN Person. knows(x, alice)".to_string(),
55 "FORALL x IN Person. mortal(x)".to_string(),
56 "tensorlogic \"expr\" --domains Person:100".to_string(),
57 ],
58 }
59 } else if error_lower.contains("arity") || error_lower.contains("argument") {
60 ErrorWithSuggestions {
61 error: error.to_string(),
62 suggestions: vec![
63 "Check the number of arguments in predicate calls".to_string(),
64 "Verify predicate signatures match their usage".to_string(),
65 "Ensure all arguments are properly specified".to_string(),
66 ],
67 examples: vec![
68 "knows(x, y) # Binary predicate".to_string(),
69 "person(x) # Unary predicate".to_string(),
70 "located(x, y, z) # Ternary predicate".to_string(),
71 ],
72 }
73 } else if error_lower.contains("type") {
74 ErrorWithSuggestions {
75 error: error.to_string(),
76 suggestions: vec![
77 "Check that operations use compatible types".to_string(),
78 "Verify arithmetic is only used with numeric expressions".to_string(),
79 "Use appropriate comparison operators for the data type".to_string(),
80 ],
81 examples: vec![
82 "age(x) + 10 # Arithmetic on numeric values".to_string(),
83 "name(x) = \"alice\" # String comparison".to_string(),
84 "score(x) > threshold # Numeric comparison".to_string(),
85 ],
86 }
87 } else if error_lower.contains("syntax") || error_lower.contains("parse") {
88 ErrorWithSuggestions {
89 error: error.to_string(),
90 suggestions: vec![
91 "Check for unmatched parentheses".to_string(),
92 "Verify operator spelling (AND, OR, NOT, IMPLIES)".to_string(),
93 "Use quotes for string literals".to_string(),
94 "Ensure quantifiers have IN domain clause".to_string(),
95 ],
96 examples: vec![
97 "(p AND q) OR r # Grouped expression".to_string(),
98 "EXISTS x IN D. p(x) # Quantifier with domain".to_string(),
99 "knows(\"alice\", bob) # String literal in quotes".to_string(),
100 ],
101 }
102 } else if error_lower.contains("domain") {
103 ErrorWithSuggestions {
104 error: error.to_string(),
105 suggestions: vec![
106 "Define the domain using --domains option".to_string(),
107 "Check domain name spelling in quantifiers".to_string(),
108 "Ensure domain size is positive".to_string(),
109 ],
110 examples: vec![
111 "tensorlogic expr --domains Person:100".to_string(),
112 "tensorlogic expr --domains User:1000 --domains Item:5000".to_string(),
113 "EXISTS x IN Person. knows(x, y) # Domain must be defined".to_string(),
114 ],
115 }
116 } else if error_lower.contains("validation") {
117 ErrorWithSuggestions {
118 error: error.to_string(),
119 suggestions: vec![
120 "Check that all inputs and outputs are properly connected".to_string(),
121 "Verify that the graph structure is valid".to_string(),
122 "Ensure all tensors have producers if required".to_string(),
123 "Use --debug to see detailed graph structure".to_string(),
124 ],
125 examples: vec![
126 "tensorlogic expr --debug # Show detailed compilation info".to_string(),
127 "tensorlogic expr --no-validate # Skip validation".to_string(),
128 ],
129 }
130 } else if error_lower.contains("strategy") {
131 ErrorWithSuggestions {
132 error: error.to_string(),
133 suggestions: vec![
134 "Use one of the 6 valid compilation strategies".to_string(),
135 "Check strategy name spelling".to_string(),
136 ],
137 examples: vec![
138 "--strategy soft_differentiable # For neural training".to_string(),
139 "--strategy hard_boolean # For discrete logic".to_string(),
140 "--strategy fuzzy_godel # For Gödel fuzzy logic".to_string(),
141 "--strategy fuzzy_product # For product fuzzy logic".to_string(),
142 "--strategy fuzzy_lukasiewicz # For Łukasiewicz logic".to_string(),
143 "--strategy probabilistic # For probabilities".to_string(),
144 ],
145 }
146 } else {
147 ErrorWithSuggestions {
149 error: error.to_string(),
150 suggestions: vec![
151 "Use --debug flag to see detailed error information".to_string(),
152 "Check the expression syntax and formatting".to_string(),
153 "Consult the documentation for correct usage".to_string(),
154 ],
155 examples: vec![
156 "tensorlogic expr --debug".to_string(),
157 "tensorlogic --help".to_string(),
158 ],
159 }
160 }
161}
162
163pub fn enhance_file_error(path: &str, error: &str) -> ErrorWithSuggestions {
165 let error_lower = error.to_lowercase();
166
167 if error_lower.contains("not found") || error_lower.contains("no such") {
168 ErrorWithSuggestions {
169 error: format!("File not found: {}", path),
170 suggestions: vec![
171 "Check the file path spelling and location".to_string(),
172 "Use absolute path or relative path from current directory".to_string(),
173 "Verify the file exists using ls or find command".to_string(),
174 ],
175 examples: vec![
176 format!("ls {}", path),
177 format!(
178 "find . -name \"{}\"",
179 path.rsplit('/').next().unwrap_or(path)
180 ),
181 ],
182 }
183 } else if error_lower.contains("permission") {
184 ErrorWithSuggestions {
185 error: format!("Permission denied: {}", path),
186 suggestions: vec![
187 "Check file permissions".to_string(),
188 "Ensure you have read access to the file".to_string(),
189 "Try using sudo if appropriate".to_string(),
190 ],
191 examples: vec![format!("ls -l {}", path), format!("chmod +r {}", path)],
192 }
193 } else {
194 ErrorWithSuggestions {
195 error: format!("File error for {}: {}", path, error),
196 suggestions: vec![
197 "Check file permissions and accessibility".to_string(),
198 "Verify disk space is available".to_string(),
199 ],
200 examples: vec!["df -h".to_string()],
201 }
202 }
203}
204
205pub fn suggest_for_cli_args(error: &str) -> Result<()> {
207 let error_lower = error.to_lowercase();
208
209 if error_lower.contains("unexpected argument") || error_lower.contains("found argument") {
210 let suggestions = ErrorWithSuggestions {
211 error: error.to_string(),
212 suggestions: vec![
213 "Check argument spelling and dashes (- vs --)".to_string(),
214 "Use --help to see all available arguments".to_string(),
215 "Some arguments require values (e.g., --domains Person:100)".to_string(),
216 ],
217 examples: vec![
218 "tensorlogic --help".to_string(),
219 "tensorlogic expr --domains Person:100 # Correct".to_string(),
220 "tensorlogic expr --domain Person:100 # Wrong (should be --domains)".to_string(),
221 ],
222 };
223
224 Err(anyhow::anyhow!("{}", suggestions))
225 } else if error_lower.contains("required") {
226 let suggestions = ErrorWithSuggestions {
227 error: error.to_string(),
228 suggestions: vec![
229 "Provide the required input expression or file".to_string(),
230 "Use a subcommand (repl, batch, etc.) or provide input".to_string(),
231 ],
232 examples: vec![
233 "tensorlogic \"knows(x, y)\" # Direct expression".to_string(),
234 "tensorlogic file.tl # From file".to_string(),
235 "tensorlogic repl # Interactive mode".to_string(),
236 ],
237 };
238
239 Err(anyhow::anyhow!("{}", suggestions))
240 } else {
241 Err(anyhow::anyhow!("{}", error))
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_enhance_free_variable_error() {
251 let error = enhance_compilation_error("free variable 'x' found in expression");
252
253 assert!(error.error.contains("free variable"));
254 assert!(!error.suggestions.is_empty());
255 assert!(error.suggestions[0].contains("quantifier"));
256 assert!(!error.examples.is_empty());
257 }
258
259 #[test]
260 fn test_enhance_arity_error() {
261 let error = enhance_compilation_error("arity mismatch: expected 2 arguments");
262
263 assert!(error.error.contains("arity"));
264 assert!(!error.suggestions.is_empty());
265 assert!(!error.examples.is_empty());
266 }
267
268 #[test]
269 fn test_enhance_strategy_error() {
270 let error = enhance_compilation_error("unknown strategy: foo_bar");
271
272 assert!(error.error.contains("strategy"));
273 assert!(!error.suggestions.is_empty());
274 assert!(error.examples.len() >= 6); }
276
277 #[test]
278 fn test_enhance_file_error() {
279 let error = enhance_file_error("/path/to/file.tl", "No such file or directory");
280
281 assert!(error.error.contains("not found"));
282 assert!(!error.suggestions.is_empty());
283 assert!(!error.examples.is_empty());
284 }
285}