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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

#[cfg(feature = "serde")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all="snake_case")]
pub enum ASTNode {
    Identifier { name: String },
    Literal { value: bool },
    #[serde(rename = "operator.not")]
    Not { operand: Box<ASTNode> },
    #[serde(rename = "operator.and")]
    And { left: Box<ASTNode>, right: Box<ASTNode> },
    #[serde(rename = "operator.or")]
    Or { left: Box<ASTNode>, right: Box<ASTNode> },
    #[serde(rename = "operator.implies")]
    Implies { left: Box<ASTNode>, right: Box<ASTNode> },
    #[serde(rename = "operator.iff")]
    IfAndOnlyIf { left: Box<ASTNode>, right: Box<ASTNode> },
}

// Serde Serialize and Deserialize traits are available when the
// optional 'serde' feature is enabled

#[cfg(not(feature = "serde"))]
#[derive(Debug)]
pub enum ASTNode {
    Identifier { name: String },
    Literal { value: bool },
    Not { operand: Box<ASTNode> },
    And { left: Box<ASTNode>, right: Box<ASTNode> },
    Or { left: Box<ASTNode>, right: Box<ASTNode> },
    Implies { left: Box<ASTNode>, right: Box<ASTNode> },
    IfAndOnlyIf { left: Box<ASTNode>, right: Box<ASTNode> },
}

impl ASTNode {
    pub fn as_string(&self) -> String {
        format!("{:#?}", self)
    }

    pub fn repr(&self) -> &str {
        match self {
            ASTNode::Identifier { name } => name,
            ASTNode::Literal { value } => if *value { "1" } else { "0" },
            ASTNode::Not { .. } => "¬",
            ASTNode::And { .. } => "∧",
            ASTNode::Or { .. } => "∨",
            ASTNode::Implies { .. } => "⇒",
            ASTNode::IfAndOnlyIf { .. } => "⟷",
        }
    }

    #[cfg(not(feature = "serde"))]
    pub fn as_json(&self) -> String {
        match self {
            ASTNode::Identifier { name } => {
                format!(r###"{{
                    "type": "identifier",
                    "name": "{name}"
                }}"###)
            },
            ASTNode::Literal { value } => {
                format!(r###"{{
                    "type": "literal",
                    "value": {value}
                }}"###)
            },
            ASTNode::Not { operand } => {
                format!(r###"{{
                    "type": "operator.not",
                    "operand": {operand}
                }}"###, operand=operand.as_json())
            },
            ASTNode::And { left, right } => {
                format!(r###"{{
                    "type": "operator.and",
                    "left": {left},
                    "right": {right}
                }}"###, left=left.as_json(), right=right.as_json())
            },
            ASTNode::Or { left, right } => {
                format!(r###"{{
                    "type": "operator.or",
                    "left": {left},
                    "right": {right}
                }}"###, left=left.as_json(), right=right.as_json())
            },
            ASTNode::Implies { left, right } => {
                format!(r###"{{
                    "type": "operator.implies",
                    "left": {left},
                    "right": {right}
                }}"###, left=left.as_json(), right=right.as_json())
            },
            ASTNode::IfAndOnlyIf { left, right } => {
                format!(r###"{{
                    "type": "operator.iff",
                    "left": {left},
                    "right": {right}
                }}"###, left=left.as_json(), right=right.as_json())
            }
        }
    }

    #[cfg(feature = "serde")]
    pub fn as_json(&self) -> String {
        serde_json::to_string(self).unwrap()
    }
}