espresso_logic/expression/
display.rs

1//! Display and Debug formatting for boolean expressions
2
3use super::{BoolExpr, BoolExprAst};
4use std::fmt;
5
6/// Context for formatting expressions with minimal parentheses
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8enum OpContext {
9    None, // Top level or inside parentheses
10    And,  // Inside an AND operation
11    Or,   // Inside an OR operation
12    Not,  // Inside a NOT operation
13}
14
15impl BoolExpr {
16    /// Format with operator precedence context to minimize parentheses
17    fn fmt_with_context(&self, f: &mut fmt::Formatter<'_>, ctx: OpContext) -> fmt::Result {
18        let ast = self.get_or_create_ast();
19        Self::fmt_ast_with_context(f, &ast, ctx)
20    }
21
22    /// Format AST with context (helper method)
23    fn fmt_ast_with_context(
24        f: &mut fmt::Formatter<'_>,
25        ast: &BoolExprAst,
26        ctx: OpContext,
27    ) -> fmt::Result {
28        match ast {
29            BoolExprAst::Variable(name) => write!(f, "{}", name),
30            BoolExprAst::Constant(val) => write!(f, "{}", if *val { "1" } else { "0" }),
31
32            BoolExprAst::And(left, right) => {
33                // AND needs parens if inside a NOT
34                let needs_parens = ctx == OpContext::Not;
35
36                if needs_parens {
37                    write!(f, "(")?;
38                }
39
40                Self::fmt_ast_with_context(f, left, OpContext::And)?;
41                write!(f, " * ")?;
42                Self::fmt_ast_with_context(f, right, OpContext::And)?;
43
44                if needs_parens {
45                    write!(f, ")")?;
46                }
47                Ok(())
48            }
49
50            BoolExprAst::Or(left, right) => {
51                // OR needs parens if inside AND or NOT (lower precedence)
52                let needs_parens = ctx == OpContext::And || ctx == OpContext::Not;
53
54                if needs_parens {
55                    write!(f, "(")?;
56                }
57
58                Self::fmt_ast_with_context(f, left, OpContext::Or)?;
59                write!(f, " + ")?;
60                Self::fmt_ast_with_context(f, right, OpContext::Or)?;
61
62                if needs_parens {
63                    write!(f, ")")?;
64                }
65                Ok(())
66            }
67
68            BoolExprAst::Not(inner) => {
69                write!(f, "~")?;
70                // NOT needs parens around compound expressions (AND/OR)
71                // but NOT of NOT or variables/constants don't need parens
72                match inner.as_ref() {
73                    BoolExprAst::Variable(_) | BoolExprAst::Constant(_) | BoolExprAst::Not(_) => {
74                        Self::fmt_ast_with_context(f, inner, OpContext::Not)
75                    }
76                    _ => {
77                        write!(f, "(")?;
78                        Self::fmt_ast_with_context(f, inner, OpContext::None)?;
79                        write!(f, ")")
80                    }
81                }
82            }
83        }
84    }
85}
86
87/// Debug formatting for boolean expressions
88///
89/// Formats expressions with minimal parentheses based on operator precedence.
90/// Uses standard boolean algebra notation: `*` for AND, `+` for OR, `~` for NOT.
91///
92/// # Examples
93///
94/// ```
95/// use espresso_logic::BoolExpr;
96///
97/// let a = BoolExpr::variable("a");
98/// let b = BoolExpr::variable("b");
99/// let c = BoolExpr::variable("c");
100/// let expr = a.and(&b).or(&c);
101///
102/// println!("{:?}", expr);  // Outputs: a * b + c (no unnecessary parentheses)
103/// ```
104impl fmt::Debug for BoolExpr {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        self.fmt_with_context(f, OpContext::None)
107    }
108}
109
110/// Display formatting for boolean expressions
111///
112/// Delegates to the `Debug` implementation. Use `{}` or `{:?}` interchangeably.
113///
114/// # Examples
115///
116/// ```
117/// use espresso_logic::BoolExpr;
118///
119/// let a = BoolExpr::variable("a");
120/// let b = BoolExpr::variable("b");
121/// let expr = a.and(&b);
122///
123/// println!("{}", expr);  // Same as println!("{:?}", expr)
124/// ```
125impl fmt::Display for BoolExpr {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        write!(f, "{:?}", self)
128    }
129}