smt_scope/
display_with.rs

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
17////////////
18// General
19////////////
20
21pub trait DisplayWithCtxt<Ctxt, Data>: Sized {
22    /// Display the term with the given context and data. Should not be used
23    /// outside of the implementations of this trait.
24    fn fmt_with(self, f: &mut fmt::Formatter<'_>, ctxt: &Ctxt, data: &mut Data) -> fmt::Result;
25
26    /// Wrap the receiver in an object which carries along a parser context to
27    /// enable efficient printing of terms. For quick debugging use the `debug`
28    /// method instead to avoid building a full context.
29    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    /// To be used with `TermIdx` or `SynthIdx` where we want to pass in the
42    /// surrounding quantifier as `data` such that qvars are printed with names.
43    /// Otherwise, always use `with` instead (which uses default `data`).
44    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    /// Only use for quickly displaying things when debugging.
61    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        // SAFETY: DisplayWrapperData is only created in `with_data` where it blocks the input `data`.
104        let data = unsafe { &mut *self.data };
105        self.inner.fmt_with(f, self.ctxt, data)
106    }
107}
108
109////////////
110// Items
111////////////
112
113#[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    /// Display quantifiers and symbols in the smt2 format, using the
127    /// [`TermDisplayContext::s_expression`] formatter is recommended.
128    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    /// Print `SynthTermKind::Generalised` terms as either `_` if true or
153    /// `...(_)` if false.
154    pub input: Option<bool>,
155    /// Use tags for formatting
156    #[cfg(feature = "display_html")]
157    pub html: bool,
158    /// Use the HTML4 `<font>` tag for setting the colour of text. This isn't
159    /// supported in HTML5 and may be used for graphviz HTML-like label
160    /// compatibility.
161    #[cfg(feature = "display_html")]
162    pub font_tag: bool,
163
164    // If `enode_char_limit` is Some, then any term longer than
165    // the limit will be truncated.
166    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
352////////////
353// Item Idx defs
354////////////
355
356impl 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        // let kind = eq.kind_str(&ctxt.parser.strings);
429        // write!(f, "{kind}: ")?;
430        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
486// Others
487
488impl 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, "&lt;null&gt;")
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
561////////////
562// Item defs
563////////////
564
565impl<'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                    // Colour the generalised term in blue
620                    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                            // The range is inclusive on both ends
867                            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        // The text block inside graphviz messes up the text alignment,
939        // skip it in that case.
940        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        // Within the body of the term of a quantified formula, we
957        // want to replace the quantified variables by their names
958        // for this, we need to store the quantifier in the context
959        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                // Print the variables in reverse since they are logged in
965                // reverse for some reason.
966                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                    // TODO: binding power!
1019                    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}