passgen/
optimize.rs

1/// Optimize a Pattern for generation.
2use crate::pattern::*;
3use std::collections::BTreeSet;
4
5type Dependencies = BTreeSet<Special>;
6
7impl Pattern<Parsed> {
8    pub fn optimize(self) -> (Pattern<Optimized>, Dependencies) {
9        let mut dependencies = Dependencies::new();
10        let output = Optimize::optimize(self, &mut dependencies);
11        (output, dependencies)
12    }
13}
14
15pub trait Optimize {
16    type Output;
17
18    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output;
19}
20
21impl Optimize for Literal<Parsed> {
22    type Output = Literal<Optimized>;
23
24    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
25        Self::Output {
26            value: self.value.to_string(),
27        }
28    }
29}
30
31impl Optimize for Special<Parsed> {
32    type Output = Special<Optimized>;
33
34    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
35        dependencies.insert(self.clone());
36        match self {
37            Self::Wordlist(name) => Self::Output::Wordlist(name),
38            Self::Markov(name) => Self::Output::Markov(name),
39            Self::Preset(name) => Self::Output::Preset(name),
40        }
41    }
42}
43
44impl Optimize for Set<Parsed> {
45    type Output = Set<Optimized>;
46
47    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
48        self.into()
49    }
50}
51
52impl Optimize for Item<Parsed> {
53    type Output = Item<Optimized>;
54
55    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
56        Self::Output {
57            pattern: Optimize::optimize(self.pattern, dependencies),
58            repeat: self.repeat,
59            optional: self.optional,
60        }
61    }
62}
63
64impl Segment<Optimized> {
65    fn is_empty(&self) -> bool {
66        self.items.iter().any(|i| !i.is_empty())
67    }
68}
69
70impl Group<Optimized> {
71    fn is_empty(&self) -> bool {
72        self.segments.iter().any(|s| !s.is_empty())
73    }
74}
75
76impl Item<Optimized> {
77    fn is_empty(&self) -> bool {
78        // empty if item is empty
79        if self.repeat == (0..=0) {
80            return true;
81        }
82
83        match &self.pattern {
84            Pattern::Group(group) => group.is_empty(),
85            Pattern::Set(_) => false,
86            Pattern::Special(_) => false,
87            Pattern::Literal(literal) => literal.value.is_empty(),
88        }
89    }
90
91    fn is_modified(&self) -> bool {
92        self.optional || self.repeat != (1..=1)
93    }
94
95    fn try_fuse(&mut self, item: Self) -> Result<(), Self> {
96        // can't do it if we're optional or repeat
97        if self.is_modified() || item.is_modified() {
98            return Err(item);
99        }
100
101        match (&mut self.pattern, &item.pattern) {
102            (Pattern::Literal(left), Pattern::Literal(right)) => {
103                left.value.push_str(&right.value);
104                Ok(())
105            }
106            _ => Err(item),
107        }
108    }
109
110    fn flatten(self) -> impl Iterator<Item = Self> {
111        if self.is_modified() {
112            // break
113        }
114
115        match &self.pattern {
116            Pattern::Group(group) if group.segments.len() == 1 => {}
117            _ => {}
118        }
119
120        std::iter::once(self)
121    }
122
123    fn simplify(self) -> Self {
124        if self.is_modified() {
125            return self;
126        }
127
128        match &self.pattern {
129            Pattern::Set(set) => {
130                // TODO: set([a]) -> literal('a') (maybe move to flatten?)
131                self
132            }
133            _ => self,
134        }
135    }
136}
137
138impl Optimize for Segment<Parsed> {
139    type Output = Segment<Optimized>;
140
141    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
142        Self::Output {
143            items: self
144                .items
145                .into_iter()
146                .map(|item| item.optimize(dependencies))
147                // TODO: simpliy (set([a]) -> literal(a))
148                .map(Item::simplify)
149                // filter empty items
150                .filter(|i| !i.is_empty())
151                // try to flatten
152                .flat_map(Item::flatten)
153                // try to fuse items
154                .fold(Vec::new(), |mut items, item| {
155                    if let Some(last) = items.last_mut() {
156                        if let Err(item) = last.try_fuse(item) {
157                            items.push(item);
158                        }
159                    } else {
160                        items.push(item);
161                    }
162                    items
163                }),
164        }
165    }
166}
167
168impl Optimize for Group<Parsed> {
169    type Output = Group<Optimized>;
170
171    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
172        Self::Output {
173            segments: self
174                .segments
175                .into_iter()
176                .map(|segment| segment.optimize(dependencies))
177                .collect(),
178        }
179    }
180}
181
182impl Optimize for Pattern<Parsed> {
183    type Output = Pattern<Optimized>;
184
185    fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
186        match self {
187            Self::Group(group) => Self::Output::Group(group.optimize(dependencies)),
188            Self::Set(set) => Self::Output::Set(set.optimize(dependencies)),
189            Self::Literal(literal) => Self::Output::Literal(literal.optimize(dependencies)),
190            Self::Special(special) => Self::Output::Special(special.optimize(dependencies)),
191        }
192    }
193}