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}