rusty_systems/symbols/
iterator.rs

1use super::*;
2use std::iter::FromIterator;
3use crate::prelude::ProductionString;
4use crate::productions::{Production, ProductionBody, ProductionHead};
5
6/// Provides iteration tools for working with collections of [`Symbol`] objects.
7pub trait SymbolIterable {
8    /// Iterate over all symbols contained in the object.
9    fn all_symbols_iter(&self) -> impl Iterator<Item=Symbol>;
10
11    /// Creates a collection of all the symbols that
12    /// [`self.all_symbols_iter`] iterates over.
13    /// 
14    /// If you use a [`HashSet`] as the return type, then you will have 
15    /// a set of unique [`Symbol`] instances.
16    fn all_symbols<C: FromIterator<Symbol>>(&self) -> C {
17        self.all_symbols_iter().collect()
18    }
19}
20
21
22impl SymbolIterable for ProductionString {
23    fn all_symbols_iter(&self) -> impl Iterator<Item=Symbol> {
24        self.iter().cloned()
25    }
26}
27
28impl SymbolIterable for ProductionHead {
29    fn all_symbols_iter(&self) -> impl Iterator<Item=Symbol> {
30        let pre : Box<dyn Iterator<Item=Symbol>> = match self.pre_context() {
31            None => Box::new(std::iter::empty()),
32            Some(val) => Box::new(val.all_symbols_iter())
33        };
34
35        let target = std::iter::once(self.target()).cloned();
36
37        let post : Box<dyn Iterator<Item=Symbol>> = match self.post_context() {
38            None => Box::new(std::iter::empty()),
39            Some(val) => Box::new(val.all_symbols_iter())
40        };
41
42        pre.chain(target).chain(post)
43    }
44}
45
46impl SymbolIterable for ProductionBody {
47    #[inline]
48    fn all_symbols_iter(&self) -> impl Iterator<Item=Symbol> {
49        self.string().all_symbols_iter()
50    }
51}
52
53impl SymbolIterable for Production {
54    fn all_symbols_iter(&self) -> impl Iterator<Item=Symbol> {
55        let mut result : Box<dyn Iterator<Item=Symbol>> = Box::new(self.head().all_symbols_iter());
56
57        for i in self.all_bodies() {
58            result = Box::new(result.chain(i.all_symbols_iter()));
59        }
60
61        result
62    }
63}
64
65#[cfg(test)]
66mod test {
67    use super::*;
68    use crate::parser::{parse_prod_string, parse_production};
69
70    #[test]
71    fn can_iterate_over_string() {
72        let string = parse_prod_string("A B B C D").unwrap();
73
74        let mut iter = string.all_symbols_iter();
75
76        assert_eq!(iter.next().unwrap().code, get_code("A").unwrap());
77        assert_eq!(iter.next().unwrap().code, get_code("B").unwrap());
78        assert_eq!(iter.next().unwrap().code, get_code("B").unwrap());
79        assert_eq!(iter.next().unwrap().code, get_code("C").unwrap());
80        assert_eq!(iter.next().unwrap().code, get_code("D").unwrap());
81
82        let b : Vec<_> = string.all_symbols();
83        assert_eq!(b.len(), 5);
84    }
85
86
87    #[test]
88    fn can_iterate_over_production() {
89        let production = parse_production("Pre < Target > Post -> A B C C").unwrap();
90
91        let mut iter = production.all_symbols_iter();
92
93        assert_eq!(iter.next().unwrap().code, get_code("Pre").unwrap());
94        assert_eq!(iter.next().unwrap().code, get_code("Target").unwrap());
95        assert_eq!(iter.next().unwrap().code, get_code("Post").unwrap());
96        assert_eq!(iter.next().unwrap().code, get_code("A").unwrap());
97        assert_eq!(iter.next().unwrap().code, get_code("B").unwrap());
98        assert_eq!(iter.next().unwrap().code, get_code("C").unwrap());
99        assert_eq!(iter.next().unwrap().code, get_code("C").unwrap());
100
101        let b : Vec<_> = production.all_symbols();
102        assert_eq!(b.len(), 7);
103
104        let b : HashSet<_> = production.all_symbols();
105        assert_eq!(b.len(), 6);
106    }
107
108}