Skip to main content

rs_teststand/expression/operator/
bitwise.rs

1//! Bitwise operators.
2
3use super::Arity;
4
5/// A bitwise operator.
6///
7/// Two measured behaviors make this family the easiest to misread:
8///
9/// * **`AND`, `OR`, `XOR` and `NOT` are bitwise, not logical**, despite reading
10///   like English. `6 AND 3` is `2`, not `true`. The logical forms live in
11///   [`LogicalOperator`](super::LogicalOperator).
12/// * **`^` is exclusive-or, not exponentiation.** `2^3` is `1`, not `8`.
13///
14/// Complement operates on 32 bits and yields an unsigned result: `~0` is
15/// `4294967295`, not `-1`.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum BitwiseOperator {
18    /// Bitwise and, `&`.
19    And,
20    /// Bitwise or, `|`.
21    Or,
22    /// Bitwise exclusive-or, `^`.
23    Xor,
24    /// Bitwise complement, `~`.
25    Not,
26    /// Left shift, `<<`.
27    ShiftLeft,
28    /// Right shift, `>>`.
29    ShiftRight,
30    /// Bitwise and, `AND`, the word spelling of [`Self::And`].
31    AndWord,
32    /// Bitwise or, `OR`, the word spelling of [`Self::Or`].
33    OrWord,
34    /// Bitwise exclusive-or, `XOR`, the word spelling of [`Self::Xor`].
35    XorWord,
36    /// Bitwise complement, `NOT`, the word spelling of [`Self::Not`].
37    NotWord,
38}
39
40impl BitwiseOperator {
41    /// Every bitwise operator.
42    pub const ALL: [Self; 10] = [
43        Self::And,
44        Self::Or,
45        Self::Xor,
46        Self::Not,
47        Self::ShiftLeft,
48        Self::ShiftRight,
49        Self::AndWord,
50        Self::OrWord,
51        Self::XorWord,
52        Self::NotWord,
53    ];
54
55    /// How the operator is written.
56    #[must_use]
57    pub const fn symbol(self) -> &'static str {
58        match self {
59            Self::And => "&",
60            Self::Or => "|",
61            Self::Xor => "^",
62            Self::Not => "~",
63            Self::ShiftLeft => "<<",
64            Self::ShiftRight => ">>",
65            Self::AndWord => "AND",
66            Self::OrWord => "OR",
67            Self::XorWord => "XOR",
68            Self::NotWord => "NOT",
69        }
70    }
71
72    /// How many operands it takes.
73    #[must_use]
74    pub const fn arity(self) -> Arity {
75        match self {
76            Self::Not | Self::NotWord => Arity::Unary,
77            _ => Arity::Binary,
78        }
79    }
80
81    /// Binding strength; lower binds tighter. Measured, see [`super`].
82    #[must_use]
83    pub const fn precedence(self) -> u8 {
84        match self {
85            Self::Not | Self::NotWord => 1,
86            Self::ShiftLeft | Self::ShiftRight => 4,
87            Self::And | Self::AndWord => 6,
88            Self::Xor | Self::XorWord => 7,
89            Self::Or | Self::OrWord => 8,
90        }
91    }
92
93    /// The other spelling, for operators that have one.
94    #[must_use]
95    pub const fn alternate_spelling(self) -> Option<Self> {
96        Some(match self {
97            Self::And => Self::AndWord,
98            Self::AndWord => Self::And,
99            Self::Or => Self::OrWord,
100            Self::OrWord => Self::Or,
101            Self::Xor => Self::XorWord,
102            Self::XorWord => Self::Xor,
103            Self::Not => Self::NotWord,
104            Self::NotWord => Self::Not,
105            _ => return None,
106        })
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::BitwiseOperator;
113
114    #[test]
115    fn and_binds_tighter_than_xor_which_binds_tighter_than_or() {
116        // Measured: 1 | 2 & 3 = 3, and 1 ^ 2 & 3 = 3.
117        assert!(BitwiseOperator::And.precedence() < BitwiseOperator::Xor.precedence());
118        assert!(BitwiseOperator::Xor.precedence() < BitwiseOperator::Or.precedence());
119    }
120
121    #[test]
122    fn word_and_symbol_spellings_agree() {
123        // Measured: 6 AND 3 = 2 = 6 & 3, and 6 XOR 3 = 5 = 6 ^ 3.
124        for operator in BitwiseOperator::ALL {
125            let Some(other) = operator.alternate_spelling() else {
126                continue;
127            };
128            assert_eq!(operator.precedence(), other.precedence(), "{operator:?}");
129            assert_eq!(operator.arity(), other.arity(), "{operator:?}");
130        }
131    }
132
133    #[test]
134    fn shifts_bind_looser_than_arithmetic_but_tighter_than_comparison() {
135        // Measured: 1 << 2 + 1 = 8, so `+` is evaluated first.
136        use crate::expression::{ArithmeticOperator, ComparisonOperator};
137        assert!(ArithmeticOperator::Add.precedence() < BitwiseOperator::ShiftLeft.precedence());
138        assert!(BitwiseOperator::ShiftLeft.precedence() < ComparisonOperator::Equal.precedence());
139    }
140}