Skip to main content

ftml_solver/rules/
symbols.rs

1use std::{borrow::Cow, hint::unreachable_unchecked, sync::LazyLock};
2
3use ftml_ontology::terms::{
4    ApplicationTerm, Argument, ArgumentMode, BindingTerm, BoundArgument, ComponentVar,
5    MaybeSequence, Term, Variable, eq::Alpha, patterns::Pattern,
6};
7use ftml_solver_trace::{CheckLogCow, Displayable, RefCheckLog, SizedSolverRule};
8use ftml_uris::{DocumentUri, Id, ModuleUri, SymbolUri};
9use smallvec::SmallVec;
10
11use crate::{
12    CheckRef,
13    impls::solving::{is_solvable_id, is_solvable_var},
14    rules::{RuleSet, implicits::ImplicitExtApp},
15    split::SplitStrategy,
16};
17
18macro_rules! uri {
19    ($(  $name:ident  $(  : $t:ty := $l:literal)?   $( = $lb:literal )?  ),* $(,)?) => {
20        $(
21            uri!{@go
22                $name $( : $t := $l )? $( = $lb )?
23            }
24        )*
25    };
26    (@go $name:ident = $l:literal) => {
27            pub static $name: LazyLock<SymbolUri> = LazyLock::new(||
28                URI.clone() | $l.parse::<ftml_uris::UriName>().expect("Is a valid URI")
29            );
30    };
31    (@go $name:ident : $t:ty := $l:literal) => {
32            pub static $name: LazyLock<$t> = LazyLock::new(||
33                $l.parse().expect("Is a valid URI")
34            );
35    }
36}
37
38pub static NAMESPACE: &str = "http://mathhub.info?a=FTML/meta";
39uri! {
40    DOC_URI:DocumentUri := "http://mathhub.info?a=FTML/meta&d=Judgments&l=en",
41    URI:ModuleUri := "http://mathhub.info?a=FTML/meta&m=Judgments",
42
43    INH = "inhabitable",
44    UNIV = "universe",
45    HAS_PROOF = "has proof",
46    HAS_TYPE = "has type",
47    SUBTYPE = "subtype of",
48    SIMPLIFY = "simplifies to",
49    EQUAL = "equal to",
50    BINDS = "binds",
51    PROOF_BARRIER = "proof barrier"
52}
53
54pub trait GenericJudgment: SizedSolverRule {
55    fn parse<Split: SplitStrategy>(params: &[Term], rules: &mut RuleSet<Split>) {
56        parse(params, rules);
57    }
58    fn vars(&self) -> &[Id];
59    fn premises(&self) -> &[Premise];
60}
61pub fn parse<Split: SplitStrategy>(params: &[Term], rules: &mut RuleSet<Split>) {
62    /*
63    println!("Here!");
64    for p in params {
65        println!(" - {:?}", p.debug_short());
66    }
67    */
68    let Some(Term::Application(concl)) = params.last() else {
69        return;
70    };
71    let mut vars = Vec::new();
72    let mut premises = Vec::with_capacity(params.len() - 1);
73    for p in &params[..params.len() - 1] {
74        if let Some(p) = Premise::parse(p, &mut vars) {
75            premises.push(p);
76        } else {
77            tracing::debug!("Invalid premise: {:?}", p.debug_short());
78            return;
79        }
80    }
81
82    if let [Argument::Simple(t)] = &*concl.arguments {
83        if concl.head.is(&*INH) {
84            Pattern::from_with_vars(t, true, &mut vars);
85            tracing::debug!("New dynamic inhabitability rule: {:?}", t.debug_short());
86            rules.push_inhabitable(Box::new(GenericInhabitable {
87                vars: vars.into_boxed_slice(),
88                premises: premises.into_boxed_slice(),
89                concl: t.clone(),
90            }));
91        } else if concl.head.is(&*UNIV) {
92            Pattern::from_with_vars(t, true, &mut vars);
93            tracing::debug!("New dynamic universe rule: {:?}", t.debug_short());
94            rules.push_universe(Box::new(GenericUniverse {
95                vars: vars.into_boxed_slice(),
96                premises: premises.into_boxed_slice(),
97                concl: t.clone(),
98            }));
99        } else if concl.head.is(&*HAS_PROOF) {
100            Pattern::from_with_vars(t, true, &mut vars);
101            tracing::debug!("New dynamic provability rule: {:?}", t.debug_short());
102            rules.push_proof(Box::new(GenericProof {
103                vars: vars.into_boxed_slice(),
104                premises: premises.into_boxed_slice(),
105                concl: t.clone(),
106            }));
107        }
108    } else if let [Argument::Simple(a), Argument::Simple(b)] = &*concl.arguments {
109        if concl.head.is(&*HAS_TYPE) {
110            Pattern::from_with_vars(a, true, &mut vars);
111            Pattern::from_with_vars(b, true, &mut vars);
112            tracing::debug!(
113                "New dynamic typing rule: {:?}  :  {:?}",
114                a.debug_short(),
115                b.debug_short()
116            );
117            rules.push_checking(Box::new(GenericTyping {
118                vars: vars.into_boxed_slice(),
119                premises: premises.into_boxed_slice(),
120                concl: (a.clone(), b.clone()),
121            }));
122        } else if concl.head.is(&*SUBTYPE) {
123            Pattern::from_with_vars(a, true, &mut vars);
124            Pattern::from_with_vars(b, true, &mut vars);
125            tracing::debug!(
126                "New dynamic subtyping rule: {:?}  <:  {:?}",
127                a.debug_short(),
128                b.debug_short()
129            );
130            rules.push_subtyping(Box::new(GenericSubtyping {
131                vars: vars.into_boxed_slice(),
132                premises: premises.into_boxed_slice(),
133                concl: (a.clone(), b.clone()),
134            }));
135        } else if concl.head.is(&*SIMPLIFY) {
136            Pattern::from_with_vars(a, true, &mut vars);
137            Pattern::from_with_vars(b, true, &mut vars);
138            tracing::debug!(
139                "New dynamic simplification rule: {:?}  -->  {:?}",
140                a.debug_short(),
141                b.debug_short()
142            );
143            rules.push_simplification(Box::new(GenericSimplification {
144                vars: vars.into_boxed_slice(),
145                premises: premises.into_boxed_slice(),
146                concl: (a.clone(), b.clone()),
147            }));
148        } else if concl.head.is(&*EQUAL) {
149            Pattern::from_with_vars(a, true, &mut vars);
150            Pattern::from_with_vars(b, true, &mut vars);
151            tracing::debug!(
152                "New dynamic equality rule: {:?}  ==  {:?}",
153                a.debug_short(),
154                b.debug_short()
155            );
156            rules.push_equality(Box::new(GenericEquality {
157                vars: vars.into_boxed_slice(),
158                premises: premises.into_boxed_slice(),
159                concl: (a.clone(), b.clone()),
160            }));
161        } else if concl.head.is(&*BINDS)
162            && let Term::Var { variable, .. } = a
163        {
164            let b = undo_implicits(b).unwrap_or_else(|| b.clone());
165            Pattern::from_with_vars(&b, true, &mut vars);
166            tracing::debug!("New dynamic preparation rule: {:?}", b.debug_short());
167            rules.push_preparation(Box::new(GenericBindPrep {
168                vars: vars.into_boxed_slice(),
169                premises: premises.into_boxed_slice(),
170                concl: (variable.clone(), b),
171            }));
172        }
173    }
174}
175
176fn undo_implicits(term: &Term) -> Option<Term> {
177    if let Some((head, _)) = term.unapply_implicits(false) {
178        Some(head.clone())
179    } else if let Term::Application(app) = term
180        && let Some((head, _)) = app.head.unapply_implicits(false)
181    {
182        Some(Term::Application(ApplicationTerm::new(
183            head.clone(),
184            app.arguments.clone(),
185            app.presentation.clone(),
186        )))
187    } else if let Term::Bound(app) = term
188        && let Some((head, _)) = app.head.unapply_implicits(false)
189    {
190        Some(Term::Bound(BindingTerm::new(
191            head.clone(),
192            app.arguments.clone(),
193            app.presentation.clone(),
194        )))
195    } else {
196        None
197    }
198}
199
200macro_rules! judg {
201    ($($name:ident($concl:ty $(>$prec:literal)?):$rl:ident { $($impl:tt)* } => $slf:ident{$($disp:tt)*} ),*$(,)?) => {
202        $(
203            #[derive(Clone,Debug,PartialEq,Eq)]
204            struct $name {
205                vars: Box<[Id]>,
206                premises: Box<[Premise]>,
207                concl: $concl,
208            }
209            impl SizedSolverRule for $name {
210                #[allow(unused_variables)]
211                fn display(&self) -> Vec<ftml_solver_trace::Displayable> {
212                    let $slf = self;
213                    $($disp)*
214                }
215                $(
216                    fn priority(&self) -> isize {
217                        $prec
218                    }
219                )?
220            }
221            impl GenericJudgment for $name {
222                fn vars(&self) -> &[Id] { &self.vars}
223                fn premises(&self) -> &[Premise] {&self.premises}
224            }
225            impl<Split:SplitStrategy> crate::rules::$rl<Split> for $name {
226                $($impl)*
227            }
228        )*
229    };
230}
231judg! {
232    GenericInhabitable(Term): InhabitableRule {
233        fn applicable(&self, term: &Term) -> bool {
234            Pattern::r#match(term, &self.concl, &self.vars, true).is_some()
235        }
236        fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
237            let conc = Pattern::r#match(term,&self.concl,&self.vars,true)?;
238            let mut conc = self.vars.iter().cloned().zip(conc.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
239            for p in &self.premises {
240                if p.check(&mut conc, &mut checker) != Some(true) { return None;}
241            }
242            if conc.iter().any(|(_,o)| o.is_none()) {None} else {
243                Some(true)
244            }
245        }
246    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
247
248    GenericUniverse(Term): UniverseRule {
249        fn applicable(&self, term: &Term) -> bool {
250            Pattern::r#match(term, &self.concl, &self.vars, true).is_some()
251        }
252        fn apply<'t>(&self, mut checker: CheckRef<'t, '_, Split>, term: &'t Term) -> Option<bool> {
253            let conc = Pattern::r#match(term,&self.concl,&self.vars,true)?;
254            let mut conc = self.vars.iter().cloned().zip(conc.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
255            for p in &self.premises {
256                if p.check(&mut conc, &mut checker) != Some(true) { return None;}
257            }
258            if conc.iter().any(|(_,o)| o.is_none()) {None} else {
259                Some(true)
260            }
261        }
262    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
263
264    GenericProof(Term): ProofRule {
265        fn applicable(&self, term: &Term) -> bool {
266            Pattern::r#match(term, &self.concl, &self.vars, true).is_some()
267        }
268        fn prove<'t>(&self, mut checker: CheckRef<'t, '_, Split>, goal: &'t Term) -> Option<Term> {
269            let conc = Pattern::r#match(goal,&self.concl,&self.vars,true)?;
270            let mut conc = self.vars.iter().cloned().zip(conc.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
271            for p in &self.premises {
272                if p.check(&mut conc, &mut checker) != Some(true) { return None;}
273            }
274            if conc.iter().any(|(_,o)| o.is_none()) {None} else {
275                Some(ftml_uris::metatheory::AUTO_PROVE.clone().into())
276            }
277        }
278    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
279
280    GenericBindPrep((Variable,Term) > 100_000): PreparationRule {
281        fn applicable(&self, checker: &CheckRef<'_, '_, Split>, t: &Term) -> bool {
282            let Term::Bound(b) = t else {
283                //tracing::trace!("Not bound");
284                return false;
285            };
286            let Some(head) = checker.get_head(t) else {
287                return false;
288            };
289            let spec = head.as_ref().either(|s| &s.data.arity, |v| &v.data.arity);
290            if spec.num() as usize != b.arguments.len() {
291                //tracing::trace!("Arguments don't match: {spec:?} != {:?}", b.arguments);
292                return false;
293            }
294            spec.iter()
295                .zip(b.arguments.iter())
296                .any(|(a, b)| match (a, b) {
297                    (ArgumentMode::BoundVariable, BoundArgument::Simple(t)) | (
298                        ArgumentMode::BoundVariableSequence,
299                        BoundArgument::Sequence(MaybeSequence::One(t)),
300                    ) => {
301                        Pattern::r#match(t, &self.concl.1, &self.vars, true).is_some()
302                    },
303                    (
304                        ArgumentMode::BoundVariableSequence,
305                        BoundArgument::Sequence(MaybeSequence::Seq(ts)),
306                    ) => ts.iter().any(|t| {
307                        Pattern::r#match(t, &self.concl.1, &self.vars, true).is_some()
308                    }),
309                    _ => false,
310                })
311        }
312        fn apply(
313            &self,
314            checker: &mut CheckRef<'_, '_, Split>,
315            t: Term,
316            path: Option<(&mut smallvec::SmallVec<u8, 16>, usize)>,
317        ) -> std::ops::ControlFlow<Term, Term> {
318            let Some(head) = checker.get_head(&t) else {
319                return std::ops::ControlFlow::Continue(t);
320            };
321            let Term::Bound(b) = t else {
322                unreachable!("wut");
323            };
324            let spec = head.as_ref().either(|s| &s.data.arity, |v| &v.data.arity);
325
326            let nargs = spec
327                .iter()
328                .zip(b.arguments.clone())
329                .map(|(m, a)| match (m, a) {
330                    (ArgumentMode::BoundVariable, BoundArgument::Simple(t)) => {
331                        if Pattern::r#match(&t, &self.concl.1, &self.vars, true).is_some() {
332                            // SAFETY: is_app
333                            let var/*(mut v, tp)*/ = unsafe { self.get_var(&t) };
334                            BoundArgument::Bound(ComponentVar {
335                                var,
336                                tp: None,//Some(tp),
337                                df: None,
338                            })
339                            /*if v.len() == 1 {
340                                // SAFETY: len==1
341                                let var = unsafe { v.pop().unwrap_unchecked() };
342                                BoundArgument::Bound(ComponentVar {
343                                    var,
344                                    tp: Some(tp),
345                                    df: None,
346                                })
347                            } else {
348                                BoundArgument::Simple(t)
349                            }*/
350                        } else {
351                            BoundArgument::Simple(t)
352                        }
353                    }
354                    (
355                        ArgumentMode::BoundVariableSequence,
356                        BoundArgument::Sequence(MaybeSequence::Seq(s)),
357                    ) => {
358                        let mut works = true;
359                        //let mut types = Vec::new();
360                        let mut ns = s
361                            .into_iter()
362                            .flat_map(|t| {
363                                if Pattern::r#match(&t, &self.concl.1, &self.vars, true).is_some() {
364                                    // SAFETY: is_app
365                                    let v = unsafe { self.get_var(&t) };
366                                    /*for _ in &v {
367                                        types.push(tp.clone());
368                                    }*/
369                                    vec![v.into()]
370                                    /*v.into_iter()
371                                        .map(|v| Term::Var {
372                                            variable: v,
373                                            presentation: None,
374                                        })
375                                        .collect::<Vec<_>>()*/
376                                } else {
377                                    works = false;
378                                    vec![t]
379                                }
380                            })
381                            .collect::<Vec<_>>();
382                        if works {
383                            BoundArgument::BoundSeq(if ns.len() == 1 && ns[0].is_sequence() {
384                                let Some(Term::Var { variable, .. }) = ns.pop() else {
385                                    // SAFETY: works == true
386                                    unsafe { unreachable_unchecked() }
387                                };
388                                MaybeSequence::One(ComponentVar {
389                                    var: variable,
390                                    tp: None,//Some(tp),
391                                    df: None,
392                                })
393                            }else {
394                            MaybeSequence::Seq(
395                                ns.into_iter()
396                                    //.zip(types)
397                                    .map(|t|{//(t, tp)| {
398                                        let Term::Var { variable, .. } = t else {
399                                            // SAFETY: works == true
400                                            unsafe { unreachable_unchecked() }
401                                        };
402                                        ComponentVar {
403                                            var: variable,
404                                            tp: None,//Some(tp),
405                                            df: None,
406                                        }
407                                    })
408                                    .collect(),
409                            )
410                            })
411                        } else {
412                            BoundArgument::Sequence(MaybeSequence::Seq(ns.into_boxed_slice()))
413                        }
414                    }
415                    (m, a) => {
416                        //tracing::trace!("Other: {m:?} = {a:?}");
417                        a
418                    }
419                })
420                .collect::<Vec<_>>()
421                .into_boxed_slice();
422            std::ops::ControlFlow::Continue(Term::Bound(BindingTerm::new(
423                b.head.clone(),
424                nargs,
425                b.presentation.clone(),
426            )))
427        }
428        fn applicable_revert(&self, _: &CheckRef<'_, '_, Split>, _: &Term) -> bool {
429            false
430        }
431        fn revert(&self, _: &CheckRef<'_, '_, Split>, t: Term) -> std::ops::ControlFlow<Term, Term> {
432            std::ops::ControlFlow::Continue(t)
433        }
434
435    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
436
437    GenericTyping((Term,Term)): CheckingRule {
438        fn applicable(&self, _: &CheckRef<'_, '_, Split>, term: &Term, tp: &Term) -> bool {
439            let (a,b) = &self.concl;
440            Pattern::r#match(term, a, &self.vars, true).is_some() &&
441            Pattern::r#match(tp, b, &self.vars, true).is_some()
442        }
443        fn apply<'t>(
444                &self,
445                mut checker: CheckRef<'t, '_, Split>,
446                term: &'t Term,
447                tp: &'t Term,
448        ) -> Option<bool> {
449            let (a,b) = &self.concl;
450            let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
451            Pattern::match_i(term,a,&self.vars,true,&mut vars,&mut Alpha::new())?;
452            Pattern::match_i(tp,b,&self.vars,true,&mut vars,&mut Alpha::new())?;
453            let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
454            for p in &self.premises {
455                if p.check(&mut conc, &mut checker) != Some(true) { return None;}
456            }
457            if conc.iter().any(|(_,o)| o.is_none()) {None} else {
458                Some(true)
459            }
460        }
461    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
462
463    GenericSubtyping((Term,Term)): SubtypeRule {
464        fn applicable(&self, _: &CheckRef<'_, '_, Split>, sub: &Term, sup: &Term) -> bool {
465            let (a,b) = &self.concl;
466            Pattern::r#match(sub, a, &self.vars, true).is_some() &&
467            Pattern::r#match(sup, b, &self.vars, true).is_some()
468        }
469        fn apply<'t>(
470            &self,
471            mut checker: CheckRef<'t, '_, Split>,
472            sub: &'t Term,
473            sup: &'t Term,
474        ) -> Option<bool> {
475            let (a,b) = &self.concl;
476            let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
477            Pattern::match_i(sub,a,&self.vars,true,&mut vars,&mut Alpha::new())?;
478            Pattern::match_i(sup,b,&self.vars,true,&mut vars,&mut Alpha::new())?;
479            let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
480            for p in &self.premises {
481                if p.check(&mut conc, &mut checker) != Some(true) { return None;}
482            }
483            if conc.iter().any(|(_,o)| o.is_none()) {None} else {
484                Some(true)
485            }
486        }
487    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
488
489    GenericEquality((Term,Term)): EqualityRule {
490        fn applicable(&self, lhs: &Term, rhs: &Term) -> bool {
491            let (a,b) = &self.concl;
492            Pattern::r#match(lhs, a, &self.vars, true).is_some() &&
493            Pattern::r#match(rhs, b, &self.vars, true).is_some()
494        }
495        fn apply<'t>(
496            &self,
497            mut checker: CheckRef<'t, '_, Split>,
498            lhs: &'t Term,
499            rhs: &'t Term,
500        ) -> Option<bool> {
501            let (a,b) = &self.concl;
502            let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
503            Pattern::match_i(lhs,a,&self.vars,true,&mut vars,&mut Alpha::new())?;
504            Pattern::match_i(rhs,b,&self.vars,true,&mut vars,&mut Alpha::new())?;
505            let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
506            for p in &self.premises {
507                if p.check(&mut conc, &mut checker) != Some(true) { return None;}
508            }
509            if conc.iter().any(|(_,o)| o.is_none()) {None} else {
510                Some(true)
511            }
512        }
513    } => slf { ftml_solver_trace::trace!("Dynamic Rule") },
514
515    GenericSimplification((Term,Term)): SimplificationRule {
516        fn applicable(&self, term: &Term) -> bool {
517            Pattern::r#match(term, &self.concl.0, &self.vars, true).is_some()
518        }
519        fn apply<'t>(
520            &self,
521            mut checker: CheckRef<'t, '_, Split>,
522            term: &'t Term,
523        ) -> Result<Term, Option<ftml_ontology::terms::termpaths::TermPath>> {
524            let (a,b) = &self.concl;
525            let mut vars: Vec<Option<Cow<'t, _>>> = vec![None; self.vars.len()];
526            Pattern::match_i(term,a,&self.vars,true,&mut vars,&mut Alpha::new()).ok_or(None)?;
527            //Pattern::match_i(rhs,b,&self.vars,true,&mut vars,&mut Alpha::new()).ok_or(None)?;
528            let mut conc = self.vars.iter().cloned().zip(vars.into_iter().map(|o| o.map(Cow::into_owned))).collect::<Vec<_>>();
529            for (i,p) in self.premises.iter().enumerate() {
530                checker.comment(format!("Premise {}",i+1));
531                if p.check(&mut conc, &mut checker) != Some(true) { return Err(None);}
532            }
533            if conc.iter().any(|(v,o)| !is_solvable_id(v) && o.is_none()) {return Err(None)}
534            let subst = conc.iter().filter_map(|(v,o)| if is_solvable_id(v) && o.is_none() {
535                Some((v.as_ref(),Cow::Owned(checker.new_solvable())))
536            } else {o.as_ref().map(|t| (v.as_ref(),Cow::Borrowed(t)))}).collect::<Vec<_>>();
537            let ret = b.clone() / subst.as_slice();
538            checker.add_msg(CheckLogCow::Borrowed(RefCheckLog::Msg(vec!["Result: ".into(),ret.clone().into()], ftml_solver_trace::MessageLevel::Comment)));
539            //println!("HERE RETURN: {:?}",(b.clone() / subst.as_slice()).debug_short());
540            Ok(ret)
541        }
542    } => slf {
543        let mut ret = vec!["Dynamic rule: ".into()];
544        if !slf.premises.is_empty() {
545            ret.push("[ ".into());
546            let mut first = true;
547            for p in &slf.premises {
548                if !first {
549                    ret.push(", ".into());
550                }
551                first = false;
552                p.disps(&mut ret);
553            }
554            ret.push(" ] ".into());
555        }
556        ret.extend(["⊢ ".into(),slf.concl.0.clone().into(),"  ==>  ".into(),slf.concl.1.clone().into()]);
557        ret
558    },
559}
560
561impl GenericBindPrep {
562    /// Only safe to call iff `self.is_app(&t)`
563    unsafe fn get_var(&self, t: &Term) -> Variable {
564        let Some(mut matches) = Pattern::r#match(t, &self.concl.1, &self.vars, true) else {
565            unreachable!("bug!");
566        };
567        self.vars
568            .iter()
569            .position(|v| v.as_ref() == self.concl.0.name())
570            .map_or_else(
571                || self.concl.0.clone(),
572                |i| {
573                    if let Some(Term::Var { variable, .. }) = matches.remove(i).map(Cow::into_owned)
574                    {
575                        variable
576                    } else {
577                        self.concl.0.clone()
578                    }
579                },
580            )
581    }
582}
583
584#[derive(Clone, Debug, PartialEq, Eq)]
585pub enum Premise {
586    Inhabitable(Term),
587    Universe(Term),
588    HasProof(Term),
589    HasType(Term, Term),
590    Subtype(Term, Term),
591    Equal(Term, Term),
592}
593impl Premise {
594    fn disps(&self, target: &mut Vec<Displayable>) {
595        match self {
596            Self::Inhabitable(t) => target.extend(["⊢ INH ".into(), t.clone().into()]),
597            Self::Universe(t) => target.extend(["⊢ UNIV ".into(), t.clone().into()]),
598            Self::HasProof(t) => target.extend(["⊢ PROOF ".into(), t.clone().into()]),
599            Self::HasType(t1, t2) => target.extend([
600                "⊢".into(),
601                t1.clone().into(),
602                " : ".into(),
603                t2.clone().into(),
604            ]),
605            Self::Subtype(t1, t2) => target.extend([
606                "⊢".into(),
607                t1.clone().into(),
608                " <: ".into(),
609                t2.clone().into(),
610            ]),
611            Self::Equal(t1, t2) => target.extend([
612                "⊢".into(),
613                t1.clone().into(),
614                " == ".into(),
615                t2.clone().into(),
616            ]),
617        }
618    }
619    fn parse(t: &Term, vars: &mut Vec<Id>) -> Option<Self> {
620        let Term::Application(p) = t else { return None };
621        if let [Argument::Simple(t)] = &*p.arguments {
622            if p.head.is(&*INH) {
623                Pattern::from_with_vars(t, true, vars);
624                Some(Self::Inhabitable(t.clone()))
625            } else if p.head.is(&*UNIV) {
626                Pattern::from_with_vars(t, true, vars);
627                Some(Self::Universe(t.clone()))
628            } else if p.head.is(&*HAS_PROOF) {
629                Pattern::from_with_vars(t, true, vars);
630                Some(Self::HasProof(t.clone()))
631            } else {
632                None
633            }
634        } else if let [Argument::Simple(a), Argument::Simple(b)] = &*p.arguments {
635            if p.head.is(&*HAS_TYPE) {
636                Pattern::from_with_vars(a, true, vars);
637                Pattern::from_with_vars(b, true, vars);
638                Some(Self::HasType(a.clone(), b.clone()))
639            } else if p.head.is(&*SUBTYPE) {
640                Pattern::from_with_vars(a, true, vars);
641                Pattern::from_with_vars(b, true, vars);
642                Some(Self::Subtype(a.clone(), b.clone()))
643            } else if p.head.is(&*EQUAL) {
644                Pattern::from_with_vars(a, true, vars);
645                Pattern::from_with_vars(b, true, vars);
646                Some(Self::Equal(a.clone(), b.clone()))
647            } else {
648                None
649            }
650        } else {
651            None
652        }
653    }
654    pub fn check<Split: SplitStrategy>(
655        &self,
656        context: &mut [(Id, Option<Term>)],
657        checker: &mut CheckRef<'_, '_, Split>,
658    ) -> Option<bool> {
659        fn has_free(t: &Term, ctx: &[(Id, Option<Term>)]) -> bool {
660            t.free_variables().iter().any(|v| {
661                ctx.iter()
662                    .all(|(a, b)| a.as_ref() != v.name() || b.is_none())
663            })
664        }
665        macro_rules! check {
666            ($c:ident.$f:ident($($t:ident),+) ) => {{
667                let subst = context.iter().filter_map(|(id,o)| o.as_ref().map(|t| (id.as_ref(),t))).collect::<Vec<_>>();
668                $(
669                    let $t = $t / subst.as_slice();
670                )*
671                checker.scoped(|$c| $c.$f($(&$t),*))
672            }}
673        }
674        match self {
675            Self::Inhabitable(t) => check!(c.check_inhabitable(t)),
676            Self::Universe(t) => check!(c.check_universe(t)),
677            Self::HasProof(t) => check!(c.prove(t)).map(|_| true),
678            Self::Subtype(a, b) => check!(c.check_subtype(a, b)),
679            Self::Equal(a, b) => {
680                if has_free(b, context) && !has_free(a, context) {
681                    let subst = context
682                        .iter()
683                        .filter_map(|(id, o)| o.as_ref().map(|t| (id.as_ref(), t)))
684                        .collect::<Vec<_>>();
685                    let na = a / subst.as_slice();
686                    let nb = b / subst.as_slice();
687                    let all_vars = nb
688                        .free_variables()
689                        .iter()
690                        .map(|v| v.name_id().into_owned())
691                        .collect::<SmallVec<_, 2>>();
692                    checker.comment(format!(
693                        "matching {:?} against {:?}",
694                        na.debug_short(),
695                        nb.debug_short()
696                    ));
697                    let r = checker.scoped(|ch| {
698                        ch.simplify_until(&na, |_, t| {
699                            #[allow(clippy::option_if_let_else)]
700                            if let Some(r) = Pattern::r#match(t, &nb, &all_vars, true) {
701                                for (name, nt) in all_vars.iter().zip(r) {
702                                    if let Some((_, t)) =
703                                        context.iter_mut().find(|(a, _)| *a == *name)
704                                    {
705                                        *t = nt.map(Cow::into_owned);
706                                    }
707                                }
708                                true
709                            } else {
710                                false
711                            }
712                        })
713                        .is_some()
714                    });
715                    if r { Some(true) } else { None }
716                } else {
717                    check!(c.check_equality(a, b))
718                }
719            }
720            Self::HasType(a, b) => {
721                if let Term::Var {
722                    variable: Variable::Name { name, .. },
723                    ..
724                } = b
725                    && context.iter().any(|(p, o)| p == name && o.is_none())
726                {
727                    let nt = check!(c.infer_type(a))?;
728                    context.iter_mut().find(|(a, _)| *a == *name)?.1 = Some(nt);
729                    Some(true)
730                } else if !has_free(a, context) && !has_free(b, context) {
731                    check!(c.check_type(a, b))
732                } else {
733                    None
734                }
735            }
736        }
737    }
738}