logicaffeine_kernel/interface/
command_parser.rs1use super::command::Command;
16use super::error::ParseError;
17use super::literate_parser;
18use super::term_parser::TermParser;
19use crate::{Term, Universe};
20
21pub fn parse_command(input: &str) -> Result<Command, ParseError> {
26 let input = input.trim();
27
28 if (input.starts_with("A ") || input.starts_with("An ")) && input.contains(" is either") {
34 return literate_parser::parse_inductive(input);
35 }
36
37 if input.starts_with("## To ") {
39 return literate_parser::parse_definition(input);
40 }
41
42 if input.starts_with("Let ") && input.contains(" be ") {
44 return literate_parser::parse_let_definition(input);
45 }
46
47 if input.starts_with("## Theorem:") {
49 return literate_parser::parse_theorem(input);
50 }
51
52 let input = input.strip_suffix('.').unwrap_or(input).trim();
58
59 if input.starts_with("Definition") {
60 parse_definition(&input[10..].trim_start())
61 } else if input.starts_with("Check") {
62 parse_check(&input[5..].trim_start())
63 } else if input.starts_with("Eval") {
64 parse_eval(&input[4..].trim_start())
65 } else if input.starts_with("Inductive") {
66 parse_inductive(&input[9..].trim_start())
67 } else {
68 Err(ParseError::UnknownCommand(
69 input.split_whitespace().next().unwrap_or(input).to_string(),
70 ))
71 }
72}
73
74fn parse_definition(input: &str) -> Result<Command, ParseError> {
76 let assign_pos = input.find(":=").ok_or(ParseError::Missing(":=".to_string()))?;
78
79 let before_assign = input[..assign_pos].trim();
80 let body_str = input[assign_pos + 2..].trim();
81
82 if let Some(colon_pos) = before_assign.find(':') {
84 let name = before_assign[..colon_pos].trim().to_string();
86 let type_str = before_assign[colon_pos + 1..].trim();
87
88 if name.is_empty() {
89 return Err(ParseError::Missing("definition name".to_string()));
90 }
91
92 let (ty, implicit_count) = TermParser::parse_with_implicits(type_str)?;
93 let body = TermParser::parse(body_str)?;
94
95 Ok(Command::Definition {
96 name,
97 ty: Some(ty),
98 body,
99 is_hint: false,
100 implicit_count,
101 })
102 } else {
103 let name = before_assign.to_string();
105
106 if name.is_empty() {
107 return Err(ParseError::Missing("definition name".to_string()));
108 }
109
110 let body = TermParser::parse(body_str)?;
111
112 Ok(Command::Definition {
113 name,
114 ty: None,
115 body,
116 is_hint: false,
117 implicit_count: 0,
118 })
119 }
120}
121
122fn parse_check(input: &str) -> Result<Command, ParseError> {
124 let term = TermParser::parse(input)?;
125 Ok(Command::Check(term))
126}
127
128fn parse_eval(input: &str) -> Result<Command, ParseError> {
130 let term = TermParser::parse(input)?;
131 Ok(Command::Eval(term))
132}
133
134fn parse_inductive(input: &str) -> Result<Command, ParseError> {
139 let assign_pos = input.find(":=").ok_or(ParseError::Missing(":=".to_string()))?;
141
142 let header = input[..assign_pos].trim();
143 let ctors_str = input[assign_pos + 2..].trim();
144
145 let (name, params) = parse_inductive_header(header)?;
147
148 if name.is_empty() {
149 return Err(ParseError::Missing("inductive name".to_string()));
150 }
151
152 let mut constructors = Vec::new();
154 for ctor_part in ctors_str.split('|') {
155 let ctor_part = ctor_part.trim();
156 if ctor_part.is_empty() {
157 continue;
158 }
159
160 let colon_pos = ctor_part
162 .find(':')
163 .ok_or(ParseError::Missing("constructor type annotation".to_string()))?;
164
165 let ctor_name = ctor_part[..colon_pos].trim().to_string();
166 let ctor_type_str = ctor_part[colon_pos + 1..].trim();
167
168 if ctor_name.is_empty() {
169 return Err(ParseError::Missing("constructor name".to_string()));
170 }
171
172 let ctor_type = TermParser::parse(ctor_type_str)?;
173 constructors.push((ctor_name, ctor_type));
174 }
175
176 if constructors.is_empty() {
177 return Err(ParseError::Missing("constructors".to_string()));
178 }
179
180 let sort = Term::Sort(Universe::Type(0));
182
183 Ok(Command::Inductive {
184 name,
185 params,
186 sort,
187 constructors,
188 })
189}
190
191fn parse_inductive_header(header: &str) -> Result<(String, Vec<(String, Term)>), ParseError> {
198 let header = header.trim();
199
200 if !header.contains('(') {
202 return Ok((header.to_string(), vec![]));
203 }
204
205 let paren_pos = header.find('(').unwrap();
207 let name = header[..paren_pos].trim().to_string();
208 let params_str = header[paren_pos..].trim();
209
210 let params = parse_param_bindings(params_str)?;
212
213 Ok((name, params))
214}
215
216fn parse_param_bindings(input: &str) -> Result<Vec<(String, Term)>, ParseError> {
220 let mut params = Vec::new();
221 let mut remaining = input.trim();
222
223 while !remaining.is_empty() {
224 remaining = remaining.trim();
226 if remaining.is_empty() {
227 break;
228 }
229
230 if !remaining.starts_with('(') {
232 return Err(ParseError::Missing("opening '(' for parameter".to_string()));
233 }
234
235 let close_pos = find_matching_paren(remaining)?;
237 let binding = &remaining[1..close_pos]; let colon_pos = binding
241 .find(':')
242 .ok_or(ParseError::Missing("':' in parameter binding".to_string()))?;
243
244 let param_name = binding[..colon_pos].trim().to_string();
245 let param_type_str = binding[colon_pos + 1..].trim();
246
247 if param_name.is_empty() {
248 return Err(ParseError::Missing("parameter name".to_string()));
249 }
250
251 let param_type = TermParser::parse(param_type_str)?;
252 params.push((param_name, param_type));
253
254 remaining = remaining[close_pos + 1..].trim();
256 }
257
258 Ok(params)
259}
260
261fn find_matching_paren(input: &str) -> Result<usize, ParseError> {
263 let mut depth = 0;
264 for (i, c) in input.chars().enumerate() {
265 match c {
266 '(' => depth += 1,
267 ')' => {
268 depth -= 1;
269 if depth == 0 {
270 return Ok(i);
271 }
272 }
273 _ => {}
274 }
275 }
276 Err(ParseError::Missing("closing ')' for parameter".to_string()))
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 #[test]
284 fn test_parse_definition_with_type() {
285 let cmd = parse_command("Definition one : Nat := Succ Zero.").unwrap();
286 if let Command::Definition { name, ty, body, .. } = cmd {
287 assert_eq!(name, "one");
288 assert!(ty.is_some());
289 assert!(matches!(body, Term::App(..)));
290 } else {
291 panic!("Expected Definition");
292 }
293 }
294
295 #[test]
296 fn test_parse_definition_without_type() {
297 let cmd = parse_command("Definition two := Succ (Succ Zero).").unwrap();
298 if let Command::Definition { name, ty, .. } = cmd {
299 assert_eq!(name, "two");
300 assert!(ty.is_none());
301 } else {
302 panic!("Expected Definition");
303 }
304 }
305
306 #[test]
307 fn test_parse_check() {
308 let cmd = parse_command("Check Zero.").unwrap();
309 assert!(matches!(cmd, Command::Check(_)));
310 }
311
312 #[test]
313 fn test_parse_eval() {
314 let cmd = parse_command("Eval (Succ Zero).").unwrap();
315 assert!(matches!(cmd, Command::Eval(_)));
316 }
317
318 #[test]
319 fn test_parse_inductive() {
320 let cmd = parse_command("Inductive Bool := True : Bool | False : Bool.").unwrap();
321 if let Command::Inductive {
322 name, constructors, ..
323 } = cmd
324 {
325 assert_eq!(name, "Bool");
326 assert_eq!(constructors.len(), 2);
327 assert_eq!(constructors[0].0, "True");
328 assert_eq!(constructors[1].0, "False");
329 } else {
330 panic!("Expected Inductive");
331 }
332 }
333}