gregex_logic/translation/
setterminal.rs

1//! Contains the `SetTerminal` enum, which represents the different types of terminals that can be used in a regular expression.
2
3use std::hash::{Hash, Hasher};
4
5/// The `SetTerminal` enum represents the different types of terminals that can be used in a regular expression.
6#[derive(Debug)]
7pub enum SetTerminal {
8    SingleElement(char, u32),            // a₁
9    DoubleElement(char, u32, char, u32), // a₁b₂
10    Epsilon,                             // ε
11    Empty,                               // ∅
12}
13
14impl SetTerminal {
15    /// Performs the cartesian product of two `SetTerminal` instances.
16    pub fn product(&self, other: &SetTerminal) -> SetTerminal {
17        match (self, other) {
18            (SetTerminal::SingleElement(a, a_code), SetTerminal::SingleElement(b, b_code)) => {
19                SetTerminal::DoubleElement(*a, *a_code, *b, *b_code)
20            }
21            (SetTerminal::SingleElement(a, a_code), SetTerminal::Epsilon) => {
22                SetTerminal::SingleElement(*a, *a_code)
23            }
24            (SetTerminal::Epsilon, SetTerminal::SingleElement(b, b_code)) => {
25                SetTerminal::SingleElement(*b, *b_code)
26            }
27            (SetTerminal::Epsilon, SetTerminal::Epsilon) => SetTerminal::Epsilon,
28            (SetTerminal::Empty, _) => SetTerminal::Empty,
29            (_, SetTerminal::Empty) => SetTerminal::Empty,
30            _ => unreachable!("Invalid product"),
31        }
32    }
33}
34
35impl PartialEq for SetTerminal {
36    fn eq(&self, other: &Self) -> bool {
37        match (self, other) {
38            (SetTerminal::SingleElement(a, a_code), SetTerminal::SingleElement(b, b_code)) => {
39                a == b && a_code == b_code
40            }
41            (
42                SetTerminal::DoubleElement(a, a_code, b, b_code),
43                SetTerminal::DoubleElement(c, c_code, d, d_code),
44            ) => a == c && a_code == c_code && b == d && b_code == d_code,
45            (SetTerminal::Epsilon, SetTerminal::Epsilon) => true,
46            (SetTerminal::Empty, SetTerminal::Empty) => true,
47            _ => false,
48        }
49    }
50}
51
52impl Eq for SetTerminal {}
53
54impl Hash for SetTerminal {
55    fn hash<H: Hasher>(&self, state: &mut H) {
56        match self {
57            SetTerminal::SingleElement(a, a_code) => {
58                a.hash(state);
59                a_code.hash(state);
60            }
61            SetTerminal::DoubleElement(a, a_code, b, b_code) => {
62                a.hash(state);
63                a_code.hash(state);
64                b.hash(state);
65                b_code.hash(state);
66            }
67            SetTerminal::Epsilon => {
68                "Epsilon".hash(state);
69            }
70            SetTerminal::Empty => {
71                "Empty".hash(state);
72            }
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_product() {
83        let a = SetTerminal::SingleElement('a', 1);
84        let b = SetTerminal::SingleElement('b', 2);
85        let c = SetTerminal::Epsilon;
86        let d = SetTerminal::Empty;
87
88        assert_eq!(a.product(&b), SetTerminal::DoubleElement('a', 1, 'b', 2));
89        assert_eq!(a.product(&c), SetTerminal::SingleElement('a', 1));
90        assert_eq!(c.product(&b), SetTerminal::SingleElement('b', 2));
91        assert_eq!(c.product(&c), SetTerminal::Epsilon);
92        assert_eq!(d.product(&a), SetTerminal::Empty);
93        assert_eq!(b.product(&d), SetTerminal::Empty);
94    }
95}