logicaffeine_kernel/interface/
term_parser.rs1use crate::{Literal, Term, Universe};
17use super::error::ParseError;
18use std::collections::HashSet;
19
20pub struct TermParser<'a> {
22 input: &'a str,
23 pos: usize,
24 bound_vars: HashSet<String>,
26 implicit_count: usize,
28}
29
30impl<'a> TermParser<'a> {
31 pub fn parse(input: &'a str) -> Result<Term, ParseError> {
33 Self::parse_with_implicits(input).map(|(t, _)| t)
34 }
35
36 pub fn parse_with_implicits(input: &'a str) -> Result<(Term, usize), ParseError> {
39 let mut parser = Self {
40 input,
41 pos: 0,
42 bound_vars: HashSet::new(),
43 implicit_count: 0,
44 };
45 let term = parser.parse_term()?;
46 parser.skip_whitespace();
47 Ok((term, parser.implicit_count))
48 }
49
50 fn parse_term(&mut self) -> Result<Term, ParseError> {
52 self.skip_whitespace();
53
54 if self.peek_keyword("fun") {
55 self.parse_lambda()
56 } else if self.peek_keyword("forall") {
57 self.parse_forall()
58 } else if self.peek_keyword("fix") {
59 self.parse_fix()
60 } else if self.peek_keyword("match") {
61 self.parse_match()
62 } else if self.peek_keyword("let") {
63 self.parse_let()
64 } else if self.peek_char('{') {
65 self.parse_implicit_binder()
66 } else {
67 self.parse_arrow()
68 }
69 }
70
71 fn parse_let(&mut self) -> Result<Term, ParseError> {
75 self.consume_keyword("let")?;
76 self.skip_whitespace();
77 let name = self.parse_ident()?;
78 self.skip_whitespace();
79 if self.peek_char(':') && !self.peek_str(":=") {
81 self.expect_char(':')?;
82 let _ty = self.parse_term()?;
83 self.skip_whitespace();
84 }
85 self.expect_str(":=")?;
86 self.skip_whitespace();
87 let value = self.parse_term()?;
88 self.skip_whitespace();
89 self.consume_keyword("in")?;
90
91 let was_bound = self.bound_vars.contains(&name);
92 self.bound_vars.insert(name.clone());
93 let body = self.parse_term()?;
94 if !was_bound {
95 self.bound_vars.remove(&name);
96 }
97
98 Ok(crate::type_checker::substitute(&body, &name, &value))
99 }
100
101 fn parse_implicit_binder(&mut self) -> Result<Term, ParseError> {
106 self.expect_char('{')?;
107 self.skip_whitespace();
108 let param = self.parse_ident()?;
109 self.skip_whitespace();
110 self.expect_char(':')?;
111 let param_type = self.parse_term()?;
112 self.skip_whitespace();
113 self.expect_char('}')?;
114 self.implicit_count += 1;
115
116 self.skip_whitespace();
117 self.try_consume("->"); let was_bound = self.bound_vars.contains(¶m);
120 self.bound_vars.insert(param.clone());
121 let body_type = self.parse_term()?;
122 if !was_bound {
123 self.bound_vars.remove(¶m);
124 }
125
126 Ok(Term::Pi {
127 param,
128 param_type: Box::new(param_type),
129 body_type: Box::new(body_type),
130 })
131 }
132
133 fn parse_arrow(&mut self) -> Result<Term, ParseError> {
135 let left = self.parse_app()?;
136
137 self.skip_whitespace();
138 if self.try_consume("->") {
139 let right = self.parse_arrow()?;
140 Ok(Term::Pi {
142 param: "_".to_string(),
143 param_type: Box::new(left),
144 body_type: Box::new(right),
145 })
146 } else {
147 Ok(left)
148 }
149 }
150
151 fn parse_app(&mut self) -> Result<Term, ParseError> {
153 let mut func = self.parse_atom()?;
154
155 loop {
156 self.skip_whitespace();
157 if self.at_end()
160 || self.peek_char(')')
161 || self.peek_char('}')
162 || self.peek_char('{')
163 || self.peek_char('.')
164 || self.peek_char(',')
165 || self.peek_char('|')
166 || self.peek_char('⟩')
167 || self.peek_str("->")
168 || self.peek_str("=>")
169 || self.peek_str(":=")
170 || self.peek_keyword("with")
171 || self.peek_keyword("return")
172 || self.peek_keyword("end")
173 || self.peek_keyword("in")
174 {
175 break;
176 }
177
178 let arg = self.parse_atom()?;
179 func = Term::App(Box::new(func), Box::new(arg));
180 }
181
182 Ok(func)
183 }
184
185 fn parse_atom(&mut self) -> Result<Term, ParseError> {
187 let base = self.parse_atom_base()?;
188 self.parse_dot_postfix(base)
189 }
190
191 fn parse_atom_base(&mut self) -> Result<Term, ParseError> {
193 self.skip_whitespace();
194
195 if self.peek_char('⟨') {
197 return self.parse_anon_ctor();
198 }
199
200 if self.peek_char('-') {
202 if let Some(lit) = self.try_parse_negative_number() {
203 return Ok(lit);
204 }
205 }
206
207 if let Some(c) = self.peek() {
209 if c.is_ascii_digit() {
210 return self.parse_number_literal();
211 }
212 }
213
214 if self.peek_char('"') {
216 return self.parse_string_literal();
217 }
218
219 if self.peek_char('(') {
220 self.parse_parens()
221 } else {
222 self.parse_ident_or_sort()
223 }
224 }
225
226 fn parse_dot_postfix(&mut self, mut base: Term) -> Result<Term, ParseError> {
230 while self.peek_char('.') {
231 let after = self.pos + '.'.len_utf8();
232 let is_field = self.input[after..]
233 .chars()
234 .next()
235 .map(|c| c.is_alphabetic() || c == '_')
236 .unwrap_or(false);
237 if !is_field {
238 break;
239 }
240 self.advance(); let field = self.parse_ident()?;
242 base = Term::App(
243 Box::new(Term::App(
244 Box::new(Term::Global(crate::elaborate::DOT_MARKER.to_string())),
245 Box::new(base),
246 )),
247 Box::new(Term::Global(field)),
248 );
249 }
250 Ok(base)
251 }
252
253 fn parse_anon_ctor(&mut self) -> Result<Term, ParseError> {
256 self.expect_char('⟨')?;
257 let mut comps = Vec::new();
258 self.skip_whitespace();
259 if !self.peek_char('⟩') {
260 loop {
261 comps.push(self.parse_term()?);
262 self.skip_whitespace();
263 if self.try_consume(",") {
264 self.skip_whitespace();
265 continue;
266 }
267 break;
268 }
269 }
270 self.expect_char('⟩')?;
271 let mut t = Term::Global(crate::elaborate::ANON_CTOR_MARKER.to_string());
272 for c in comps {
273 t = Term::App(Box::new(t), Box::new(c));
274 }
275 Ok(t)
276 }
277
278 fn parse_number_literal(&mut self) -> Result<Term, ParseError> {
280 let start = self.pos;
281
282 while let Some(c) = self.peek() {
283 if c.is_ascii_digit() {
284 self.advance();
285 } else {
286 break;
287 }
288 }
289
290 let num_str = &self.input[start..self.pos];
291 let value: i64 = num_str
292 .parse()
293 .map_err(|_| ParseError::InvalidNumber(num_str.to_string()))?;
294
295 Ok(Term::Lit(Literal::Int(value)))
296 }
297
298 fn parse_string_literal(&mut self) -> Result<Term, ParseError> {
300 self.expect_char('"')?;
301
302 let mut content = String::new();
304 loop {
305 match self.peek() {
306 Some('"') => {
307 self.advance();
308 break;
309 }
310 Some('\\') => {
311 self.advance();
313 match self.peek() {
314 Some('n') => {
315 content.push('\n');
316 self.advance();
317 }
318 Some('t') => {
319 content.push('\t');
320 self.advance();
321 }
322 Some('\\') => {
323 content.push('\\');
324 self.advance();
325 }
326 Some('"') => {
327 content.push('"');
328 self.advance();
329 }
330 Some(c) => {
331 content.push(c);
332 self.advance();
333 }
334 None => return Err(ParseError::UnexpectedEof),
335 }
336 }
337 Some(c) => {
338 content.push(c);
339 self.advance();
340 }
341 None => return Err(ParseError::UnexpectedEof),
342 }
343 }
344
345 Ok(Term::Lit(Literal::Text(content)))
346 }
347
348 fn try_parse_negative_number(&mut self) -> Option<Term> {
350 if !self.peek_char('-') {
352 return None;
353 }
354 let after_dash = self.pos + 1;
355 if after_dash >= self.input.len() {
356 return None;
357 }
358 let next = self.input[after_dash..].chars().next()?;
359 if !next.is_ascii_digit() {
360 return None;
361 }
362
363 self.advance();
365
366 let start = self.pos;
368 while let Some(c) = self.peek() {
369 if c.is_ascii_digit() {
370 self.advance();
371 } else {
372 break;
373 }
374 }
375
376 let num_str = &self.input[start..self.pos];
377 let value: i64 = num_str.parse().ok()?;
378
379 Some(Term::Lit(Literal::Int(-value)))
380 }
381
382 fn parse_parens(&mut self) -> Result<Term, ParseError> {
384 self.expect_char('(')?;
385 let term = self.parse_term()?;
386 self.skip_whitespace();
387 self.expect_char(')')?;
388 Ok(term)
389 }
390
391 fn parse_ident_or_sort(&mut self) -> Result<Term, ParseError> {
393 let ident = self.parse_ident()?;
394
395 match ident.as_str() {
396 "Prop" => Ok(Term::Sort(Universe::Prop)),
397 "Type" => {
398 self.skip_whitespace();
400 if let Some(n) = self.try_parse_number() {
401 Ok(Term::Sort(Universe::Type(n)))
402 } else {
403 Ok(Term::Sort(Universe::Type(0)))
404 }
405 }
406 _ => {
407 if ident.starts_with("Type") {
409 if let Ok(n) = ident[4..].parse::<u32>() {
410 return Ok(Term::Sort(Universe::Type(n)));
411 }
412 }
413 if self.bound_vars.contains(&ident) {
415 Ok(Term::Var(ident))
416 } else {
417 Ok(Term::Global(ident))
418 }
419 }
420 }
421 }
422
423 fn parse_lambda(&mut self) -> Result<Term, ParseError> {
425 self.consume_keyword("fun")?;
426 self.skip_whitespace();
427 let param = self.parse_ident()?;
428 self.skip_whitespace();
429 self.expect_char(':')?;
430 let param_type = self.parse_term()?;
431 self.skip_whitespace();
432 self.expect_str("=>")?;
433
434 let was_bound = self.bound_vars.contains(¶m);
436 self.bound_vars.insert(param.clone());
437 let body = self.parse_term()?;
438 if !was_bound {
439 self.bound_vars.remove(¶m);
440 }
441
442 Ok(Term::Lambda {
443 param,
444 param_type: Box::new(param_type),
445 body: Box::new(body),
446 })
447 }
448
449 fn parse_forall(&mut self) -> Result<Term, ParseError> {
451 self.consume_keyword("forall")?;
452 self.skip_whitespace();
453 let param = self.parse_ident()?;
454 self.skip_whitespace();
455 self.expect_char(':')?;
456 let param_type = self.parse_term()?;
457 self.skip_whitespace();
458 self.expect_char(',')?;
459
460 let was_bound = self.bound_vars.contains(¶m);
462 self.bound_vars.insert(param.clone());
463 let body_type = self.parse_term()?;
464 if !was_bound {
465 self.bound_vars.remove(¶m);
466 }
467
468 Ok(Term::Pi {
469 param,
470 param_type: Box::new(param_type),
471 body_type: Box::new(body_type),
472 })
473 }
474
475 fn parse_fix(&mut self) -> Result<Term, ParseError> {
477 self.consume_keyword("fix")?;
478 self.skip_whitespace();
479 let name = self.parse_ident()?;
480 self.skip_whitespace();
481 self.expect_str("=>")?;
482
483 let was_bound = self.bound_vars.contains(&name);
485 self.bound_vars.insert(name.clone());
486 let body = self.parse_term()?;
487 if !was_bound {
488 self.bound_vars.remove(&name);
489 }
490
491 Ok(Term::Fix {
492 name,
493 body: Box::new(body),
494 })
495 }
496
497 fn parse_match(&mut self) -> Result<Term, ParseError> {
499 self.consume_keyword("match")?;
500 self.skip_whitespace();
501 let discriminant = self.parse_app()?;
502 self.skip_whitespace();
503 let motive = if self.try_consume_keyword("return") {
506 self.skip_whitespace();
507 let m = self.parse_app()?;
508 self.skip_whitespace();
509 m
510 } else {
511 Term::Hole
512 };
513 self.consume_keyword("with")?;
514
515 let mut cases = Vec::new();
516 loop {
517 self.skip_whitespace();
518 if !self.try_consume("|") {
519 break;
520 }
521 self.skip_whitespace();
522
523 let case_term = self.parse_case_body()?;
525 cases.push(case_term);
526 }
527
528 self.skip_whitespace();
530 let _ = self.try_consume_keyword("end");
531
532 Ok(Term::Match {
533 discriminant: Box::new(discriminant),
534 motive: Box::new(motive),
535 cases,
536 })
537 }
538
539 fn parse_case_body(&mut self) -> Result<Term, ParseError> {
543 self.skip_whitespace();
545 let _ctor_name = self.parse_ident()?;
546
547 let mut binders = Vec::new();
549 loop {
550 self.skip_whitespace();
551 if self.peek_str("=>") {
552 break;
553 }
554 let ident = self.parse_ident()?;
555 binders.push(ident);
556 }
557 self.expect_str("=>")?;
558
559 let mut previously_bound = Vec::new();
561 for binder in &binders {
562 previously_bound.push(self.bound_vars.contains(binder));
563 self.bound_vars.insert(binder.clone());
564 }
565
566 let mut body = self.parse_term()?;
567
568 for (i, binder) in binders.iter().enumerate() {
570 if !previously_bound[i] {
571 self.bound_vars.remove(binder);
572 }
573 }
574
575 for binder in binders.into_iter().rev() {
578 body = Term::Lambda {
579 param: binder,
580 param_type: Box::new(Term::Global("_".to_string())), body: Box::new(body),
582 };
583 }
584
585 Ok(body)
586 }
587
588 fn parse_ident(&mut self) -> Result<String, ParseError> {
594 self.skip_whitespace();
595 let start = self.pos;
596
597 if let Some(c) = self.peek() {
599 if !c.is_alphabetic() && c != '_' {
600 return Err(ParseError::Expected {
601 expected: "identifier".to_string(),
602 found: format!("{}", c),
603 });
604 }
605 } else {
606 return Err(ParseError::UnexpectedEof);
607 }
608
609 while let Some(c) = self.peek() {
611 if c.is_alphanumeric() || c == '_' {
612 self.advance();
613 } else {
614 break;
615 }
616 }
617
618 let ident = self.input[start..self.pos].to_string();
619 if ident.is_empty() {
620 Err(ParseError::InvalidIdent("empty".to_string()))
621 } else {
622 Ok(ident)
623 }
624 }
625
626 fn try_parse_number(&mut self) -> Option<u32> {
628 self.skip_whitespace();
629 let start = self.pos;
630
631 while let Some(c) = self.peek() {
632 if c.is_ascii_digit() {
633 self.advance();
634 } else {
635 break;
636 }
637 }
638
639 if self.pos > start {
640 self.input[start..self.pos].parse().ok()
641 } else {
642 None
643 }
644 }
645
646 fn skip_whitespace(&mut self) {
648 while let Some(c) = self.peek() {
649 if c.is_whitespace() {
650 self.advance();
651 } else {
652 break;
653 }
654 }
655 }
656
657 fn peek(&self) -> Option<char> {
659 self.input[self.pos..].chars().next()
660 }
661
662 fn peek_char(&self, c: char) -> bool {
664 self.peek() == Some(c)
665 }
666
667 fn peek_str(&self, s: &str) -> bool {
669 self.input[self.pos..].starts_with(s)
670 }
671
672 fn peek_keyword(&self, keyword: &str) -> bool {
674 if !self.peek_str(keyword) {
675 return false;
676 }
677 let after = self.pos + keyword.len();
679 if after >= self.input.len() {
680 return true;
681 }
682 let next_char = self.input[after..].chars().next();
683 !next_char.map(|c| c.is_alphanumeric() || c == '_').unwrap_or(false)
684 }
685
686 fn advance(&mut self) {
688 if let Some(c) = self.peek() {
689 self.pos += c.len_utf8();
690 }
691 }
692
693 fn at_end(&self) -> bool {
695 self.pos >= self.input.len()
696 }
697
698 fn try_consume(&mut self, s: &str) -> bool {
700 if self.peek_str(s) {
701 self.pos += s.len();
702 true
703 } else {
704 false
705 }
706 }
707
708 fn try_consume_keyword(&mut self, keyword: &str) -> bool {
710 self.skip_whitespace();
711 if self.peek_keyword(keyword) {
712 self.pos += keyword.len();
713 true
714 } else {
715 false
716 }
717 }
718
719 fn expect_char(&mut self, expected: char) -> Result<(), ParseError> {
721 self.skip_whitespace();
722 if self.peek_char(expected) {
723 self.advance();
724 Ok(())
725 } else {
726 Err(ParseError::Expected {
727 expected: format!("'{}'", expected),
728 found: self.peek().map(|c| c.to_string()).unwrap_or("EOF".to_string()),
729 })
730 }
731 }
732
733 fn expect_str(&mut self, expected: &str) -> Result<(), ParseError> {
735 self.skip_whitespace();
736 if self.try_consume(expected) {
737 Ok(())
738 } else {
739 let found: String = self.input[self.pos..].chars().take(expected.len()).collect();
740 Err(ParseError::Expected {
741 expected: format!("'{}'", expected),
742 found: if found.is_empty() { "EOF".to_string() } else { found },
743 })
744 }
745 }
746
747 fn consume_keyword(&mut self, keyword: &str) -> Result<(), ParseError> {
749 self.skip_whitespace();
750 if self.peek_keyword(keyword) {
751 self.pos += keyword.len();
752 Ok(())
753 } else {
754 Err(ParseError::Expected {
755 expected: keyword.to_string(),
756 found: self.peek().map(|c| c.to_string()).unwrap_or("EOF".to_string()),
757 })
758 }
759 }
760
761 #[allow(dead_code)]
763 fn remaining(&self) -> &str {
764 &self.input[self.pos..]
765 }
766}
767
768#[cfg(test)]
769mod tests {
770 use super::*;
771
772 #[test]
773 fn test_parse_global() {
774 let term = TermParser::parse("Zero").unwrap();
775 assert!(matches!(term, Term::Global(ref s) if s == "Zero"));
776 }
777
778 #[test]
779 fn test_parse_sort() {
780 let term = TermParser::parse("Type").unwrap();
781 assert!(matches!(term, Term::Sort(Universe::Type(0))));
782
783 let term2 = TermParser::parse("Prop").unwrap();
784 assert!(matches!(term2, Term::Sort(Universe::Prop)));
785 }
786
787 #[test]
788 fn test_parse_app() {
789 let term = TermParser::parse("Succ Zero").unwrap();
790 if let Term::App(func, arg) = term {
791 assert!(matches!(*func, Term::Global(ref s) if s == "Succ"));
792 assert!(matches!(*arg, Term::Global(ref s) if s == "Zero"));
793 } else {
794 panic!("Expected App");
795 }
796 }
797
798 #[test]
799 fn test_parse_parens() {
800 let term = TermParser::parse("(Succ Zero)").unwrap();
801 assert!(matches!(term, Term::App(..)));
802 }
803
804 #[test]
805 fn test_parse_arrow() {
806 let term = TermParser::parse("Nat -> Nat").unwrap();
807 if let Term::Pi { param, param_type, body_type } = term {
808 assert_eq!(param, "_");
809 assert!(matches!(*param_type, Term::Global(ref s) if s == "Nat"));
810 assert!(matches!(*body_type, Term::Global(ref s) if s == "Nat"));
811 } else {
812 panic!("Expected Pi");
813 }
814 }
815
816 #[test]
817 fn test_parse_lambda() {
818 let term = TermParser::parse("fun x : Nat => Succ x").unwrap();
819 if let Term::Lambda { param, body, .. } = term {
820 assert_eq!(param, "x");
821 if let Term::App(_, arg) = *body {
823 assert!(matches!(*arg, Term::Var(ref s) if s == "x"));
824 } else {
825 panic!("Expected App in lambda body");
826 }
827 } else {
828 panic!("Expected Lambda");
829 }
830 }
831
832 #[test]
833 fn test_parse_lambda_bound_var() {
834 let term = TermParser::parse("fun n : Nat => Succ n").unwrap();
835 if let Term::Lambda { body, .. } = term {
836 if let Term::App(_, arg) = *body {
837 assert!(
838 matches!(*arg, Term::Var(ref s) if s == "n"),
839 "Expected Var(n), got {:?}",
840 arg
841 );
842 } else {
843 panic!("Expected App in lambda body");
844 }
845 } else {
846 panic!("Expected Lambda");
847 }
848 }
849}