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