1use std::{borrow::Cow, fmt};
2
3use crate::{
4 formatter::{
5 BindPowerPair, ChildIndex, ChildPath, MatchResult, SubFormatter, TermDisplayContext,
6 DEFAULT_BIND_POWER, QUANT_BIND,
7 },
8 items::*,
9 parsers::z3::{
10 smt2::EventKind,
11 synthetic::{AnyTerm, SynthIdx, SynthTermKind},
12 Z3Parser,
13 },
14 NonMaxU32, StringTable,
15};
16
17pub trait DisplayWithCtxt<Ctxt, Data>: Sized {
22 fn fmt_with(self, f: &mut fmt::Formatter<'_>, ctxt: &Ctxt, data: &mut Data) -> fmt::Result;
25
26 fn with(self, ctxt: &Ctxt) -> DisplayWrapperEmpty<'_, Ctxt, Data, Self>
30 where
31 Self: Copy,
32 Data: Default,
33 {
34 DisplayWrapperEmpty {
35 inner: self,
36 ctxt,
37 data_marker: std::marker::PhantomData,
38 }
39 }
40
41 fn with_data<'a, 'b>(
45 self,
46 ctxt: &'a Ctxt,
47 data: &'b mut Data,
48 ) -> DisplayWrapperData<'a, 'b, Ctxt, Data, Self>
49 where
50 Self: Copy,
51 {
52 DisplayWrapperData {
53 inner: self,
54 ctxt,
55 data,
56 data_marker: std::marker::PhantomData,
57 }
58 }
59
60 fn debug<'a>(self, parser: &'a Z3Parser) -> String
62 where
63 Self: DisplayWithCtxt<DisplayCtxt<'a>, Data> + Copy,
64 Data: Default,
65 {
66 static TERM_DISPLAY: std::sync::OnceLock<TermDisplayContext> = std::sync::OnceLock::new();
67 let term_display = TERM_DISPLAY.get_or_init(TermDisplayContext::basic);
68 let config = DisplayConfiguration::default();
69 let ctxt = DisplayCtxt {
70 parser,
71 term_display,
72 config,
73 };
74 self.with(&ctxt).to_string()
75 }
76}
77
78pub struct DisplayWrapperEmpty<'a, Ctxt, Data: Default, T: DisplayWithCtxt<Ctxt, Data> + Copy> {
79 inner: T,
80 ctxt: &'a Ctxt,
81 data_marker: std::marker::PhantomData<Data>,
82}
83
84impl<'a, Ctxt, Data: Default, T: DisplayWithCtxt<Ctxt, Data> + Copy> fmt::Display
85 for DisplayWrapperEmpty<'a, Ctxt, Data, T>
86{
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 self.inner.fmt_with(f, self.ctxt, &mut Data::default())
89 }
90}
91
92pub struct DisplayWrapperData<'a, 'b, Ctxt, Data, T: DisplayWithCtxt<Ctxt, Data> + Copy> {
93 inner: T,
94 ctxt: &'a Ctxt,
95 data: *mut Data,
96 data_marker: std::marker::PhantomData<&'b mut Data>,
97}
98
99impl<'a, 'b, Ctxt, Data, T: DisplayWithCtxt<Ctxt, Data> + Copy> fmt::Display
100 for DisplayWrapperData<'a, 'b, Ctxt, Data, T>
101{
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 let data = unsafe { &mut *self.data };
105 self.inner.fmt_with(f, self.ctxt, data)
106 }
107}
108
109#[derive(Clone, Copy)]
114pub struct DisplayCtxt<'a> {
115 pub parser: &'a Z3Parser,
116 pub term_display: &'a TermDisplayContext,
117 pub config: DisplayConfiguration,
118}
119
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
122pub enum SymbolReplacement {
123 Math,
124 #[default]
125 Code,
126 None,
129}
130
131impl SymbolReplacement {
132 pub fn as_math(self) -> Option<bool> {
133 match self {
134 SymbolReplacement::Math => Some(true),
135 SymbolReplacement::Code => Some(false),
136 SymbolReplacement::None => None,
137 }
138 }
139
140 pub fn is_none(self) -> bool {
141 matches!(self, SymbolReplacement::None)
142 }
143}
144
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146#[cfg_attr(feature = "serde", serde(default))]
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
148pub struct DisplayConfiguration {
149 pub debug: bool,
150 pub display_quantifier_name: bool,
151 pub replace_symbols: SymbolReplacement,
152 pub input: Option<bool>,
155 #[cfg(feature = "display_html")]
157 pub html: bool,
158 #[cfg(feature = "display_html")]
162 pub font_tag: bool,
163
164 pub enode_char_limit: Option<NonMaxU32>,
167 pub ast_depth_limit: Option<NonMaxU32>,
168}
169
170impl DisplayConfiguration {
171 pub fn html(&self) -> bool {
172 #[cfg(feature = "display_html")]
173 return self.html;
174 #[cfg(not(feature = "display_html"))]
175 return false;
176 }
177 pub fn font_tag(&self) -> bool {
178 #[cfg(feature = "display_html")]
179 return self.font_tag;
180 #[cfg(not(feature = "display_html"))]
181 return false;
182 }
183
184 pub fn with_html_italic(
185 &self,
186 f: &mut fmt::Formatter<'_>,
187 rest: impl FnMut(&mut fmt::Formatter) -> fmt::Result,
188 ) -> fmt::Result {
189 self.with_html_tag(f, "i", None, rest)
190 }
191 pub fn with_html_colour(
192 &self,
193 f: &mut fmt::Formatter<'_>,
194 colour: &str,
195 rest: impl FnMut(&mut fmt::Formatter) -> fmt::Result,
196 ) -> fmt::Result {
197 let (tag, attribute) = if self.font_tag() {
198 ("font", format!("color=\"{colour}\""))
199 } else {
200 ("span", format!("style=\"color:{colour}\""))
201 };
202 self.with_html_tag(f, tag, Some(&attribute), rest)
203 }
204
205 pub fn with_html_tag(
206 &self,
207 f: &mut fmt::Formatter<'_>,
208 tag: &str,
209 attributes: Option<&str>,
210 mut rest: impl FnMut(&mut fmt::Formatter<'_>) -> fmt::Result,
211 ) -> fmt::Result {
212 if self.html() {
213 write!(f, "<{tag}")?;
214 if let Some(attributes) = attributes {
215 write!(f, " {}", attributes)?;
216 }
217 write!(f, ">")?;
218 }
219 rest(f)?;
220 if self.html() {
221 write!(f, "</{tag}>")?;
222 }
223 Ok(())
224 }
225}
226
227mod private {
228 use super::*;
229
230 #[derive(Debug, Clone)]
231 pub(super) struct DisplayData<'a> {
232 pub(super) term: SynthIdx,
233 children: &'a [SynthIdx],
234 quant: Vec<&'a Quantifier>,
235 bind_power: BindPowerPair,
236 ast_depth: u32,
237 newline_spaces: u32,
238 }
239 impl<'a> DisplayData<'a> {
240 pub(super) fn new(term: SynthIdx) -> Self {
241 Self {
242 term,
243 children: &[],
244 quant: Vec::new(),
245 bind_power: BindPowerPair::symmetric(DEFAULT_BIND_POWER),
246 ast_depth: 0,
247 newline_spaces: 0,
248 }
249 }
250 pub(super) fn with_children<T>(
251 &mut self,
252 children: &'a [SynthIdx],
253 f: impl FnOnce(&mut Self) -> T,
254 ) -> T {
255 let old = std::mem::replace(&mut self.children, children);
256 let result = f(self);
257 self.children = old;
258 result
259 }
260 pub(super) fn with_quant<T>(
261 &mut self,
262 quant: &'a Quantifier,
263 f: impl FnOnce(&mut Self) -> T,
264 ) -> T {
265 self.quant.push(quant);
266 let result = f(self);
267 self.quant.pop();
268 result
269 }
270
271 pub(super) fn with_outer_bind_power<'b>(
272 &mut self,
273 f: &mut fmt::Formatter<'b>,
274 bind_power: BindPowerPair,
275 inner: impl FnOnce(&mut Self, &mut fmt::Formatter<'b>) -> fmt::Result,
276 ) -> fmt::Result {
277 let needs_brackets = bind_power.is_smaller(&self.bind_power);
278 if needs_brackets {
279 write!(f, "(")?;
280 }
281 inner(self, f)?;
282 if needs_brackets {
283 write!(f, ")")?;
284 }
285 Ok(())
286 }
287 pub(super) fn with_inner_bind_power<T>(
288 &mut self,
289 bind_power: BindPowerPair,
290 f: impl FnOnce(&mut Self) -> T,
291 ) -> T {
292 let old = std::mem::replace(&mut self.bind_power, bind_power);
293 let result = f(self);
294 self.bind_power = old;
295 result
296 }
297 pub(super) fn with_term<T>(&mut self, term: SynthIdx, f: impl FnOnce(&mut Self) -> T) -> T {
298 let term = std::mem::replace(&mut self.term, term);
299 let result = f(self);
300 self.term = term;
301 result
302 }
303
304 pub(super) fn children(&self) -> &'a [SynthIdx] {
305 self.children
306 }
307 pub(super) fn find_quant(&self, idx: &mut NonMaxU32) -> Option<&Quantifier> {
308 self.quant
309 .iter()
310 .rev()
311 .find(|q| {
312 if let Some(new) = idx.get().checked_sub(q.num_vars.get()) {
313 *idx = NonMaxU32::new(new).unwrap();
314 false
315 } else {
316 true
317 }
318 })
319 .copied()
320 }
321 pub(super) fn incr_ast_depth_with_limit<T>(
322 &mut self,
323 limit: Option<NonMaxU32>,
324 f: impl FnOnce(&mut Self) -> T,
325 ) -> Option<T> {
326 if limit.is_some_and(|limit| self.ast_depth >= limit.get()) {
327 return None;
328 }
329 self.ast_depth += 1;
330 let result = f(self);
331 self.ast_depth -= 1;
332 Some(result)
333 }
334
335 pub(super) fn with_indented<'b>(
336 &mut self,
337 spaces: u32,
338 f: &mut fmt::Formatter<'b>,
339 inner: impl FnOnce(&mut Self, &mut fmt::Formatter<'b>) -> fmt::Result,
340 ) -> fmt::Result {
341 self.newline_spaces += spaces;
342 write!(f, "\n{: <1$}", "", self.newline_spaces as usize)?;
343 let result = inner(self, f);
344 self.newline_spaces -= spaces;
345 result
346 }
347 }
348}
349
350use private::*;
351
352impl DisplayWithCtxt<DisplayCtxt<'_>, Option<QuantIdx>> for TermIdx {
357 fn fmt_with(
358 self,
359 f: &mut fmt::Formatter<'_>,
360 ctxt: &DisplayCtxt<'_>,
361 quant: &mut Option<QuantIdx>,
362 ) -> fmt::Result {
363 SynthIdx::from(self).fmt_with(f, ctxt, quant)
364 }
365}
366
367impl DisplayWithCtxt<DisplayCtxt<'_>, Option<QuantIdx>> for SynthIdx {
368 fn fmt_with(
369 self,
370 f: &mut fmt::Formatter<'_>,
371 ctxt: &DisplayCtxt<'_>,
372 quant: &mut Option<QuantIdx>,
373 ) -> fmt::Result {
374 let mut data = DisplayData::new(self);
375 if let Some(quant) = quant {
376 data.with_quant(&ctxt.parser[*quant], |data| {
377 ctxt.parser[self].fmt_with(f, ctxt, data)
378 })
379 } else {
380 ctxt.parser[self].fmt_with(f, ctxt, &mut data)
381 }
382 }
383}
384
385impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for ENodeIdx {
386 fn fmt_with(
387 self,
388 f: &mut fmt::Formatter<'_>,
389 ctxt: &DisplayCtxt<'_>,
390 _data: &mut (),
391 ) -> fmt::Result {
392 if let Some(enode_char_limit) = ctxt.config.enode_char_limit {
393 let owner = ctxt.parser[self]
394 .owner
395 .with_data(ctxt, &mut None)
396 .to_string();
397 if owner.len() <= enode_char_limit.get() as usize {
398 write!(f, "{owner}")
399 } else {
400 write!(f, "{}...", &owner[..enode_char_limit.get() as usize - 3])
401 }
402 } else {
403 ctxt.parser[self].owner.fmt_with(f, ctxt, &mut None)
404 }
405 }
406}
407
408impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for QuantIdx {
409 fn fmt_with(
410 self,
411 f: &mut fmt::Formatter<'_>,
412 ctxt: &DisplayCtxt<'_>,
413 _data: &mut (),
414 ) -> fmt::Result {
415 let quant = &ctxt.parser[self];
416 quant.term.fmt_with(f, ctxt, &mut Some(self))
417 }
418}
419
420impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for EqGivenIdx {
421 fn fmt_with(
422 self,
423 f: &mut fmt::Formatter<'_>,
424 ctxt: &DisplayCtxt<'_>,
425 data: &mut (),
426 ) -> fmt::Result {
427 let eq = &ctxt.parser[self];
428 eq.from().fmt_with(f, ctxt, data)?;
431 write!(f, " = ")?;
432 eq.to().fmt_with(f, ctxt, data)
433 }
434}
435
436impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for EqTransIdx {
437 fn fmt_with(
438 self,
439 f: &mut fmt::Formatter<'_>,
440 ctxt: &DisplayCtxt<'_>,
441 data: &mut (),
442 ) -> fmt::Result {
443 let path = ctxt.parser.egraph.equalities.path(self);
444 path.first().unwrap().fmt_with(f, ctxt, data)?;
445 if ctxt.config.html() {
446 write!(f, " =<sup>")?;
447 } else {
448 write!(f, " =[ ")?;
449 }
450 if let Some(len) = ctxt.parser[self].given_len {
451 write!(f, "{len}")?;
452 } else {
453 write!(f, "?")?;
454 }
455 if ctxt.config.html() {
456 write!(f, "</sup> ")?;
457 } else {
458 write!(f, "] ")?;
459 }
460 path.last().unwrap().fmt_with(f, ctxt, data)
461 }
462}
463
464impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for LitIdx {
465 fn fmt_with(
466 self,
467 f: &mut fmt::Formatter<'_>,
468 ctxt: &DisplayCtxt<'_>,
469 data: &mut (),
470 ) -> fmt::Result {
471 ctxt.parser[self].term.fmt_with(f, ctxt, data)
472 }
473}
474
475impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for Assignment {
476 fn fmt_with(
477 self,
478 f: &mut fmt::Formatter<'_>,
479 ctxt: &DisplayCtxt<'_>,
480 _data: &mut (),
481 ) -> fmt::Result {
482 write!(f, "{} = {}", self.value, self.literal.with(ctxt))
483 }
484}
485
486impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for &MatchKind {
489 fn fmt_with(
490 self,
491 f: &mut fmt::Formatter<'_>,
492 ctxt: &DisplayCtxt<'_>,
493 data: &mut (),
494 ) -> fmt::Result {
495 match self {
496 MatchKind::MBQI { quant, .. } => {
497 write!(f, "[MBQI] ")?;
498 quant.fmt_with(f, ctxt, data)
499 }
500 MatchKind::TheorySolving { axiom_id, .. } => {
501 write!(f, "[TheorySolving] ")?;
502 axiom_id.fmt_with(f, ctxt, data)?;
503 Ok(())
504 }
505 MatchKind::Axiom { axiom, .. } => {
506 write!(f, "[Ax] ")?;
507 axiom.fmt_with(f, ctxt, data)
508 }
509 MatchKind::Quantifier { quant, .. } => quant.fmt_with(f, ctxt, data),
510 }
511 }
512}
513
514impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for &QuantKind {
515 fn fmt_with(
516 self,
517 f: &mut fmt::Formatter<'_>,
518 ctxt: &DisplayCtxt<'_>,
519 _data: &mut (),
520 ) -> fmt::Result {
521 match *self {
522 QuantKind::Lambda(_) => {
523 if matches!(ctxt.config.replace_symbols, SymbolReplacement::Math) {
524 write!(f, "λ")
525 } else if ctxt.config.html() {
526 write!(f, "<null>")
527 } else {
528 write!(f, "<null>")
529 }
530 }
531 QuantKind::NamedQuant(name) => write!(f, "{}", &ctxt.parser[name]),
532 QuantKind::UnnamedQuant { name, id, .. } => {
533 write!(f, "{}!{id}", &ctxt.parser[name])
534 }
535 }
536 }
537}
538
539impl DisplayWithCtxt<DisplayCtxt<'_>, ()> for QuantPat {
540 fn fmt_with(
541 self,
542 f: &mut fmt::Formatter<'_>,
543 ctxt: &DisplayCtxt<'_>,
544 data: &mut (),
545 ) -> fmt::Result {
546 ctxt.parser[self.quant].kind.fmt_with(f, ctxt, data)?;
547 match self.pat {
548 Some(pat) => {
549 let patterns = ctxt.parser.patterns(self.quant);
550 if patterns.is_some_and(|p| p.len() > 1) {
551 write!(f, "{pat}")
552 } else {
553 Ok(())
554 }
555 }
556 None => write!(f, "{{MBQI}}"),
557 }
558 }
559}
560
561impl<'a: 'b, 'b> DisplayWithCtxt<DisplayCtxt<'b>, DisplayData<'b>> for &'a Term {
566 fn fmt_with(
567 self,
568 f: &mut fmt::Formatter<'_>,
569 ctxt: &DisplayCtxt<'b>,
570 data: &mut DisplayData<'b>,
571 ) -> fmt::Result {
572 self.as_any().fmt_with(f, ctxt, data)
573 }
574}
575impl<'a: 'b, 'b> DisplayWithCtxt<DisplayCtxt<'b>, DisplayData<'b>> for &'a AnyTerm {
576 fn fmt_with(
577 self,
578 f: &mut fmt::Formatter<'_>,
579 ctxt: &DisplayCtxt<'b>,
580 data: &mut DisplayData<'b>,
581 ) -> fmt::Result {
582 data.with_children(self.child_ids(), |data| {
583 if ctxt.config.debug {
584 match self.id() {
585 Some(id) => write!(f, "[{}]", id.with(ctxt))?,
586 None => write!(f, "[synth]")?,
587 }
588 }
589 let meaning = ctxt
590 .parser
591 .as_tidx(data.term)
592 .and_then(|tidx| ctxt.parser.meaning(tidx));
593 let meaning = meaning.or(match self {
594 AnyTerm::Constant(meaning) => Some(meaning),
595 _ => None,
596 });
597 if let Some(meaning) = meaning {
598 meaning.fmt_with(f, ctxt, data)
599 } else {
600 self.kind().fmt_with(f, ctxt, data)
601 }
602 })
603 }
604}
605
606impl<'b> DisplayWithCtxt<DisplayCtxt<'b>, DisplayData<'b>> for &SynthTermKind {
607 fn fmt_with(
608 self,
609 f: &mut fmt::Formatter<'_>,
610 ctxt: &DisplayCtxt<'b>,
611 data: &mut DisplayData<'b>,
612 ) -> fmt::Result {
613 match self {
614 SynthTermKind::Parsed(kind) => kind.fmt_with(f, ctxt, data),
615 SynthTermKind::Generalised(_) => {
616 let synth_idx = data.children()[0];
617 match ctxt.config.input {
618 Some(true) => write!(f, "⭐"),
619 Some(false) => ctxt
621 .config
622 .with_html_colour(f, "#4285f4", |f| synth_idx.fmt_with(f, ctxt, &mut None)),
623 None => {
624 write!(f, "[⭐ → ")?;
625 ctxt.config.with_html_colour(f, "#4285f4", |f| {
626 synth_idx.fmt_with(f, ctxt, &mut None)
627 })?;
628 write!(f, "]")
629 }
630 }
631 }
632 SynthTermKind::Variable(var) => write!(f, "${var}"),
633 SynthTermKind::Input(offset) => match offset {
634 Some(offset) => {
635 offset.fmt_with(f, ctxt, &mut None)?;
636 write!(f, " + ⭐")
637 }
638 None => write!(f, "⭐"),
639 },
640 SynthTermKind::Constant => write!(f, "CONST"),
641 }
642 }
643}
644
645impl<'a: 'b, 'b> DisplayWithCtxt<DisplayCtxt<'b>, ()> for &'a TermId {
646 fn fmt_with(
647 self,
648 f: &mut fmt::Formatter<'_>,
649 ctxt: &DisplayCtxt<'b>,
650 _data: &mut (),
651 ) -> fmt::Result {
652 let (namespace, id) = self.to_string_components(&ctxt.parser.strings);
653 write!(f, "{namespace}#")?;
654 if let Some(id) = id {
655 write!(f, "{id}")
656 } else {
657 Ok(())
658 }
659 }
660}
661
662pub struct VarName<F: Fn(&mut fmt::Formatter) -> fmt::Result>(F);
663impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Display for VarName<F> {
664 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
665 (self.0)(f)
666 }
667}
668
669impl VarNames {
670 pub fn get_name<'a>(
671 strings: &'a StringTable,
672 this: Option<&Self>,
673 idx: u32,
674 config: DisplayConfiguration,
675 ) -> VarName<impl Fn(&mut fmt::Formatter) -> fmt::Result + 'a> {
676 let name = match this {
677 Some(Self::NameAndType(names)) => Cow::Borrowed(&strings[*names[idx as usize].0]),
678 None | Some(Self::TypeOnly(_)) => Cow::Owned(
679 if matches!(config.replace_symbols, SymbolReplacement::Math) {
680 format!("•{idx}")
681 } else {
682 format!("_{idx}")
683 },
684 ),
685 };
686
687 #[cfg(feature = "display_html")]
688 {
689 let mut colour = None;
690 let name = if config.html() {
691 const COLOURS: [&str; 9] = [
692 "blue", "green", "olive", "maroon", "teal", "purple", "red", "fuchsia", "navy",
693 ];
694 colour = Some(COLOURS[idx as usize % COLOURS.len()]);
695 use std::borrow::Borrow;
696 let name = ammonia::clean_text(name.borrow());
697 Cow::Owned(name)
698 } else {
699 name
700 };
701 VarName(move |f| {
702 if let Some(colour) = colour {
703 config.with_html_colour(f, colour, |f| write!(f, "{name}"))
704 } else {
705 write!(f, "{name}")
706 }
707 })
708 }
709 #[cfg(not(feature = "display_html"))]
710 VarName(move |f| write!(f, "{name}"))
711 }
712}
713
714impl<'b> DisplayWithCtxt<DisplayCtxt<'b>, DisplayData<'b>> for &TermKind {
715 fn fmt_with(
716 self,
717 f: &mut fmt::Formatter<'_>,
718 ctxt: &DisplayCtxt<'b>,
719 data: &mut DisplayData<'b>,
720 ) -> fmt::Result {
721 match *self {
722 TermKind::Var(mut idx) => {
723 let vars = data.find_quant(&mut idx).and_then(|q| q.vars.as_ref());
724 let name = VarNames::get_name(&ctxt.parser.strings, vars, idx.get(), ctxt.config);
725 write!(f, "{name}")
726 }
727 TermKind::App(name) => {
728 let name = &ctxt.parser[name];
729 let children = NonMaxU32::new(data.children().len() as u32).unwrap();
730 let match_ = ctxt.term_display.match_str(name, children);
731 match_.fmt_with(f, ctxt, data)
732 }
733 TermKind::Quant(idx) => ctxt.parser[idx].fmt_with(f, ctxt, data),
734 }
735 }
736}
737
738fn display_child<'b>(
739 f: &mut fmt::Formatter<'_>,
740 child: SynthIdx,
741 ctxt: &DisplayCtxt<'b>,
742 data: &mut DisplayData<'b>,
743) -> fmt::Result {
744 let cterm = &ctxt.parser[child];
745 let mut display =
746 |data: &mut DisplayData<'b>| data.with_term(child, |data| cterm.fmt_with(f, ctxt, data));
747 if cterm.child_ids().is_empty() {
748 display(data)
749 } else {
750 data.incr_ast_depth_with_limit(ctxt.config.ast_depth_limit, display)
751 .unwrap_or_else(|| write!(f, "..."))
752 }
753}
754
755impl<'a, 'b> DisplayWithCtxt<DisplayCtxt<'b>, DisplayData<'b>> for &'a MatchResult<'a, 'a> {
756 fn fmt_with(
757 self,
758 f: &mut fmt::Formatter<'_>,
759 ctxt: &DisplayCtxt<'b>,
760 data: &mut DisplayData<'b>,
761 ) -> fmt::Result {
762 data.with_outer_bind_power(f, self.formatter.bind_power, |data, f| {
763 let get_capture = |idx: NonMaxU32| {
764 if idx == NonMaxU32::ZERO {
765 Some(self.haystack)
766 } else {
767 self.captures
768 .as_ref()
769 .and_then(|c| c.get(idx.get() as usize).map(|c| c.as_str()))
770 }
771 };
772 fn get_children<'a>(
773 path: &ChildPath,
774 ctxt: &DisplayCtxt<'a>,
775 data: &DisplayData<'a>,
776 ) -> Option<&'a [SynthIdx]> {
777 let mut children = data.children();
778 for index in path.get() {
779 let child = get_child(*index, children)?;
780 children = ctxt.parser[children[child]].child_ids();
781 }
782 Some(children)
783 }
784 fn get_child(index: ChildIndex, children: &[SynthIdx]) -> Option<usize> {
785 if index.get().is_negative() {
786 children
787 .len()
788 .checked_sub(index.get().wrapping_abs() as u32 as usize)
789 } else {
790 let index = index.get() as usize;
791 (index < children.len()).then_some(index)
792 }
793 }
794 fn write_formatter<'b, 's>(
795 formatter_outputs: &[SubFormatter],
796 f: &mut fmt::Formatter<'_>,
797 ctxt: &DisplayCtxt<'b>,
798 data: &mut DisplayData<'b>,
799 get_capture: &impl Fn(NonMaxU32) -> Option<&'s str>,
800 ) -> fmt::Result {
801 for output in formatter_outputs {
802 match output {
803 SubFormatter::String(s) => write!(f, "{s}")?,
804 SubFormatter::Capture(idx) => {
805 let Some(capture) = get_capture(*idx) else {
806 continue;
807 };
808 let capture =
809 if let Some(as_math) = ctxt.config.replace_symbols.as_math() {
810 match capture {
811 "and" if as_math => "∧",
812 "and" if !as_math => "&&",
813 "or" if as_math => "∨",
814 "or" if !as_math => "||",
815 "not" if as_math => "¬",
816 "not" if !as_math => "!",
817 "<=" if as_math => "≤",
818 ">=" if as_math => "≥",
819 _ => capture,
820 }
821 } else {
822 capture
823 };
824 #[cfg(feature = "display_html")]
825 let capture = if ctxt.config.html() {
826 Cow::Owned(ammonia::clean_text(capture))
827 } else {
828 Cow::Borrowed(capture)
829 };
830 write!(f, "{capture}")?;
831 }
832 SubFormatter::Single {
833 path,
834 index,
835 bind_power,
836 } => {
837 let Some(children) = get_children(path, ctxt, data) else {
838 continue;
839 };
840 let Some(child) = get_child(*index, children) else {
841 continue;
842 };
843 let child = children[child];
844 data.with_inner_bind_power(*bind_power, |data| {
845 display_child(f, child, ctxt, data)
846 })?;
847 }
848 SubFormatter::Repeat(r) => {
849 let Some(children) = get_children(&r.path, ctxt, data) else {
850 continue;
851 };
852 let (Some(from), Some(to)) =
853 (get_child(r.from, children), get_child(r.to, children))
854 else {
855 continue;
856 };
857 if !r.from.get().is_negative() && r.to.get().is_negative() && from > to
858 {
859 continue;
860 }
861 if r.from.get().is_negative() && !r.to.get().is_negative() && from < to
862 {
863 continue;
864 }
865 let forwards = from <= to;
866 let mut curr = if forwards {
868 from.wrapping_sub(1)
869 } else {
870 from.wrapping_add(1)
871 };
872 let iter = std::iter::from_fn(move || {
873 if curr == to {
874 None
875 } else {
876 curr = if forwards {
877 curr.wrapping_add(1)
878 } else {
879 curr.wrapping_sub(1)
880 };
881 Some(curr)
882 }
883 });
884 write_formatter(&r.left_sep.outputs, f, ctxt, data, get_capture)?;
885 let mut bind_power = BindPowerPair::asymmetric(r.left, r.middle.left);
886 for child in iter {
887 let is_final = child == to;
888 if is_final {
889 bind_power.right = r.right;
890 }
891 let child = children[child];
892 data.with_inner_bind_power(bind_power, |data| {
893 display_child(f, child, ctxt, data)
894 })?;
895 if !is_final {
896 write_formatter(
897 &r.middle_sep.outputs,
898 f,
899 ctxt,
900 data,
901 get_capture,
902 )?;
903 bind_power.left = r.middle.right;
904 }
905 }
906 write_formatter(&r.right_sep.outputs, f, ctxt, data, get_capture)?;
907 }
908 }
909 }
910 Ok(())
911 }
912
913 write_formatter(&self.formatter.outputs, f, ctxt, data, &get_capture)
914 })
915 }
916}
917
918impl<'a> DisplayWithCtxt<DisplayCtxt<'a>, DisplayData<'a>> for &'a Meaning {
919 fn fmt_with(
920 self,
921 f: &mut fmt::Formatter<'_>,
922 ctxt: &DisplayCtxt<'a>,
923 _data: &mut DisplayData<'a>,
924 ) -> fmt::Result {
925 let fn_ = |f: &mut fmt::Formatter| match self {
926 Meaning::Arith(value) => write!(f, "{value}"),
927 Meaning::BitVec(value) => {
928 write!(f, "0x")?;
929 for digit in value.value.iter_u64_digits() {
930 write!(f, "{:x}", digit)?;
931 }
932 Ok(())
933 }
934 &Meaning::Unknown { theory, value } => {
935 write!(f, "/{} {}\\", &ctxt.parser[theory], &ctxt.parser[value])
936 }
937 };
938 if ctxt.config.font_tag() {
941 fn_(f)
942 } else {
943 ctxt.config
944 .with_html_colour(f, "#666666", |f| ctxt.config.with_html_italic(f, fn_))
945 }
946 }
947}
948
949impl<'a> DisplayWithCtxt<DisplayCtxt<'a>, DisplayData<'a>> for &'a Quantifier {
950 fn fmt_with(
951 self,
952 f: &mut fmt::Formatter<'_>,
953 ctxt: &DisplayCtxt<'a>,
954 data: &mut DisplayData<'a>,
955 ) -> fmt::Result {
956 data.with_quant(self, |data| {
960 data.with_outer_bind_power(f, QUANT_BIND, |data, f| {
961 let Some((body, patterns)) = data.children().split_last() else {
962 return write!(f, "lambda");
963 };
964 let vars = (0..self.num_vars.get()).rev().map(|idx| {
967 let name = VarNames::get_name(
968 &ctxt.parser.strings,
969 self.vars.as_ref(),
970 idx,
971 ctxt.config,
972 );
973 let ty =
974 VarNames::get_type(&ctxt.parser.strings, self.vars.as_ref(), idx as usize);
975 (idx + 1 != self.num_vars.get(), name, ty)
976 });
977 if ctxt.config.replace_symbols.is_none() {
978 write!(f, "(forall (")?;
979 for (not_first, name, ty) in vars {
980 if not_first {
981 write!(f, " ")?;
982 }
983 write!(f, "({name} {})", ty.unwrap_or("?"))?;
984 }
985 write!(f, ") (!")?;
986 data.with_indented(2, f, |data, f| display_child(f, *body, ctxt, data))?;
987 for &pattern in patterns.iter() {
988 data.with_indented(2, f, |data, f| display_child(f, pattern, ctxt, data))?;
989 }
990 if let Some(name) = self.kind.user_name() {
991 data.with_indented(2, f, |_, f| write!(f, ":qid {}", &ctxt.parser[name]))?;
992 }
993 data.with_indented(0, f, |_, f| write!(f, "))"))?;
994 } else {
995 let (start, sep) = match ctxt.config.replace_symbols {
996 SymbolReplacement::Math => ("∀", "."),
997 SymbolReplacement::Code => ("FORALL", " ::"),
998 SymbolReplacement::None => unreachable!(),
999 };
1000 write!(f, "{} ", start)?;
1001 if ctxt.config.display_quantifier_name {
1002 write!(f, "\"{}\" ", self.kind.with(ctxt))?;
1003 }
1004 for (not_first, name, ty) in vars {
1005 if not_first {
1006 write!(f, ", ")?;
1007 }
1008 write!(f, "{name}")?;
1009 if let Some(ty) = ty {
1010 write!(f, ": {ty}")?;
1011 }
1012 }
1013 write!(f, "{sep} ")?;
1014 for &pattern in patterns.iter() {
1015 display_child(f, pattern, ctxt, data)?;
1016 write!(f, " ")?;
1017 }
1018 display_child(f, *body, ctxt, data)?;
1020 }
1021 Ok(())
1022 })
1023 })
1024 }
1025}
1026
1027impl<'a> DisplayWithCtxt<DisplayCtxt<'a>, ()> for &'a EventKind {
1028 fn fmt_with(
1029 self,
1030 f: &mut fmt::Formatter<'_>,
1031 ctxt: &DisplayCtxt<'a>,
1032 _data: &mut (),
1033 ) -> fmt::Result {
1034 match *self {
1035 EventKind::NewConst(term_idx) => {
1036 let term = &ctxt.parser[term_idx];
1037 let name = &ctxt.parser[term.app_name().unwrap()];
1038 if term.child_ids.is_empty() {
1039 write!(f, "(declare-const {name} ?)")
1040 } else {
1041 write!(f, "(declare-fun {name} (?")?;
1042 for _ in 0..term.child_ids.len() - 1 {
1043 write!(f, " ?")?;
1044 }
1045 write!(f, ") ?)")
1046 }
1047 }
1048 EventKind::Assert(proof_idx) => {
1049 let proof = &ctxt.parser[proof_idx];
1050 let display = proof.result.with(ctxt);
1051 write!(f, "(assert {display})")
1052 }
1053 EventKind::Push(false) => write!(f, "(push)"),
1054 EventKind::Push(true, ..) => write!(f, "; CDCL push"),
1055 EventKind::Pop(false, num) => match num {
1056 Some(num) => write!(f, "(pop {})", num.get()),
1057 None => write!(f, "(pop)"),
1058 },
1059 EventKind::Pop(true, ..) => write!(f, "; CDCL pop"),
1060 EventKind::BeginCheck => write!(f, "(check-sat)"),
1061 }
1062 }
1063}