1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! Module containing formatting logic.
use super::Node;

/// Helper type for propagating bracketing information.
///
/// Some expressions do not need to be surrounded by brackets. All of
/// this depends entirely on context, and this type helps propagate that
/// information for better formatting.
pub struct PreWrap {
    /// The internal contents of the string representation.
    pub text: String,

    /// This value is true if `text` needs surrounding brackets.
    pub needs_wrap: bool,
}

impl PreWrap {
    /// Return a wrapped string iff `needs_wrap` is true.
    pub fn to_safe_string(&self, l: char, r: char) -> String {
        if self.needs_wrap {
            format!("{}{}{}", l, self.text, r)
        } else {
            self.text.clone()
        }
    }
}

impl std::fmt::Display for PreWrap {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.text)
    }
}

/// Wrapper for expressions that can be written as strings.
pub enum Expr {
    /// An expression with known value zero.
    Zero,

    /// An expression with known value one.
    One,

    /// An expression with pre-computed string representation.
    Text(PreWrap),
}

impl std::fmt::Display for Expr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Expr::Zero => Ok(()),
            Expr::One => f.write_str("1"),
            Expr::Text(pw) => f.write_str(&pw.text),
        }
    }
}

/// Trait for operator nodes that can be expressed as a string.
pub trait ToExpr: Node {
    /// Convert the node to an expression string.
    ///
    /// # Examples
    ///
    /// ```
    /// # use aegir::{Identifier, fmt::ToExpr, ops::{Add, Mul}, ids::{X, Y, Z}};
    /// let op = Mul(Add(X.into_var(), Y.into_var()), Z.into_var());
    ///
    /// assert_eq!(op.to_expr().to_string(), "(x + y) ∘ z");
    /// ```
    fn to_expr(&self) -> Expr;
}