1pub mod results;
2
3use ftml_ontology::terms::{ComponentVar, Term, Variable};
4use ftml_uris::{FtmlUri, Uri, UriRef};
5#[cfg(feature = "colors")]
6use owo_colors::OwoColorize;
7use std::borrow::Cow;
8use std::{fmt::Write, marker::PhantomData};
9
10#[cfg(feature = "full")]
11pub trait CheckerRule: std::fmt::Debug + Send + Sync + std::any::Any {
12 fn priority(&self) -> isize {
13 0
14 }
15 fn display(&self) -> Vec<Displayable>;
16 fn as_box_dyn(&self) -> Box<dyn CheckerRule>;
17 fn as_dyn(&self) -> &dyn CheckerRule;
18 fn as_any(&self) -> &dyn std::any::Any;
19 fn eq(&self, o: &dyn CheckerRule) -> bool;
20}
21
22#[cfg(feature = "full")]
23pub trait SizedSolverRule:
24 std::fmt::Debug + Send + Sync + std::any::Any + Clone + Sized + PartialEq + Eq
25{
26 fn priority(&self) -> isize {
27 0
28 }
29 fn display(&self) -> Vec<Displayable>;
30}
31
32#[cfg(feature = "full")]
33impl<T: SizedSolverRule> CheckerRule for T {
34 #[allow(clippy::inline_always)]
35 #[inline(always)]
36 fn priority(&self) -> isize {
37 <Self as SizedSolverRule>::priority(self)
38 }
39
40 #[inline]
41 fn display(
42 &self,
43 ) -> Vec<Displayable> {
45 <T as SizedSolverRule>::display(self)
46 }
47
48 fn as_box_dyn(&self) -> Box<dyn CheckerRule> {
49 Box::new(self.clone()) as _
50 }
51 fn as_dyn(&self) -> &dyn CheckerRule {
52 self as _
53 }
54 fn as_any(&self) -> &dyn std::any::Any {
55 self as _
56 }
57 fn eq(&self, o: &dyn CheckerRule) -> bool {
58 o.as_any().downcast_ref::<T>().is_some_and(|v| v == self)
59 }
60}
61
62#[cfg(feature = "full")]
63#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
64pub enum MessageLevel {
65 Failure,
66 Comment,
67 Header,
68 Emph,
69}
70
71#[cfg(feature = "full")]
72#[derive(Clone, Copy, Default)]
73pub struct Indent(pub usize);
74#[cfg(feature = "full")]
75impl Indent {
76 pub const fn increase(&mut self) {
77 self.0 += 1;
78 }
79 pub const fn decrease(&mut self) {
80 self.0 = self.0.saturating_sub(1);
81 }
82}
83#[cfg(feature = "full")]
84impl std::fmt::Display for Indent {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 if self.0 == 0 {
87 return Ok(());
88 }
89 for _ in 0..self.0 - 1 {
90 f.write_str(" │")?;
91 }
92 f.write_str(" ├─")?;
93 Ok(())
94 }
95}
96
97#[cfg(feature = "full")]
98#[derive(Debug)]
99pub enum CheckLogCow<'t> {
100 Owned(PreCheckLog),
101 Borrowed(RefCheckLog<'t>),
102}
103#[cfg(feature = "full")]
104impl<'t> From<RefCheckLog<'t>> for CheckLogCow<'t> {
105 #[inline]
106 fn from(value: RefCheckLog<'t>) -> Self {
107 Self::Borrowed(value)
108 }
109}
110#[cfg(feature = "full")]
111impl From<PreCheckLog> for CheckLogCow<'_> {
112 #[inline]
113 fn from(value: PreCheckLog) -> Self {
114 Self::Owned(value)
115 }
116}
117
118macro_rules! tasks {
119 (
120 $(
121 $name:ident($($field:ident : $tp:ident),*) => $res:tt
122 ),* $(,)?
123 ) => {
124
125 #[cfg(feature = "full")]
126 #[derive(Debug)]
127 pub enum RefCheckLog<'t> {
128 $(
129 $name {
130 $($field: tasks!(@TPBORROW $tp),)*
131 steps:Box<[CheckLogCow<'t>]>,
132 context: Box<[Cow<'t, ComponentVar>]>,
133 result: Option<$res>,
134 },
135 )*
136 Rule{
137 rule: &'t dyn CheckerRule,
138 steps:Box<[CheckLogCow<'t>]>,
139 },
140 Strategy{
141 name: &'static str,
142 steps:Box<[CheckLogCow<'t>]>,
143 success:bool
144 },
145 Msg(Vec<DisplayableRef<'t>>, MessageLevel),
146 }
147 #[cfg(feature = "full")]
148 impl RefCheckLog<'_> {
149 pub fn into_owned(self,term:&impl Fn(Term) -> Term) -> PreCheckLog {
150 match self {
151 $(
152 Self::$name{$($field,)* steps,context,result} => PreCheckLog::$name{
153 $($field:tasks!(@CONV $tp $field term),)*
154 steps: steps.into_iter().map(|t| CheckLogCow::into_owned(t,term)).collect(),
155 context: context.into_iter().map(Cow::into_owned).collect(),
156 result,
157
158 },
159 )*
160 Self::Msg(txt,lvl) => PreCheckLog::Msg(txt.into_iter().map(|s| s.into_owned(term)).collect(),lvl),
161 Self::Rule{rule,steps} => PreCheckLog::Rule{
162 rule:rule.as_box_dyn(),
163 steps: steps.into_iter().map(|t| CheckLogCow::into_owned(t,term)).collect(),
164 },
165 Self::Strategy{name,steps,success} => PreCheckLog::Strategy{
166 name,
167 steps: steps.into_iter().map(|t| CheckLogCow::into_owned(t,term)).collect(),
168 success
169 }
170 }
171 }
172 }
173 #[cfg(feature = "full")]
174 #[derive(Debug)]
175 pub enum PreCheckLog {
176 $(
177 $name {
178 $($field: tasks!(@TPOWN $tp),)*
179 steps:Vec<Self>,
180 context:Box<[ComponentVar]>,
181 result:Option<$res>
182 },
183 )*
184 Rule{
185 rule:Box<dyn CheckerRule>,
186 steps:Vec<Self>,
187 },
188 Strategy{
189 name: &'static str,
190 steps:Vec<Self>,
191 success:bool
192 },
193 Msg(Vec<Displayable>, MessageLevel),
195 }
198
199 #[cfg(feature = "full")]
200 impl CheckLog {
201 pub fn from_pre(v:PreCheckLog,terms:&mut impl FnMut(Term) -> Term) -> Self {
202 use PreCheckLog as P;
203 match v {
204 $(
205 P::$name{
206 $($field,)*steps,context,result
207 } => Self::$name{
208 $( $field:tasks!(@FROMPRE $tp $field terms),)*
209 context,result:result.map(|r| tasks!(@FROMPRE $res r terms)),
210 steps:steps.into_iter().map(|e| Self::from_pre(e,terms)).collect()
211 },
212 )*
213 P::Rule{ rule, steps } => Self::Rule {
214 header:Displayable::map(rule.display(),terms),
215 steps:steps.into_iter().map(|e| Self::from_pre(e,terms)).collect()
216 },
217 P::Strategy{ name, steps, success } => Self::Strategy {
218 name:name.to_string(),
219 steps:steps.into_iter().map(|e| Self::from_pre(e,terms)).collect(),
220 success
221 },
222 P::Msg(s, MessageLevel::Comment) => Self::Comment(s),
223 P::Msg(s, MessageLevel::Emph) => Self::Emph(s),
224 P::Msg(s, MessageLevel::Header) => Self::Header(s),
225 P::Msg(s, MessageLevel::Failure) => Self::Fail(s),
226 }
229 }
230 }
231 #[cfg(feature = "full")]
232 impl<'t> CheckingTask<'t> {
233 pub fn close<R:Clone>(self,res:Option<&R>,steps:Box<[CheckLogCow<'t>]>,context:&[Cow<'t,ComponentVar>]) -> RefCheckLog<'t> {
234 let context = context.iter().map(Cow::clone).collect();
235 match self {
236 $(
237 Self::$name( $($field),* ) => RefCheckLog::$name {
238 $($field,)*
239 steps,
240 context,
241 result: res.map(|r| unsafe{&*std::ptr::from_ref(r).cast::<$res>()}.clone() )
242 },
243 )*
244 Self::Strategy(name) => RefCheckLog::Strategy {
245 name,
246 steps,success:res.is_some_and(|v| {
247 if std::mem::size_of::<R>() == 1 {
248 unsafe{
250 std::mem::transmute_copy::<R,bool>(v)
251 }
252 } else {
253 true
254 }
255 })
256 },
257 Self::Rule(rule) => RefCheckLog::Rule { rule, steps }
258 }
259 }
260 }
261 #[cfg(feature = "full")]
262 impl<'t> CheckLogCow<'t> {
263 pub fn into_owned(self,term:&impl Fn(Term) -> Term) -> PreCheckLog {
264 match self {
265 Self::Owned(o) => o,
266 Self::Borrowed(b) => b.into_owned(term)
267 }
268 }
269 }
270
271 #[cfg(feature = "full")]
272 #[derive(Copy,Clone,Debug)]
273 pub enum CheckingTask<'t> {
274 $(
275 $name($(tasks!(@TPBORROW $tp)),*)
276 ),*,
277 Rule(&'t dyn CheckerRule),
278 Strategy(&'static str),
279 }
280
281 #[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
282 pub enum CheckLog {
283 $(
284 $name {
285 $($field: tasks!(@TPOWN $tp),)*
286 steps:Vec<Self>,
287 context:Box<[ComponentVar]>,
288 result:Option<$res>
289 },
290 )*
291 Comment(Vec<Displayable>),
292 Emph(Vec<Displayable>),
293 Header(Vec<Displayable>),
294 Fail(Vec<Displayable>),
295 Strategy {
296 name: String,
297 steps: Vec<Self>,
298 success: bool,
299 },
300 Rule {
301 header: Vec<Displayable>,
302 steps: Vec<Self>,
303 },
304 }
305 #[cfg(feature = "full")]
306 impl CheckLog {
307 pub(crate) fn display_i(&self,displayer:&mut impl TraceDisplay) -> std::fmt::Result {
308 let mut curr = std::slice::from_ref(self).iter();
309 let mut stack = Vec::new();
310 let mut indent = Indent::default();
311 loop {
312 while let Some(next) = curr.next() {
313 if displayer.line(next,indent)? == std::ops::ControlFlow::Continue(()) {
314 match next {
315 $(
316 Self::$name{ $($field,)* steps, context,result } => {
317 displayer.task(CheckingTask::$name($($field),*),context,result.is_some())?;
318 tasks!(@DISPL result displayer $res);
319 indent.increase();
320 stack.push(std::mem::replace(&mut curr,steps.iter()));
321 }
322 )*
323 Self::Rule{header,steps} => {
324 for e in header {
325 displayer.displayable(e,None)?;
326 }
327 indent.increase();
328 stack.push(std::mem::replace(&mut curr,steps.iter()));
329 }
330 Self::Strategy{name,steps,success} => {
331 displayer.strategy(name,&[],*success)?;
332 indent.increase();
333 stack.push(std::mem::replace(&mut curr,steps.iter()));
334 }
335 Self::Comment(s) => {
336 for s in s {
337 displayer.displayable(s,Some(MessageLevel::Comment))?;
338 }
339 }
340 Self::Emph(s) => {
341 for s in s {
342 displayer.displayable(s,Some(MessageLevel::Emph))?;
343 }
344 }
345 Self::Header(s) => {
346 for s in s {
347 displayer.displayable(s,Some(MessageLevel::Header))?;
348 }
349 }
350 Self::Fail(s) => {
351 for s in s {
352 displayer.displayable(s,Some(MessageLevel::Failure))?;
353 }
354 }
355 }
356 }
357 }
358 if let Some(next) = stack.pop() {
359 indent.decrease();
360 curr = next;
361 } else {
362 break
363 }
364 }
365 Ok(())
366 }
367 }
368
369 };
370 (@DISPL $res:ident $disp:ident Term) => {
371 if let Some(t) = $res {
372 $disp.string(": ",None)?;
373 $disp.term(t,None)?;
374 }
375 };
376 (@DISPL $res:ident $disp:ident bool) => {};
377 (@TPBORROW Term) => {&'t Term};
378 (@TPOWN Term) => {Term};
379 (@TPBORROW str) => {&'t str};
380 (@TPOWN str) => {Box<str>};
381 (@FROMPRE Term $name:ident $f:ident) => {$f($name)};
382 (@FROMPRE str $name:ident $f:ident) => {$name};
383 (@FROMPRE bool $name:ident $f:ident) => {$name};
384 (@CONV Term $name:ident $f:ident) => {$name.clone()};(@CONV str $name:ident $f:ident) => { $name.to_string().into_boxed_str() };
386 (@CONV SolverRule $name:ident $f:ident) => { $name.as_box_dyn() };
389}
390
391#[cfg(feature = "full")]
392impl PreCheckLog {
393 pub fn push(&mut self, msg: Self) {
394 if let Some(steps) = self.steps_mut() {
395 steps.push(msg);
396 }
397 }
398 pub const fn steps_mut(&mut self) -> Option<&mut Vec<Self>> {
399 match self {
400 Self::Equality { steps, .. }
401 | Self::HasType { steps, .. }
402 | Self::Inference { steps, .. }
403 | Self::Inhabitable { steps, .. }
404 | Self::Rule { steps, .. }
405 | Self::Simplify { steps, .. }
406 | Self::Strategy { steps, .. }
407 | Self::Subtype { steps, .. }
408 | Self::Universe { steps, .. }
409 | Self::VariableInference { steps, .. }
410 | Self::Proving { steps, .. } => Some(steps),
411 Self::Msg(_, _) => None,
412 }
413 }
414}
415
416impl CheckLog {
417 pub fn filter_failures(&mut self, preserve_siblings: bool) {
418 let Some(steps) = self.steps_mut() else {
419 return;
420 };
421 if preserve_siblings {
422 for s in steps {
423 if s.success() {
424 s.filter_failures(false);
425 } else {
426 s.filter_failures(true);
427 }
428 }
429 } else {
430 *steps = std::mem::take(steps)
431 .into_iter()
432 .filter(|s| !s.success())
433 .map(|mut s| {
434 s.filter_failures(preserve_siblings);
435 s
436 })
437 .collect();
438 }
439 }
440 pub const fn steps_mut(&mut self) -> Option<&mut Vec<Self>> {
441 match self {
442 Self::Equality { steps, .. }
443 | Self::HasType { steps, .. }
444 | Self::Inference { steps, .. }
445 | Self::Inhabitable { steps, .. }
446 | Self::Rule { steps, .. }
447 | Self::Simplify { steps, .. }
448 | Self::Strategy { steps, .. }
449 | Self::Subtype { steps, .. }
450 | Self::Universe { steps, .. }
451 | Self::VariableInference { steps, .. }
452 | Self::Proving { steps, .. } => Some(steps),
453 Self::Comment(_) | Self::Emph(_) | Self::Header(_) | Self::Fail(_) => None,
454 }
455 }
456 pub fn success(&self) -> bool {
457 match self {
458 Self::Equality { result, .. }
459 | Self::HasType { result, .. }
460 | Self::Inhabitable { result, .. }
461 | Self::Subtype { result, .. }
462 | Self::Universe { result, .. } => *result == Some(true),
463 Self::Rule { steps, .. } => steps.iter().all(Self::success),
464 Self::Inference { result, .. }
465 | Self::VariableInference { result, .. }
466 | Self::Simplify { result, .. }
467 | Self::Proving { result, .. } => result.is_some(),
468 Self::Strategy { success, .. } => *success,
469 Self::Comment(_) | Self::Header(_) | Self::Emph(_) | Self::Fail(_) => false,
470 }
471 }
472 pub fn add_failure(&mut self, s: &'static str) {
473 if let Some(steps) = self.steps_mut() {
474 steps.push(Self::Fail(vec![s.to_string().into()]));
475 }
476 }
477}
478
479#[cfg(feature = "full")]
480#[derive(Debug, Clone)]
481pub enum DisplayableRef<'r> {
482 Num(i128),
483 String(Cow<'static, str>),
485 Term(Cow<'r, Term>),
486 Uri(either::Either<UriRef<'r>, Uri>),
487 Var(Cow<'r, Variable>),
488}
489#[cfg(feature = "full")]
490impl DisplayableRef<'_> {
491 pub fn into_owned(self, term: &impl Fn(Term) -> Term) -> Displayable {
492 match self {
493 Self::Num(i) => Displayable::Num(i),
494 Self::String(s) => Displayable::String(s.to_string()),
495 Self::Term(t) => Displayable::Term(term(t.into_owned())),
496 Self::Uri(u) => Displayable::Uri(match u {
497 either::Left(u) => u.owned(),
498 either::Right(u) => u,
499 }),
500 Self::Var(v) => Displayable::Var(v.into_owned()),
501 }
502 }
503}
504
505#[allow(clippy::large_enum_variant)]
506#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
507pub enum Displayable {
508 Num(i128),
510 String(String),
512 Term(Term),
513 Uri(Uri),
514 Var(Variable),
515}
516impl Displayable {
517 fn map(v: Vec<Self>, terms: &mut impl FnMut(Term) -> Term) -> Vec<Self> {
518 v.into_iter()
519 .map(|e| {
520 if let Self::Term(t) = e {
521 Self::Term(terms(t))
522 } else {
523 e
524 }
525 })
526 .collect()
527 }
528}
529
530tasks! {
531 Simplify(term:Term) => Term,
532 Proving(term:Term) => Term,
533 Inference(term: Term) => Term,
534 VariableInference(var: str) => Term,
535 Inhabitable(term: Term) => bool,
537 Universe(term:Term) => bool,
538 Subtype(sub:Term,sup:Term) => bool,
539 HasType(tm:Term,tp:Term) => bool,
540 Equality(lhs:Term,rhs:Term) => bool,
541}
542
543#[cfg(feature = "full")]
544impl<'b> PartialEq<CheckingTask<'b>> for CheckingTask<'_> {
545 fn eq(&self, other: &CheckingTask<'b>) -> bool {
546 match (self, other) {
547 (Self::Simplify(t), CheckingTask::Simplify(t2))
548 | (Self::Proving(t), CheckingTask::Proving(t2))
549 | (Self::Inference(t), CheckingTask::Inference(t2))
550 | (Self::Inhabitable(t), CheckingTask::Inhabitable(t2))
551 | (Self::Universe(t), CheckingTask::Universe(t2)) => t.alpha_equal(t2),
552 (Self::VariableInference(v), CheckingTask::VariableInference(v2)) => v == v2,
553 (Self::Subtype(a, b), CheckingTask::Subtype(a2, b2))
554 | (Self::HasType(a, b), CheckingTask::HasType(a2, b2))
555 | (Self::Equality(a, b), CheckingTask::Equality(a2, b2)) => {
556 a.alpha_equal(a2) && b.alpha_equal(b2)
557 }
558 _ => false,
559 }
560 }
561}
562
563#[cfg(feature = "full")]
564impl CheckLog {
565 #[must_use]
566 pub fn display<D: FmtTraceDisplay>(&self) -> impl std::fmt::Display + use<'_, D> {
567 TraceDisplayer::<'_, D> {
568 trace: self,
569 d: PhantomData,
570 }
571 }
572 #[cfg(feature = "colors")]
573 #[must_use]
574 pub fn colored(&self) -> impl std::fmt::Display {
575 TraceDisplayer {
576 d: PhantomData::<ColorDisplay>,
577 trace: self,
578 }
579 }
580}
581#[cfg(feature = "full")]
582impl std::fmt::Display for CheckLog {
583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584 self.display::<()>().fmt(f)
585 }
586}
587
588#[cfg(feature = "full")]
589pub trait FmtTraceDisplay {
590 fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay;
591}
592
593#[cfg(feature = "full")]
594pub trait TraceDisplay {
595 fn displayable(&mut self, d: &Displayable, lvl: Option<MessageLevel>) -> std::fmt::Result {
600 match d {
601 Displayable::Num(i) => self.num(*i, lvl),
602 Displayable::String(s) => self.string(s, lvl),
604 Displayable::Term(t) => self.term(t, lvl),
605 Displayable::Uri(u) => self.uri(u.as_uri(), lvl),
606 Displayable::Var(v) => self.variable(v, lvl),
607 }
608 }
609
610 fn line(
612 &mut self,
613 _: &CheckLog,
614 indent: Indent,
615 ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error>; fn task(
623 &mut self,
624 task: CheckingTask<'_>,
625 context: &[ComponentVar],
626 success: bool,
627 ) -> std::fmt::Result;
628
629 fn strategy(&mut self, name: &str, context: &[ComponentVar], success: bool)
631 -> std::fmt::Result;
632
633 fn uri(&mut self, uri: ftml_uris::UriRef, lvl: Option<MessageLevel>) -> std::fmt::Result;
635
636 fn term(&mut self, term: &Term, lvl: Option<MessageLevel>) -> std::fmt::Result;
638
639 fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result;
641
642 fn variable(&mut self, var: &Variable, lvl: Option<MessageLevel>) -> std::fmt::Result;
644
645 fn num(&mut self, num: i128, lvl: Option<MessageLevel>) -> std::fmt::Result;
647
648 fn indent(&mut self, indent: Indent, lvl: Option<MessageLevel>) -> std::fmt::Result;
650
651 fn space(&mut self) -> std::fmt::Result;
653}
654#[cfg(feature = "full")]
655impl FmtTraceDisplay for () {
656 #[allow(clippy::inline_always)]
657 #[inline(always)]
658 fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay {
659 f
660 }
661}
662#[cfg(feature = "full")]
663impl TraceDisplay for &mut std::fmt::Formatter<'_> {
664 fn line(
665 &mut self,
666 _: &CheckLog,
667 indent: Indent,
668 ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error> {
670 self.write_char('\n')?;
671 self.indent(indent, None)?;
672 Ok(std::ops::ControlFlow::Continue(()))
673 }
674 fn space(&mut self) -> std::fmt::Result {
675 self.write_char(' ')
676 }
677 fn strategy(&mut self, name: &str, _: &[ComponentVar], _: bool) -> std::fmt::Result {
685 write!(self, "Strategy: {name}")
686 }
687 fn task(
688 &mut self,
689 task: CheckingTask<'_>,
690 context: &[ComponentVar],
691 success: bool,
692 ) -> std::fmt::Result {
694 fn do_context(
695 context: &[ComponentVar],
696 mut f: &mut std::fmt::Formatter<'_>,
697 ) -> std::fmt::Result {
698 if context.is_empty() {
699 return Ok(());
700 }
701 f.write_str("{... ")?;
702 let mut first = true;
703 for ComponentVar { var, tp, df } in context {
704 if first {
705 first = false;
706 } else {
707 f.write_str(", ")?;
708 }
709 f.variable(var, None)?;
710 if let Some(tp) = tp {
711 f.write_str(" : ")?;
712 f.term(tp, None)?;
713 }
714 if let Some(df) = df {
715 f.write_str(" : ")?;
716 f.term(df, None)?;
717 }
718 }
719 f.write_str(" } ")
720 }
721 if success {
722 self.write_str("[SUCCESS] ")?;
723 } else {
724 self.write_str("[FAILED] ")?;
725 }
726 match task {
727 CheckingTask::Simplify(t) => {
728 self.write_str("Simplifying ")?;
729 do_context(context, self)?;
730 self.term(t, None)
731 }
732 CheckingTask::Proving(t) => {
733 self.write_str("Proving ")?;
734 do_context(context, self)?;
735 self.term(t, None)
736 }
737 CheckingTask::Inference(t) => {
738 self.write_str("Inferring type of ")?;
739 do_context(context, self)?;
740 self.term(t, None)
741 }
742 CheckingTask::VariableInference(t) => {
743 self.write_str("Inferring type of variable ")?;
744 do_context(context, self)?;
745 self.write_str(t)
746 }
747 CheckingTask::Inhabitable(tm) => {
748 self.write_str("Checking inhabitability ")?;
749 do_context(context, self)?;
750 self.write_str("⊢ INH ")?;
751 self.term(tm, None)
752 }
753 CheckingTask::Universe(tm) => {
754 self.write_str("Checking universe ")?;
755 do_context(context, self)?;
756 self.write_str("⊢ UNIV ")?;
757 self.term(tm, None)
758 }
759 CheckingTask::Subtype(sub, sup) => {
760 self.write_str("Checking subtyping ")?;
761 do_context(context, self)?;
762 self.write_str("⊢ ")?;
763 self.term(sub, None)?;
764 self.write_str(" <: ")?;
765 self.term(sup, None)
766 }
767 CheckingTask::HasType(tm, tp) => {
768 self.write_str("Checking typing ")?;
769 do_context(context, self)?;
770 self.write_str("⊢ ")?;
771 self.term(tm, None)?;
772 self.write_str(" : ")?;
773 self.term(tp, None)
774 }
775 CheckingTask::Equality(lhs, rhs) => {
776 self.write_str("Checking equality ")?;
777 do_context(context, self)?;
778 self.write_str("⊢ ")?;
779 self.term(lhs, None)?;
780 self.write_str(" == ")?;
781 self.term(rhs, None)
782 }
783 CheckingTask::Rule(rl) => {
784 for e in rl.display() {
785 self.displayable(&e, None)?;
786 }
787 Ok(())
788 }
789 CheckingTask::Strategy(s) => self.strategy(s, context, success),
790 }
791 }
792 fn uri(&mut self, uri: ftml_uris::UriRef, _: Option<MessageLevel>) -> std::fmt::Result {
793 match uri {
794 ftml_uris::UriRef::Symbol(s) => {
795 std::fmt::Display::fmt(&s.module.name, self)?;
796 self.write_char('?')?;
797 std::fmt::Display::fmt(&s.name, self)
798 }
799 _ => std::fmt::Display::fmt(&uri, self),
800 }
801 }
802 fn indent(&mut self, indent: Indent, _: Option<MessageLevel>) -> std::fmt::Result {
803 write!(self, "{indent} ")
804 }
805 fn term(&mut self, term: &Term, _: Option<MessageLevel>) -> std::fmt::Result {
806 <_ as std::fmt::Debug>::fmt(&term.debug_short(), self)
807 }
808 fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result {
809 if lvl == Some(MessageLevel::Failure) {
810 self.write_str("[FAILED] ")?;
811 }
812 self.write_str(s)
813 }
814 fn variable(&mut self, var: &Variable, _: Option<MessageLevel>) -> std::fmt::Result {
815 self.write_str(var.name())
816 }
817 fn num(&mut self, num: i128, _: Option<MessageLevel>) -> std::fmt::Result {
818 <i128 as std::fmt::Display>::fmt(&num, self)
819 }
820}
821
822#[cfg(feature = "colors")]
823pub struct ColorDisplay<'a, 'b>(&'a mut std::fmt::Formatter<'b>);
824#[cfg(feature = "colors")]
825impl FmtTraceDisplay for ColorDisplay<'_, '_> {
826 fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay {
827 ColorDisplay(f)
828 }
829}
830#[cfg(feature = "colors")]
831impl TraceDisplay for ColorDisplay<'_, '_> {
832 fn line(
833 &mut self,
834 _: &CheckLog,
835 indent: Indent,
836 ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error> {
837 self.0.write_char('\n')?;
838 self.indent(indent, None)?;
839 Ok(std::ops::ControlFlow::Continue(()))
840 }
841
842 fn space(&mut self) -> std::fmt::Result {
843 self.0.write_char(' ')
844 }
845
846 fn strategy(&mut self, name: &str, _: &[ComponentVar], _: bool) -> std::fmt::Result {
854 write!(self.0, "Strategy: {}", name.italic())
855 }
856
857 fn task(
858 &mut self,
859 task: CheckingTask<'_>,
860 context: &[ComponentVar],
861 success: bool,
862 ) -> std::fmt::Result {
863 fn do_context(context: &[ComponentVar], f: &mut ColorDisplay<'_, '_>) -> std::fmt::Result {
864 if context.is_empty() {
865 return Ok(());
866 }
867 f.0.write_str("{... ")?;
868 let mut first = true;
869 for ComponentVar { var, tp, df } in context {
870 if first {
871 first = false;
872 } else {
873 f.0.write_str(", ")?;
874 }
875 f.variable(var, None)?;
876 if let Some(tp) = tp {
877 f.0.write_str(" : ")?;
878 f.term(tp, None)?;
879 }
880 if let Some(df) = df {
881 f.0.write_str(" : ")?;
882 f.term(df, None)?;
883 }
884 }
885 f.0.write_str(" } ")
886 }
887 if success {
888 write!(self.0, "{} ", "[SUCCESS]".green())?;
889 } else {
890 write!(self.0, "{} ", "[FAILED]".red())?;
891 }
892 match task {
893 CheckingTask::Simplify(t) => {
894 write!(self.0, "{} ", "Simplifying".bright_white().bold())?;
895 do_context(context, self)?;
896 self.term(t, None)
897 }
898 CheckingTask::Proving(t) => {
899 write!(self.0, "{} ", "Proving".bright_white().bold())?;
900 do_context(context, self)?;
901 self.term(t, None)
902 }
903 CheckingTask::Inference(t) => {
904 write!(self.0, "{} ", "Inferring type of".bright_white().bold())?;
905 do_context(context, self)?;
906 self.term(t, None)
907 }
908 CheckingTask::VariableInference(t) => {
909 write!(
910 self.0,
911 "{} ",
912 "Inferring type of variable".bright_white().bold()
913 )?;
914 do_context(context, self)?;
915 self.0.write_str(t)
916 }
917 CheckingTask::Inhabitable(tm) => {
918 write!(
919 self.0,
920 "{} ",
921 "Checking inhabitability".bright_white().bold()
922 )?;
923 do_context(context, self)?;
924 write!(self.0, "{} ", "⊢ INH".bright_white().bold())?;
925 self.term(tm, None)
926 }
927 CheckingTask::Universe(tm) => {
928 write!(self.0, "{} ", "Checking universe".bright_white().bold())?;
929 do_context(context, self)?;
930 write!(self.0, "{} ", "⊢ UNIV".bright_white().bold())?;
931 self.term(tm, None)
932 }
933 CheckingTask::Subtype(sub, sup) => {
934 write!(self.0, "{} ", "Checking subtyping".bright_white().bold())?;
935 do_context(context, self)?;
936 write!(self.0, "{} ", "⊢".bright_white().bold())?;
937 self.term(sub, None)?;
938 write!(self.0, " {} ", "<:".bright_white().bold())?;
939 self.term(sup, None)
940 }
941 CheckingTask::HasType(tm, tp) => {
942 write!(self.0, "{} ", "Checking typing".bright_white().bold())?;
943 do_context(context, self)?;
944 write!(self.0, "{} ", "⊢".bright_white().bold())?;
945 self.term(tm, None)?;
946 write!(self.0, " {} ", ":".bright_white().bold())?;
947 self.term(tp, None)
948 }
949 CheckingTask::Equality(lhs, rhs) => {
950 write!(self.0, "{} ", "Checking equality".bright_white().bold())?;
951 do_context(context, self)?;
952 write!(self.0, "{} ", "⊢".bright_white().bold())?;
953 self.term(lhs, None)?;
954 write!(self.0, " {} ", "==".bright_white().bold())?;
955 self.term(rhs, None)
956 }
957 CheckingTask::Rule(rl) => {
958 for e in rl.display() {
959 self.displayable(&e, None)?;
960 }
961 Ok(())
962 }
963 CheckingTask::Strategy(s) => self.strategy(s, context, success),
964 }
965 }
966 fn uri(&mut self, uri: ftml_uris::UriRef, _: Option<MessageLevel>) -> std::fmt::Result {
967 match uri {
968 ftml_uris::UriRef::Symbol(s) => {
969 std::fmt::Display::fmt(&s.module.name, self.0)?;
970 self.0.write_char('?')?;
971 std::fmt::Display::fmt(&s.name, self.0)
972 }
973 _ => std::fmt::Display::fmt(&uri, self.0),
974 }
975 }
976 fn indent(&mut self, indent: Indent, _: Option<MessageLevel>) -> std::fmt::Result {
977 write!(self.0, "{} ", indent.blue())
978 }
979 fn term(&mut self, term: &Term, _: Option<MessageLevel>) -> std::fmt::Result {
980 write!(self.0, "{:?}", term.debug_short().yellow())
981 }
982 fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result {
983 if lvl == Some(MessageLevel::Failure) {
984 write!(self.0, "{} ", "[FAILED]".red())?;
985 write!(self.0, "{}", s.red())
986 } else {
987 write!(self.0, "{}", s.bright_black())
988 }
989 }
990 fn variable(&mut self, var: &Variable, _: Option<MessageLevel>) -> std::fmt::Result {
991 self.0.write_str(var.name())
992 }
993 fn num(&mut self, num: i128, _: Option<MessageLevel>) -> std::fmt::Result {
994 <i128 as std::fmt::Display>::fmt(&num, self.0)
995 }
996}
997
998#[cfg(feature = "full")]
999struct TraceDisplayer<'d, D: FmtTraceDisplay> {
1000 trace: &'d CheckLog,
1001 d: PhantomData<D>,
1002}
1003#[cfg(feature = "full")]
1004impl<D: FmtTraceDisplay> std::fmt::Display for TraceDisplayer<'_, D> {
1005 #[inline]
1006 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1007 self.trace.display_i(&mut D::new(f))
1008 }
1009}
1010
1011#[cfg(feature = "full")]
1012impl From<i128> for DisplayableRef<'_> {
1013 fn from(value: i128) -> Self {
1014 Self::Num(value)
1015 }
1016}
1017#[cfg(feature = "full")]
1018impl From<usize> for DisplayableRef<'_> {
1019 fn from(value: usize) -> Self {
1020 Self::Num(value as _)
1021 }
1022}
1023#[cfg(feature = "full")]
1024impl From<&'static str> for DisplayableRef<'_> {
1025 fn from(value: &'static str) -> Self {
1026 Self::String(Cow::Borrowed(value))
1027 }
1028}
1029#[cfg(feature = "full")]
1030impl From<String> for DisplayableRef<'_> {
1031 fn from(value: String) -> Self {
1032 Self::String(Cow::Owned(value))
1033 }
1034}
1035#[cfg(feature = "full")]
1036impl<'r> From<&'r Term> for DisplayableRef<'r> {
1037 fn from(value: &'r Term) -> Self {
1038 Self::Term(Cow::Borrowed(value))
1039 }
1040}
1041#[cfg(feature = "full")]
1042impl From<Term> for DisplayableRef<'_> {
1043 fn from(value: Term) -> Self {
1044 Self::Term(Cow::Owned(value))
1045 }
1046}
1047#[cfg(feature = "full")]
1048impl<'r> From<UriRef<'r>> for DisplayableRef<'r> {
1049 fn from(value: UriRef<'r>) -> Self {
1050 Self::Uri(either::Left(value))
1051 }
1052}
1053#[cfg(feature = "full")]
1054impl From<Uri> for DisplayableRef<'_> {
1055 fn from(value: Uri) -> Self {
1056 Self::Uri(either::Right(value))
1057 }
1058}
1059#[cfg(feature = "full")]
1060impl<'r> From<&'r Variable> for DisplayableRef<'r> {
1061 fn from(value: &'r Variable) -> Self {
1062 Self::Var(Cow::Borrowed(value))
1063 }
1064}
1065#[cfg(feature = "full")]
1066impl From<Variable> for DisplayableRef<'_> {
1067 fn from(value: Variable) -> Self {
1068 Self::Var(Cow::Owned(value))
1069 }
1070}
1071
1072impl<T: FtmlUri> From<&T> for Displayable {
1073 fn from(value: &T) -> Self {
1074 Self::Uri(value.as_uri().owned())
1075 }
1076}
1077impl From<&str> for Displayable {
1078 fn from(value: &str) -> Self {
1079 Self::String(value.to_string())
1080 }
1081}
1082impl From<String> for Displayable {
1083 fn from(value: String) -> Self {
1084 Self::String(value)
1085 }
1086}
1087impl From<Term> for Displayable {
1088 fn from(value: Term) -> Self {
1089 Self::Term(value)
1090 }
1091}
1092
1093impl From<i128> for Displayable {
1094 fn from(value: i128) -> Self {
1095 Self::Num(value)
1096 }
1097}
1098impl From<usize> for Displayable {
1099 fn from(value: usize) -> Self {
1100 Self::Num(value as _)
1101 }
1102}
1103
1104#[cfg(feature = "full")]
1105#[macro_export]
1106macro_rules! trace {
1107 ($($e:expr),* $(,)? ) => {
1108 {vec![$(
1109 $e.into()
1110 ),*]
1111 }
1112 }
1113}
1114
1115#[cfg(feature = "full")]
1116#[macro_export]
1117macro_rules! traceref {
1118 (FAIL $($e:expr),* $(,)? ) => {
1119 $crate::RefCheckLog::Msg(
1120 vec![$( $e.into() ),*],
1121 $crate::MessageLevel::Failure
1122 )
1123 };
1124 ($($e:expr),* $(,)? ) => {
1125 $crate::RefCheckLog::Msg(
1126 vec![$( $e.into() ),*],
1127 $crate::MessageLevel::Comment
1128 )
1129 }
1130}