Skip to main content

logicaffeine_kernel/interface/
literate_parser.rs

1//! Literate Specification Parser for the Kernel.
2//!
3//! This module parses English-like "Literate" syntax and emits the same
4//! `Command` and `Term` structures as the Coq-style parser.
5//!
6//! # Supported Syntax
7//!
8//! ## Inductive Types
9//! ```text
10//! A Bool is either Yes or No.
11//! A Nat is either:
12//!     Zero.
13//!     a Successor with pred: Nat.
14//! ```
15//!
16//! ## Function Definitions (with implicit recursion)
17//! ```text
18//! ## To add (n: Nat) and (m: Nat) -> Nat:
19//!     Consider n:
20//!         When Zero: Yield m.
21//!         When Successor k: Yield Successor (add k m).
22//! ```
23//!
24//! ## Pattern Matching
25//! ```text
26//! Consider x:
27//!     When Zero: Yield 0.
28//!     When Successor k: Yield k.
29//! ```
30//!
31//! ## Lambda Expressions
32//! ```text
33//! given x: Nat yields Successor x
34//! |x: Nat| -> Successor x
35//! ```
36
37use crate::{Literal, Term, Universe};
38use super::command::Command;
39use super::error::ParseError;
40use std::collections::HashSet;
41
42// ============================================================
43// PUBLIC API (called by command_parser.rs dispatcher)
44// ============================================================
45
46/// Parse "A \[Name\] is either..." into Command::Inductive
47///
48/// Supports:
49/// - `A Bool is either Yes or No.`
50/// - `A Nat is either: Zero. a Successor with pred: Nat.`
51/// - `A List of (T: Type) is either: Empty. a Node with head: T and tail: List of T.`
52pub fn parse_inductive(input: &str) -> Result<Command, ParseError> {
53    let mut parser = LiterateParser::new(input);
54    parser.parse_literate_inductive()
55}
56
57/// Parse "## To \[name\] (params) -> RetType:" into Command::Definition
58///
59/// Handles implicit fixpoints: if the function name appears in the body,
60/// the body is automatically wrapped in Term::Fix.
61pub fn parse_definition(input: &str) -> Result<Command, ParseError> {
62    let mut parser = LiterateParser::new(input);
63    parser.parse_literate_definition()
64}
65
66/// Parse "Let \[name\] be \[term\]." into Command::Definition (constant, not function)
67///
68/// This is for simple constant bindings like:
69/// - `Let T be Apply(Name "Not", Variable 0).`
70/// - `Let G be the diagonalization of T.`
71pub fn parse_let_definition(input: &str) -> Result<Command, ParseError> {
72    let mut parser = LiterateParser::new(input);
73    parser.parse_literate_let()
74}
75
76/// Parse "## Theorem: \[name\] Statement: \[proposition\]." into Command::Definition
77///
78/// This is for theorem declarations like:
79/// - `## Theorem: Godel_First_Incompleteness\n    Statement: Consistent implies Not(Provable(G)).`
80pub fn parse_theorem(input: &str) -> Result<Command, ParseError> {
81    let mut parser = LiterateParser::new(input);
82    parser.parse_literate_theorem()
83}
84
85// ============================================================
86// PARSER STATE
87// ============================================================
88
89struct LiterateParser<'a> {
90    input: &'a str,
91    pos: usize,
92    bound_vars: HashSet<String>,
93    current_function: Option<String>,
94    /// The return type of the current function (used as motive in Consider)
95    return_type: Option<Term>,
96}
97
98impl<'a> LiterateParser<'a> {
99    fn new(input: &'a str) -> Self {
100        Self {
101            input,
102            pos: 0,
103            bound_vars: HashSet::new(),
104            current_function: None,
105            return_type: None,
106        }
107    }
108
109    // ============================================================
110    // INDUCTIVE TYPE PARSING
111    // ============================================================
112
113    /// Parse: A [Name] (params)? is either [variants]
114    fn parse_literate_inductive(&mut self) -> Result<Command, ParseError> {
115        self.skip_whitespace();
116
117        // Consume "A" or "An"
118        if self.try_consume_keyword("An") || self.try_consume_keyword("A") {
119            // Good
120        } else {
121            return Err(ParseError::Expected {
122                expected: "'A' or 'An'".to_string(),
123                found: self.peek_word().unwrap_or("EOF".to_string()),
124            });
125        }
126
127        self.skip_whitespace();
128
129        // Parse the type name
130        let name = self.parse_ident()?;
131
132        self.skip_whitespace();
133
134        // Check for type parameters: "of (T: Type)"
135        let params = if self.try_consume_keyword("of") {
136            self.skip_whitespace();
137            self.parse_param_list()?
138        } else {
139            vec![]
140        };
141
142        self.skip_whitespace();
143
144        // Expect "is either"
145        if !self.try_consume_keyword("is") {
146            return Err(ParseError::Expected {
147                expected: "'is'".to_string(),
148                found: self.peek_word().unwrap_or("EOF".to_string()),
149            });
150        }
151        self.skip_whitespace();
152        if !self.try_consume_keyword("either") {
153            return Err(ParseError::Expected {
154                expected: "'either'".to_string(),
155                found: self.peek_word().unwrap_or("EOF".to_string()),
156            });
157        }
158
159        self.skip_whitespace();
160
161        // Parse variants - either inline with "or" or block with indentation
162        let constructors = if self.peek_char(':') {
163            // Block format: "is either:\n    Zero.\n    Successor with pred: Nat."
164            self.advance(); // consume ':'
165            self.parse_indented_variants(&name, &params)?
166        } else {
167            // Inline format: "is either Yes or No."
168            self.parse_inline_variants(&name, &params)?
169        };
170
171        if constructors.is_empty() {
172            return Err(ParseError::Missing("constructors".to_string()));
173        }
174
175        Ok(Command::Inductive {
176            name,
177            params,
178            sort: Term::Sort(Universe::Type(0)),
179            constructors,
180        })
181    }
182
183    /// Parse inline variants: "Yes or No" or "Yes or No."
184    fn parse_inline_variants(
185        &mut self,
186        inductive_name: &str,
187        params: &[(String, Term)],
188    ) -> Result<Vec<(String, Term)>, ParseError> {
189        let mut constructors = Vec::new();
190
191        loop {
192            self.skip_whitespace();
193
194            if self.at_end() || self.peek_char('.') {
195                break;
196            }
197
198            // Parse a single variant
199            let (ctor_name, ctor_type) = self.parse_variant(inductive_name, params)?;
200            constructors.push((ctor_name, ctor_type));
201
202            self.skip_whitespace();
203
204            // Check for "or" separator
205            if !self.try_consume_keyword("or") {
206                break;
207            }
208        }
209
210        // Consume trailing period if present
211        self.skip_whitespace();
212        let _ = self.try_consume(".");
213
214        Ok(constructors)
215    }
216
217    /// Parse indented variants (after "is either:")
218    fn parse_indented_variants(
219        &mut self,
220        inductive_name: &str,
221        params: &[(String, Term)],
222    ) -> Result<Vec<(String, Term)>, ParseError> {
223        let mut constructors = Vec::new();
224
225        loop {
226            self.skip_whitespace_and_newlines();
227
228            if self.at_end() {
229                break;
230            }
231
232            // Check for end of block (non-indented line or empty)
233            if !self.peek_char(' ') && !self.peek_char('\t') && !self.peek_char('a') && !self.peek_char('A') {
234                // Check if this is a variant starting with capital letter
235                if let Some(c) = self.peek() {
236                    if !c.is_uppercase() && c != 'a' && c != 'A' {
237                        break;
238                    }
239                }
240            }
241
242            // Skip leading whitespace for indentation
243            self.skip_whitespace();
244
245            // Check for "a/an" prefix or capital letter
246            if self.at_end() {
247                break;
248            }
249
250            // Parse variant
251            let (ctor_name, ctor_type) = self.parse_variant(inductive_name, params)?;
252            constructors.push((ctor_name, ctor_type));
253
254            // Consume trailing period
255            self.skip_whitespace();
256            let _ = self.try_consume(".");
257        }
258
259        Ok(constructors)
260    }
261
262    /// Parse a single variant: "Zero" or "a Successor with pred: Nat"
263    fn parse_variant(
264        &mut self,
265        inductive_name: &str,
266        params: &[(String, Term)],
267    ) -> Result<(String, Term), ParseError> {
268        self.skip_whitespace();
269
270        // Skip optional "a" or "an" prefix (for readability)
271        let _ = self.try_consume_keyword("an") || self.try_consume_keyword("a");
272        self.skip_whitespace();
273
274        // Parse constructor name
275        let ctor_name = self.parse_ident()?;
276
277        self.skip_whitespace();
278
279        // Build the result type (possibly with parameters applied)
280        let result_type = self.build_applied_type(inductive_name, params);
281
282        // Check for "with" clause (fields)
283        if self.try_consume_keyword("with") {
284            // Parse fields: "field: Type and field2: Type2"
285            let fields = self.parse_field_list()?;
286
287            // Build constructor type: field1_type -> field2_type -> ... -> ResultType
288            let mut ctor_type = result_type;
289            for (_field_name, field_type) in fields.into_iter().rev() {
290                ctor_type = Term::Pi {
291                    param: "_".to_string(),
292                    param_type: Box::new(field_type),
293                    body_type: Box::new(ctor_type),
294                };
295            }
296
297            Ok((ctor_name, ctor_type))
298        } else {
299            // Unit constructor (no fields)
300            Ok((ctor_name, result_type))
301        }
302    }
303
304    /// Parse field list: "field: Type" or "field: Type and field2: Type2"
305    fn parse_field_list(&mut self) -> Result<Vec<(String, Term)>, ParseError> {
306        let mut fields = Vec::new();
307
308        loop {
309            self.skip_whitespace();
310
311            // Parse field name
312            let field_name = self.parse_ident()?;
313
314            self.skip_whitespace();
315
316            // Expect ":"
317            if !self.try_consume(":") {
318                return Err(ParseError::Expected {
319                    expected: "':'".to_string(),
320                    found: self.peek_word().unwrap_or("EOF".to_string()),
321                });
322            }
323
324            self.skip_whitespace();
325
326            // Parse field type
327            let field_type = self.parse_type()?;
328
329            fields.push((field_name, field_type));
330
331            self.skip_whitespace();
332
333            // Check for "and" separator
334            if !self.try_consume_keyword("and") {
335                break;
336            }
337        }
338
339        Ok(fields)
340    }
341
342    /// Build type with parameters applied: List T
343    fn build_applied_type(&self, name: &str, params: &[(String, Term)]) -> Term {
344        let mut result = Term::Global(name.to_string());
345        for (param_name, _) in params {
346            result = Term::App(Box::new(result), Box::new(Term::Var(param_name.clone())));
347        }
348        result
349    }
350
351    // ============================================================
352    // DEFINITION PARSING
353    // ============================================================
354
355    /// Parse: ## To [name] (params) -> RetType: body
356    fn parse_literate_definition(&mut self) -> Result<Command, ParseError> {
357        self.skip_whitespace();
358
359        // Consume "## To "
360        if !self.try_consume("## To ") && !self.try_consume("##To ") {
361            return Err(ParseError::Expected {
362                expected: "'## To'".to_string(),
363                found: self.peek_word().unwrap_or("EOF".to_string()),
364            });
365        }
366
367        self.skip_whitespace();
368
369        // Parse function name
370        let mut name = self.parse_ident()?;
371
372        // Handle predicate syntax: "## To be Provable" means define "Provable", not "be"
373        // This allows "## To be X (params) -> Prop:" for predicate definitions
374        if name == "be" {
375            self.skip_whitespace();
376            name = self.parse_ident()?;
377        }
378
379        self.current_function = Some(name.clone());
380
381        self.skip_whitespace();
382
383        // Parse parameter groups: (x: T) and (y: U) or (x: T) (y: U)
384        // Note: For nullary predicates like "## To be Consistent -> Prop:", params will be empty
385        let all_params = self.parse_function_params()?;
386
387        self.skip_whitespace();
388
389        // Parse optional return type: -> RetType
390        let return_type = if self.try_consume("->") {
391            self.skip_whitespace();
392            let ret = self.parse_type()?;
393            // Store return type for use as motive in Consider blocks
394            self.return_type = Some(ret.clone());
395            Some(ret)
396        } else {
397            None
398        };
399
400        self.skip_whitespace();
401
402        // Expect ":"
403        if !self.try_consume(":") {
404            return Err(ParseError::Expected {
405                expected: "':'".to_string(),
406                found: self.peek_word().unwrap_or("EOF".to_string()),
407            });
408        }
409
410        // Add parameters to bound vars
411        for (param_name, _) in &all_params {
412            self.bound_vars.insert(param_name.clone());
413        }
414
415        // Parse body
416        self.skip_whitespace_and_newlines();
417        let body = self.parse_body()?;
418
419        // Check for self-reference (implicit fixpoint)
420        let needs_fix = self.contains_self_reference(&name, &body);
421
422        // Build the function body with lambdas
423        let mut func_body = body;
424        for (param_name, param_type) in all_params.iter().rev() {
425            func_body = Term::Lambda {
426                param: param_name.clone(),
427                param_type: Box::new(param_type.clone()),
428                body: Box::new(func_body),
429            };
430        }
431
432        // Wrap in Fix if recursive
433        if needs_fix {
434            func_body = Term::Fix {
435                name: name.clone(),
436                body: Box::new(func_body),
437            };
438        }
439
440        // Build the full type annotation if we have a return type
441        let ty = if let Some(ret) = return_type {
442            let mut full_type = ret;
443            for (_, param_type) in all_params.iter().rev() {
444                full_type = Term::Pi {
445                    param: "_".to_string(),
446                    param_type: Box::new(param_type.clone()),
447                    body_type: Box::new(full_type),
448                };
449            }
450            Some(full_type)
451        } else {
452            None
453        };
454
455        self.current_function = None;
456
457        Ok(Command::Definition {
458            name,
459            ty,
460            body: func_body,
461            is_hint: false,
462        })
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::Duration(_d)) => {
1791                // Duration literals - treat as error for ring (ring doesn't handle durations)
1792                Term::App(
1793                    Box::new(Term::Global("SName".to_string())),
1794                    Box::new(Term::Lit(Literal::Text("Error_Duration".to_string()))),
1795                )
1796            }
1797            Term::Lit(Literal::Date(_d)) => {
1798                // Date literals - treat as error for ring (ring doesn't handle dates)
1799                Term::App(
1800                    Box::new(Term::Global("SName".to_string())),
1801                    Box::new(Term::Lit(Literal::Text("Error_Date".to_string()))),
1802                )
1803            }
1804            Term::Lit(Literal::Moment(_m)) => {
1805                // Moment literals - treat as error for ring (ring doesn't handle moments)
1806                Term::App(
1807                    Box::new(Term::Global("SName".to_string())),
1808                    Box::new(Term::Lit(Literal::Text("Error_Moment".to_string()))),
1809                )
1810            }
1811            Term::Lit(Literal::Text(s)) => {
1812                // Text literals become SLit (wrapped in SName for now)
1813                Term::App(
1814                    Box::new(Term::Global("SName".to_string())),
1815                    Box::new(Term::Lit(Literal::Text(s.clone()))),
1816                )
1817            }
1818            Term::App(f, x) => {
1819                // Applications become SApp
1820                let f_syn = self.term_to_syntax(f, bound_vars);
1821                let x_syn = self.term_to_syntax(x, bound_vars);
1822                Term::App(
1823                    Box::new(Term::App(
1824                        Box::new(Term::Global("SApp".to_string())),
1825                        Box::new(f_syn),
1826                    )),
1827                    Box::new(x_syn),
1828                )
1829            }
1830            Term::Lambda { param, param_type, body } => {
1831                // Lambdas become SLam (if we have that constructor)
1832                let ty_syn = self.term_to_syntax(param_type, bound_vars);
1833                let mut new_bound = bound_vars.to_vec();
1834                new_bound.push(param.clone());
1835                let body_syn = self.term_to_syntax(body, &new_bound);
1836                Term::App(
1837                    Box::new(Term::App(
1838                        Box::new(Term::Global("SLam".to_string())),
1839                        Box::new(ty_syn),
1840                    )),
1841                    Box::new(body_syn),
1842                )
1843            }
1844            Term::Pi { param, param_type, body_type } => {
1845                // Pi types become SPi
1846                let ty_syn = self.term_to_syntax(param_type, bound_vars);
1847                let mut new_bound = bound_vars.to_vec();
1848                new_bound.push(param.clone());
1849                let body_syn = self.term_to_syntax(body_type, &new_bound);
1850                Term::App(
1851                    Box::new(Term::App(
1852                        Box::new(Term::Global("SPi".to_string())),
1853                        Box::new(ty_syn),
1854                    )),
1855                    Box::new(body_syn),
1856                )
1857            }
1858            Term::Sort(Universe::Prop) => {
1859                // Prop sort becomes SSort UProp
1860                Term::App(
1861                    Box::new(Term::Global("SSort".to_string())),
1862                    Box::new(Term::Global("UProp".to_string())),
1863                )
1864            }
1865            Term::Sort(Universe::Type(n)) => {
1866                // Type sort becomes SSort (UType n)
1867                Term::App(
1868                    Box::new(Term::Global("SSort".to_string())),
1869                    Box::new(Term::App(
1870                        Box::new(Term::Global("UType".to_string())),
1871                        Box::new(Term::Lit(Literal::Int(*n as i64))),
1872                    )),
1873                )
1874            }
1875            Term::Hole => {
1876                // Holes default to Int type for ring proofs
1877                Term::App(
1878                    Box::new(Term::Global("SName".to_string())),
1879                    Box::new(Term::Lit(Literal::Text("Int".to_string()))),
1880                )
1881            }
1882            Term::Match { .. } | Term::Fix { .. } => {
1883                // Match and Fix are complex - return error marker
1884                Term::App(
1885                    Box::new(Term::Global("SName".to_string())),
1886                    Box::new(Term::Lit(Literal::Text("Error".to_string()))),
1887                )
1888            }
1889        }
1890    }
1891
1892    // ============================================================
1893    // SELF-REFERENCE DETECTION
1894    // ============================================================
1895
1896    /// Check if the function name appears in the term (for implicit fixpoint)
1897    fn contains_self_reference(&self, name: &str, term: &Term) -> bool {
1898        match term {
1899            Term::Var(v) => v == name,
1900            Term::Global(_) => false,
1901            Term::Sort(_) => false,
1902            Term::Lit(_) => false,
1903            Term::Pi { param_type, body_type, .. } => {
1904                self.contains_self_reference(name, param_type)
1905                    || self.contains_self_reference(name, body_type)
1906            }
1907            Term::Lambda { param_type, body, .. } => {
1908                self.contains_self_reference(name, param_type)
1909                    || self.contains_self_reference(name, body)
1910            }
1911            Term::App(f, a) => {
1912                self.contains_self_reference(name, f) || self.contains_self_reference(name, a)
1913            }
1914            Term::Match { discriminant, motive, cases } => {
1915                self.contains_self_reference(name, discriminant)
1916                    || self.contains_self_reference(name, motive)
1917                    || cases.iter().any(|c| self.contains_self_reference(name, c))
1918            }
1919            Term::Fix { body, .. } => self.contains_self_reference(name, body),
1920            Term::Hole => false, // Holes never contain self-references
1921        }
1922    }
1923
1924    // ============================================================
1925    // LOW-LEVEL UTILITIES
1926    // ============================================================
1927
1928    fn skip_whitespace(&mut self) {
1929        while let Some(c) = self.peek() {
1930            if c == ' ' || c == '\t' {
1931                self.advance();
1932            } else {
1933                break;
1934            }
1935        }
1936    }
1937
1938    fn skip_whitespace_and_newlines(&mut self) {
1939        while let Some(c) = self.peek() {
1940            if c.is_whitespace() {
1941                self.advance();
1942            } else {
1943                break;
1944            }
1945        }
1946    }
1947
1948    fn peek(&self) -> Option<char> {
1949        self.input[self.pos..].chars().next()
1950    }
1951
1952    fn peek_char(&self, c: char) -> bool {
1953        self.peek() == Some(c)
1954    }
1955
1956    fn peek_keyword(&self, keyword: &str) -> bool {
1957        if !self.input[self.pos..].starts_with(keyword) {
1958            return false;
1959        }
1960        let after = self.pos + keyword.len();
1961        if after >= self.input.len() {
1962            return true;
1963        }
1964        let next_char = self.input[after..].chars().next();
1965        !next_char.map(|c| c.is_alphanumeric() || c == '_').unwrap_or(false)
1966    }
1967
1968    fn peek_word(&self) -> Option<String> {
1969        let start = self.pos;
1970        let mut end = start;
1971        for c in self.input[start..].chars() {
1972            if c.is_alphanumeric() || c == '_' {
1973                end += c.len_utf8();
1974            } else {
1975                break;
1976            }
1977        }
1978        if end > start {
1979            Some(self.input[start..end].to_string())
1980        } else {
1981            self.peek().map(|c| c.to_string())
1982        }
1983    }
1984
1985    fn advance(&mut self) {
1986        if let Some(c) = self.peek() {
1987            self.pos += c.len_utf8();
1988        }
1989    }
1990
1991    fn at_end(&self) -> bool {
1992        self.pos >= self.input.len()
1993    }
1994
1995    fn try_consume(&mut self, s: &str) -> bool {
1996        if self.input[self.pos..].starts_with(s) {
1997            self.pos += s.len();
1998            true
1999        } else {
2000            false
2001        }
2002    }
2003
2004    fn try_consume_keyword(&mut self, keyword: &str) -> bool {
2005        if self.peek_keyword(keyword) {
2006            self.pos += keyword.len();
2007            true
2008        } else {
2009            false
2010        }
2011    }
2012
2013    fn consume_keyword(&mut self, keyword: &str) -> Result<(), ParseError> {
2014        if self.try_consume_keyword(keyword) {
2015            Ok(())
2016        } else {
2017            Err(ParseError::Expected {
2018                expected: format!("'{}'", keyword),
2019                found: self.peek_word().unwrap_or("EOF".to_string()),
2020            })
2021        }
2022    }
2023
2024    fn parse_ident(&mut self) -> Result<String, ParseError> {
2025        self.skip_whitespace();
2026        let start = self.pos;
2027
2028        // First char must be alphabetic or underscore
2029        if let Some(c) = self.peek() {
2030            if !c.is_alphabetic() && c != '_' {
2031                return Err(ParseError::Expected {
2032                    expected: "identifier".to_string(),
2033                    found: c.to_string(),
2034                });
2035            }
2036        } else {
2037            return Err(ParseError::UnexpectedEof);
2038        }
2039
2040        // Consume alphanumeric and underscore
2041        while let Some(c) = self.peek() {
2042            if c.is_alphanumeric() || c == '_' {
2043                self.advance();
2044            } else {
2045                break;
2046            }
2047        }
2048
2049        let ident = self.input[start..self.pos].to_string();
2050        if ident.is_empty() {
2051            Err(ParseError::InvalidIdent("empty".to_string()))
2052        } else {
2053            Ok(ident)
2054        }
2055    }
2056
2057    // ============================================================
2058    // INFIX OPERATOR HELPERS
2059    // ============================================================
2060
2061    /// Check if current position starts a negative number (- followed by digit)
2062    fn peek_negative_number(&self) -> bool {
2063        if !self.peek_char('-') {
2064            return false;
2065        }
2066        self.input.get(self.pos + 1..)
2067            .and_then(|s| s.chars().next())
2068            .map(|c| c.is_ascii_digit())
2069            .unwrap_or(false)
2070    }
2071
2072    /// Check if current position starts an arrow (->)
2073    fn peek_arrow(&self) -> bool {
2074        self.input[self.pos..].starts_with("->")
2075    }
2076
2077    /// Check if current position has a comparison operator
2078    fn peek_comparison_operator(&self) -> bool {
2079        let rest = &self.input[self.pos..];
2080        rest.starts_with("<=") || rest.starts_with(">=")
2081            || rest.starts_with("≤") || rest.starts_with("≥")
2082            || rest.starts_with('<') || rest.starts_with('>')
2083    }
2084}
2085
2086// ============================================================
2087// UNIT TESTS
2088// ============================================================
2089
2090#[cfg(test)]
2091mod tests {
2092    use super::*;
2093
2094    #[test]
2095    fn test_parse_simple_inductive() {
2096        let cmd = parse_inductive("A Bool is either Yes or No").unwrap();
2097        if let Command::Inductive { name, constructors, .. } = cmd {
2098            assert_eq!(name, "Bool");
2099            assert_eq!(constructors.len(), 2);
2100            assert_eq!(constructors[0].0, "Yes");
2101            assert_eq!(constructors[1].0, "No");
2102        } else {
2103            panic!("Expected Inductive command");
2104        }
2105    }
2106
2107    #[test]
2108    fn test_parse_inductive_with_article() {
2109        let cmd = parse_inductive("A Decision is either a Yes or a No").unwrap();
2110        if let Command::Inductive { name, constructors, .. } = cmd {
2111            assert_eq!(name, "Decision");
2112            assert_eq!(constructors.len(), 2);
2113        } else {
2114            panic!("Expected Inductive command");
2115        }
2116    }
2117
2118    #[test]
2119    fn test_parse_recursive_inductive() {
2120        let cmd = parse_inductive("A Nat is either Zero or a Succ with pred: Nat").unwrap();
2121        if let Command::Inductive { name, constructors, .. } = cmd {
2122            assert_eq!(name, "Nat");
2123            assert_eq!(constructors.len(), 2);
2124            assert_eq!(constructors[0].0, "Zero");
2125            assert_eq!(constructors[1].0, "Succ");
2126            // Succ should have type Nat -> Nat
2127            if let Term::Pi { .. } = &constructors[1].1 {
2128                // Good - it's a function type
2129            } else {
2130                panic!("Expected Succ to have Pi type");
2131            }
2132        } else {
2133            panic!("Expected Inductive command");
2134        }
2135    }
2136
2137    #[test]
2138    fn test_parse_simple_definition() {
2139        let cmd = parse_definition("## To id (x: Nat) -> Nat: Yield x").unwrap();
2140        if let Command::Definition { name, body, .. } = cmd {
2141            assert_eq!(name, "id");
2142            // Body should be a lambda
2143            if let Term::Lambda { param, .. } = body {
2144                assert_eq!(param, "x");
2145            } else {
2146                panic!("Expected Lambda body");
2147            }
2148        } else {
2149            panic!("Expected Definition command");
2150        }
2151    }
2152
2153    #[test]
2154    fn test_implicit_fixpoint_detection() {
2155        // This definition is recursive (add appears in body)
2156        let cmd = parse_definition(
2157            "## To add (n: Nat) and (m: Nat) -> Nat: Consider n: When Zero: Yield m. When Succ k: Yield Succ (add k m)."
2158        ).unwrap();
2159
2160        if let Command::Definition { name, body, .. } = cmd {
2161            assert_eq!(name, "add");
2162            // Body should be wrapped in Fix
2163            if let Term::Fix { name: fix_name, .. } = body {
2164                assert_eq!(fix_name, "add");
2165            } else {
2166                panic!("Expected Fix wrapper for recursive function");
2167            }
2168        } else {
2169            panic!("Expected Definition command");
2170        }
2171    }
2172
2173    #[test]
2174    fn test_parse_given_lambda() {
2175        let mut parser = LiterateParser::new("given x: Nat yields Succ x");
2176        let term = parser.parse_given_lambda().unwrap();
2177
2178        if let Term::Lambda { param, .. } = term {
2179            assert_eq!(param, "x");
2180        } else {
2181            panic!("Expected Lambda");
2182        }
2183    }
2184
2185    #[test]
2186    fn test_parse_pipe_lambda() {
2187        let mut parser = LiterateParser::new("|x: Nat| -> Succ x");
2188        let term = parser.parse_pipe_lambda().unwrap();
2189
2190        if let Term::Lambda { param, .. } = term {
2191            assert_eq!(param, "x");
2192        } else {
2193            panic!("Expected Lambda");
2194        }
2195    }
2196
2197    // ============================================================
2198    // Syntax (Deep Embedding) Tests
2199    // ============================================================
2200
2201    #[test]
2202    fn test_parse_let_definition() {
2203        let cmd = parse_let_definition("Let T be Zero").unwrap();
2204        if let Command::Definition { name, ty, body, .. } = cmd {
2205            assert_eq!(name, "T");
2206            assert!(ty.is_none());
2207            if let Term::Global(g) = body {
2208                assert_eq!(g, "Zero");
2209            } else {
2210                panic!("Expected Global term");
2211            }
2212        } else {
2213            panic!("Expected Definition command");
2214        }
2215    }
2216
2217    #[test]
2218    fn test_parse_name_syntax() {
2219        let mut parser = LiterateParser::new("Name \"Not\"");
2220        let term = parser.parse_term().unwrap();
2221
2222        // Should be SName "Not"
2223        if let Term::App(f, arg) = term {
2224            if let Term::Global(g) = *f {
2225                assert_eq!(g, "SName");
2226            } else {
2227                panic!("Expected Global SName");
2228            }
2229            if let Term::Lit(Literal::Text(s)) = *arg {
2230                assert_eq!(s, "Not");
2231            } else {
2232                panic!("Expected Text literal");
2233            }
2234        } else {
2235            panic!("Expected App");
2236        }
2237    }
2238
2239    #[test]
2240    fn test_parse_variable_syntax() {
2241        let mut parser = LiterateParser::new("Variable 0");
2242        let term = parser.parse_term().unwrap();
2243
2244        // Should be SVar 0
2245        if let Term::App(f, arg) = term {
2246            if let Term::Global(g) = *f {
2247                assert_eq!(g, "SVar");
2248            } else {
2249                panic!("Expected Global SVar");
2250            }
2251            if let Term::Lit(Literal::Int(n)) = *arg {
2252                assert_eq!(n, 0);
2253            } else {
2254                panic!("Expected Int literal");
2255            }
2256        } else {
2257            panic!("Expected App");
2258        }
2259    }
2260
2261    #[test]
2262    fn test_parse_apply_syntax() {
2263        let mut parser = LiterateParser::new("Apply(Name \"Not\", Variable 0)");
2264        let term = parser.parse_term().unwrap();
2265
2266        // Should be (SApp (SName "Not") (SVar 0))
2267        if let Term::App(outer_f, _outer_arg) = term {
2268            if let Term::App(inner_f, _inner_arg) = *outer_f {
2269                if let Term::Global(g) = *inner_f {
2270                    assert_eq!(g, "SApp");
2271                } else {
2272                    panic!("Expected Global SApp");
2273                }
2274            } else {
2275                panic!("Expected inner App");
2276            }
2277        } else {
2278            panic!("Expected outer App");
2279        }
2280    }
2281
2282    #[test]
2283    fn test_parse_diagonalization() {
2284        let mut parser = LiterateParser::new("the diagonalization of T");
2285        let term = parser.parse_term().unwrap();
2286
2287        // Should be syn_diag T
2288        if let Term::App(f, arg) = term {
2289            if let Term::Global(g) = *f {
2290                assert_eq!(g, "syn_diag");
2291            } else {
2292                panic!("Expected Global syn_diag");
2293            }
2294            if let Term::Global(a) = *arg {
2295                assert_eq!(a, "T");
2296            } else {
2297                panic!("Expected Global T");
2298            }
2299        } else {
2300            panic!("Expected App");
2301        }
2302    }
2303
2304    #[test]
2305    fn test_parse_implies() {
2306        let mut parser = LiterateParser::new("A implies B");
2307        let term = parser.parse_term().unwrap();
2308
2309        // Should be forall _ : A, B (non-dependent Pi)
2310        if let Term::Pi { param, param_type, body_type } = term {
2311            assert_eq!(param, "_");
2312            if let Term::Global(a) = *param_type {
2313                assert_eq!(a, "A");
2314            } else {
2315                panic!("Expected Global A");
2316            }
2317            if let Term::Global(b) = *body_type {
2318                assert_eq!(b, "B");
2319            } else {
2320                panic!("Expected Global B");
2321            }
2322        } else {
2323            panic!("Expected Pi");
2324        }
2325    }
2326
2327    #[test]
2328    fn test_parse_existential() {
2329        let mut parser = LiterateParser::new("there exists a d: Derivation such that P");
2330        let term = parser.parse_term().unwrap();
2331
2332        // Should be Ex Derivation (fun d => P)
2333        if let Term::App(outer_f, lambda) = term {
2334            if let Term::App(ex, typ) = *outer_f {
2335                if let Term::Global(g) = *ex {
2336                    assert_eq!(g, "Ex");
2337                } else {
2338                    panic!("Expected Global Ex");
2339                }
2340                if let Term::Global(t) = *typ {
2341                    assert_eq!(t, "Derivation");
2342                } else {
2343                    panic!("Expected Global Derivation");
2344                }
2345            } else {
2346                panic!("Expected inner App for Ex");
2347            }
2348            if let Term::Lambda { param, body, .. } = *lambda {
2349                assert_eq!(param, "d");
2350                if let Term::Global(p) = *body {
2351                    assert_eq!(p, "P");
2352                } else {
2353                    panic!("Expected Global P in lambda body");
2354                }
2355            } else {
2356                panic!("Expected Lambda");
2357            }
2358        } else {
2359            panic!("Expected App");
2360        }
2361    }
2362
2363    #[test]
2364    fn test_parse_complex_let_with_apply() {
2365        let cmd = parse_let_definition("Let T be Apply(Name \"Not\", Apply(Name \"Provable\", Variable 0)).").unwrap();
2366        if let Command::Definition { name, ty, body, .. } = cmd {
2367            assert_eq!(name, "T");
2368            assert!(ty.is_none());
2369            // Body should be nested applications
2370            if let Term::App(_, _) = body {
2371                // Good - it's an application
2372            } else {
2373                panic!("Expected App body");
2374            }
2375        } else {
2376            panic!("Expected Definition command");
2377        }
2378    }
2379
2380    // ============================================================
2381    // Predicate Syntax Tests
2382    // ============================================================
2383
2384    #[test]
2385    fn test_parse_predicate_definition() {
2386        // ## To be Provable (s: Syntax) -> Prop: Yield s
2387        let cmd = parse_definition("## To be Provable (s: Syntax) -> Prop: Yield s").unwrap();
2388        if let Command::Definition { name, .. } = cmd {
2389            assert_eq!(name, "Provable"); // NOT "be"
2390        } else {
2391            panic!("Expected Definition command");
2392        }
2393    }
2394
2395    #[test]
2396    fn test_parse_nullary_predicate() {
2397        // ## To be Consistent -> Prop: Yield True
2398        let cmd = parse_definition("## To be Consistent -> Prop: Yield True").unwrap();
2399        if let Command::Definition { name, body, .. } = cmd {
2400            assert_eq!(name, "Consistent");
2401            // Body should be True (no lambda wrapper since no params)
2402            if let Term::Global(g) = body {
2403                assert_eq!(g, "True");
2404            } else {
2405                panic!("Expected Global True");
2406            }
2407        } else {
2408            panic!("Expected Definition command");
2409        }
2410    }
2411
2412    #[test]
2413    fn test_parse_the_name_syntax() {
2414        // the Name "Not" → SName "Not"
2415        let mut parser = LiterateParser::new("the Name \"Not\"");
2416        let term = parser.parse_term().unwrap();
2417
2418        if let Term::App(f, arg) = term {
2419            if let Term::Global(g) = *f {
2420                assert_eq!(g, "SName");
2421            } else {
2422                panic!("Expected Global SName");
2423            }
2424            if let Term::Lit(Literal::Text(s)) = *arg {
2425                assert_eq!(s, "Not");
2426            } else {
2427                panic!("Expected Text literal");
2428            }
2429        } else {
2430            panic!("Expected App");
2431        }
2432    }
2433
2434    #[test]
2435    fn test_parse_theorem() {
2436        // ## Theorem: MyTheorem\n    Statement: True implies True.
2437        let cmd = parse_theorem("## Theorem: MyTheorem\n    Statement: A implies B.").unwrap();
2438        if let Command::Definition { name, ty, body, .. } = cmd {
2439            assert_eq!(name, "MyTheorem");
2440            // Type should be Prop
2441            assert!(ty.is_some());
2442            if let Some(Term::Sort(Universe::Prop)) = ty {
2443                // Good
2444            } else {
2445                panic!("Expected Prop type");
2446            }
2447            // Body should be Pi (A implies B)
2448            if let Term::Pi { .. } = body {
2449                // Good
2450            } else {
2451                panic!("Expected Pi body (implication)");
2452            }
2453        } else {
2454            panic!("Expected Definition command");
2455        }
2456    }
2457
2458    #[test]
2459    fn test_parse_theorem_with_complex_statement() {
2460        // ## Theorem: Godel\n    Statement: Consistent implies Not(Provable(G)).
2461        let cmd = parse_theorem("## Theorem: Godel\n    Statement: Consistent implies Not(Provable(G)).").unwrap();
2462        if let Command::Definition { name, ty, .. } = cmd {
2463            assert_eq!(name, "Godel");
2464            assert!(ty.is_some());
2465            if let Some(Term::Sort(Universe::Prop)) = ty {
2466                // Good
2467            } else {
2468                panic!("Expected Prop type");
2469            }
2470        } else {
2471            panic!("Expected Definition command");
2472        }
2473    }
2474
2475    #[test]
2476    fn test_parse_equals_infix() {
2477        // X equals Y → Eq Hole X Y
2478        let mut parser = LiterateParser::new("A equals B");
2479        let term = parser.parse_term().unwrap();
2480
2481        // Should be (Eq Hole A B) = App(App(App(Eq, Hole), A), B)
2482        if let Term::App(outer, rhs) = term {
2483            if let Term::Global(b) = *rhs {
2484                assert_eq!(b, "B");
2485            } else {
2486                panic!("Expected Global B");
2487            }
2488            if let Term::App(mid, lhs) = *outer {
2489                if let Term::Global(a) = *lhs {
2490                    assert_eq!(a, "A");
2491                } else {
2492                    panic!("Expected Global A");
2493                }
2494                if let Term::App(inner, placeholder) = *mid {
2495                    if let Term::Global(eq) = *inner {
2496                        assert_eq!(eq, "Eq");
2497                    } else {
2498                        panic!("Expected Global Eq");
2499                    }
2500                    if !matches!(*placeholder, Term::Hole) {
2501                        panic!("Expected Hole placeholder");
2502                    }
2503                } else {
2504                    panic!("Expected inner App");
2505                }
2506            } else {
2507                panic!("Expected mid App");
2508            }
2509        } else {
2510            panic!("Expected outer App");
2511        }
2512    }
2513
2514    #[test]
2515    fn test_parse_equals_with_application() {
2516        // f(x) equals y → Eq _ (f x) y
2517        let mut parser = LiterateParser::new("f(x) equals y");
2518        let term = parser.parse_term().unwrap();
2519
2520        // Should be App(App(App(Eq, _), App(f, x)), y)
2521        if let Term::App(outer, rhs) = term {
2522            if let Term::Global(y) = *rhs {
2523                assert_eq!(y, "y");
2524            } else {
2525                panic!("Expected Global y");
2526            }
2527            if let Term::App(mid, lhs) = *outer {
2528                // lhs should be App(f, x)
2529                if let Term::App(f_box, x_box) = *lhs {
2530                    if let Term::Global(f) = *f_box {
2531                        assert_eq!(f, "f");
2532                    }
2533                    if let Term::Global(x) = *x_box {
2534                        assert_eq!(x, "x");
2535                    }
2536                } else {
2537                    panic!("Expected lhs to be App(f, x)");
2538                }
2539            } else {
2540                panic!("Expected mid App");
2541            }
2542        } else {
2543            panic!("Expected outer App");
2544        }
2545    }
2546
2547    // ============================================================
2548    // INFIX OPERATOR TESTS
2549    // ============================================================
2550
2551    #[test]
2552    fn test_parse_infix_le() {
2553        let mut parser = LiterateParser::new("x <= y");
2554        let term = parser.parse_term().unwrap();
2555        // Should produce: App(App(Global("le"), Global("x")), Global("y"))
2556        if let Term::App(outer, rhs) = term {
2557            if let Term::App(inner, lhs) = *outer {
2558                if let Term::Global(op) = *inner {
2559                    assert_eq!(op, "le");
2560                } else {
2561                    panic!("Expected Global le");
2562                }
2563                if let Term::Global(l) = *lhs {
2564                    assert_eq!(l, "x");
2565                } else {
2566                    panic!("Expected Global x");
2567                }
2568            } else {
2569                panic!("Expected inner App");
2570            }
2571            if let Term::Global(r) = *rhs {
2572                assert_eq!(r, "y");
2573            } else {
2574                panic!("Expected Global y");
2575            }
2576        } else {
2577            panic!("Expected outer App");
2578        }
2579    }
2580
2581    #[test]
2582    fn test_parse_infix_lt() {
2583        let mut parser = LiterateParser::new("a < b");
2584        let term = parser.parse_term().unwrap();
2585        if let Term::App(outer, _) = term {
2586            if let Term::App(inner, _) = *outer {
2587                if let Term::Global(op) = *inner {
2588                    assert_eq!(op, "lt");
2589                } else {
2590                    panic!("Expected Global lt");
2591                }
2592            } else {
2593                panic!("Expected inner App");
2594            }
2595        } else {
2596            panic!("Expected outer App");
2597        }
2598    }
2599
2600    #[test]
2601    fn test_parse_infix_ge() {
2602        let mut parser = LiterateParser::new("x >= y");
2603        let term = parser.parse_term().unwrap();
2604        if let Term::App(outer, _) = term {
2605            if let Term::App(inner, _) = *outer {
2606                if let Term::Global(op) = *inner {
2607                    assert_eq!(op, "ge");
2608                } else {
2609                    panic!("Expected Global ge");
2610                }
2611            } else {
2612                panic!("Expected inner App");
2613            }
2614        } else {
2615            panic!("Expected outer App");
2616        }
2617    }
2618
2619    #[test]
2620    fn test_parse_infix_gt() {
2621        let mut parser = LiterateParser::new("x > y");
2622        let term = parser.parse_term().unwrap();
2623        if let Term::App(outer, _) = term {
2624            if let Term::App(inner, _) = *outer {
2625                if let Term::Global(op) = *inner {
2626                    assert_eq!(op, "gt");
2627                } else {
2628                    panic!("Expected Global gt");
2629                }
2630            } else {
2631                panic!("Expected inner App");
2632            }
2633        } else {
2634            panic!("Expected outer App");
2635        }
2636    }
2637
2638    #[test]
2639    fn test_parse_infix_add() {
2640        let mut parser = LiterateParser::new("x + y");
2641        let term = parser.parse_term().unwrap();
2642        if let Term::App(outer, _) = term {
2643            if let Term::App(inner, _) = *outer {
2644                if let Term::Global(op) = *inner {
2645                    assert_eq!(op, "add");
2646                } else {
2647                    panic!("Expected Global add");
2648                }
2649            } else {
2650                panic!("Expected inner App");
2651            }
2652        } else {
2653            panic!("Expected outer App");
2654        }
2655    }
2656
2657    #[test]
2658    fn test_parse_infix_sub() {
2659        let mut parser = LiterateParser::new("x - y");
2660        let term = parser.parse_term().unwrap();
2661        if let Term::App(outer, _) = term {
2662            if let Term::App(inner, _) = *outer {
2663                if let Term::Global(op) = *inner {
2664                    assert_eq!(op, "sub");
2665                } else {
2666                    panic!("Expected Global sub");
2667                }
2668            } else {
2669                panic!("Expected inner App");
2670            }
2671        } else {
2672            panic!("Expected outer App");
2673        }
2674    }
2675
2676    #[test]
2677    fn test_parse_infix_mul() {
2678        let mut parser = LiterateParser::new("x * y");
2679        let term = parser.parse_term().unwrap();
2680        if let Term::App(outer, _) = term {
2681            if let Term::App(inner, _) = *outer {
2682                if let Term::Global(op) = *inner {
2683                    assert_eq!(op, "mul");
2684                } else {
2685                    panic!("Expected Global mul");
2686                }
2687            } else {
2688                panic!("Expected inner App");
2689            }
2690        } else {
2691            panic!("Expected outer App");
2692        }
2693    }
2694
2695    #[test]
2696    fn test_parse_precedence_mul_over_add() {
2697        // x + y * z should parse as add(x, mul(y, z))
2698        let mut parser = LiterateParser::new("x + y * z");
2699        let term = parser.parse_term().unwrap();
2700        // Outer should be add
2701        if let Term::App(outer, rhs) = term {
2702            if let Term::App(inner, _) = *outer {
2703                if let Term::Global(op) = *inner {
2704                    assert_eq!(op, "add");
2705                } else {
2706                    panic!("Expected add");
2707                }
2708            } else {
2709                panic!("Expected inner App");
2710            }
2711            // rhs should be mul(y, z)
2712            if let Term::App(mul_outer, _) = *rhs {
2713                if let Term::App(mul_inner, _) = *mul_outer {
2714                    if let Term::Global(op) = *mul_inner {
2715                        assert_eq!(op, "mul");
2716                    } else {
2717                        panic!("Expected mul");
2718                    }
2719                } else {
2720                    panic!("Expected mul inner App");
2721                }
2722            } else {
2723                panic!("Expected mul App");
2724            }
2725        } else {
2726            panic!("Expected outer App");
2727        }
2728    }
2729
2730    #[test]
2731    fn test_parse_left_associative_add() {
2732        // x + y + z should parse as add(add(x, y), z)
2733        let mut parser = LiterateParser::new("x + y + z");
2734        let term = parser.parse_term().unwrap();
2735        // Outer add, lhs is add(x, y), rhs is z
2736        if let Term::App(outer, rhs) = term {
2737            if let Term::Global(z) = *rhs {
2738                assert_eq!(z, "z");
2739            } else {
2740                panic!("Expected z");
2741            }
2742            if let Term::App(mid, lhs_add) = *outer {
2743                if let Term::Global(op) = *mid {
2744                    assert_eq!(op, "add");
2745                }
2746                // lhs_add should be add(x, y)
2747                if let Term::App(inner_outer, _) = *lhs_add {
2748                    if let Term::App(inner_inner, _) = *inner_outer {
2749                        if let Term::Global(op2) = *inner_inner {
2750                            assert_eq!(op2, "add");
2751                        }
2752                    }
2753                }
2754            }
2755        } else {
2756            panic!("Expected App");
2757        }
2758    }
2759
2760    #[test]
2761    fn test_parse_comparison_with_arithmetic() {
2762        // x + 1 <= y * 2 should parse as le(add(x, 1), mul(y, 2))
2763        let mut parser = LiterateParser::new("x + 1 <= y * 2");
2764        let term = parser.parse_term().unwrap();
2765        if let Term::App(outer, _) = term {
2766            if let Term::App(inner, _) = *outer {
2767                if let Term::Global(op) = *inner {
2768                    assert_eq!(op, "le");
2769                } else {
2770                    panic!("Expected le");
2771                }
2772            } else {
2773                panic!("Expected inner App");
2774            }
2775        } else {
2776            panic!("Expected outer App");
2777        }
2778    }
2779
2780    #[test]
2781    fn test_parse_unicode_le() {
2782        let mut parser = LiterateParser::new("x ≤ y");
2783        let term = parser.parse_term().unwrap();
2784        if let Term::App(outer, _) = term {
2785            if let Term::App(inner, _) = *outer {
2786                if let Term::Global(op) = *inner {
2787                    assert_eq!(op, "le");
2788                } else {
2789                    panic!("Expected le for ≤");
2790                }
2791            }
2792        } else {
2793            panic!("Expected App");
2794        }
2795    }
2796
2797    #[test]
2798    fn test_parse_unicode_ge() {
2799        let mut parser = LiterateParser::new("x ≥ y");
2800        let term = parser.parse_term().unwrap();
2801        if let Term::App(outer, _) = term {
2802            if let Term::App(inner, _) = *outer {
2803                if let Term::Global(op) = *inner {
2804                    assert_eq!(op, "ge");
2805                } else {
2806                    panic!("Expected ge for ≥");
2807                }
2808            }
2809        } else {
2810            panic!("Expected App");
2811        }
2812    }
2813
2814    #[test]
2815    fn test_parse_negative_number_preserved() {
2816        // -5 + x should NOT parse - as subtraction; should be add(-5, x)
2817        let mut parser = LiterateParser::new("-5 + x");
2818        let term = parser.parse_term().unwrap();
2819        // Should be add(-5, x)
2820        if let Term::App(outer, _) = term {
2821            if let Term::App(inner, lhs) = *outer {
2822                if let Term::Global(op) = *inner {
2823                    assert_eq!(op, "add");
2824                }
2825                if let Term::Lit(Literal::Int(n)) = *lhs {
2826                    assert_eq!(n, -5);
2827                } else {
2828                    panic!("Expected -5 literal");
2829                }
2830            }
2831        } else {
2832            panic!("Expected App");
2833        }
2834    }
2835
2836    #[test]
2837    fn test_parse_subtraction_not_negative() {
2838        // x - 5 should parse as sub(x, 5)
2839        let mut parser = LiterateParser::new("x - 5");
2840        let term = parser.parse_term().unwrap();
2841        if let Term::App(outer, rhs) = term {
2842            if let Term::App(inner, _) = *outer {
2843                if let Term::Global(op) = *inner {
2844                    assert_eq!(op, "sub");
2845                }
2846            }
2847            if let Term::Lit(Literal::Int(n)) = *rhs {
2848                assert_eq!(n, 5); // Should be positive 5, not -5
2849            } else {
2850                panic!("Expected 5 literal");
2851            }
2852        } else {
2853            panic!("Expected App");
2854        }
2855    }
2856
2857    #[test]
2858    fn test_parse_infix_with_sexp_mix() {
2859        // Ensure S-expression syntax still works: (add x y) <= z
2860        let mut parser = LiterateParser::new("(add x y) <= z");
2861        let term = parser.parse_term().unwrap();
2862        if let Term::App(outer, _) = term {
2863            if let Term::App(inner, lhs) = *outer {
2864                if let Term::Global(op) = *inner {
2865                    assert_eq!(op, "le");
2866                }
2867                // lhs should be add(x, y) from S-expression parsing
2868                if let Term::App(add_outer, _) = *lhs {
2869                    if let Term::App(add_inner, _) = *add_outer {
2870                        if let Term::Global(add_op) = *add_inner {
2871                            assert_eq!(add_op, "add");
2872                        }
2873                    }
2874                }
2875            }
2876        } else {
2877            panic!("Expected App");
2878        }
2879    }
2880}