Skip to main content

jay/
dtype.rs

1//! Element types. The set is deliberately small; nothing here may assume it
2//! stays small.
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum DType {
6    Bool,
7    I64,
8    /// An arbitrary-precision integer (J's "extended", `123x`).
9    Ext,
10    /// An exact ratio of two arbitrary-precision integers (J `1r3`).
11    Rat,
12    F64,
13    /// A complex number, held as an interleaved `[re, im]` pair.
14    Complex,
15    Char,
16    /// An interned name (J `s:`). The element is an index into the
17    /// process-wide symbol table, not the text itself.
18    Symbol,
19    /// A box: every element is itself an array (J `<`, APL `⊂`).
20    Box,
21}
22
23impl DType {
24    pub fn name(self) -> &'static str {
25        match self {
26            DType::Bool => "boolean",
27            DType::I64 => "integer",
28            DType::Ext => "extended",
29            DType::Rat => "rational",
30            DType::F64 => "float",
31            DType::Complex => "complex",
32            DType::Char => "character",
33            DType::Symbol => "symbol",
34            DType::Box => "boxed",
35        }
36    }
37
38    pub fn is_numeric(self) -> bool {
39        matches!(
40            self,
41            DType::Bool | DType::I64 | DType::Ext | DType::Rat | DType::F64 | DType::Complex
42        )
43    }
44
45    /// True for the two types that never round.
46    pub fn is_exact(self) -> bool {
47        matches!(self, DType::Ext | DType::Rat)
48    }
49
50    /// Common type two numeric operands widen to. None if incompatible.
51    ///
52    /// The order is J's numeric tower: an exact type sits above the machine
53    /// integers and below the floats, so `1x + 1r2` stays exact while
54    /// `1x + 1.5` rounds.
55    pub fn promote(a: DType, b: DType) -> Option<DType> {
56        use DType::*;
57        match (a, b) {
58            (Box, Box) => Some(Box),
59            (Box, _) | (_, Box) => None,
60            (Char, Char) => Some(Char),
61            (Char, _) | (_, Char) => None,
62            (Symbol, Symbol) => Some(Symbol),
63            (Symbol, _) | (_, Symbol) => None,
64            (Complex, _) | (_, Complex) => Some(Complex),
65            (F64, _) | (_, F64) => Some(F64),
66            (Rat, _) | (_, Rat) => Some(Rat),
67            (Ext, _) | (_, Ext) => Some(Ext),
68            (I64, _) | (_, I64) => Some(I64),
69            (Bool, Bool) => Some(Bool),
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::DType::*;
77
78    #[test]
79    fn the_numeric_tower_climbs_bool_integer_extended_rational_float_complex() {
80        let tower = [Bool, I64, Ext, Rat, F64, Complex];
81        for (i, &a) in tower.iter().enumerate() {
82            for (j, &b) in tower.iter().enumerate() {
83                let want = tower[i.max(j)];
84                // Two booleans are the one pair that stays boolean.
85                assert_eq!(super::DType::promote(a, b), Some(want), "{a:?} with {b:?}");
86            }
87        }
88    }
89
90    #[test]
91    fn characters_and_boxes_mix_with_nothing() {
92        assert_eq!(super::DType::promote(Char, Ext), None);
93        assert_eq!(super::DType::promote(Box, Rat), None);
94        assert_eq!(super::DType::promote(Symbol, Char), None);
95        assert_eq!(super::DType::promote(Symbol, I64), None);
96        assert_eq!(super::DType::promote(Symbol, Symbol), Some(Symbol));
97    }
98}