Skip to main content

doge_runtime/ops/
bits.rs

1use crate::error::{DogeError, DogeResult};
2use crate::value::Value;
3
4/// The two Int operands of a bitwise binary operator, or a catchable type error
5/// naming both values — bitwise operators are Int-only.
6fn int_pair(sym: &str, a: &Value, b: &Value) -> DogeResult<(i64, i64)> {
7    match (a, b) {
8        (Value::Int(x), Value::Int(y)) => Ok((*x, *y)),
9        _ => Err(DogeError::type_error(format!(
10            "cannot {sym} {} and {} (bitwise operators need Ints)",
11            a.describe(),
12            b.describe()
13        ))),
14    }
15}
16
17/// A shift count as a `u32`: a negative count is a catchable value error, since
18/// shifting by a negative amount has no meaning.
19fn shift_count(y: i64) -> DogeResult<u32> {
20    u32::try_from(y).map_err(|_| {
21        DogeError::value_error(format!(
22            "cannot shift by {y} — the shift count must be 0 or more"
23        ))
24    })
25}
26
27fn shift_overflow(x: i64, y: i64) -> DogeError {
28    DogeError::overflow(format!("{x} << {y} overflowed the Int range"))
29}
30
31/// `&` — bitwise AND.
32pub fn bitand(a: Value, b: Value) -> DogeResult {
33    let (x, y) = int_pair("&", &a, &b)?;
34    Ok(Value::Int(x & y))
35}
36
37/// `|` — bitwise OR.
38pub fn bitor(a: Value, b: Value) -> DogeResult {
39    let (x, y) = int_pair("|", &a, &b)?;
40    Ok(Value::Int(x | y))
41}
42
43/// `^` — bitwise XOR.
44pub fn bitxor(a: Value, b: Value) -> DogeResult {
45    let (x, y) = int_pair("^", &a, &b)?;
46    Ok(Value::Int(x ^ y))
47}
48
49/// `<<` — left shift. Losing significant bits (or a count of 64 or more) is a
50/// catchable overflow error, never a silent wraparound.
51pub fn shl(a: Value, b: Value) -> DogeResult {
52    let (x, y) = int_pair("<<", &a, &b)?;
53    let n = shift_count(y)?;
54    if n >= i64::BITS {
55        return Err(shift_overflow(x, y));
56    }
57    let result = x << n;
58    // The shift is only lossless when it shifts back to the original value.
59    if result >> n != x {
60        return Err(shift_overflow(x, y));
61    }
62    Ok(Value::Int(result))
63}
64
65/// `>>` — arithmetic right shift. A count of 64 or more saturates to `0` for a
66/// non-negative value and `-1` for a negative one, matching the sign fill.
67pub fn shr(a: Value, b: Value) -> DogeResult {
68    let (x, y) = int_pair(">>", &a, &b)?;
69    let n = shift_count(y)?;
70    let result = if n >= i64::BITS {
71        if x < 0 {
72            -1
73        } else {
74            0
75        }
76    } else {
77        x >> n
78    };
79    Ok(Value::Int(result))
80}
81
82/// `~` — bitwise NOT (Int-only).
83pub fn bitnot(a: Value) -> DogeResult {
84    match &a {
85        Value::Int(n) => Ok(Value::Int(!n)),
86        _ => Err(DogeError::type_error(format!(
87            "cannot ~ {} (bitwise NOT needs an Int)",
88            a.describe()
89        ))),
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::error::ErrorKind;
97
98    fn int(n: i64) -> Value {
99        Value::Int(n)
100    }
101
102    #[test]
103    fn basic_bitwise() {
104        assert!(matches!(
105            bitand(int(0b1100), int(0b1010)).unwrap(),
106            Value::Int(0b1000)
107        ));
108        assert!(matches!(
109            bitor(int(0b1100), int(0b1010)).unwrap(),
110            Value::Int(0b1110)
111        ));
112        assert!(matches!(
113            bitxor(int(0b1100), int(0b1010)).unwrap(),
114            Value::Int(0b0110)
115        ));
116        assert!(matches!(bitnot(int(0)).unwrap(), Value::Int(-1)));
117        assert!(matches!(bitnot(int(5)).unwrap(), Value::Int(-6)));
118    }
119
120    #[test]
121    fn shifts() {
122        assert!(matches!(shl(int(1), int(4)).unwrap(), Value::Int(16)));
123        assert!(matches!(shr(int(16), int(4)).unwrap(), Value::Int(1)));
124        // Arithmetic right shift keeps the sign.
125        assert!(matches!(shr(int(-8), int(1)).unwrap(), Value::Int(-4)));
126        // A huge right-shift saturates to the sign fill.
127        assert!(matches!(shr(int(-8), int(200)).unwrap(), Value::Int(-1)));
128        assert!(matches!(shr(int(8), int(200)).unwrap(), Value::Int(0)));
129    }
130
131    #[test]
132    fn shift_errors() {
133        assert_eq!(shl(int(1), int(64)).unwrap_err().kind, ErrorKind::Overflow);
134        assert_eq!(shl(int(1), int(63)).unwrap_err().kind, ErrorKind::Overflow);
135        assert_eq!(
136            shl(int(-1), int(-1)).unwrap_err().kind,
137            ErrorKind::ValueError
138        );
139        assert_eq!(
140            shr(int(1), int(-1)).unwrap_err().kind,
141            ErrorKind::ValueError
142        );
143    }
144
145    #[test]
146    fn non_int_is_a_type_error() {
147        assert_eq!(
148            bitand(int(1), Value::str("x")).unwrap_err().kind,
149            ErrorKind::TypeError
150        );
151        assert_eq!(
152            bitnot(Value::str("x")).unwrap_err().kind,
153            ErrorKind::TypeError
154        );
155    }
156}