inator/
ops.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! Operations on NFAs.
8
9use crate::nfa::{Graph as Nfa, State};
10
11/// Unevaluated binary operation.
12#[non_exhaustive]
13#[allow(clippy::ref_option_ref)]
14#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub enum Lazy<I: Clone + Ord> {
16    /// NFA already made.
17    Immediate(Nfa<I>),
18    /// NFA promised.
19    Postponed,
20    /// NFA promised.
21    PostponedReference(*const Self),
22    /// Either one NFA or another, in parallel.
23    Or(Box<Self>, Box<Self>),
24    /// One then an epsilon transition to another.
25    ShrEps(Box<Self>, Box<Self>),
26    /// One then a non-epsilon transition (on a particular token) to another.
27    ShrNonEps(Box<Self>, (I, Option<&'static str>, Box<Self>)),
28    /// Repeat an NFA.
29    Repeat(Box<Self>),
30}
31
32impl<I: Clone + Ord> Clone for Lazy<I> {
33    #[inline]
34    fn clone(&self) -> Self {
35        match *self {
36            Self::Immediate(ref lhs) => Self::Immediate(lhs.clone()),
37            Self::Postponed => Self::PostponedReference(self), // <-- This is the crucial bit
38            Self::PostponedReference(ptr) => Self::PostponedReference(ptr),
39            Self::Or(ref lhs, ref rhs) => Self::Or(lhs.clone(), rhs.clone()),
40            Self::ShrEps(ref lhs, ref rhs) => Self::ShrEps(lhs.clone(), rhs.clone()),
41            Self::ShrNonEps(ref lhs, ref rhs) => Self::ShrNonEps(lhs.clone(), rhs.clone()),
42            Self::Repeat(ref lhs) => Self::Repeat(lhs.clone()),
43        }
44    }
45}
46
47impl<I: Clone + Ord> Lazy<I> {
48    /// Define a postponed value.
49    /// # Panics
50    /// If this value was already defined or if the definition is still postponed.
51    #[inline]
52    #[allow(clippy::manual_assert, clippy::panic)]
53    pub fn finally(&mut self, value: Self) {
54        if *self != Self::Postponed {
55            panic!("Called `finally` on a value that was already defined");
56        }
57        if value == Self::Postponed {
58            panic!("Called `finally` with a definition that is itself postponed");
59        }
60        *self = value;
61    }
62
63    /// Brzozowski's algorithm for minimizing automata.
64    #[inline]
65    #[must_use]
66    #[allow(clippy::missing_assert_message)]
67    pub fn compile(self) -> crate::dfa::Graph<I> {
68        self.evaluate().compile()
69    }
70
71    /// Match at least one time, then as many times as we want.
72    /// Note that if ANY number of times leads to an accepting state, we take it!
73    #[inline]
74    #[must_use]
75    pub fn repeat(self) -> Self {
76        Lazy::Repeat(Box::new(self))
77    }
78
79    /// Match at most one time (i.e. ignore if not present).
80    #[inline]
81    #[must_use]
82    pub fn optional(self) -> Self {
83        crate::empty() | self
84    }
85
86    /// Match zero or more times (a.k.a. Kleene star).
87    #[inline]
88    #[must_use]
89    pub fn star(self) -> Self {
90        self.repeat().optional()
91    }
92
93    /// Turn an expression into a value.
94    /// Note that this requires all postponed terms to be present.
95    /// # Panics
96    /// If we postponed a value and never defined it.
97    #[inline]
98    #[allow(
99        clippy::arithmetic_side_effects,
100        clippy::panic,
101        clippy::shadow_reuse,
102        clippy::suspicious_arithmetic_impl,
103        unsafe_code
104    )]
105    pub fn evaluate(self) -> Nfa<I> {
106        match self {
107            Self::Immediate(nfa) => nfa,
108            Self::Postponed => panic!("Needed a postponed value that had not been initialized"),
109            // SAFETY: Up to you. Don't be stupid. <3
110            Self::PostponedReference(ptr) => unsafe { &*ptr }.clone().evaluate(),
111            Self::Or(lhs, rhs) => {
112                let mut lhs = lhs.evaluate();
113                let mut rhs = rhs.evaluate();
114                let index = lhs.states.len();
115                for state in &mut rhs.states {
116                    *state += index;
117                }
118                lhs.states.extend(rhs.states);
119                lhs.initial.extend(
120                    rhs.initial
121                        .into_iter()
122                        .map(|x| x.checked_add(index).expect("Huge number of states")),
123                );
124                lhs
125            }
126            Self::ShrEps(lhs, rhs) => {
127                let mut lhs = lhs.evaluate();
128                let mut rhs = rhs.evaluate();
129                let index = lhs.states.len();
130                for state in &mut rhs.states {
131                    *state += index;
132                }
133                let incr_initial = rhs
134                    .initial
135                    .iter()
136                    .map(|x| x.checked_add(index).expect("Huge number of states"));
137                for state in &mut lhs.states {
138                    if state.accepting {
139                        state.accepting = false;
140                        state.epsilon.extend(incr_initial.clone());
141                    }
142                }
143                lhs.states.extend(rhs.states);
144                lhs
145            }
146            Self::ShrNonEps(lhs, (token, fn_name, rhs)) => {
147                let mut lhs = lhs.evaluate();
148                let mut rhs = rhs.evaluate();
149                let index = lhs.states.len();
150                for state in &mut rhs.states {
151                    *state += index;
152                }
153                let incr_initial = rhs
154                    .initial
155                    .iter()
156                    .map(|x| x.checked_add(index).expect("Huge number of states"));
157                for state in &mut lhs.states {
158                    if state.accepting {
159                        state.accepting = false;
160                        state.non_epsilon.extend(
161                            incr_initial
162                                .clone()
163                                .map(|i| (token.clone(), (core::iter::once(i).collect(), fn_name)))
164                                .clone(),
165                        );
166                    }
167                }
168                lhs.states.extend(rhs.states);
169                lhs
170            }
171            Self::Repeat(lhs) => {
172                let mut eval = lhs.evaluate();
173                for state in &mut eval.states {
174                    if state.accepting {
175                        state.epsilon.extend(eval.initial.iter());
176                    }
177                }
178                eval
179            }
180        }
181    }
182}
183
184impl<I: Clone + Ord> core::ops::AddAssign<usize> for State<I> {
185    #[inline]
186    fn add_assign(&mut self, rhs: usize) {
187        // TODO: We can totally use unsafe here since the order doesn't change
188        self.epsilon = self
189            .epsilon
190            .iter()
191            .map(|x| x.checked_add(rhs).expect("Huge number of states"))
192            .collect();
193        for &mut (ref mut set, _fn_name) in &mut self.non_epsilon.values_mut() {
194            *set = set
195                .iter()
196                .map(|&x| x.checked_add(rhs).expect("Huge number of states"))
197                .collect();
198        }
199    }
200}
201
202impl<I: Clone + Ord> core::ops::BitOr for Lazy<I> {
203    type Output = Self;
204    #[inline]
205    #[allow(clippy::arithmetic_side_effects, clippy::suspicious_arithmetic_impl)]
206    fn bitor(self, rhs: Self) -> Self::Output {
207        Lazy::Or(Box::new(self), Box::new(rhs))
208    }
209}
210
211impl<I: Clone + Ord> core::ops::Shr<(I, Option<&'static str>, Lazy<I>)> for Lazy<I> {
212    type Output = Self;
213    #[inline]
214    #[allow(clippy::arithmetic_side_effects, clippy::suspicious_arithmetic_impl)]
215    fn shr(self, (token, fn_name, rhs): (I, Option<&'static str>, Self)) -> Self::Output {
216        Lazy::ShrNonEps(Box::new(self), (token, fn_name, Box::new(rhs)))
217    }
218}
219
220impl<I: Clone + Ord> core::ops::Shr for Lazy<I> {
221    type Output = Self;
222    #[inline]
223    #[allow(clippy::arithmetic_side_effects, clippy::suspicious_arithmetic_impl)]
224    fn shr(self, rhs: Self) -> Self::Output {
225        Lazy::ShrEps(Box::new(self), Box::new(rhs))
226    }
227}
228
229impl core::ops::Add for Lazy<char> {
230    type Output = Self;
231    #[inline]
232    #[allow(clippy::arithmetic_side_effects, clippy::suspicious_arithmetic_impl)]
233    fn add(self, rhs: Self) -> Self::Output {
234        self >> crate::space() >> rhs
235    }
236}