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) {
418 let Some(steps) = self.steps_mut() else {
419 return;
420 };
421 *steps = std::mem::take(steps)
422 .into_iter()
423 .filter(|s| !s.success())
424 .map(|mut s| {
425 s.filter_failures();
426 s
427 })
428 .collect();
429 }
430 pub const fn steps_mut(&mut self) -> Option<&mut Vec<Self>> {
431 match self {
432 Self::Equality { steps, .. }
433 | Self::HasType { steps, .. }
434 | Self::Inference { steps, .. }
435 | Self::Inhabitable { steps, .. }
436 | Self::Rule { steps, .. }
437 | Self::Simplify { steps, .. }
438 | Self::Strategy { steps, .. }
439 | Self::Subtype { steps, .. }
440 | Self::Universe { steps, .. }
441 | Self::VariableInference { steps, .. }
442 | Self::Proving { steps, .. } => Some(steps),
443 Self::Comment(_) | Self::Emph(_) | Self::Header(_) | Self::Fail(_) => None,
444 }
445 }
446 pub fn success(&self) -> bool {
447 match self {
448 Self::Equality { result, .. }
449 | Self::HasType { result, .. }
450 | Self::Inhabitable { result, .. }
451 | Self::Subtype { result, .. }
452 | Self::Universe { result, .. } => *result == Some(true),
453 Self::Rule { steps, .. } => steps.iter().all(Self::success),
454 Self::Inference { result, .. }
455 | Self::VariableInference { result, .. }
456 | Self::Simplify { result, .. }
457 | Self::Proving { result, .. } => result.is_some(),
458 Self::Strategy { success, .. } => *success,
459 Self::Comment(_) | Self::Header(_) | Self::Emph(_) | Self::Fail(_) => false,
460 }
461 }
462 pub fn add_failure(&mut self, s: &'static str) {
463 if let Some(steps) = self.steps_mut() {
464 steps.push(Self::Fail(vec![s.to_string().into()]));
465 }
466 }
467}
468
469#[cfg(feature = "full")]
470#[derive(Debug, Clone)]
471pub enum DisplayableRef<'r> {
472 Num(i128),
473 String(Cow<'static, str>),
475 Term(Cow<'r, Term>),
476 Uri(either::Either<UriRef<'r>, Uri>),
477 Var(Cow<'r, Variable>),
478}
479#[cfg(feature = "full")]
480impl DisplayableRef<'_> {
481 pub fn into_owned(self, term: &impl Fn(Term) -> Term) -> Displayable {
482 match self {
483 Self::Num(i) => Displayable::Num(i),
484 Self::String(s) => Displayable::String(s.to_string()),
485 Self::Term(t) => Displayable::Term(term(t.into_owned())),
486 Self::Uri(u) => Displayable::Uri(match u {
487 either::Left(u) => u.owned(),
488 either::Right(u) => u,
489 }),
490 Self::Var(v) => Displayable::Var(v.into_owned()),
491 }
492 }
493}
494
495#[allow(clippy::large_enum_variant)]
496#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
497pub enum Displayable {
498 Num(i128),
500 String(String),
502 Term(Term),
503 Uri(Uri),
504 Var(Variable),
505}
506impl Displayable {
507 fn map(v: Vec<Self>, terms: &mut impl FnMut(Term) -> Term) -> Vec<Self> {
508 v.into_iter()
509 .map(|e| {
510 if let Self::Term(t) = e {
511 Self::Term(terms(t))
512 } else {
513 e
514 }
515 })
516 .collect()
517 }
518}
519
520tasks! {
521 Simplify(term:Term) => Term,
522 Proving(term:Term) => Term,
523 Inference(term: Term) => Term,
524 VariableInference(var: str) => Term,
525 Inhabitable(term: Term) => bool,
527 Universe(term:Term) => bool,
528 Subtype(sub:Term,sup:Term) => bool,
529 HasType(tm:Term,tp:Term) => bool,
530 Equality(lhs:Term,rhs:Term) => bool,
531}
532
533#[cfg(feature = "full")]
534impl CheckLog {
535 #[must_use]
536 pub fn display<D: FmtTraceDisplay>(&self) -> impl std::fmt::Display + use<'_, D> {
537 TraceDisplayer::<'_, D> {
538 trace: self,
539 d: PhantomData,
540 }
541 }
542 #[cfg(feature = "colors")]
543 #[must_use]
544 pub fn colored(&self) -> impl std::fmt::Display {
545 TraceDisplayer {
546 d: PhantomData::<ColorDisplay>,
547 trace: self,
548 }
549 }
550}
551#[cfg(feature = "full")]
552impl std::fmt::Display for CheckLog {
553 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
554 self.display::<()>().fmt(f)
555 }
556}
557
558#[cfg(feature = "full")]
559pub trait FmtTraceDisplay {
560 fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay;
561}
562
563#[cfg(feature = "full")]
564pub trait TraceDisplay {
565 fn displayable(&mut self, d: &Displayable, lvl: Option<MessageLevel>) -> std::fmt::Result {
570 match d {
571 Displayable::Num(i) => self.num(*i, lvl),
572 Displayable::String(s) => self.string(s, lvl),
574 Displayable::Term(t) => self.term(t, lvl),
575 Displayable::Uri(u) => self.uri(u.as_uri(), lvl),
576 Displayable::Var(v) => self.variable(v, lvl),
577 }
578 }
579
580 fn line(
582 &mut self,
583 _: &CheckLog,
584 indent: Indent,
585 ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error>; fn task(
593 &mut self,
594 task: CheckingTask<'_>,
595 context: &[ComponentVar],
596 success: bool,
597 ) -> std::fmt::Result;
598
599 fn strategy(&mut self, name: &str, context: &[ComponentVar], success: bool)
601 -> std::fmt::Result;
602
603 fn uri(&mut self, uri: ftml_uris::UriRef, lvl: Option<MessageLevel>) -> std::fmt::Result;
605
606 fn term(&mut self, term: &Term, lvl: Option<MessageLevel>) -> std::fmt::Result;
608
609 fn string(&mut self, s: &str, lvl: Option<MessageLevel>) -> std::fmt::Result;
611
612 fn variable(&mut self, var: &Variable, lvl: Option<MessageLevel>) -> std::fmt::Result;
614
615 fn num(&mut self, num: i128, lvl: Option<MessageLevel>) -> std::fmt::Result;
617
618 fn indent(&mut self, indent: Indent, lvl: Option<MessageLevel>) -> std::fmt::Result;
620
621 fn space(&mut self) -> std::fmt::Result;
623}
624#[cfg(feature = "full")]
625impl FmtTraceDisplay for () {
626 #[allow(clippy::inline_always)]
627 #[inline(always)]
628 fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay {
629 f
630 }
631}
632#[cfg(feature = "full")]
633impl TraceDisplay for &mut std::fmt::Formatter<'_> {
634 fn line(
635 &mut self,
636 _: &CheckLog,
637 indent: Indent,
638 ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error> {
640 self.write_char('\n')?;
641 self.indent(indent, None)?;
642 Ok(std::ops::ControlFlow::Continue(()))
643 }
644 fn space(&mut self) -> std::fmt::Result {
645 self.write_char(' ')
646 }
647 fn strategy(&mut self, name: &str, _: &[ComponentVar], _: bool) -> std::fmt::Result {
655 write!(self, "Strategy: {name}")
656 }
657 fn task(
658 &mut self,
659 task: CheckingTask<'_>,
660 context: &[ComponentVar],
661 success: bool,
662 ) -> std::fmt::Result {
664 fn do_context(
665 context: &[ComponentVar],
666 mut f: &mut std::fmt::Formatter<'_>,
667 ) -> std::fmt::Result {
668 if context.is_empty() {
669 return Ok(());
670 }
671 f.write_str("{... ")?;
672 let mut first = true;
673 for ComponentVar { var, tp, df } in context {
674 if first {
675 first = false;
676 } else {
677 f.write_str(", ")?;
678 }
679 f.variable(var, None)?;
680 if let Some(tp) = tp {
681 f.write_str(" : ")?;
682 f.term(tp, None)?;
683 }
684 if let Some(df) = df {
685 f.write_str(" : ")?;
686 f.term(df, None)?;
687 }
688 }
689 f.write_str(" } ")
690 }
691 if success {
692 self.write_str("[SUCCESS] ")?;
693 } else {
694 self.write_str("[FAILED] ")?;
695 }
696 match task {
697 CheckingTask::Simplify(t) => {
698 self.write_str("Simplifying ")?;
699 do_context(context, self)?;
700 self.term(t, None)
701 }
702 CheckingTask::Proving(t) => {
703 self.write_str("Proving ")?;
704 do_context(context, self)?;
705 self.term(t, None)
706 }
707 CheckingTask::Inference(t) => {
708 self.write_str("Inferring type of ")?;
709 do_context(context, self)?;
710 self.term(t, None)
711 }
712 CheckingTask::VariableInference(t) => {
713 self.write_str("Inferring type of variable ")?;
714 do_context(context, self)?;
715 self.write_str(t)
716 }
717 CheckingTask::Inhabitable(tm) => {
718 self.write_str("Checking inhabitability ")?;
719 do_context(context, self)?;
720 self.write_str("⊢ INH ")?;
721 self.term(tm, None)
722 }
723 CheckingTask::Universe(tm) => {
724 self.write_str("Checking universe ")?;
725 do_context(context, self)?;
726 self.write_str("⊢ UNIV ")?;
727 self.term(tm, None)
728 }
729 CheckingTask::Subtype(sub, sup) => {
730 self.write_str("Checking subtyping ")?;
731 do_context(context, self)?;
732 self.write_str("⊢ ")?;
733 self.term(sub, None)?;
734 self.write_str(" <: ")?;
735 self.term(sup, None)
736 }
737 CheckingTask::HasType(tm, tp) => {
738 self.write_str("Checking typing ")?;
739 do_context(context, self)?;
740 self.write_str("⊢ ")?;
741 self.term(tm, None)?;
742 self.write_str(" : ")?;
743 self.term(tp, None)
744 }
745 CheckingTask::Equality(lhs, rhs) => {
746 self.write_str("Checking equality ")?;
747 do_context(context, self)?;
748 self.write_str("⊢ ")?;
749 self.term(lhs, None)?;
750 self.write_str(" == ")?;
751 self.term(rhs, None)
752 }
753 CheckingTask::Rule(rl) => {
754 for e in rl.display() {
755 self.displayable(&e, None)?;
756 }
757 Ok(())
758 }
759 CheckingTask::Strategy(s) => self.strategy(s, context, success),
760 }
761 }
762 fn uri(&mut self, uri: ftml_uris::UriRef, _: Option<MessageLevel>) -> std::fmt::Result {
763 match uri {
764 ftml_uris::UriRef::Symbol(s) => {
765 std::fmt::Display::fmt(&s.module.name, self)?;
766 self.write_char('?')?;
767 std::fmt::Display::fmt(&s.name, self)
768 }
769 _ => std::fmt::Display::fmt(&uri, self),
770 }
771 }
772 fn indent(&mut self, indent: Indent, _: Option<MessageLevel>) -> std::fmt::Result {
773 write!(self, "{indent} ")
774 }
775 fn term(&mut self, term: &Term, _: Option<MessageLevel>) -> std::fmt::Result {
776 <_ as std::fmt::Debug>::fmt(&term.debug_short(), self)
777 }
778 fn string(&mut self, s: &str, _: Option<MessageLevel>) -> std::fmt::Result {
779 self.write_str(s)
780 }
781 fn variable(&mut self, var: &Variable, _: Option<MessageLevel>) -> std::fmt::Result {
782 self.write_str(var.name())
783 }
784 fn num(&mut self, num: i128, _: Option<MessageLevel>) -> std::fmt::Result {
785 <i128 as std::fmt::Display>::fmt(&num, self)
786 }
787}
788
789#[cfg(feature = "colors")]
790pub struct ColorDisplay<'a, 'b>(&'a mut std::fmt::Formatter<'b>);
791#[cfg(feature = "colors")]
792impl FmtTraceDisplay for ColorDisplay<'_, '_> {
793 fn new(f: &mut std::fmt::Formatter<'_>) -> impl TraceDisplay {
794 ColorDisplay(f)
795 }
796}
797#[cfg(feature = "colors")]
798impl TraceDisplay for ColorDisplay<'_, '_> {
799 fn line(
800 &mut self,
801 _: &CheckLog,
802 indent: Indent,
803 ) -> Result<std::ops::ControlFlow<()>, std::fmt::Error> {
804 self.0.write_char('\n')?;
805 self.indent(indent, None)?;
806 Ok(std::ops::ControlFlow::Continue(()))
807 }
808
809 fn space(&mut self) -> std::fmt::Result {
810 self.0.write_char(' ')
811 }
812
813 fn strategy(&mut self, name: &str, _: &[ComponentVar], _: bool) -> std::fmt::Result {
821 write!(self.0, "Strategy: {}", name.italic())
822 }
823
824 fn task(
825 &mut self,
826 task: CheckingTask<'_>,
827 context: &[ComponentVar],
828 success: bool,
829 ) -> std::fmt::Result {
830 fn do_context(context: &[ComponentVar], f: &mut ColorDisplay<'_, '_>) -> std::fmt::Result {
831 if context.is_empty() {
832 return Ok(());
833 }
834 f.0.write_str("{... ")?;
835 let mut first = true;
836 for ComponentVar { var, tp, df } in context {
837 if first {
838 first = false;
839 } else {
840 f.0.write_str(", ")?;
841 }
842 f.variable(var, None)?;
843 if let Some(tp) = tp {
844 f.0.write_str(" : ")?;
845 f.term(tp, None)?;
846 }
847 if let Some(df) = df {
848 f.0.write_str(" : ")?;
849 f.term(df, None)?;
850 }
851 }
852 f.0.write_str(" } ")
853 }
854 if success {
855 write!(self.0, "{} ", "[SUCCESS]".green())?;
856 } else {
857 write!(self.0, "{} ", "[FAILED]".red())?;
858 }
859 match task {
860 CheckingTask::Simplify(t) => {
861 write!(self.0, "{} ", "Simplifying".bright_white().bold())?;
862 do_context(context, self)?;
863 self.term(t, None)
864 }
865 CheckingTask::Proving(t) => {
866 write!(self.0, "{} ", "Proving".bright_white().bold())?;
867 do_context(context, self)?;
868 self.term(t, None)
869 }
870 CheckingTask::Inference(t) => {
871 write!(self.0, "{} ", "Inferring type of".bright_white().bold())?;
872 do_context(context, self)?;
873 self.term(t, None)
874 }
875 CheckingTask::VariableInference(t) => {
876 write!(
877 self.0,
878 "{} ",
879 "Inferring type of variable".bright_white().bold()
880 )?;
881 do_context(context, self)?;
882 self.0.write_str(t)
883 }
884 CheckingTask::Inhabitable(tm) => {
885 write!(
886 self.0,
887 "{} ",
888 "Checking inhabitability".bright_white().bold()
889 )?;
890 do_context(context, self)?;
891 write!(self.0, "{} ", "⊢ INH".bright_white().bold())?;
892 self.term(tm, None)
893 }
894 CheckingTask::Universe(tm) => {
895 write!(self.0, "{} ", "Checking universe".bright_white().bold())?;
896 do_context(context, self)?;
897 write!(self.0, "{} ", "⊢ UNIV".bright_white().bold())?;
898 self.term(tm, None)
899 }
900 CheckingTask::Subtype(sub, sup) => {
901 write!(self.0, "{} ", "Checking subtyping".bright_white().bold())?;
902 do_context(context, self)?;
903 write!(self.0, "{} ", "⊢".bright_white().bold())?;
904 self.term(sub, None)?;
905 write!(self.0, " {} ", "<:".bright_white().bold())?;
906 self.term(sup, None)
907 }
908 CheckingTask::HasType(tm, tp) => {
909 write!(self.0, "{} ", "Checking typing".bright_white().bold())?;
910 do_context(context, self)?;
911 write!(self.0, "{} ", "⊢".bright_white().bold())?;
912 self.term(tm, None)?;
913 write!(self.0, " {} ", ":".bright_white().bold())?;
914 self.term(tp, None)
915 }
916 CheckingTask::Equality(lhs, rhs) => {
917 write!(self.0, "{} ", "Checking equality".bright_white().bold())?;
918 do_context(context, self)?;
919 write!(self.0, "{} ", "⊢".bright_white().bold())?;
920 self.term(lhs, None)?;
921 write!(self.0, " {} ", "==".bright_white().bold())?;
922 self.term(rhs, None)
923 }
924 CheckingTask::Rule(rl) => {
925 for e in rl.display() {
926 self.displayable(&e, None)?;
927 }
928 Ok(())
929 }
930 CheckingTask::Strategy(s) => self.strategy(s, context, success),
931 }
932 }
933 fn uri(&mut self, uri: ftml_uris::UriRef, _: Option<MessageLevel>) -> std::fmt::Result {
934 match uri {
935 ftml_uris::UriRef::Symbol(s) => {
936 std::fmt::Display::fmt(&s.module.name, self.0)?;
937 self.0.write_char('?')?;
938 std::fmt::Display::fmt(&s.name, self.0)
939 }
940 _ => std::fmt::Display::fmt(&uri, self.0),
941 }
942 }
943 fn indent(&mut self, indent: Indent, _: Option<MessageLevel>) -> std::fmt::Result {
944 write!(self.0, "{} ", indent.blue())
945 }
946 fn term(&mut self, term: &Term, _: Option<MessageLevel>) -> std::fmt::Result {
947 write!(self.0, "{:?}", term.debug_short().yellow())
948 }
949 fn string(&mut self, s: &str, _: Option<MessageLevel>) -> std::fmt::Result {
950 write!(self.0, "{}", s.bright_black())
951 }
952 fn variable(&mut self, var: &Variable, _: Option<MessageLevel>) -> std::fmt::Result {
953 self.0.write_str(var.name())
954 }
955 fn num(&mut self, num: i128, _: Option<MessageLevel>) -> std::fmt::Result {
956 <i128 as std::fmt::Display>::fmt(&num, self.0)
957 }
958}
959
960#[cfg(feature = "full")]
961struct TraceDisplayer<'d, D: FmtTraceDisplay> {
962 trace: &'d CheckLog,
963 d: PhantomData<D>,
964}
965#[cfg(feature = "full")]
966impl<D: FmtTraceDisplay> std::fmt::Display for TraceDisplayer<'_, D> {
967 #[inline]
968 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
969 self.trace.display_i(&mut D::new(f))
970 }
971}
972
973#[cfg(feature = "full")]
974impl From<i128> for DisplayableRef<'_> {
975 fn from(value: i128) -> Self {
976 Self::Num(value)
977 }
978}
979#[cfg(feature = "full")]
980impl From<usize> for DisplayableRef<'_> {
981 fn from(value: usize) -> Self {
982 Self::Num(value as _)
983 }
984}
985#[cfg(feature = "full")]
986impl From<&'static str> for DisplayableRef<'_> {
987 fn from(value: &'static str) -> Self {
988 Self::String(Cow::Borrowed(value))
989 }
990}
991#[cfg(feature = "full")]
992impl From<String> for DisplayableRef<'_> {
993 fn from(value: String) -> Self {
994 Self::String(Cow::Owned(value))
995 }
996}
997#[cfg(feature = "full")]
998impl<'r> From<&'r Term> for DisplayableRef<'r> {
999 fn from(value: &'r Term) -> Self {
1000 Self::Term(Cow::Borrowed(value))
1001 }
1002}
1003#[cfg(feature = "full")]
1004impl From<Term> for DisplayableRef<'_> {
1005 fn from(value: Term) -> Self {
1006 Self::Term(Cow::Owned(value))
1007 }
1008}
1009#[cfg(feature = "full")]
1010impl<'r> From<UriRef<'r>> for DisplayableRef<'r> {
1011 fn from(value: UriRef<'r>) -> Self {
1012 Self::Uri(either::Left(value))
1013 }
1014}
1015#[cfg(feature = "full")]
1016impl From<Uri> for DisplayableRef<'_> {
1017 fn from(value: Uri) -> Self {
1018 Self::Uri(either::Right(value))
1019 }
1020}
1021#[cfg(feature = "full")]
1022impl<'r> From<&'r Variable> for DisplayableRef<'r> {
1023 fn from(value: &'r Variable) -> Self {
1024 Self::Var(Cow::Borrowed(value))
1025 }
1026}
1027#[cfg(feature = "full")]
1028impl From<Variable> for DisplayableRef<'_> {
1029 fn from(value: Variable) -> Self {
1030 Self::Var(Cow::Owned(value))
1031 }
1032}
1033
1034impl<T: FtmlUri> From<&T> for Displayable {
1035 fn from(value: &T) -> Self {
1036 Self::Uri(value.as_uri().owned())
1037 }
1038}
1039impl From<&str> for Displayable {
1040 fn from(value: &str) -> Self {
1041 Self::String(value.to_string())
1042 }
1043}
1044impl From<String> for Displayable {
1045 fn from(value: String) -> Self {
1046 Self::String(value)
1047 }
1048}
1049impl From<Term> for Displayable {
1050 fn from(value: Term) -> Self {
1051 Self::Term(value)
1052 }
1053}
1054
1055impl From<i128> for Displayable {
1056 fn from(value: i128) -> Self {
1057 Self::Num(value)
1058 }
1059}
1060impl From<usize> for Displayable {
1061 fn from(value: usize) -> Self {
1062 Self::Num(value as _)
1063 }
1064}
1065
1066#[cfg(feature = "full")]
1067#[macro_export]
1068macro_rules! trace {
1069 ($($e:expr),* $(,)? ) => {
1070 {vec![$(
1071 $e.into()
1072 ),*]
1073 }
1074 }
1075}
1076
1077#[cfg(feature = "full")]
1078#[macro_export]
1079macro_rules! traceref {
1080 (FAIL $($e:expr),* $(,)? ) => {
1081 $crate::RefCheckLog::Msg(
1082 vec![$( $e.into() ),*],
1083 $crate::MessageLevel::Failure
1084 )
1085 };
1086 ($($e:expr),* $(,)? ) => {
1087 $crate::RefCheckLog::Msg(
1088 vec![$( $e.into() ),*],
1089 $crate::MessageLevel::Comment
1090 )
1091 }
1092}