Skip to main content

logicaffeine_kernel/interface/
literate_parser.rs

1//! Literate Specification Parser for the Kernel.
2//!
3//! This module parses English-like "Literate" syntax and emits the same
4//! `Command` and `Term` structures as the Coq-style parser.
5//!
6//! # Supported Syntax
7//!
8//! ## Inductive Types
9//! ```text
10//! A Bool is either Yes or No.
11//! A Nat is either:
12//!     Zero.
13//!     a Successor with pred: Nat.
14//! ```
15//!
16//! ## Function Definitions (with implicit recursion)
17//! ```text
18//! ## To add (n: Nat) and (m: Nat) -> Nat:
19//!     Consider n:
20//!         When Zero: Yield m.
21//!         When Successor k: Yield Successor (add k m).
22//! ```
23//!
24//! ## Pattern Matching
25//! ```text
26//! Consider x:
27//!     When Zero: Yield 0.
28//!     When Successor k: Yield k.
29//! ```
30//!
31//! ## Lambda Expressions
32//! ```text
33//! given x: Nat yields Successor x
34//! |x: Nat| -> Successor x
35//! ```
36
37use crate::{Literal, Term, Universe};
38use super::command::Command;
39use super::error::ParseError;
40use std::collections::HashSet;
41
42// ============================================================
43// PUBLIC API (called by command_parser.rs dispatcher)
44// ============================================================
45
46/// Parse "A \[Name\] is either..." into Command::Inductive
47///
48/// Supports:
49/// - `A Bool is either Yes or No.`
50/// - `A Nat is either: Zero. a Successor with pred: Nat.`
51/// - `A List of (T: Type) is either: Empty. a Node with head: T and tail: List of T.`
52pub fn parse_inductive(input: &str) -> Result<Command, ParseError> {
53    let mut parser = LiterateParser::new(input);
54    parser.parse_literate_inductive()
55}
56
57/// Parse "## To \[name\] (params) -> RetType:" into Command::Definition
58///
59/// Handles implicit fixpoints: if the function name appears in the body,
60/// the body is automatically wrapped in Term::Fix.
61pub fn parse_definition(input: &str) -> Result<Command, ParseError> {
62    let mut parser = LiterateParser::new(input);
63    parser.parse_literate_definition()
64}
65
66/// Parse "Let \[name\] be \[term\]." into Command::Definition (constant, not function)
67///
68/// This is for simple constant bindings like:
69/// - `Let T be Apply(Name "Not", Variable 0).`
70/// - `Let G be the diagonalization of T.`
71pub fn parse_let_definition(input: &str) -> Result<Command, ParseError> {
72    let mut parser = LiterateParser::new(input);
73    parser.parse_literate_let()
74}
75
76/// Parse "## Theorem: \[name\] Statement: \[proposition\]." into Command::Definition
77///
78/// This is for theorem declarations like:
79/// - `## Theorem: Godel_First_Incompleteness\n    Statement: Consistent implies Not(Provable(G)).`
80pub fn parse_theorem(input: &str) -> Result<Command, ParseError> {
81    let mut parser = LiterateParser::new(input);
82    parser.parse_literate_theorem()
83}
84
85// ============================================================
86// PARSER STATE
87// ============================================================
88
89struct LiterateParser<'a> {
90    input: &'a str,
91    pos: usize,
92    bound_vars: HashSet<String>,
93    current_function: Option<String>,
94    /// The return type of the current function (used as motive in Consider)
95    return_type: Option<Term>,
96}
97
98impl<'a> LiterateParser<'a> {
99    fn new(input: &'a str) -> Self {
100        Self {
101            input,
102            pos: 0,
103            bound_vars: HashSet::new(),
104            current_function: None,
105            return_type: None,
106        }
107    }
108
109    // ============================================================
110    // INDUCTIVE TYPE PARSING
111    // ============================================================
112
113    /// Parse: A [Name] (params)? is either [variants]
114    fn parse_literate_inductive(&mut self) -> Result<Command, ParseError> {
115        self.skip_whitespace();
116
117        // Consume "A" or "An"
118        if self.try_consume_keyword("An") || self.try_consume_keyword("A") {
119            // Good
120        } else {
121            return Err(ParseError::Expected {
122                expected: "'A' or 'An'".to_string(),
123                found: self.peek_word().unwrap_or("EOF".to_string()),
124            });
125        }
126
127        self.skip_whitespace();
128
129        // Parse the type name
130        let name = self.parse_ident()?;
131
132        self.skip_whitespace();
133
134        // Check for type parameters: "of (T: Type)"
135        let params = if self.try_consume_keyword("of") {
136            self.skip_whitespace();
137            self.parse_param_list()?
138        } else {
139            vec![]
140        };
141
142        self.skip_whitespace();
143
144        // Expect "is either"
145        if !self.try_consume_keyword("is") {
146            return Err(ParseError::Expected {
147                expected: "'is'".to_string(),
148                found: self.peek_word().unwrap_or("EOF".to_string()),
149            });
150        }
151        self.skip_whitespace();
152        if !self.try_consume_keyword("either") {
153            return Err(ParseError::Expected {
154                expected: "'either'".to_string(),
155                found: self.peek_word().unwrap_or("EOF".to_string()),
156            });
157        }
158
159        self.skip_whitespace();
160
161        // Parse variants - either inline with "or" or block with indentation
162        let constructors = if self.peek_char(':') {
163            // Block format: "is either:\n    Zero.\n    Successor with pred: Nat."
164            self.advance(); // consume ':'
165            self.parse_indented_variants(&name, &params)?
166        } else {
167            // Inline format: "is either Yes or No."
168            self.parse_inline_variants(&name, &params)?
169        };
170
171        if constructors.is_empty() {
172            return Err(ParseError::Missing("constructors".to_string()));
173        }
174
175        Ok(Command::Inductive {
176            name,
177            params,
178            sort: Term::Sort(Universe::Type(0)),
179            constructors,
180        })
181    }
182
183    /// Parse inline variants: "Yes or No" or "Yes or No."
184    fn parse_inline_variants(
185        &mut self,
186        inductive_name: &str,
187        params: &[(String, Term)],
188    ) -> Result<Vec<(String, Term)>, ParseError> {
189        let mut constructors = Vec::new();
190
191        loop {
192            self.skip_whitespace();
193
194            if self.at_end() || self.peek_char('.') {
195                break;
196            }
197
198            // Parse a single variant
199            let (ctor_name, ctor_type) = self.parse_variant(inductive_name, params)?;
200            constructors.push((ctor_name, ctor_type));
201
202            self.skip_whitespace();
203
204            // Check for "or" separator
205            if !self.try_consume_keyword("or") {
206                break;
207            }
208        }
209
210        // Consume trailing period if present
211        self.skip_whitespace();
212        let _ = self.try_consume(".");
213
214        Ok(constructors)
215    }
216
217    /// Parse indented variants (after "is either:")
218    fn parse_indented_variants(
219        &mut self,
220        inductive_name: &str,
221        params: &[(String, Term)],
222    ) -> Result<Vec<(String, Term)>, ParseError> {
223        let mut constructors = Vec::new();
224
225        loop {
226            self.skip_whitespace_and_newlines();
227
228            if self.at_end() {
229                break;
230            }
231
232            // Check for end of block (non-indented line or empty)
233            if !self.peek_char(' ') && !self.peek_char('\t') && !self.peek_char('a') && !self.peek_char('A') {
234                // Check if this is a variant starting with capital letter
235                if let Some(c) = self.peek() {
236                    if !c.is_uppercase() && c != 'a' && c != 'A' {
237                        break;
238                    }
239                }
240            }
241
242            // Skip leading whitespace for indentation
243            self.skip_whitespace();
244
245            // Check for "a/an" prefix or capital letter
246            if self.at_end() {
247                break;
248            }
249
250            // Parse variant
251            let (ctor_name, ctor_type) = self.parse_variant(inductive_name, params)?;
252            constructors.push((ctor_name, ctor_type));
253
254            // Consume trailing period
255            self.skip_whitespace();
256            let _ = self.try_consume(".");
257        }
258
259        Ok(constructors)
260    }
261
262    /// Parse a single variant: "Zero" or "a Successor with pred: Nat"
263    fn parse_variant(
264        &mut self,
265        inductive_name: &str,
266        params: &[(String, Term)],
267    ) -> Result<(String, Term), ParseError> {
268        self.skip_whitespace();
269
270        // Skip optional "a" or "an" prefix (for readability)
271        let _ = self.try_consume_keyword("an") || self.try_consume_keyword("a");
272        self.skip_whitespace();
273
274        // Parse constructor name
275        let ctor_name = self.parse_ident()?;
276
277        self.skip_whitespace();
278
279        // Build the result type (possibly with parameters applied)
280        let result_type = self.build_applied_type(inductive_name, params);
281
282        // Check for "with" clause (fields)
283        if self.try_consume_keyword("with") {
284            // Parse fields: "field: Type and field2: Type2"
285            let fields = self.parse_field_list()?;
286
287            // Build constructor type: field1_type -> field2_type -> ... -> ResultType
288            let mut ctor_type = result_type;
289            for (_field_name, field_type) in fields.into_iter().rev() {
290                ctor_type = Term::Pi {
291                    param: "_".to_string(),
292                    param_type: Box::new(field_type),
293                    body_type: Box::new(ctor_type),
294                };
295            }
296
297            Ok((ctor_name, ctor_type))
298        } else {
299            // Unit constructor (no fields)
300            Ok((ctor_name, result_type))
301        }
302    }
303
304    /// Parse field list: "field: Type" or "field: Type and field2: Type2"
305    fn parse_field_list(&mut self) -> Result<Vec<(String, Term)>, ParseError> {
306        let mut fields = Vec::new();
307
308        loop {
309            self.skip_whitespace();
310
311            // Parse field name
312            let field_name = self.parse_ident()?;
313
314            self.skip_whitespace();
315
316            // Expect ":"
317            if !self.try_consume(":") {
318                return Err(ParseError::Expected {
319                    expected: "':'".to_string(),
320                    found: self.peek_word().unwrap_or("EOF".to_string()),
321                });
322            }
323
324            self.skip_whitespace();
325
326            // Parse field type
327            let field_type = self.parse_type()?;
328
329            fields.push((field_name, field_type));
330
331            self.skip_whitespace();
332
333            // Check for "and" separator
334            if !self.try_consume_keyword("and") {
335                break;
336            }
337        }
338
339        Ok(fields)
340    }
341
342    /// Build type with parameters applied: List T
343    fn build_applied_type(&self, name: &str, params: &[(String, Term)]) -> Term {
344        let mut result = Term::Global(name.to_string());
345        for (param_name, _) in params {
346            result = Term::App(Box::new(result), Box::new(Term::Var(param_name.clone())));
347        }
348        result
349    }
350
351    // ============================================================
352    // DEFINITION PARSING
353    // ============================================================
354
355    /// Parse: ## To [name] (params) -> RetType: body
356    fn parse_literate_definition(&mut self) -> Result<Command, ParseError> {
357        self.skip_whitespace();
358
359        // Consume "## To "
360        if !self.try_consume("## To ") && !self.try_consume("##To ") {
361            return Err(ParseError::Expected {
362                expected: "'## To'".to_string(),
363                found: self.peek_word().unwrap_or("EOF".to_string()),
364            });
365        }
366
367        self.skip_whitespace();
368
369        // Parse function name
370        let mut name = self.parse_ident()?;
371
372        // Handle predicate syntax: "## To be Provable" means define "Provable", not "be"
373        // This allows "## To be X (params) -> Prop:" for predicate definitions
374        if name == "be" {
375            self.skip_whitespace();
376            name = self.parse_ident()?;
377        }
378
379        self.current_function = Some(name.clone());
380
381        self.skip_whitespace();
382
383        // Parse parameter groups: (x: T) and (y: U) or (x: T) (y: U)
384        // Note: For nullary predicates like "## To be Consistent -> Prop:", params will be empty
385        let all_params = self.parse_function_params()?;
386
387        self.skip_whitespace();
388
389        // Parse optional return type: -> RetType
390        let return_type = if self.try_consume("->") {
391            self.skip_whitespace();
392            let ret = self.parse_type()?;
393            // Store return type for use as motive in Consider blocks
394            self.return_type = Some(ret.clone());
395            Some(ret)
396        } else {
397            None
398        };
399
400        self.skip_whitespace();
401
402        // Expect ":"
403        if !self.try_consume(":") {
404            return Err(ParseError::Expected {
405                expected: "':'".to_string(),
406                found: self.peek_word().unwrap_or("EOF".to_string()),
407            });
408        }
409
410        // Add parameters to bound vars
411        for (param_name, _) in &all_params {
412            self.bound_vars.insert(param_name.clone());
413        }
414
415        // Parse body
416        self.skip_whitespace_and_newlines();
417        let body = self.parse_body()?;
418
419        // Check for self-reference (implicit fixpoint)
420        let needs_fix = self.contains_self_reference(&name, &body);
421
422        // Build the function body with lambdas
423        let mut func_body = body;
424        for (param_name, param_type) in all_params.iter().rev() {
425            func_body = Term::Lambda {
426                param: param_name.clone(),
427                param_type: Box::new(param_type.clone()),
428                body: Box::new(func_body),
429            };
430        }
431
432        // Wrap in Fix if recursive
433        if needs_fix {
434            func_body = Term::Fix {
435                name: name.clone(),
436                body: Box::new(func_body),
437            };
438        }
439
440        // Build the full type annotation if we have a return type
441        let ty = if let Some(ret) = return_type {
442            let mut full_type = ret;
443            for (_, param_type) in all_params.iter().rev() {
444                full_type = Term::Pi {
445                    param: "_".to_string(),
446                    param_type: Box::new(param_type.clone()),
447                    body_type: Box::new(full_type),
448                };
449            }
450            Some(full_type)
451        } else {
452            None
453        };
454
455        self.current_function = None;
456
457        Ok(Command::Definition {
458            name,
459            ty,
460            body: func_body,
461            is_hint: false,
462            implicit_count: 0,
463        })
464    }
465
466    // ============================================================
467    // LET DEFINITION PARSING
468    // ============================================================
469
470    /// Parse: Let [name] be [term].
471    fn parse_literate_let(&mut self) -> Result<Command, ParseError> {
472        self.skip_whitespace();
473
474        // Consume "Let "
475        if !self.try_consume_keyword("Let") {
476            return Err(ParseError::Expected {
477                expected: "'Let'".to_string(),
478                found: self.peek_word().unwrap_or("EOF".to_string()),
479            });
480        }
481
482        self.skip_whitespace();
483
484        // Parse the name
485        let name = self.parse_ident()?;
486
487        self.skip_whitespace();
488
489        // Expect "be"
490        if !self.try_consume_keyword("be") {
491            return Err(ParseError::Expected {
492                expected: "'be'".to_string(),
493                found: self.peek_word().unwrap_or("EOF".to_string()),
494            });
495        }
496
497        self.skip_whitespace();
498
499        // Parse the body term
500        let body = self.parse_term()?;
501
502        // Consume trailing period if present
503        self.skip_whitespace();
504        let _ = self.try_consume(".");
505
506        Ok(Command::Definition {
507            name,
508            ty: None,
509            body,
510            is_hint: false,
511            implicit_count: 0,
512        })
513    }
514
515    /// Parse: ## Theorem: [name]\n    Statement: [proposition].\n    Proof: [tactic].
516    ///
517    /// The Proof section is optional. When provided, it applies a tactic like `ring.`
518    /// to automatically prove the statement.
519    fn parse_literate_theorem(&mut self) -> Result<Command, ParseError> {
520        self.skip_whitespace();
521
522        // Consume "## Theorem:"
523        if !self.try_consume("## Theorem:") {
524            return Err(ParseError::Expected {
525                expected: "'## Theorem:'".to_string(),
526                found: self.peek_word().unwrap_or("EOF".to_string()),
527            });
528        }
529
530        self.skip_whitespace();
531
532        // Parse the theorem name
533        let name = self.parse_ident()?;
534
535        self.skip_whitespace_and_newlines();
536
537        // Expect "Statement:"
538        if !self.try_consume_keyword("Statement") {
539            return Err(ParseError::Expected {
540                expected: "'Statement'".to_string(),
541                found: self.peek_word().unwrap_or("EOF".to_string()),
542            });
543        }
544        self.skip_whitespace();
545        if !self.try_consume(":") {
546            return Err(ParseError::Expected {
547                expected: "':'".to_string(),
548                found: self.peek_word().unwrap_or("EOF".to_string()),
549            });
550        }
551
552        self.skip_whitespace();
553
554        // Parse the statement (proposition)
555        let statement = self.parse_term()?;
556
557        // Consume trailing period if present
558        self.skip_whitespace();
559        let _ = self.try_consume(".");
560
561        self.skip_whitespace_and_newlines();
562
563        // Check for optional "Proof:" section
564        let (body, ty) = if self.try_consume_keyword("Proof") {
565            self.skip_whitespace();
566            if !self.try_consume(":") {
567                return Err(ParseError::Expected {
568                    expected: "':'".to_string(),
569                    found: self.peek_word().unwrap_or("EOF".to_string()),
570                });
571            }
572            self.skip_whitespace();
573
574            // Parse proof tactic and apply it to the statement
575            let proof = self.parse_proof_tactic(&statement)?;
576
577            // Consume trailing period if present
578            self.skip_whitespace();
579            let _ = self.try_consume(".");
580
581            (proof, Some(Term::Global("Derivation".to_string())))
582        } else {
583            // No proof - existing behavior (statement as body, Prop as type)
584            (statement, Some(Term::Sort(Universe::Prop)))
585        };
586
587        // Check for optional "Attribute: hint." section
588        self.skip_whitespace_and_newlines();
589        let is_hint = if self.try_consume_keyword("Attribute") {
590            self.skip_whitespace();
591            if !self.try_consume(":") {
592                return Err(ParseError::Expected {
593                    expected: "':'".to_string(),
594                    found: self.peek_word().unwrap_or("EOF".to_string()),
595                });
596            }
597            self.skip_whitespace();
598
599            let hint = self.try_consume_keyword("hint");
600
601            // Consume trailing period
602            self.skip_whitespace();
603            let _ = self.try_consume(".");
604
605            hint
606        } else {
607            false
608        };
609
610        Ok(Command::Definition {
611            name,
612            ty,
613            body,
614            is_hint,
615            implicit_count: 0,
616        })
617    }
618
619    /// Parse a proof tactic like `ring.` and return the proof term.
620    ///
621    /// Supported tactics:
622    /// - `ring.` - Proves polynomial equalities by normalization
623    /// - `refl.` - Proves reflexivity goals
624    /// - `lia.` - Proves linear integer arithmetic (inequalities)
625    fn parse_proof_tactic(&mut self, statement: &Term) -> Result<Term, ParseError> {
626        if self.try_consume_keyword("ring") {
627            // Consume optional trailing period
628            self.skip_whitespace();
629            let _ = self.try_consume(".");
630
631            // Convert statement to Syntax and apply try_ring
632            let goal_syntax = self.term_to_syntax(statement, &[]);
633            Ok(Term::App(
634                Box::new(Term::Global("try_ring".to_string())),
635                Box::new(goal_syntax),
636            ))
637        } else if self.try_consume_keyword("refl") {
638            // Refl tactic
639            self.skip_whitespace();
640            let _ = self.try_consume(".");
641
642            let goal_syntax = self.term_to_syntax(statement, &[]);
643            Ok(Term::App(
644                Box::new(Term::Global("try_refl".to_string())),
645                Box::new(goal_syntax),
646            ))
647        } else if self.try_consume_keyword("lia") {
648            // LIA tactic: linear integer arithmetic
649            self.skip_whitespace();
650            let _ = self.try_consume(".");
651
652            let goal_syntax = self.term_to_syntax(statement, &[]);
653            Ok(Term::App(
654                Box::new(Term::Global("try_lia".to_string())),
655                Box::new(goal_syntax),
656            ))
657        } else if self.try_consume_keyword("cc") {
658            // CC tactic: congruence closure
659            self.skip_whitespace();
660            let _ = self.try_consume(".");
661
662            let goal_syntax = self.term_to_syntax(statement, &[]);
663            Ok(Term::App(
664                Box::new(Term::Global("try_cc".to_string())),
665                Box::new(goal_syntax),
666            ))
667        } else if self.try_consume_keyword("simp") {
668            // Simp tactic: simplification and arithmetic
669            self.skip_whitespace();
670            let _ = self.try_consume(".");
671
672            let goal_syntax = self.term_to_syntax(statement, &[]);
673            Ok(Term::App(
674                Box::new(Term::Global("try_simp".to_string())),
675                Box::new(goal_syntax),
676            ))
677        } else if self.try_consume_keyword("omega") {
678            // Omega tactic: true integer arithmetic (Omega Test)
679            self.skip_whitespace();
680            let _ = self.try_consume(".");
681
682            let goal_syntax = self.term_to_syntax(statement, &[]);
683            Ok(Term::App(
684                Box::new(Term::Global("try_omega".to_string())),
685                Box::new(goal_syntax),
686            ))
687        } else if self.try_consume_keyword("auto") {
688            // Auto tactic: tries all decision procedures in sequence
689            self.skip_whitespace();
690            let _ = self.try_consume(".");
691
692            let goal_syntax = self.term_to_syntax(statement, &[]);
693            Ok(Term::App(
694                Box::new(Term::Global("try_auto".to_string())),
695                Box::new(goal_syntax),
696            ))
697        } else if self.try_consume_keyword("induction") {
698            // Induction tactic: structural induction on a variable
699            self.skip_whitespace();
700            let var_name = self.parse_ident()?;
701            self.skip_whitespace();
702            let _ = self.try_consume(".");
703
704            // Parse bullet cases
705            let cases = self.parse_bullet_cases(statement)?;
706
707            // Build induction derivation
708            self.build_induction_derivation(&var_name, statement, cases)
709        } else {
710            Err(ParseError::Expected {
711                expected: "proof tactic (ring, refl, lia, cc, simp, omega, auto, induction)".to_string(),
712                found: self.peek_word().unwrap_or("EOF".to_string()),
713            })
714        }
715    }
716
717    /// Check if the next non-whitespace character is a bullet point
718    fn peek_bullet(&mut self) -> bool {
719        // Save position
720        let saved_pos = self.pos;
721        self.skip_whitespace_and_newlines();
722        let result = matches!(self.peek(), Some('-') | Some('*') | Some('+'));
723        self.pos = saved_pos;
724        result
725    }
726
727    /// Consume a bullet point character
728    fn consume_bullet(&mut self) {
729        self.skip_whitespace_and_newlines();
730        if matches!(self.peek(), Some('-') | Some('*') | Some('+')) {
731            self.advance();
732        }
733    }
734
735    /// Parse bullet-pointed cases for induction
736    fn parse_bullet_cases(&mut self, statement: &Term) -> Result<Vec<Term>, ParseError> {
737        let mut cases = Vec::new();
738
739        while self.peek_bullet() {
740            self.consume_bullet();
741            self.skip_whitespace();
742
743            // Parse tactics for this case
744            let case_proof = self.parse_tactic_sequence(statement)?;
745            cases.push(case_proof);
746        }
747
748        Ok(cases)
749    }
750
751    /// Parse a sequence of tactics (simp. auto.) on a single line
752    fn parse_tactic_sequence(&mut self, statement: &Term) -> Result<Term, ParseError> {
753        let mut tactics = Vec::new();
754
755        loop {
756            self.skip_whitespace();
757
758            // Stop at newline followed by bullet, end of input, or Attribute keyword
759            if self.peek_bullet() || self.at_end() || self.peek_keyword("Attribute") {
760                break;
761            }
762
763            // Try to parse a single tactic
764            match self.parse_single_tactic(statement) {
765                Ok(tactic) => tactics.push(tactic),
766                Err(_) => break,
767            }
768        }
769
770        if tactics.is_empty() {
771            return Err(ParseError::Missing("tactic in bullet case".to_string()));
772        }
773
774        // Combine tactics: if multiple, wrap with tact_seq (right-associative)
775        let mut result = tactics.pop().unwrap();
776        while let Some(prev) = tactics.pop() {
777            result = Term::App(
778                Box::new(Term::App(
779                    Box::new(Term::Global("tact_seq".to_string())),
780                    Box::new(prev),
781                )),
782                Box::new(result),
783            );
784        }
785
786        Ok(result)
787    }
788
789    /// Parse a single tactic (ring, auto, etc.) without consuming trailing period
790    fn parse_single_tactic(&mut self, statement: &Term) -> Result<Term, ParseError> {
791        let goal_syntax = self.term_to_syntax(statement, &[]);
792
793        if self.try_consume_keyword("ring") {
794            self.skip_whitespace();
795            let _ = self.try_consume(".");
796            Ok(Term::App(
797                Box::new(Term::Global("try_ring".to_string())),
798                Box::new(goal_syntax),
799            ))
800        } else if self.try_consume_keyword("refl") {
801            self.skip_whitespace();
802            let _ = self.try_consume(".");
803            Ok(Term::App(
804                Box::new(Term::Global("try_refl".to_string())),
805                Box::new(goal_syntax),
806            ))
807        } else if self.try_consume_keyword("lia") {
808            self.skip_whitespace();
809            let _ = self.try_consume(".");
810            Ok(Term::App(
811                Box::new(Term::Global("try_lia".to_string())),
812                Box::new(goal_syntax),
813            ))
814        } else if self.try_consume_keyword("cc") {
815            self.skip_whitespace();
816            let _ = self.try_consume(".");
817            Ok(Term::App(
818                Box::new(Term::Global("try_cc".to_string())),
819                Box::new(goal_syntax),
820            ))
821        } else if self.try_consume_keyword("simp") {
822            self.skip_whitespace();
823            let _ = self.try_consume(".");
824            Ok(Term::App(
825                Box::new(Term::Global("try_simp".to_string())),
826                Box::new(goal_syntax),
827            ))
828        } else if self.try_consume_keyword("omega") {
829            self.skip_whitespace();
830            let _ = self.try_consume(".");
831            Ok(Term::App(
832                Box::new(Term::Global("try_omega".to_string())),
833                Box::new(goal_syntax),
834            ))
835        } else if self.try_consume_keyword("auto") {
836            self.skip_whitespace();
837            let _ = self.try_consume(".");
838            Ok(Term::App(
839                Box::new(Term::Global("try_auto".to_string())),
840                Box::new(goal_syntax),
841            ))
842        } else if self.try_consume_keyword("intro") {
843            self.skip_whitespace();
844            // Optional variable name after intro
845            let _var = if !self.peek_char('.') && !self.at_end() {
846                self.parse_ident().ok()
847            } else {
848                None
849            };
850            self.skip_whitespace();
851            let _ = self.try_consume(".");
852            Ok(Term::App(
853                Box::new(Term::Global("try_intro".to_string())),
854                Box::new(goal_syntax),
855            ))
856        } else {
857            Err(ParseError::Expected {
858                expected: "tactic".to_string(),
859                found: self.peek_word().unwrap_or("EOF".to_string()),
860            })
861        }
862    }
863
864    /// Build an induction derivation from variable name and case proofs
865    fn build_induction_derivation(
866        &self,
867        _var_name: &str,
868        statement: &Term,
869        cases: Vec<Term>,
870    ) -> Result<Term, ParseError> {
871        // Build the inductive type (for now assume Nat if not specified)
872        let ind_type = Term::App(
873            Box::new(Term::Global("SName".to_string())),
874            Box::new(Term::Lit(Literal::Text("Nat".to_string()))),
875        );
876
877        // Build the motive from the statement
878        let motive = self.term_to_syntax(statement, &[]);
879
880        // Build the cases as DCase chain: DCase c1 (DCase c2 ... DCaseEnd)
881        let mut case_chain = Term::Global("DCaseEnd".to_string());
882        for case_proof in cases.into_iter().rev() {
883            case_chain = Term::App(
884                Box::new(Term::App(
885                    Box::new(Term::Global("DCase".to_string())),
886                    Box::new(case_proof),
887                )),
888                Box::new(case_chain),
889            );
890        }
891
892        // Build: try_induction ind_type motive cases
893        Ok(Term::App(
894            Box::new(Term::App(
895                Box::new(Term::App(
896                    Box::new(Term::Global("try_induction".to_string())),
897                    Box::new(ind_type),
898                )),
899                Box::new(motive),
900            )),
901            Box::new(case_chain),
902        ))
903    }
904
905    /// Parse function parameters: (x: T) and (y: U) or (x: T) (y: U)
906    fn parse_function_params(&mut self) -> Result<Vec<(String, Term)>, ParseError> {
907        let mut params = Vec::new();
908
909        loop {
910            self.skip_whitespace();
911
912            // Check for opening paren
913            if !self.peek_char('(') {
914                break;
915            }
916
917            // Parse one parameter group
918            self.advance(); // consume '('
919            self.skip_whitespace();
920
921            let param_name = self.parse_ident()?;
922            self.skip_whitespace();
923
924            if !self.try_consume(":") {
925                return Err(ParseError::Expected {
926                    expected: "':'".to_string(),
927                    found: self.peek_word().unwrap_or("EOF".to_string()),
928                });
929            }
930            self.skip_whitespace();
931
932            let param_type = self.parse_type()?;
933            self.skip_whitespace();
934
935            if !self.try_consume(")") {
936                return Err(ParseError::Expected {
937                    expected: "')'".to_string(),
938                    found: self.peek_word().unwrap_or("EOF".to_string()),
939                });
940            }
941
942            params.push((param_name, param_type));
943
944            self.skip_whitespace();
945
946            // Check for "and" separator
947            let _ = self.try_consume_keyword("and");
948        }
949
950        Ok(params)
951    }
952
953    /// Parse optional type parameters: (T: Type)
954    fn parse_param_list(&mut self) -> Result<Vec<(String, Term)>, ParseError> {
955        let mut params = Vec::new();
956
957        if !self.peek_char('(') {
958            return Ok(params);
959        }
960
961        self.advance(); // consume '('
962        self.skip_whitespace();
963
964        loop {
965            let param_name = self.parse_ident()?;
966            self.skip_whitespace();
967
968            if !self.try_consume(":") {
969                return Err(ParseError::Expected {
970                    expected: "':'".to_string(),
971                    found: self.peek_word().unwrap_or("EOF".to_string()),
972                });
973            }
974            self.skip_whitespace();
975
976            let param_type = self.parse_type()?;
977            params.push((param_name, param_type));
978
979            self.skip_whitespace();
980
981            if self.try_consume(")") {
982                break;
983            }
984
985            if !self.try_consume(",") && !self.try_consume_keyword("and") {
986                return Err(ParseError::Expected {
987                    expected: "')' or ','".to_string(),
988                    found: self.peek_word().unwrap_or("EOF".to_string()),
989                });
990            }
991            self.skip_whitespace();
992        }
993
994        Ok(params)
995    }
996
997    // ============================================================
998    // BODY / TERM PARSING
999    // ============================================================
1000
1001    /// Parse function body (indented block after ":")
1002    fn parse_body(&mut self) -> Result<Term, ParseError> {
1003        self.skip_whitespace();
1004
1005        // Check for Consider (pattern matching)
1006        if self.peek_keyword("Consider") {
1007            return self.parse_consider();
1008        }
1009
1010        // Check for Yield (direct return)
1011        if self.peek_keyword("Yield") {
1012            return self.parse_yield();
1013        }
1014
1015        // Check for "given" lambda
1016        if self.peek_keyword("given") {
1017            return self.parse_given_lambda();
1018        }
1019
1020        // Check for pipe lambda |x: T| -> body
1021        if self.peek_char('|') {
1022            return self.parse_pipe_lambda();
1023        }
1024
1025        // Otherwise, parse as a term expression
1026        self.parse_term()
1027    }
1028
1029    /// Parse: Consider x: When C1: body1. When C2: body2.
1030    fn parse_consider(&mut self) -> Result<Term, ParseError> {
1031        self.consume_keyword("Consider")?;
1032        self.skip_whitespace();
1033
1034        // Parse the discriminant
1035        let discriminant = self.parse_term()?;
1036
1037        self.skip_whitespace();
1038
1039        // Expect ":"
1040        if !self.try_consume(":") {
1041            return Err(ParseError::Expected {
1042                expected: "':'".to_string(),
1043                found: self.peek_word().unwrap_or("EOF".to_string()),
1044            });
1045        }
1046
1047        // Parse cases
1048        let mut cases = Vec::new();
1049        // Use return type as motive if available, otherwise a sort placeholder
1050        // The type checker handles constant motives by wrapping them
1051        let motive = self.return_type.clone().unwrap_or_else(|| Term::Sort(Universe::Type(0)));
1052
1053        loop {
1054            self.skip_whitespace_and_newlines();
1055
1056            if !self.peek_keyword("When") {
1057                break;
1058            }
1059
1060            self.consume_keyword("When")?;
1061            self.skip_whitespace();
1062
1063            // Parse constructor name
1064            let ctor_name = self.parse_ident()?;
1065            self.skip_whitespace();
1066
1067            // Parse optional binders (constructor arguments)
1068            let mut binders = Vec::new();
1069            while !self.peek_char(':') && !self.at_end() {
1070                let binder = self.parse_ident()?;
1071                binders.push(binder);
1072                self.skip_whitespace();
1073            }
1074
1075            // Expect ":"
1076            if !self.try_consume(":") {
1077                return Err(ParseError::Expected {
1078                    expected: "':'".to_string(),
1079                    found: self.peek_word().unwrap_or("EOF".to_string()),
1080                });
1081            }
1082
1083            self.skip_whitespace();
1084
1085            // Add binders to scope
1086            for binder in &binders {
1087                self.bound_vars.insert(binder.clone());
1088            }
1089
1090            // Parse case body
1091            let case_body = self.parse_body()?;
1092
1093            // Remove binders from scope
1094            for binder in &binders {
1095                self.bound_vars.remove(binder);
1096            }
1097
1098            // Wrap body in lambdas for each binder (from right to left)
1099            let mut wrapped_body = case_body;
1100            for binder in binders.into_iter().rev() {
1101                wrapped_body = Term::Lambda {
1102                    param: binder,
1103                    param_type: Box::new(Term::Global("_".to_string())), // Placeholder
1104                    body: Box::new(wrapped_body),
1105                };
1106            }
1107
1108            cases.push(wrapped_body);
1109
1110            // Consume trailing period if present
1111            self.skip_whitespace();
1112            let _ = self.try_consume(".");
1113        }
1114
1115        if cases.is_empty() {
1116            return Err(ParseError::Missing("When clauses".to_string()));
1117        }
1118
1119        // Try to infer motive from discriminant if it's a simple variable
1120        if let Term::Var(ref _v) = discriminant {
1121            // We leave motive as placeholder; kernel will infer
1122        }
1123
1124        Ok(Term::Match {
1125            discriminant: Box::new(discriminant),
1126            motive: Box::new(motive),
1127            cases,
1128        })
1129    }
1130
1131    /// Parse: Yield expression
1132    fn parse_yield(&mut self) -> Result<Term, ParseError> {
1133        self.consume_keyword("Yield")?;
1134        self.skip_whitespace();
1135        self.parse_term()
1136    }
1137
1138    /// Parse: given x: Type yields body
1139    fn parse_given_lambda(&mut self) -> Result<Term, ParseError> {
1140        self.consume_keyword("given")?;
1141        self.skip_whitespace();
1142
1143        let param = self.parse_ident()?;
1144        self.skip_whitespace();
1145
1146        if !self.try_consume(":") {
1147            return Err(ParseError::Expected {
1148                expected: "':'".to_string(),
1149                found: self.peek_word().unwrap_or("EOF".to_string()),
1150            });
1151        }
1152        self.skip_whitespace();
1153
1154        let param_type = self.parse_type()?;
1155        self.skip_whitespace();
1156
1157        self.consume_keyword("yields")?;
1158        self.skip_whitespace();
1159
1160        self.bound_vars.insert(param.clone());
1161        let body = self.parse_term()?;
1162        self.bound_vars.remove(&param);
1163
1164        Ok(Term::Lambda {
1165            param,
1166            param_type: Box::new(param_type),
1167            body: Box::new(body),
1168        })
1169    }
1170
1171    /// Parse: |x: Type| -> body
1172    fn parse_pipe_lambda(&mut self) -> Result<Term, ParseError> {
1173        self.advance(); // consume '|'
1174        self.skip_whitespace();
1175
1176        let param = self.parse_ident()?;
1177        self.skip_whitespace();
1178
1179        if !self.try_consume(":") {
1180            return Err(ParseError::Expected {
1181                expected: "':'".to_string(),
1182                found: self.peek_word().unwrap_or("EOF".to_string()),
1183            });
1184        }
1185        self.skip_whitespace();
1186
1187        let param_type = self.parse_type()?;
1188        self.skip_whitespace();
1189
1190        if !self.try_consume("|") {
1191            return Err(ParseError::Expected {
1192                expected: "'|'".to_string(),
1193                found: self.peek_word().unwrap_or("EOF".to_string()),
1194            });
1195        }
1196        self.skip_whitespace();
1197
1198        if !self.try_consume("->") {
1199            return Err(ParseError::Expected {
1200                expected: "'->'".to_string(),
1201                found: self.peek_word().unwrap_or("EOF".to_string()),
1202            });
1203        }
1204        self.skip_whitespace();
1205
1206        self.bound_vars.insert(param.clone());
1207        let body = self.parse_term()?;
1208        self.bound_vars.remove(&param);
1209
1210        Ok(Term::Lambda {
1211            param,
1212            param_type: Box::new(param_type),
1213            body: Box::new(body),
1214        })
1215    }
1216
1217    /// Parse a term expression (handles infix operators like `equals` and `implies`)
1218    fn parse_term(&mut self) -> Result<Term, ParseError> {
1219        let lhs = self.parse_comparison()?;
1220
1221        self.skip_whitespace();
1222
1223        // Check for "equals" infix operator: X equals Y → Eq Hole X Y
1224        if self.peek_keyword("equals") {
1225            self.consume_keyword("equals")?;
1226            self.skip_whitespace();
1227            let rhs = self.parse_comparison()?; // Parse RHS as comparison (not full term to avoid recursion issues)
1228            return Ok(Term::App(
1229                Box::new(Term::App(
1230                    Box::new(Term::App(
1231                        Box::new(Term::Global("Eq".to_string())),
1232                        Box::new(Term::Hole), // Type placeholder (implicit argument)
1233                    )),
1234                    Box::new(lhs),
1235                )),
1236                Box::new(rhs),
1237            ));
1238        }
1239
1240        // Check for "implies" infix operator at term level too: X implies Y → Pi _ : X, Y
1241        if self.peek_keyword("implies") {
1242            self.consume_keyword("implies")?;
1243            self.skip_whitespace();
1244            let rhs = self.parse_term()?; // Full term for right-associativity
1245            return Ok(Term::Pi {
1246                param: "_".to_string(),
1247                param_type: Box::new(lhs),
1248                body_type: Box::new(rhs),
1249            });
1250        }
1251
1252        Ok(lhs)
1253    }
1254
1255    // ============================================================
1256    // INFIX OPERATOR PARSING (comparison, additive, multiplicative)
1257    // ============================================================
1258
1259    /// Parse comparison operators: x <= y, x < y, x >= y, x > y
1260    /// Precedence: lower than arithmetic, higher than equals/implies
1261    /// Non-associative: a < b < c is not valid
1262    fn parse_comparison(&mut self) -> Result<Term, ParseError> {
1263        let lhs = self.parse_additive()?;
1264        self.skip_whitespace();
1265
1266        // Check for comparison operators (order matters: >= before >, <= before <)
1267        let op_name = if self.try_consume("<=") || self.try_consume("≤") {
1268            Some("le")
1269        } else if self.try_consume(">=") || self.try_consume("≥") {
1270            Some("ge")
1271        } else if self.try_consume("<") {
1272            Some("lt")
1273        } else if self.try_consume(">") {
1274            Some("gt")
1275        } else {
1276            None
1277        };
1278
1279        if let Some(op) = op_name {
1280            self.skip_whitespace();
1281            let rhs = self.parse_additive()?; // Non-associative: parse additive, not comparison
1282            return Ok(Term::App(
1283                Box::new(Term::App(
1284                    Box::new(Term::Global(op.to_string())),
1285                    Box::new(lhs),
1286                )),
1287                Box::new(rhs),
1288            ));
1289        }
1290
1291        Ok(lhs)
1292    }
1293
1294    /// Parse additive operators: x + y, x - y
1295    /// Left-associative: a + b - c = (a + b) - c
1296    fn parse_additive(&mut self) -> Result<Term, ParseError> {
1297        let mut result = self.parse_multiplicative()?;
1298
1299        loop {
1300            self.skip_whitespace();
1301
1302            let op_name = if self.try_consume("+") {
1303                Some("add")
1304            } else if self.peek_char('-') && !self.peek_arrow() && !self.peek_negative_number() {
1305                self.advance(); // consume '-'
1306                Some("sub")
1307            } else {
1308                None
1309            };
1310
1311            if let Some(op) = op_name {
1312                self.skip_whitespace();
1313                let rhs = self.parse_multiplicative()?;
1314                result = Term::App(
1315                    Box::new(Term::App(
1316                        Box::new(Term::Global(op.to_string())),
1317                        Box::new(result),
1318                    )),
1319                    Box::new(rhs),
1320                );
1321            } else {
1322                break;
1323            }
1324        }
1325
1326        Ok(result)
1327    }
1328
1329    /// Parse multiplicative operators: x * y
1330    /// Left-associative: a * b * c = (a * b) * c
1331    fn parse_multiplicative(&mut self) -> Result<Term, ParseError> {
1332        let mut result = self.parse_app()?;
1333
1334        loop {
1335            self.skip_whitespace();
1336
1337            if self.try_consume("*") {
1338                self.skip_whitespace();
1339                let rhs = self.parse_app()?;
1340                result = Term::App(
1341                    Box::new(Term::App(
1342                        Box::new(Term::Global("mul".to_string())),
1343                        Box::new(result),
1344                    )),
1345                    Box::new(rhs),
1346                );
1347            } else {
1348                break;
1349            }
1350        }
1351
1352        Ok(result)
1353    }
1354
1355    /// Parse application: f x y or f(x, y)
1356    fn parse_app(&mut self) -> Result<Term, ParseError> {
1357        let mut func = self.parse_atom()?;
1358
1359        loop {
1360            self.skip_whitespace();
1361
1362            // Check for tuple-style call: f(x, y)
1363            if self.peek_char('(') {
1364                self.advance(); // consume '('
1365                self.skip_whitespace();
1366
1367                if !self.peek_char(')') {
1368                    loop {
1369                        let arg = self.parse_term()?;
1370                        func = Term::App(Box::new(func), Box::new(arg));
1371                        self.skip_whitespace();
1372
1373                        if self.try_consume(",") {
1374                            self.skip_whitespace();
1375                        } else {
1376                            break;
1377                        }
1378                    }
1379                }
1380
1381                if !self.try_consume(")") {
1382                    return Err(ParseError::Expected {
1383                        expected: "')'".to_string(),
1384                        found: self.peek_word().unwrap_or("EOF".to_string()),
1385                    });
1386                }
1387                continue;
1388            }
1389
1390            // Check for curried application (next token is an atom)
1391            // Stop before infix operators like `equals`, `implies`, `+`, `-`, `*`, `<`, etc.
1392            if self.at_end()
1393                || self.peek_char(')')
1394                || self.peek_char('.')
1395                || self.peek_char(',')
1396                || self.peek_char(':')
1397                || self.peek_char('|')
1398                || self.peek_keyword("When")
1399                || self.peek_keyword("Yield")
1400                || self.peek_keyword("and")
1401                || self.peek_keyword("or")
1402                || self.peek_keyword("equals")
1403                || self.peek_keyword("implies")
1404                // Stop at infix arithmetic and comparison operators
1405                || self.peek_char('+')
1406                || self.peek_char('*')
1407                || self.peek_comparison_operator()
1408                || (self.peek_char('-') && !self.peek_arrow() && !self.peek_negative_number())
1409            {
1410                break;
1411            }
1412
1413            // Try to parse next atom for curried application
1414            if let Ok(arg) = self.parse_atom() {
1415                func = Term::App(Box::new(func), Box::new(arg));
1416            } else {
1417                break;
1418            }
1419        }
1420
1421        Ok(func)
1422    }
1423
1424    /// Parse an atom: identifier, literal, or parenthesized term
1425    fn parse_atom(&mut self) -> Result<Term, ParseError> {
1426        self.skip_whitespace();
1427
1428        // Check for number literal
1429        if let Some(c) = self.peek() {
1430            if c.is_ascii_digit() {
1431                return self.parse_number();
1432            }
1433            if c == '-' {
1434                // Check for negative number
1435                let saved_pos = self.pos;
1436                self.advance();
1437                if let Some(next) = self.peek() {
1438                    if next.is_ascii_digit() {
1439                        self.pos = saved_pos;
1440                        return self.parse_number();
1441                    }
1442                }
1443                self.pos = saved_pos;
1444            }
1445        }
1446
1447        // Check for string literal
1448        if self.peek_char('"') {
1449            return self.parse_string();
1450        }
1451
1452        // Check for parenthesized term
1453        if self.peek_char('(') {
1454            self.advance();
1455            let term = self.parse_term()?;
1456            self.skip_whitespace();
1457            if !self.try_consume(")") {
1458                return Err(ParseError::Expected {
1459                    expected: "')'".to_string(),
1460                    found: self.peek_word().unwrap_or("EOF".to_string()),
1461                });
1462            }
1463            return Ok(term);
1464        }
1465
1466        // Check for "the" prefix (allows "the Successor of ...", "the diagonalization of ...", "the Name X")
1467        if self.try_consume_keyword("the") {
1468            self.skip_whitespace();
1469            // Check for "diagonalization of" special syntax
1470            if self.try_consume_keyword("diagonalization") {
1471                self.skip_whitespace();
1472                if !self.try_consume_keyword("of") {
1473                    return Err(ParseError::Expected {
1474                        expected: "'of'".to_string(),
1475                        found: self.peek_word().unwrap_or("EOF".to_string()),
1476                    });
1477                }
1478                self.skip_whitespace();
1479                let arg = self.parse_atom()?;
1480                return Ok(Term::App(
1481                    Box::new(Term::Global("syn_diag".to_string())),
1482                    Box::new(arg),
1483                ));
1484            }
1485            // Check for "the Name X" syntax → SName X
1486            if self.peek_keyword("Name") {
1487                self.consume_keyword("Name")?;
1488                self.skip_whitespace();
1489                let arg = self.parse_atom()?;
1490                return Ok(Term::App(
1491                    Box::new(Term::Global("SName".to_string())),
1492                    Box::new(arg),
1493                ));
1494            }
1495            // Otherwise continue to parse the identifier after "the"
1496        }
1497
1498        // Check for "Name" special syntax: Name "X" → SName "X"
1499        if self.peek_keyword("Name") {
1500            self.consume_keyword("Name")?;
1501            self.skip_whitespace();
1502            let arg = self.parse_atom()?;
1503            return Ok(Term::App(
1504                Box::new(Term::Global("SName".to_string())),
1505                Box::new(arg),
1506            ));
1507        }
1508
1509        // Check for "Variable" special syntax: Variable 0 → SVar 0
1510        if self.peek_keyword("Variable") {
1511            self.consume_keyword("Variable")?;
1512            self.skip_whitespace();
1513            let arg = self.parse_atom()?;
1514            return Ok(Term::App(
1515                Box::new(Term::Global("SVar".to_string())),
1516                Box::new(arg),
1517            ));
1518        }
1519
1520        // Check for "Apply" special syntax: Apply(f, x) → SApp f x
1521        if self.peek_keyword("Apply") {
1522            self.consume_keyword("Apply")?;
1523            self.skip_whitespace();
1524            if !self.try_consume("(") {
1525                return Err(ParseError::Expected {
1526                    expected: "'('".to_string(),
1527                    found: self.peek_word().unwrap_or("EOF".to_string()),
1528                });
1529            }
1530            self.skip_whitespace();
1531            let func_arg = self.parse_term()?;
1532            self.skip_whitespace();
1533            if !self.try_consume(",") {
1534                return Err(ParseError::Expected {
1535                    expected: "','".to_string(),
1536                    found: self.peek_word().unwrap_or("EOF".to_string()),
1537                });
1538            }
1539            self.skip_whitespace();
1540            let arg_arg = self.parse_term()?;
1541            self.skip_whitespace();
1542            if !self.try_consume(")") {
1543                return Err(ParseError::Expected {
1544                    expected: "')'".to_string(),
1545                    found: self.peek_word().unwrap_or("EOF".to_string()),
1546                });
1547            }
1548            return Ok(Term::App(
1549                Box::new(Term::App(
1550                    Box::new(Term::Global("SApp".to_string())),
1551                    Box::new(func_arg),
1552                )),
1553                Box::new(arg_arg),
1554            ));
1555        }
1556
1557        // Check for "there exists" special syntax
1558        if self.peek_keyword("there") {
1559            return self.parse_existential();
1560        }
1561
1562        // Parse identifier
1563        let ident = self.parse_ident()?;
1564
1565        // Check for "of" suffix (e.g., "Successor of x")
1566        self.skip_whitespace();
1567        if self.try_consume_keyword("of") {
1568            self.skip_whitespace();
1569            let arg = self.parse_atom()?;
1570            let func = if self.bound_vars.contains(&ident) {
1571                Term::Var(ident)
1572            } else {
1573                Term::Global(ident)
1574            };
1575            return Ok(Term::App(Box::new(func), Box::new(arg)));
1576        }
1577
1578        // Return as Var if bound, Global otherwise
1579        if self.bound_vars.contains(&ident) || self.current_function.as_ref() == Some(&ident) {
1580            Ok(Term::Var(ident))
1581        } else {
1582            // Check for special sorts
1583            match ident.as_str() {
1584                "Prop" => Ok(Term::Sort(Universe::Prop)),
1585                "Type" => Ok(Term::Sort(Universe::Type(0))),
1586                _ => Ok(Term::Global(ident)),
1587            }
1588        }
1589    }
1590
1591    /// Parse: there exists a [var]: [Type] such that [body]
1592    /// → Ex Type (fun var => body)
1593    fn parse_existential(&mut self) -> Result<Term, ParseError> {
1594        self.consume_keyword("there")?;
1595        self.skip_whitespace();
1596        self.consume_keyword("exists")?;
1597        self.skip_whitespace();
1598
1599        // Optional "a" or "an"
1600        let _ = self.try_consume_keyword("an") || self.try_consume_keyword("a");
1601        self.skip_whitespace();
1602
1603        // Parse variable name
1604        let var_name = self.parse_ident()?;
1605        self.skip_whitespace();
1606
1607        // Expect ":"
1608        if !self.try_consume(":") {
1609            return Err(ParseError::Expected {
1610                expected: "':'".to_string(),
1611                found: self.peek_word().unwrap_or("EOF".to_string()),
1612            });
1613        }
1614        self.skip_whitespace();
1615
1616        // Parse the type
1617        let var_type = self.parse_type()?;
1618        self.skip_whitespace();
1619
1620        // Expect "such that"
1621        if !self.try_consume_keyword("such") {
1622            return Err(ParseError::Expected {
1623                expected: "'such'".to_string(),
1624                found: self.peek_word().unwrap_or("EOF".to_string()),
1625            });
1626        }
1627        self.skip_whitespace();
1628        if !self.try_consume_keyword("that") {
1629            return Err(ParseError::Expected {
1630                expected: "'that'".to_string(),
1631                found: self.peek_word().unwrap_or("EOF".to_string()),
1632            });
1633        }
1634        self.skip_whitespace();
1635
1636        // Add variable to bound vars and parse body
1637        self.bound_vars.insert(var_name.clone());
1638        let body = self.parse_term()?;
1639        self.bound_vars.remove(&var_name);
1640
1641        // Build: Ex Type (fun var => body)
1642        Ok(Term::App(
1643            Box::new(Term::App(
1644                Box::new(Term::Global("Ex".to_string())),
1645                Box::new(var_type),
1646            )),
1647            Box::new(Term::Lambda {
1648                param: var_name,
1649                param_type: Box::new(Term::Global("_".to_string())), // Type inferred
1650                body: Box::new(body),
1651            }),
1652        ))
1653    }
1654
1655    /// Parse a type expression (same as term for now)
1656    fn parse_type(&mut self) -> Result<Term, ParseError> {
1657        self.skip_whitespace();
1658
1659        // Handle "List of T" style
1660        let base = self.parse_ident()?;
1661
1662        self.skip_whitespace();
1663
1664        // Check for "of" (polymorphic application)
1665        if self.try_consume_keyword("of") {
1666            self.skip_whitespace();
1667            let arg = self.parse_type()?;
1668            return Ok(Term::App(
1669                Box::new(Term::Global(base)),
1670                Box::new(arg),
1671            ));
1672        }
1673
1674        // Check for special sorts
1675        match base.as_str() {
1676            "Prop" => Ok(Term::Sort(Universe::Prop)),
1677            "Type" => Ok(Term::Sort(Universe::Type(0))),
1678            _ => Ok(Term::Global(base)),
1679        }
1680    }
1681
1682    /// Parse a number literal
1683    fn parse_number(&mut self) -> Result<Term, ParseError> {
1684        let mut num_str = String::new();
1685
1686        // Handle negative sign
1687        if self.peek_char('-') {
1688            num_str.push('-');
1689            self.advance();
1690        }
1691
1692        // Collect digits
1693        while let Some(c) = self.peek() {
1694            if c.is_ascii_digit() {
1695                num_str.push(c);
1696                self.advance();
1697            } else {
1698                break;
1699            }
1700        }
1701
1702        let value: i64 = num_str
1703            .parse()
1704            .map_err(|_| ParseError::InvalidNumber(num_str))?;
1705
1706        Ok(Term::Lit(Literal::Int(value)))
1707    }
1708
1709    /// Parse a string literal
1710    fn parse_string(&mut self) -> Result<Term, ParseError> {
1711        self.advance(); // consume opening '"'
1712
1713        let mut content = String::new();
1714        loop {
1715            match self.peek() {
1716                Some('"') => {
1717                    self.advance();
1718                    break;
1719                }
1720                Some('\\') => {
1721                    self.advance();
1722                    match self.peek() {
1723                        Some('n') => {
1724                            content.push('\n');
1725                            self.advance();
1726                        }
1727                        Some('t') => {
1728                            content.push('\t');
1729                            self.advance();
1730                        }
1731                        Some(c) => {
1732                            content.push(c);
1733                            self.advance();
1734                        }
1735                        None => return Err(ParseError::UnexpectedEof),
1736                    }
1737                }
1738                Some(c) => {
1739                    content.push(c);
1740                    self.advance();
1741                }
1742                None => return Err(ParseError::UnexpectedEof),
1743            }
1744        }
1745
1746        Ok(Term::Lit(Literal::Text(content)))
1747    }
1748
1749    // ============================================================
1750    // TERM TO SYNTAX REIFICATION
1751    // ============================================================
1752
1753    /// Convert a high-level Term to deeply-embedded Syntax representation.
1754    /// This is used by proof tactics like `ring.` that operate on Syntax.
1755    fn term_to_syntax(&self, term: &Term, bound_vars: &[String]) -> Term {
1756        match term {
1757            Term::Var(name) => {
1758                // Convert to SVar with de Bruijn index if bound, else SName
1759                if let Some(idx) = bound_vars.iter().rev().position(|n| n == name) {
1760                    Term::App(
1761                        Box::new(Term::Global("SVar".to_string())),
1762                        Box::new(Term::Lit(Literal::Int(idx as i64))),
1763                    )
1764                } else {
1765                    // Free variable - treat as SName
1766                    Term::App(
1767                        Box::new(Term::Global("SName".to_string())),
1768                        Box::new(Term::Lit(Literal::Text(name.clone()))),
1769                    )
1770                }
1771            }
1772            Term::Global(name) => {
1773                // Global names become SName
1774                Term::App(
1775                    Box::new(Term::Global("SName".to_string())),
1776                    Box::new(Term::Lit(Literal::Text(name.clone()))),
1777                )
1778            }
1779            Term::Const { name, .. } => {
1780                // A universe-polymorphic reference reifies by its name (reflection never
1781                // sees level arguments).
1782                Term::App(
1783                    Box::new(Term::Global("SName".to_string())),
1784                    Box::new(Term::Lit(Literal::Text(name.clone()))),
1785                )
1786            }
1787            Term::Lit(Literal::Int(n)) => {
1788                // Integer literals become SLit
1789                Term::App(
1790                    Box::new(Term::Global("SLit".to_string())),
1791                    Box::new(Term::Lit(Literal::Int(*n))),
1792                )
1793            }
1794            Term::Lit(Literal::Float(_f)) => {
1795                // Float literals - treat as error for ring (ring doesn't handle floats)
1796                Term::App(
1797                    Box::new(Term::Global("SName".to_string())),
1798                    Box::new(Term::Lit(Literal::Text("Error_Float".to_string()))),
1799                )
1800            }
1801            Term::Lit(Literal::BigInt(_)) => {
1802                // BigInt literals do not fit the ring-syntax layer's machine-int SLit.
1803                Term::App(
1804                    Box::new(Term::Global("SName".to_string())),
1805                    Box::new(Term::Lit(Literal::Text("Error_BigInt".to_string()))),
1806                )
1807            }
1808            Term::Lit(Literal::Nat(_)) => {
1809                // Nat literals do not fit the ring-syntax layer's machine-int SLit.
1810                Term::App(
1811                    Box::new(Term::Global("SName".to_string())),
1812                    Box::new(Term::Lit(Literal::Text("Error_Nat".to_string()))),
1813                )
1814            }
1815            Term::Lit(Literal::Duration(_d)) => {
1816                // Duration literals - treat as error for ring (ring doesn't handle durations)
1817                Term::App(
1818                    Box::new(Term::Global("SName".to_string())),
1819                    Box::new(Term::Lit(Literal::Text("Error_Duration".to_string()))),
1820                )
1821            }
1822            Term::Lit(Literal::Date(_d)) => {
1823                // Date literals - treat as error for ring (ring doesn't handle dates)
1824                Term::App(
1825                    Box::new(Term::Global("SName".to_string())),
1826                    Box::new(Term::Lit(Literal::Text("Error_Date".to_string()))),
1827                )
1828            }
1829            Term::Lit(Literal::Moment(_m)) => {
1830                // Moment literals - treat as error for ring (ring doesn't handle moments)
1831                Term::App(
1832                    Box::new(Term::Global("SName".to_string())),
1833                    Box::new(Term::Lit(Literal::Text("Error_Moment".to_string()))),
1834                )
1835            }
1836            Term::Lit(Literal::Text(s)) => {
1837                // Text literals become SLit (wrapped in SName for now)
1838                Term::App(
1839                    Box::new(Term::Global("SName".to_string())),
1840                    Box::new(Term::Lit(Literal::Text(s.clone()))),
1841                )
1842            }
1843            Term::App(f, x) => {
1844                // Applications become SApp
1845                let f_syn = self.term_to_syntax(f, bound_vars);
1846                let x_syn = self.term_to_syntax(x, bound_vars);
1847                Term::App(
1848                    Box::new(Term::App(
1849                        Box::new(Term::Global("SApp".to_string())),
1850                        Box::new(f_syn),
1851                    )),
1852                    Box::new(x_syn),
1853                )
1854            }
1855            Term::Lambda { param, param_type, body } => {
1856                // Lambdas become SLam (if we have that constructor)
1857                let ty_syn = self.term_to_syntax(param_type, bound_vars);
1858                let mut new_bound = bound_vars.to_vec();
1859                new_bound.push(param.clone());
1860                let body_syn = self.term_to_syntax(body, &new_bound);
1861                Term::App(
1862                    Box::new(Term::App(
1863                        Box::new(Term::Global("SLam".to_string())),
1864                        Box::new(ty_syn),
1865                    )),
1866                    Box::new(body_syn),
1867                )
1868            }
1869            Term::Pi { param, param_type, body_type } => {
1870                // Pi types become SPi
1871                let ty_syn = self.term_to_syntax(param_type, bound_vars);
1872                let mut new_bound = bound_vars.to_vec();
1873                new_bound.push(param.clone());
1874                let body_syn = self.term_to_syntax(body_type, &new_bound);
1875                Term::App(
1876                    Box::new(Term::App(
1877                        Box::new(Term::Global("SPi".to_string())),
1878                        Box::new(ty_syn),
1879                    )),
1880                    Box::new(body_syn),
1881                )
1882            }
1883            Term::Sort(Universe::Prop) => {
1884                // Prop sort becomes SSort UProp
1885                Term::App(
1886                    Box::new(Term::Global("SSort".to_string())),
1887                    Box::new(Term::Global("UProp".to_string())),
1888                )
1889            }
1890            Term::Sort(Universe::Type(n)) => {
1891                // Type sort becomes SSort (UType n)
1892                Term::App(
1893                    Box::new(Term::Global("SSort".to_string())),
1894                    Box::new(Term::App(
1895                        Box::new(Term::Global("UType".to_string())),
1896                        Box::new(Term::Lit(Literal::Int(*n as i64))),
1897                    )),
1898                )
1899            }
1900            Term::Sort(_) => {
1901                // Universe-polymorphic levels (Var/Succ/Max) don't arise in ring proofs;
1902                // reify conservatively as an opaque SSort so the conversion stays total.
1903                Term::App(
1904                    Box::new(Term::Global("SSort".to_string())),
1905                    Box::new(Term::Global("UPoly".to_string())),
1906                )
1907            }
1908            Term::Hole => {
1909                // Holes default to Int type for ring proofs
1910                Term::App(
1911                    Box::new(Term::Global("SName".to_string())),
1912                    Box::new(Term::Lit(Literal::Text("Int".to_string()))),
1913                )
1914            }
1915            Term::Match { .. } | Term::Fix { .. } | Term::MutualFix { .. } | Term::Let { .. } => {
1916                // Match, Fix, MutualFix, and Let are complex - return error marker
1917                Term::App(
1918                    Box::new(Term::Global("SName".to_string())),
1919                    Box::new(Term::Lit(Literal::Text("Error".to_string()))),
1920                )
1921            }
1922        }
1923    }
1924
1925    // ============================================================
1926    // SELF-REFERENCE DETECTION
1927    // ============================================================
1928
1929    /// Check if the function name appears in the term (for implicit fixpoint)
1930    fn contains_self_reference(&self, name: &str, term: &Term) -> bool {
1931        match term {
1932            Term::Var(v) => v == name,
1933            Term::Global(_) => false,
1934            Term::Const { .. } => false,
1935            Term::Sort(_) => false,
1936            Term::Lit(_) => false,
1937            Term::Pi { param_type, body_type, .. } => {
1938                self.contains_self_reference(name, param_type)
1939                    || self.contains_self_reference(name, body_type)
1940            }
1941            Term::Lambda { param_type, body, .. } => {
1942                self.contains_self_reference(name, param_type)
1943                    || self.contains_self_reference(name, body)
1944            }
1945            Term::App(f, a) => {
1946                self.contains_self_reference(name, f) || self.contains_self_reference(name, a)
1947            }
1948            Term::Match { discriminant, motive, cases } => {
1949                self.contains_self_reference(name, discriminant)
1950                    || self.contains_self_reference(name, motive)
1951                    || cases.iter().any(|c| self.contains_self_reference(name, c))
1952            }
1953            Term::Fix { body, .. } => self.contains_self_reference(name, body),
1954            Term::MutualFix { defs, .. } => {
1955                defs.iter().any(|(_, b)| self.contains_self_reference(name, b))
1956            }
1957            Term::Let { ty, value, body, .. } => {
1958                self.contains_self_reference(name, ty)
1959                    || self.contains_self_reference(name, value)
1960                    || self.contains_self_reference(name, body)
1961            }
1962            Term::Hole => false, // Holes never contain self-references
1963        }
1964    }
1965
1966    // ============================================================
1967    // LOW-LEVEL UTILITIES
1968    // ============================================================
1969
1970    fn skip_whitespace(&mut self) {
1971        while let Some(c) = self.peek() {
1972            if c == ' ' || c == '\t' {
1973                self.advance();
1974            } else {
1975                break;
1976            }
1977        }
1978    }
1979
1980    fn skip_whitespace_and_newlines(&mut self) {
1981        while let Some(c) = self.peek() {
1982            if c.is_whitespace() {
1983                self.advance();
1984            } else {
1985                break;
1986            }
1987        }
1988    }
1989
1990    fn peek(&self) -> Option<char> {
1991        self.input[self.pos..].chars().next()
1992    }
1993
1994    fn peek_char(&self, c: char) -> bool {
1995        self.peek() == Some(c)
1996    }
1997
1998    fn peek_keyword(&self, keyword: &str) -> bool {
1999        if !self.input[self.pos..].starts_with(keyword) {
2000            return false;
2001        }
2002        let after = self.pos + keyword.len();
2003        if after >= self.input.len() {
2004            return true;
2005        }
2006        let next_char = self.input[after..].chars().next();
2007        !next_char.map(|c| c.is_alphanumeric() || c == '_').unwrap_or(false)
2008    }
2009
2010    fn peek_word(&self) -> Option<String> {
2011        let start = self.pos;
2012        let mut end = start;
2013        for c in self.input[start..].chars() {
2014            if c.is_alphanumeric() || c == '_' {
2015                end += c.len_utf8();
2016            } else {
2017                break;
2018            }
2019        }
2020        if end > start {
2021            Some(self.input[start..end].to_string())
2022        } else {
2023            self.peek().map(|c| c.to_string())
2024        }
2025    }
2026
2027    fn advance(&mut self) {
2028        if let Some(c) = self.peek() {
2029            self.pos += c.len_utf8();
2030        }
2031    }
2032
2033    fn at_end(&self) -> bool {
2034        self.pos >= self.input.len()
2035    }
2036
2037    fn try_consume(&mut self, s: &str) -> bool {
2038        if self.input[self.pos..].starts_with(s) {
2039            self.pos += s.len();
2040            true
2041        } else {
2042            false
2043        }
2044    }
2045
2046    fn try_consume_keyword(&mut self, keyword: &str) -> bool {
2047        if self.peek_keyword(keyword) {
2048            self.pos += keyword.len();
2049            true
2050        } else {
2051            false
2052        }
2053    }
2054
2055    fn consume_keyword(&mut self, keyword: &str) -> Result<(), ParseError> {
2056        if self.try_consume_keyword(keyword) {
2057            Ok(())
2058        } else {
2059            Err(ParseError::Expected {
2060                expected: format!("'{}'", keyword),
2061                found: self.peek_word().unwrap_or("EOF".to_string()),
2062            })
2063        }
2064    }
2065
2066    fn parse_ident(&mut self) -> Result<String, ParseError> {
2067        self.skip_whitespace();
2068        let start = self.pos;
2069
2070        // First char must be alphabetic or underscore
2071        if let Some(c) = self.peek() {
2072            if !c.is_alphabetic() && c != '_' {
2073                return Err(ParseError::Expected {
2074                    expected: "identifier".to_string(),
2075                    found: c.to_string(),
2076                });
2077            }
2078        } else {
2079            return Err(ParseError::UnexpectedEof);
2080        }
2081
2082        // Consume alphanumeric and underscore
2083        while let Some(c) = self.peek() {
2084            if c.is_alphanumeric() || c == '_' {
2085                self.advance();
2086            } else {
2087                break;
2088            }
2089        }
2090
2091        let ident = self.input[start..self.pos].to_string();
2092        if ident.is_empty() {
2093            Err(ParseError::InvalidIdent("empty".to_string()))
2094        } else {
2095            Ok(ident)
2096        }
2097    }
2098
2099    // ============================================================
2100    // INFIX OPERATOR HELPERS
2101    // ============================================================
2102
2103    /// Check if current position starts a negative number (- followed by digit)
2104    fn peek_negative_number(&self) -> bool {
2105        if !self.peek_char('-') {
2106            return false;
2107        }
2108        self.input.get(self.pos + 1..)
2109            .and_then(|s| s.chars().next())
2110            .map(|c| c.is_ascii_digit())
2111            .unwrap_or(false)
2112    }
2113
2114    /// Check if current position starts an arrow (->)
2115    fn peek_arrow(&self) -> bool {
2116        self.input[self.pos..].starts_with("->")
2117    }
2118
2119    /// Check if current position has a comparison operator
2120    fn peek_comparison_operator(&self) -> bool {
2121        let rest = &self.input[self.pos..];
2122        rest.starts_with("<=") || rest.starts_with(">=")
2123            || rest.starts_with("≤") || rest.starts_with("≥")
2124            || rest.starts_with('<') || rest.starts_with('>')
2125    }
2126}
2127
2128// ============================================================
2129// UNIT TESTS
2130// ============================================================
2131
2132#[cfg(test)]
2133mod tests {
2134    use super::*;
2135
2136    #[test]
2137    fn test_parse_simple_inductive() {
2138        let cmd = parse_inductive("A Bool is either Yes or No").unwrap();
2139        if let Command::Inductive { name, constructors, .. } = cmd {
2140            assert_eq!(name, "Bool");
2141            assert_eq!(constructors.len(), 2);
2142            assert_eq!(constructors[0].0, "Yes");
2143            assert_eq!(constructors[1].0, "No");
2144        } else {
2145            panic!("Expected Inductive command");
2146        }
2147    }
2148
2149    #[test]
2150    fn test_parse_inductive_with_article() {
2151        let cmd = parse_inductive("A Decision is either a Yes or a No").unwrap();
2152        if let Command::Inductive { name, constructors, .. } = cmd {
2153            assert_eq!(name, "Decision");
2154            assert_eq!(constructors.len(), 2);
2155        } else {
2156            panic!("Expected Inductive command");
2157        }
2158    }
2159
2160    #[test]
2161    fn test_parse_recursive_inductive() {
2162        let cmd = parse_inductive("A Nat is either Zero or a Succ with pred: Nat").unwrap();
2163        if let Command::Inductive { name, constructors, .. } = cmd {
2164            assert_eq!(name, "Nat");
2165            assert_eq!(constructors.len(), 2);
2166            assert_eq!(constructors[0].0, "Zero");
2167            assert_eq!(constructors[1].0, "Succ");
2168            // Succ should have type Nat -> Nat
2169            if let Term::Pi { .. } = &constructors[1].1 {
2170                // Good - it's a function type
2171            } else {
2172                panic!("Expected Succ to have Pi type");
2173            }
2174        } else {
2175            panic!("Expected Inductive command");
2176        }
2177    }
2178
2179    #[test]
2180    fn test_parse_simple_definition() {
2181        let cmd = parse_definition("## To id (x: Nat) -> Nat: Yield x").unwrap();
2182        if let Command::Definition { name, body, .. } = cmd {
2183            assert_eq!(name, "id");
2184            // Body should be a lambda
2185            if let Term::Lambda { param, .. } = body {
2186                assert_eq!(param, "x");
2187            } else {
2188                panic!("Expected Lambda body");
2189            }
2190        } else {
2191            panic!("Expected Definition command");
2192        }
2193    }
2194
2195    #[test]
2196    fn test_implicit_fixpoint_detection() {
2197        // This definition is recursive (add appears in body)
2198        let cmd = parse_definition(
2199            "## To add (n: Nat) and (m: Nat) -> Nat: Consider n: When Zero: Yield m. When Succ k: Yield Succ (add k m)."
2200        ).unwrap();
2201
2202        if let Command::Definition { name, body, .. } = cmd {
2203            assert_eq!(name, "add");
2204            // Body should be wrapped in Fix
2205            if let Term::Fix { name: fix_name, .. } = body {
2206                assert_eq!(fix_name, "add");
2207            } else {
2208                panic!("Expected Fix wrapper for recursive function");
2209            }
2210        } else {
2211            panic!("Expected Definition command");
2212        }
2213    }
2214
2215    #[test]
2216    fn test_parse_given_lambda() {
2217        let mut parser = LiterateParser::new("given x: Nat yields Succ x");
2218        let term = parser.parse_given_lambda().unwrap();
2219
2220        if let Term::Lambda { param, .. } = term {
2221            assert_eq!(param, "x");
2222        } else {
2223            panic!("Expected Lambda");
2224        }
2225    }
2226
2227    #[test]
2228    fn test_parse_pipe_lambda() {
2229        let mut parser = LiterateParser::new("|x: Nat| -> Succ x");
2230        let term = parser.parse_pipe_lambda().unwrap();
2231
2232        if let Term::Lambda { param, .. } = term {
2233            assert_eq!(param, "x");
2234        } else {
2235            panic!("Expected Lambda");
2236        }
2237    }
2238
2239    // ============================================================
2240    // Syntax (Deep Embedding) Tests
2241    // ============================================================
2242
2243    #[test]
2244    fn test_parse_let_definition() {
2245        let cmd = parse_let_definition("Let T be Zero").unwrap();
2246        if let Command::Definition { name, ty, body, .. } = cmd {
2247            assert_eq!(name, "T");
2248            assert!(ty.is_none());
2249            if let Term::Global(g) = body {
2250                assert_eq!(g, "Zero");
2251            } else {
2252                panic!("Expected Global term");
2253            }
2254        } else {
2255            panic!("Expected Definition command");
2256        }
2257    }
2258
2259    #[test]
2260    fn test_parse_name_syntax() {
2261        let mut parser = LiterateParser::new("Name \"Not\"");
2262        let term = parser.parse_term().unwrap();
2263
2264        // Should be SName "Not"
2265        if let Term::App(f, arg) = term {
2266            if let Term::Global(g) = *f {
2267                assert_eq!(g, "SName");
2268            } else {
2269                panic!("Expected Global SName");
2270            }
2271            if let Term::Lit(Literal::Text(s)) = *arg {
2272                assert_eq!(s, "Not");
2273            } else {
2274                panic!("Expected Text literal");
2275            }
2276        } else {
2277            panic!("Expected App");
2278        }
2279    }
2280
2281    #[test]
2282    fn test_parse_variable_syntax() {
2283        let mut parser = LiterateParser::new("Variable 0");
2284        let term = parser.parse_term().unwrap();
2285
2286        // Should be SVar 0
2287        if let Term::App(f, arg) = term {
2288            if let Term::Global(g) = *f {
2289                assert_eq!(g, "SVar");
2290            } else {
2291                panic!("Expected Global SVar");
2292            }
2293            if let Term::Lit(Literal::Int(n)) = *arg {
2294                assert_eq!(n, 0);
2295            } else {
2296                panic!("Expected Int literal");
2297            }
2298        } else {
2299            panic!("Expected App");
2300        }
2301    }
2302
2303    #[test]
2304    fn test_parse_apply_syntax() {
2305        let mut parser = LiterateParser::new("Apply(Name \"Not\", Variable 0)");
2306        let term = parser.parse_term().unwrap();
2307
2308        // Should be (SApp (SName "Not") (SVar 0))
2309        if let Term::App(outer_f, _outer_arg) = term {
2310            if let Term::App(inner_f, _inner_arg) = *outer_f {
2311                if let Term::Global(g) = *inner_f {
2312                    assert_eq!(g, "SApp");
2313                } else {
2314                    panic!("Expected Global SApp");
2315                }
2316            } else {
2317                panic!("Expected inner App");
2318            }
2319        } else {
2320            panic!("Expected outer App");
2321        }
2322    }
2323
2324    #[test]
2325    fn test_parse_diagonalization() {
2326        let mut parser = LiterateParser::new("the diagonalization of T");
2327        let term = parser.parse_term().unwrap();
2328
2329        // Should be syn_diag T
2330        if let Term::App(f, arg) = term {
2331            if let Term::Global(g) = *f {
2332                assert_eq!(g, "syn_diag");
2333            } else {
2334                panic!("Expected Global syn_diag");
2335            }
2336            if let Term::Global(a) = *arg {
2337                assert_eq!(a, "T");
2338            } else {
2339                panic!("Expected Global T");
2340            }
2341        } else {
2342            panic!("Expected App");
2343        }
2344    }
2345
2346    #[test]
2347    fn test_parse_implies() {
2348        let mut parser = LiterateParser::new("A implies B");
2349        let term = parser.parse_term().unwrap();
2350
2351        // Should be forall _ : A, B (non-dependent Pi)
2352        if let Term::Pi { param, param_type, body_type } = term {
2353            assert_eq!(param, "_");
2354            if let Term::Global(a) = *param_type {
2355                assert_eq!(a, "A");
2356            } else {
2357                panic!("Expected Global A");
2358            }
2359            if let Term::Global(b) = *body_type {
2360                assert_eq!(b, "B");
2361            } else {
2362                panic!("Expected Global B");
2363            }
2364        } else {
2365            panic!("Expected Pi");
2366        }
2367    }
2368
2369    #[test]
2370    fn test_parse_existential() {
2371        let mut parser = LiterateParser::new("there exists a d: Derivation such that P");
2372        let term = parser.parse_term().unwrap();
2373
2374        // Should be Ex Derivation (fun d => P)
2375        if let Term::App(outer_f, lambda) = term {
2376            if let Term::App(ex, typ) = *outer_f {
2377                if let Term::Global(g) = *ex {
2378                    assert_eq!(g, "Ex");
2379                } else {
2380                    panic!("Expected Global Ex");
2381                }
2382                if let Term::Global(t) = *typ {
2383                    assert_eq!(t, "Derivation");
2384                } else {
2385                    panic!("Expected Global Derivation");
2386                }
2387            } else {
2388                panic!("Expected inner App for Ex");
2389            }
2390            if let Term::Lambda { param, body, .. } = *lambda {
2391                assert_eq!(param, "d");
2392                if let Term::Global(p) = *body {
2393                    assert_eq!(p, "P");
2394                } else {
2395                    panic!("Expected Global P in lambda body");
2396                }
2397            } else {
2398                panic!("Expected Lambda");
2399            }
2400        } else {
2401            panic!("Expected App");
2402        }
2403    }
2404
2405    #[test]
2406    fn test_parse_complex_let_with_apply() {
2407        let cmd = parse_let_definition("Let T be Apply(Name \"Not\", Apply(Name \"Provable\", Variable 0)).").unwrap();
2408        if let Command::Definition { name, ty, body, .. } = cmd {
2409            assert_eq!(name, "T");
2410            assert!(ty.is_none());
2411            // Body should be nested applications
2412            if let Term::App(_, _) = body {
2413                // Good - it's an application
2414            } else {
2415                panic!("Expected App body");
2416            }
2417        } else {
2418            panic!("Expected Definition command");
2419        }
2420    }
2421
2422    // ============================================================
2423    // Predicate Syntax Tests
2424    // ============================================================
2425
2426    #[test]
2427    fn test_parse_predicate_definition() {
2428        // ## To be Provable (s: Syntax) -> Prop: Yield s
2429        let cmd = parse_definition("## To be Provable (s: Syntax) -> Prop: Yield s").unwrap();
2430        if let Command::Definition { name, .. } = cmd {
2431            assert_eq!(name, "Provable"); // NOT "be"
2432        } else {
2433            panic!("Expected Definition command");
2434        }
2435    }
2436
2437    #[test]
2438    fn test_parse_nullary_predicate() {
2439        // ## To be Consistent -> Prop: Yield True
2440        let cmd = parse_definition("## To be Consistent -> Prop: Yield True").unwrap();
2441        if let Command::Definition { name, body, .. } = cmd {
2442            assert_eq!(name, "Consistent");
2443            // Body should be True (no lambda wrapper since no params)
2444            if let Term::Global(g) = body {
2445                assert_eq!(g, "True");
2446            } else {
2447                panic!("Expected Global True");
2448            }
2449        } else {
2450            panic!("Expected Definition command");
2451        }
2452    }
2453
2454    #[test]
2455    fn test_parse_the_name_syntax() {
2456        // the Name "Not" → SName "Not"
2457        let mut parser = LiterateParser::new("the Name \"Not\"");
2458        let term = parser.parse_term().unwrap();
2459
2460        if let Term::App(f, arg) = term {
2461            if let Term::Global(g) = *f {
2462                assert_eq!(g, "SName");
2463            } else {
2464                panic!("Expected Global SName");
2465            }
2466            if let Term::Lit(Literal::Text(s)) = *arg {
2467                assert_eq!(s, "Not");
2468            } else {
2469                panic!("Expected Text literal");
2470            }
2471        } else {
2472            panic!("Expected App");
2473        }
2474    }
2475
2476    #[test]
2477    fn test_parse_theorem() {
2478        // ## Theorem: MyTheorem\n    Statement: True implies True.
2479        let cmd = parse_theorem("## Theorem: MyTheorem\n    Statement: A implies B.").unwrap();
2480        if let Command::Definition { name, ty, body, .. } = cmd {
2481            assert_eq!(name, "MyTheorem");
2482            // Type should be Prop
2483            assert!(ty.is_some());
2484            if let Some(Term::Sort(Universe::Prop)) = ty {
2485                // Good
2486            } else {
2487                panic!("Expected Prop type");
2488            }
2489            // Body should be Pi (A implies B)
2490            if let Term::Pi { .. } = body {
2491                // Good
2492            } else {
2493                panic!("Expected Pi body (implication)");
2494            }
2495        } else {
2496            panic!("Expected Definition command");
2497        }
2498    }
2499
2500    #[test]
2501    fn test_parse_theorem_with_complex_statement() {
2502        // ## Theorem: Godel\n    Statement: Consistent implies Not(Provable(G)).
2503        let cmd = parse_theorem("## Theorem: Godel\n    Statement: Consistent implies Not(Provable(G)).").unwrap();
2504        if let Command::Definition { name, ty, .. } = cmd {
2505            assert_eq!(name, "Godel");
2506            assert!(ty.is_some());
2507            if let Some(Term::Sort(Universe::Prop)) = ty {
2508                // Good
2509            } else {
2510                panic!("Expected Prop type");
2511            }
2512        } else {
2513            panic!("Expected Definition command");
2514        }
2515    }
2516
2517    #[test]
2518    fn test_parse_equals_infix() {
2519        // X equals Y → Eq Hole X Y
2520        let mut parser = LiterateParser::new("A equals B");
2521        let term = parser.parse_term().unwrap();
2522
2523        // Should be (Eq Hole A B) = App(App(App(Eq, Hole), A), B)
2524        if let Term::App(outer, rhs) = term {
2525            if let Term::Global(b) = *rhs {
2526                assert_eq!(b, "B");
2527            } else {
2528                panic!("Expected Global B");
2529            }
2530            if let Term::App(mid, lhs) = *outer {
2531                if let Term::Global(a) = *lhs {
2532                    assert_eq!(a, "A");
2533                } else {
2534                    panic!("Expected Global A");
2535                }
2536                if let Term::App(inner, placeholder) = *mid {
2537                    if let Term::Global(eq) = *inner {
2538                        assert_eq!(eq, "Eq");
2539                    } else {
2540                        panic!("Expected Global Eq");
2541                    }
2542                    if !matches!(*placeholder, Term::Hole) {
2543                        panic!("Expected Hole placeholder");
2544                    }
2545                } else {
2546                    panic!("Expected inner App");
2547                }
2548            } else {
2549                panic!("Expected mid App");
2550            }
2551        } else {
2552            panic!("Expected outer App");
2553        }
2554    }
2555
2556    #[test]
2557    fn test_parse_equals_with_application() {
2558        // f(x) equals y → Eq _ (f x) y
2559        let mut parser = LiterateParser::new("f(x) equals y");
2560        let term = parser.parse_term().unwrap();
2561
2562        // Should be App(App(App(Eq, _), App(f, x)), y)
2563        if let Term::App(outer, rhs) = term {
2564            if let Term::Global(y) = *rhs {
2565                assert_eq!(y, "y");
2566            } else {
2567                panic!("Expected Global y");
2568            }
2569            if let Term::App(mid, lhs) = *outer {
2570                // lhs should be App(f, x)
2571                if let Term::App(f_box, x_box) = *lhs {
2572                    if let Term::Global(f) = *f_box {
2573                        assert_eq!(f, "f");
2574                    }
2575                    if let Term::Global(x) = *x_box {
2576                        assert_eq!(x, "x");
2577                    }
2578                } else {
2579                    panic!("Expected lhs to be App(f, x)");
2580                }
2581            } else {
2582                panic!("Expected mid App");
2583            }
2584        } else {
2585            panic!("Expected outer App");
2586        }
2587    }
2588
2589    // ============================================================
2590    // INFIX OPERATOR TESTS
2591    // ============================================================
2592
2593    #[test]
2594    fn test_parse_infix_le() {
2595        let mut parser = LiterateParser::new("x <= y");
2596        let term = parser.parse_term().unwrap();
2597        // Should produce: App(App(Global("le"), Global("x")), Global("y"))
2598        if let Term::App(outer, rhs) = term {
2599            if let Term::App(inner, lhs) = *outer {
2600                if let Term::Global(op) = *inner {
2601                    assert_eq!(op, "le");
2602                } else {
2603                    panic!("Expected Global le");
2604                }
2605                if let Term::Global(l) = *lhs {
2606                    assert_eq!(l, "x");
2607                } else {
2608                    panic!("Expected Global x");
2609                }
2610            } else {
2611                panic!("Expected inner App");
2612            }
2613            if let Term::Global(r) = *rhs {
2614                assert_eq!(r, "y");
2615            } else {
2616                panic!("Expected Global y");
2617            }
2618        } else {
2619            panic!("Expected outer App");
2620        }
2621    }
2622
2623    #[test]
2624    fn test_parse_infix_lt() {
2625        let mut parser = LiterateParser::new("a < b");
2626        let term = parser.parse_term().unwrap();
2627        if let Term::App(outer, _) = term {
2628            if let Term::App(inner, _) = *outer {
2629                if let Term::Global(op) = *inner {
2630                    assert_eq!(op, "lt");
2631                } else {
2632                    panic!("Expected Global lt");
2633                }
2634            } else {
2635                panic!("Expected inner App");
2636            }
2637        } else {
2638            panic!("Expected outer App");
2639        }
2640    }
2641
2642    #[test]
2643    fn test_parse_infix_ge() {
2644        let mut parser = LiterateParser::new("x >= y");
2645        let term = parser.parse_term().unwrap();
2646        if let Term::App(outer, _) = term {
2647            if let Term::App(inner, _) = *outer {
2648                if let Term::Global(op) = *inner {
2649                    assert_eq!(op, "ge");
2650                } else {
2651                    panic!("Expected Global ge");
2652                }
2653            } else {
2654                panic!("Expected inner App");
2655            }
2656        } else {
2657            panic!("Expected outer App");
2658        }
2659    }
2660
2661    #[test]
2662    fn test_parse_infix_gt() {
2663        let mut parser = LiterateParser::new("x > y");
2664        let term = parser.parse_term().unwrap();
2665        if let Term::App(outer, _) = term {
2666            if let Term::App(inner, _) = *outer {
2667                if let Term::Global(op) = *inner {
2668                    assert_eq!(op, "gt");
2669                } else {
2670                    panic!("Expected Global gt");
2671                }
2672            } else {
2673                panic!("Expected inner App");
2674            }
2675        } else {
2676            panic!("Expected outer App");
2677        }
2678    }
2679
2680    #[test]
2681    fn test_parse_infix_add() {
2682        let mut parser = LiterateParser::new("x + y");
2683        let term = parser.parse_term().unwrap();
2684        if let Term::App(outer, _) = term {
2685            if let Term::App(inner, _) = *outer {
2686                if let Term::Global(op) = *inner {
2687                    assert_eq!(op, "add");
2688                } else {
2689                    panic!("Expected Global add");
2690                }
2691            } else {
2692                panic!("Expected inner App");
2693            }
2694        } else {
2695            panic!("Expected outer App");
2696        }
2697    }
2698
2699    #[test]
2700    fn test_parse_infix_sub() {
2701        let mut parser = LiterateParser::new("x - y");
2702        let term = parser.parse_term().unwrap();
2703        if let Term::App(outer, _) = term {
2704            if let Term::App(inner, _) = *outer {
2705                if let Term::Global(op) = *inner {
2706                    assert_eq!(op, "sub");
2707                } else {
2708                    panic!("Expected Global sub");
2709                }
2710            } else {
2711                panic!("Expected inner App");
2712            }
2713        } else {
2714            panic!("Expected outer App");
2715        }
2716    }
2717
2718    #[test]
2719    fn test_parse_infix_mul() {
2720        let mut parser = LiterateParser::new("x * y");
2721        let term = parser.parse_term().unwrap();
2722        if let Term::App(outer, _) = term {
2723            if let Term::App(inner, _) = *outer {
2724                if let Term::Global(op) = *inner {
2725                    assert_eq!(op, "mul");
2726                } else {
2727                    panic!("Expected Global mul");
2728                }
2729            } else {
2730                panic!("Expected inner App");
2731            }
2732        } else {
2733            panic!("Expected outer App");
2734        }
2735    }
2736
2737    #[test]
2738    fn test_parse_precedence_mul_over_add() {
2739        // x + y * z should parse as add(x, mul(y, z))
2740        let mut parser = LiterateParser::new("x + y * z");
2741        let term = parser.parse_term().unwrap();
2742        // Outer should be add
2743        if let Term::App(outer, rhs) = term {
2744            if let Term::App(inner, _) = *outer {
2745                if let Term::Global(op) = *inner {
2746                    assert_eq!(op, "add");
2747                } else {
2748                    panic!("Expected add");
2749                }
2750            } else {
2751                panic!("Expected inner App");
2752            }
2753            // rhs should be mul(y, z)
2754            if let Term::App(mul_outer, _) = *rhs {
2755                if let Term::App(mul_inner, _) = *mul_outer {
2756                    if let Term::Global(op) = *mul_inner {
2757                        assert_eq!(op, "mul");
2758                    } else {
2759                        panic!("Expected mul");
2760                    }
2761                } else {
2762                    panic!("Expected mul inner App");
2763                }
2764            } else {
2765                panic!("Expected mul App");
2766            }
2767        } else {
2768            panic!("Expected outer App");
2769        }
2770    }
2771
2772    #[test]
2773    fn test_parse_left_associative_add() {
2774        // x + y + z should parse as add(add(x, y), z)
2775        let mut parser = LiterateParser::new("x + y + z");
2776        let term = parser.parse_term().unwrap();
2777        // Outer add, lhs is add(x, y), rhs is z
2778        if let Term::App(outer, rhs) = term {
2779            if let Term::Global(z) = *rhs {
2780                assert_eq!(z, "z");
2781            } else {
2782                panic!("Expected z");
2783            }
2784            if let Term::App(mid, lhs_add) = *outer {
2785                if let Term::Global(op) = *mid {
2786                    assert_eq!(op, "add");
2787                }
2788                // lhs_add should be add(x, y)
2789                if let Term::App(inner_outer, _) = *lhs_add {
2790                    if let Term::App(inner_inner, _) = *inner_outer {
2791                        if let Term::Global(op2) = *inner_inner {
2792                            assert_eq!(op2, "add");
2793                        }
2794                    }
2795                }
2796            }
2797        } else {
2798            panic!("Expected App");
2799        }
2800    }
2801
2802    #[test]
2803    fn test_parse_comparison_with_arithmetic() {
2804        // x + 1 <= y * 2 should parse as le(add(x, 1), mul(y, 2))
2805        let mut parser = LiterateParser::new("x + 1 <= y * 2");
2806        let term = parser.parse_term().unwrap();
2807        if let Term::App(outer, _) = term {
2808            if let Term::App(inner, _) = *outer {
2809                if let Term::Global(op) = *inner {
2810                    assert_eq!(op, "le");
2811                } else {
2812                    panic!("Expected le");
2813                }
2814            } else {
2815                panic!("Expected inner App");
2816            }
2817        } else {
2818            panic!("Expected outer App");
2819        }
2820    }
2821
2822    #[test]
2823    fn test_parse_unicode_le() {
2824        let mut parser = LiterateParser::new("x ≤ y");
2825        let term = parser.parse_term().unwrap();
2826        if let Term::App(outer, _) = term {
2827            if let Term::App(inner, _) = *outer {
2828                if let Term::Global(op) = *inner {
2829                    assert_eq!(op, "le");
2830                } else {
2831                    panic!("Expected le for ≤");
2832                }
2833            }
2834        } else {
2835            panic!("Expected App");
2836        }
2837    }
2838
2839    #[test]
2840    fn test_parse_unicode_ge() {
2841        let mut parser = LiterateParser::new("x ≥ y");
2842        let term = parser.parse_term().unwrap();
2843        if let Term::App(outer, _) = term {
2844            if let Term::App(inner, _) = *outer {
2845                if let Term::Global(op) = *inner {
2846                    assert_eq!(op, "ge");
2847                } else {
2848                    panic!("Expected ge for ≥");
2849                }
2850            }
2851        } else {
2852            panic!("Expected App");
2853        }
2854    }
2855
2856    #[test]
2857    fn test_parse_negative_number_preserved() {
2858        // -5 + x should NOT parse - as subtraction; should be add(-5, x)
2859        let mut parser = LiterateParser::new("-5 + x");
2860        let term = parser.parse_term().unwrap();
2861        // Should be add(-5, x)
2862        if let Term::App(outer, _) = term {
2863            if let Term::App(inner, lhs) = *outer {
2864                if let Term::Global(op) = *inner {
2865                    assert_eq!(op, "add");
2866                }
2867                if let Term::Lit(Literal::Int(n)) = *lhs {
2868                    assert_eq!(n, -5);
2869                } else {
2870                    panic!("Expected -5 literal");
2871                }
2872            }
2873        } else {
2874            panic!("Expected App");
2875        }
2876    }
2877
2878    #[test]
2879    fn test_parse_subtraction_not_negative() {
2880        // x - 5 should parse as sub(x, 5)
2881        let mut parser = LiterateParser::new("x - 5");
2882        let term = parser.parse_term().unwrap();
2883        if let Term::App(outer, rhs) = term {
2884            if let Term::App(inner, _) = *outer {
2885                if let Term::Global(op) = *inner {
2886                    assert_eq!(op, "sub");
2887                }
2888            }
2889            if let Term::Lit(Literal::Int(n)) = *rhs {
2890                assert_eq!(n, 5); // Should be positive 5, not -5
2891            } else {
2892                panic!("Expected 5 literal");
2893            }
2894        } else {
2895            panic!("Expected App");
2896        }
2897    }
2898
2899    #[test]
2900    fn test_parse_infix_with_sexp_mix() {
2901        // Ensure S-expression syntax still works: (add x y) <= z
2902        let mut parser = LiterateParser::new("(add x y) <= z");
2903        let term = parser.parse_term().unwrap();
2904        if let Term::App(outer, _) = term {
2905            if let Term::App(inner, lhs) = *outer {
2906                if let Term::Global(op) = *inner {
2907                    assert_eq!(op, "le");
2908                }
2909                // lhs should be add(x, y) from S-expression parsing
2910                if let Term::App(add_outer, _) = *lhs {
2911                    if let Term::App(add_inner, _) = *add_outer {
2912                        if let Term::Global(add_op) = *add_inner {
2913                            assert_eq!(add_op, "add");
2914                        }
2915                    }
2916                }
2917            }
2918        } else {
2919            panic!("Expected App");
2920        }
2921    }
2922}