rain_lang/value/primitive/
logical.rs

1/*!
2Logical types and operations
3*/
4
5use std::fmt::{Debug, Display, Formatter, self};
6
7/// The boolean type
8#[derive(Copy, Clone, Eq, PartialEq, Hash)]
9pub struct Bool;
10
11impl Bool {
12    /// Get the string representing the Boolean type
13    #[inline(always)]
14    pub fn get_string(&self) -> &'static str { "#bool" }
15}
16
17impl Debug for Bool {
18    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
19        <Self as Display>::fmt(self, fmt)
20    }
21}
22
23impl Display for Bool {
24    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
25        write!(fmt, "{}", self.get_string())
26    }
27}
28
29/// Logical operations
30#[derive(Copy, Clone, Eq, PartialEq, Hash)]
31pub enum LogicalOp {
32    /// Binary logical operations
33    Binary(Binary),
34    /// Unary logical operations
35    Unary(Unary)
36}
37
38impl Debug for LogicalOp {
39    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
40        <Self as Display>::fmt(self, fmt)
41    }
42}
43
44impl Display for LogicalOp {
45    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
46        match self {
47            Self::Binary(b) => <Binary as fmt::Display>::fmt(b, fmt),
48            Self::Unary(u) => <Unary as fmt::Display>::fmt(u, fmt)
49        }
50    }
51}
52
53impl From<Binary> for LogicalOp {
54    #[inline] fn from(b: Binary) -> LogicalOp { LogicalOp::Binary(b) }
55}
56
57impl PartialEq<Binary> for LogicalOp {
58    #[inline] fn eq(&self, b: &Binary) -> bool { self.eq(&LogicalOp::from(*b)) }
59}
60
61impl From<Unary> for LogicalOp {
62    #[inline] fn from(u: Unary) -> LogicalOp { LogicalOp::Unary(u) }
63}
64
65impl PartialEq<Unary> for LogicalOp {
66    #[inline] fn eq(&self, u: &Unary) -> bool { self.eq(&LogicalOp::from(*u)) }
67}
68
69/// Binary logical operations
70#[derive(Copy, Clone, Eq, PartialEq, Hash)]
71pub enum Binary {
72    /// Logical AND
73    And,
74    /// Logical OR
75    Or,
76    /// Logical XOR
77    Xor,
78    /// Logical NAND
79    Nand,
80    /// Logical NOR
81    Nor,
82    /// Boolean equality
83    Eq,
84    /// Implication
85    Implies,
86    /// Reverse implication
87    ImpliedBy
88}
89
90use Binary::*;
91/// A list of all builtin binary logical operations
92pub const BINARY_OPS: &[Binary] = &[And, Or, Xor, Nand, Nor, Eq, Implies, ImpliedBy];
93
94impl Binary {
95    /// Get the string representing each logical operation
96    #[inline(always)]
97    pub fn get_string(&self) -> &'static str {
98        use Binary::*;
99        match self {
100            And => "#and",
101            Or => "#or",
102            Xor => "#xor",
103            Nand => "#nand",
104            Nor => "#nor",
105            Eq => "#eq",
106            Implies => "#implies",
107            ImpliedBy => "#impliedby"
108        }
109    }
110    /// Fully apply this logical operation
111    #[inline(always)]
112    pub fn apply(&self, left: bool, right: bool) -> bool {
113        use Binary::*;
114        match self {
115            And => left & right,
116            Or => left | right,
117            Xor => left ^ right,
118            Nand => !(left & right),
119            Nor => !(left | right),
120            Eq => left == right,
121            Implies => !left | right,
122            ImpliedBy => left | !right
123        }
124    }
125    /// Partially apply this logical operation
126    #[inline(always)]
127    pub fn partial_apply(&self, arg: bool) -> Unary {
128        use Unary::*;
129        use Binary::*;
130        match (self, arg) {
131            (And, true) => Id,
132            (And, false) => Constant(false),
133            (Or, true) => Constant(true),
134            (Or, false) => Id,
135            (Xor, true) => Not,
136            (Xor, false) => Id,
137            (Nand, true) => Not,
138            (Nand, false) => Constant(true),
139            (Nor, true) => Constant(false),
140            (Nor, false) => Not,
141            (Eq, true) => Id,
142            (Eq, false) => Not,
143            (Implies, true) => Id,
144            (Implies, false) => Constant(true),
145            (ImpliedBy, true) => Constant(true),
146            (ImpliedBy, false) => Not
147        }
148    }
149}
150
151impl Debug for Binary {
152    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
153        <Self as Display>::fmt(self, fmt)
154    }
155}
156
157impl Display for Binary {
158    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
159        write!(fmt, "{}", self.get_string())
160    }
161}
162
163/// Unary logical operations
164#[derive(Copy, Clone, Eq, PartialEq, Hash)]
165pub enum Unary {
166    /// The identity operation on booleans
167    Id,
168    /// Logical negation
169    Not,
170    /// Boolean constant
171    Constant(bool)
172}
173
174use Unary::*;
175/// A list of all builtin unary logical operations
176pub const UNARY_OPS: &[Unary] = &[Id, Not, Constant(true), Constant(false)];
177
178impl Unary {
179    /// Get the string representing logical not
180    #[inline(always)]
181    pub fn get_string(&self) -> &'static str {
182        match self {
183            Unary::Id => "#id", //TODO: think about this
184            Unary::Not => "#not",
185            //TODO: think about this
186            Unary::Constant(c) => if *c { "#constant_true" } else { "#constant_false" }
187        }
188    }
189    /// Apply this loigcal operation to a boolean
190    #[inline(always)]
191    pub fn apply(&self, arg: bool) -> bool {
192        match self {
193            Unary::Id => arg,
194            Unary::Not => !arg,
195            Unary::Constant(c) => *c
196        }
197    }
198}
199
200impl Debug for Unary {
201    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
202        <Self as Display>::fmt(self, fmt)
203    }
204}
205
206impl Display for Unary {
207    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
208        write!(fmt, "{}", self.get_string())
209    }
210}
211
212#[cfg(test)]
213pub mod tests {
214    #[test]
215    fn logical_operations_work() {
216        use super::Binary::*;
217        let f = false;
218        let t = true;
219        let ops = [
220            (And, f, f, f),
221            (And, t, f, f),
222            (And, f, t, f),
223            (And, t, t, t),
224            (Or, f, f, f),
225            (Or, t, f, t),
226            (Or, f, t, t),
227            (Or, t, t, t),
228            (Xor, f, f, f),
229            (Xor, t, f, t),
230            (Xor, f, t, t),
231            (Xor, t, t, f),
232            (Nand, f, f, t),
233            (Nand, t, f, t),
234            (Nand, f, t, t),
235            (Nand, t, t, f),
236            (Nor, f, f, t),
237            (Nor, t, f, f),
238            (Nor, f, t, f),
239            (Nor, t, t, f),
240            (Eq, f, f, t),
241            (Eq, t, f, f),
242            (Eq, f, t, f),
243            (Eq, t, t, t),
244            (Implies, f, f, t),
245            (Implies, t, f, f),
246            (Implies, f, t, t),
247            (Implies, t, t, t),
248            (ImpliedBy, f, f, t),
249            (ImpliedBy, t, f, t),
250            (ImpliedBy, f, t, f),
251            (ImpliedBy, t, t, t),
252        ];
253        for op in ops.iter() {
254            assert_eq!(op.0.apply(op.1, op.2), op.3,
255                "Incorrect value for ({} {} {}) = {}", op.0, op.1, op.2, op.3);
256            assert_eq!(op.0.partial_apply(op.1).apply(op.2), op.3,
257                "Incorrect value for (({} {}) {}) = ({} {}) = {}",
258                op.0, op.1, op.2, op.0.partial_apply(op.1), op.2, op.3);
259        }
260    }
261}