Skip to main content

liblet/
production.rs

1//! Module for handling all productions related operations.
2//!
3//! Its goal is to emulate a formal grammar production.
4//!
5//! It defines a `Production` type which can be used to conveniently work with productions in grammars,
6//! and provide abstraction over normal collection of symbols, organized in a left and right hand side (form A -> B).
7//!
8//! It can be easily constructed from `&str`s or collections of [Symbol](../symbol/struct.Symbol.html)s.
9
10use crate::symbol::{sentential_form, Symbol, SymbolError};
11use crate::tokenizer;
12use crate::tokenizer::TokenizerError;
13use serde::{Deserialize, Serialize};
14use std::collections::HashSet;
15use std::error::Error;
16use std::fmt;
17
18#[derive(Debug, PartialEq, Eq, Clone, Hash)]
19pub enum ProductionError {
20    /// Error returned for trying to create a Production with an empty / missing left hand side.
21    /// Left hand side of productions should be an ordered collection of n >= 1 symbols.
22    NoLhs,
23    /// Error returned for trying to create a Production with an empty / missing right hand side.
24    /// Right hand side of productions should be an ordered collection of n >= 1 symbols.
25    NoRhs,
26    /// Error returned for trying to create a Production with invalid symbols.
27    /// To be expected when creating productions from raw string inputs.
28    SymbolError(SymbolError),
29    /// Error returned for trying to create a Production with a bad formatted raw input string.
30    /// Properly formatted productions as strings are described in the
31    /// [Production](struct.Production.html) documentation.
32    FormatError(TokenizerError),
33}
34
35impl fmt::Display for ProductionError {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            ProductionError::NoLhs => write!(f, "ProductionError: no lhs in production"),
39            ProductionError::NoRhs => write!(f, "ProductionError: no rhs in production"),
40            ProductionError::SymbolError(e) => {
41                write!(f, "ProductionError: symbol error encountered = {}", e)
42            }
43            ProductionError::FormatError(e) => write!(
44                f,
45                "ProductionError: bad formatted string encountered, error = {}",
46                e
47            ),
48        }
49    }
50}
51
52impl Error for ProductionError {
53    fn source(&self) -> Option<&(dyn Error + 'static)> {
54        match self {
55            ProductionError::SymbolError(e) => Some(e),
56            ProductionError::FormatError(e) => Some(e),
57            _ => None,
58        }
59    }
60}
61
62impl std::convert::From<TokenizerError> for ProductionError {
63    fn from(e: TokenizerError) -> Self {
64        ProductionError::FormatError(e)
65    }
66}
67
68/// A predicate for checking if production match some expected characteristics.
69///
70/// You can test a predicate on a Production by using the [test]: #method.test method.
71#[derive(Debug, Clone, PartialEq, Eq, Hash)]
72pub enum ProductionPredicate {
73    /// It checks if the left hand side of the production equals the given ordered collection of symbols.
74    LhsEquals(Vec<Symbol>),
75    /// It checks if the right hand side of the production equals the given ordered collection of symbols.
76    RhsEquals(Vec<Symbol>),
77    /// It checks if the right hand side length (symbols count) of the production equals the given number.
78    RhsLengthEquals(usize),
79    /// It checks if the right hand side of the production ends with the given ordered collection of symbols.
80    RhsIsSuffixOf(Vec<Symbol>),
81}
82
83impl ProductionPredicate {
84    /// Test if a production match the predicate
85    ///
86    /// # Examples
87    /// ```rust
88    /// use liblet::production::{ProductionPredicate, production};
89    /// use liblet::symbol::symbol;
90    ///
91    /// // create a new production "A -> B C"
92    /// let p = production("A", "B C");
93    /// let rhs = vec![symbol("B"), symbol("C")];
94    ///
95    /// assert!(ProductionPredicate::RhsEquals(rhs).test(&p));
96    /// ```
97    pub fn test(&self, p: &Production) -> bool {
98        match self {
99            ProductionPredicate::LhsEquals(symbols) => p.lhs() == *symbols,
100            ProductionPredicate::RhsEquals(symbols) => p.rhs() == *symbols,
101            ProductionPredicate::RhsLengthEquals(length) => p.rhs().len() == *length,
102            ProductionPredicate::RhsIsSuffixOf(symbols) => p.rhs().ends_with(&symbols),
103        }
104    }
105}
106
107impl fmt::Display for ProductionPredicate {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        match self {
110            ProductionPredicate::LhsEquals(symbols) => write!(
111                f,
112                "ProductionPredicate: lhs == {}",
113                sentential_form(symbols.clone())
114            ),
115            ProductionPredicate::RhsEquals(symbols) => write!(
116                f,
117                "ProductionPredicate: rhs == {}",
118                sentential_form(symbols.clone())
119            ),
120            ProductionPredicate::RhsIsSuffixOf(symbols) => write!(
121                f,
122                "ProductionPredicate: rhs suffix == {}",
123                sentential_form(symbols.clone())
124            ),
125            ProductionPredicate::RhsLengthEquals(length) => {
126                write!(f, "ProductionPredicate: rhs length == {}", length)
127            }
128        }
129    }
130}
131
132/// The main type of this module.
133///
134/// It allows to abstract over grammar productions, having a defined left and right and side,
135/// which are ordered collections of symbols.
136///
137/// A production can be created easily from strings, following these rules:
138/// - string can contain any number of whitespace
139/// - multiple productions are divided by '\n' char
140/// - string must have a sequence of symbol to the left of a "->"
141/// - string must have a sequence of alternatives to the right of a "->"
142/// - string can have only one "->" per production (per line)
143/// - a sequence of symbol is a sequence of symbols divided by some whitespace
144/// - a sequence of alternative is one ore more sequence of symbols divided by the "|" char
145/// - string can't contain anything else
146///
147/// # Examples
148/// `A -> B` leads to a production from the symbol `A` to the symbol `B`
149///
150/// `A -> B | C` leads to two productions, one for each alternative (`A` -> `B` and `A` -> `C`)
151///
152/// `A -> B\nA -> C` leads to the same result of the above one, defining the two productions separately
153///
154/// `A -> ` will results in an error, because there's no right hand side for the production
155#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Serialize, Deserialize)]
156pub struct Production {
157    lhs: Vec<Symbol>,
158    rhs: Vec<Symbol>,
159}
160
161impl fmt::Display for Production {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        for lhs in self.lhs() {
164            write!(f, "{} ", lhs)?;
165        }
166        write!(f, "->")?;
167        for rhs in self.rhs() {
168            write!(f, " {}", rhs)?;
169        }
170
171        Ok(())
172    }
173}
174
175impl std::convert::TryFrom<&str> for Production {
176    type Error = ProductionError;
177
178    fn try_from(value: &str) -> Result<Self, Self::Error> {
179        let p = Production::from_string(value)?;
180        let mut p_iter = p.iter();
181        if let Some(p) = p_iter.next() {
182            if p_iter.next().is_none() {
183                Ok(p.clone())
184            } else {
185                Err(ProductionError::FormatError(
186                    TokenizerError::ProductionMultiple(value.to_string()),
187                ))
188            }
189        } else {
190            Err(ProductionError::FormatError(
191                TokenizerError::ProductionEmpty(value.to_string()),
192            ))
193        }
194    }
195}
196
197impl Production {
198    /// Creates a new Production based on the left and right hand side given as collections of symbols.
199    ///
200    /// # Errors
201    /// It can return an error if either the left or right hand side is empty.
202    ///
203    /// # Examples
204    /// ```rust
205    /// use liblet::production::Production;
206    /// use liblet::symbol::symbol;
207    ///
208    /// // create left and right hand side for production "A -> B C"
209    /// let lhs = vec![symbol("A")];
210    /// let rhs = vec![symbol("B"), symbol("C")];
211    ///
212    /// // create a new production based on the previously defined left and right hand side
213    /// let production = Production::new(lhs, rhs);
214    ///
215    /// assert!(production.is_ok());
216    /// ```
217    pub fn new<I>(lhs: I, rhs: I) -> Result<Production, ProductionError>
218    where
219        I: IntoIterator<Item = Symbol>,
220    {
221        let lhs: Vec<Symbol> = lhs.into_iter().collect();
222        let rhs: Vec<Symbol> = rhs.into_iter().collect();
223
224        if lhs.is_empty() {
225            return Err(ProductionError::NoLhs);
226        }
227        if rhs.is_empty() {
228            return Err(ProductionError::NoRhs);
229        }
230
231        Ok(Production { lhs: lhs, rhs: rhs })
232    }
233
234    /// Creates a new Production based on the left and right hand side given as collections of `&str`.
235    ///
236    /// # Errors
237    /// It can return an error if the strings are not convertible to [Symbol](../symbol/struct.Symbol.html)s.
238    /// Otherwise, it acts like [new](#method.new).
239    ///
240    /// # Examples
241    /// ```rust
242    /// use liblet::production::Production;
243    /// use liblet::symbol::symbol;
244    ///
245    /// // create left and right hand side for production "A -> B C"
246    /// let lhs = vec!["A"];
247    /// let rhs = vec!["B", "C"];
248    ///
249    /// // create a new production based on the previously defined left and right hand side
250    /// let production = Production::new_from_string(lhs, rhs);
251    ///
252    /// assert!(production.is_ok());
253    /// ```
254    pub fn new_from_string<'a, I>(lhs: I, rhs: I) -> Result<Production, ProductionError>
255    where
256        I: IntoIterator<Item = &'a str>,
257    {
258        let lhs = lhs.into_iter().try_fold(Vec::new(), |mut acc, s| {
259            let s = Symbol::new(s)?;
260            acc.push(s);
261            Ok(acc)
262        });
263        let rhs = rhs.into_iter().try_fold(Vec::new(), |mut acc, s| {
264            let s = Symbol::new(s)?;
265            acc.push(s);
266            Ok(acc)
267        });
268
269        match (lhs, rhs) {
270            (Ok(lhs), Ok(rhs)) => Production::new(lhs, rhs),
271            (Err(e), _) => Err(ProductionError::SymbolError(e)),
272            (_, Err(e)) => Err(ProductionError::SymbolError(e)),
273        }
274    }
275
276    /// Return the ordered collection of symbol representing the left hand side of the production.
277    ///
278    /// # Examples
279    /// ```rust
280    /// use liblet::production::{Production,production};
281    /// use liblet::symbol::symbol;
282    ///
283    /// // create a production representing "A -> B C"
284    /// let p = production("A", "B C");
285    ///
286    /// assert_eq!(p.lhs(), vec![symbol("A")]);
287    /// ```
288    pub fn lhs(&self) -> Vec<Symbol> {
289        self.lhs.clone()
290    }
291
292    /// Return the ordered collection of symbol representing the right hand side of the production.
293    ///
294    /// # Examples
295    /// ```rust
296    /// use liblet::production::{Production,production};
297    /// use liblet::symbol::symbol;
298    ///
299    /// // create a production representing "A -> B C"
300    /// let p = production("A", "B C");
301    ///
302    /// assert_eq!(p.rhs(), vec![symbol("B"), symbol("C")]);
303    /// ```
304    pub fn rhs(&self) -> Vec<Symbol> {
305        self.rhs.clone()
306    }
307
308    /// Return a set containing all the different symbols which appears on the left
309    /// and right hand side of the production.
310    ///
311    /// # Examples
312    /// ```rust
313    /// use liblet::production::{Production,production};
314    /// use liblet::symbol::{Symbol,symbol};
315    /// use std::collections::HashSet;
316    ///
317    /// // create a production representing "A -> B C"
318    /// let p = production("A", "B C");
319    /// let symbols: HashSet<Symbol> = vec![symbol("A"), symbol("B"), symbol("C")].into_iter().collect();
320    ///
321    /// assert_eq!(p.symbols(), symbols);
322    /// ```
323    pub fn symbols(&self) -> HashSet<Symbol> {
324        let mut symbols: Vec<Symbol> = self.lhs();
325        symbols.append(&mut self.rhs());
326        symbols.into_iter().collect()
327    }
328
329    /// Create a collection of productions from a raw input string.
330    ///
331    /// # Errors
332    /// Can return an error if the raw string can't be parsed to obtain actual productions both due to wrong
333    /// string formatting (LHS -> RHS | ...)(see [from_string](../symbol/struct.Symbol.html) method from symbols
334    /// for more info about string formatting symbols) and due to production creation error (see production [constructor](#method.new) for more info).
335    ///
336    /// In the case an empty or whitespace only string is given, it just returns an empty collection of productions.
337    ///
338    /// # Examples
339    /// ```rust
340    /// # use std::error::Error;
341    /// #
342    /// # fn main() -> Result<(), Box<dyn Error>> {
343    /// use liblet::production::Production;
344    ///
345    /// let result = Production::from_string("
346    ///     A -> B C
347    ///     B -> b
348    /// ")?;
349    ///
350    /// assert_eq!(result.len(), 2);
351    /// #
352    /// #     Ok(())
353    /// # }
354    /// ```
355    pub fn from_string(string: &str) -> Result<Vec<Production>, ProductionError> {
356        tokenizer::productions_from_string(string)?
357            .iter()
358            .try_fold(Vec::new(), |mut acc, p| {
359                let p = Production::new_from_string::<Vec<&str>>(
360                    p.0.iter().map(String::as_str).collect(),
361                    p.1.iter().map(String::as_str).collect(),
362                )?;
363                acc.push(p);
364                Ok(acc)
365            })
366    }
367
368    /// Create a collection of productions from a collection of raw input string.
369    /// Same as [from_string](#method.from_string) but accepts an `IntoIterator`.
370    ///
371    /// # Errors
372    /// Can return an error if any of the raw strings can't be parsed to obtain actual productions both due to wrong
373    /// string formatting (LHS -> RHS | ...)(see [from_string](../symbol/struct.Symbol.html) method from symbols
374    /// for more info about string formatting symbols) and due to production creation error (see production [constructor](#method.new) for more info).
375    ///
376    /// In the case empty or whitespace only strings are given, it just returns an empty collection of productions.
377    ///
378    /// # Examples
379    /// ```rust
380    /// # use std::error::Error;
381    /// #
382    /// # fn main() -> Result<(), Box<dyn Error>> {
383    /// use liblet::production::Production;
384    ///
385    /// let result = Production::from_iter(vec![
386    ///     "A -> B C",
387    ///     "B -> b"
388    /// ])?;
389    ///
390    /// assert_eq!(result.len(), 2);
391    /// #
392    /// #     Ok(())
393    /// # }
394    /// ```
395    pub fn from_iter<'a, I>(strings: I) -> Result<Vec<Production>, ProductionError>
396    where
397        I: IntoIterator<Item = &'a str>,
398    {
399        let mut p: Vec<Production> = Vec::new();
400
401        for string in strings {
402            p.append(&mut Production::from_string(string)?)
403        }
404
405        Ok(p)
406    }
407
408    /// Return a boxed `FnMut` which accepts a production and test the given predicate on it, returning a `bool`ean value.
409    ///
410    /// # Examples
411    /// ```rust
412    /// # use std::error::Error;
413    /// #
414    /// # fn main() -> Result<(), Box<dyn Error>> {
415    /// use liblet::production::{Production,ProductionPredicate};
416    /// use liblet::symbol::symbol;
417    ///
418    /// let p = Production::from_string("
419    ///     A -> B C
420    ///     B -> b
421    /// ")?;
422    /// let closure = Production::such_that(ProductionPredicate::RhsEquals(vec![symbol("b")]));
423    /// let expected = Production::new(vec![symbol("B")], vec![symbol("b")])?;
424    /// let mut result = p.iter().filter(closure);
425    ///
426    /// assert_eq!(result.next(), Some(&expected));
427    /// assert_eq!(result.next(), None);
428    /// #
429    /// #     Ok(())
430    /// # }
431    /// ```
432    pub fn such_that(predicate: ProductionPredicate) -> Box<dyn FnMut(&&Production) -> bool> {
433        Box::new(move |p| predicate.test(&p))
434    }
435}
436
437/// Convenience function for creating a production from a pair of raw strings
438/// (left hand side and right hand side).
439///
440/// It returns the production directly,
441/// as opposed to the `Result` returned from the production [constructor](struct.Production.html#method.new).
442///
443/// # Panics
444/// Panics if some error occurred during production creation (see production [consructor](struct.Production.html#method.new) for further details)
445///
446/// # Examples
447/// ```rust
448/// use liblet::production::production;
449/// use liblet::symbol::symbol;
450///
451/// let p = production("A", "B C");
452///
453/// assert_eq!(p.lhs(), vec![symbol("A")]);
454/// assert_eq!(p.rhs(), vec![symbol("B"),symbol("C")]);
455/// ```
456pub fn production(lhs: &str, rhs: &str) -> Production {
457    Production::new(
458        Symbol::from_string(lhs).unwrap(),
459        Symbol::from_string(rhs).unwrap(),
460    )
461    .unwrap()
462}
463
464/// Convenience function for creating a collection of productions from a raw string.
465///
466/// It returns the productions directly,
467/// as opposed to the `Result` returned from the production [from_string](struct.Production.html#method.from_string) method.
468///
469/// # Panics
470/// Panics if some error occurred during productions creation (see production [from_string](struct.Production.html#method.from_string) method for further details)
471///
472/// # Examples
473/// ```rust
474/// use liblet::production::productions;
475///
476/// let p = productions("
477///     A -> B C
478///     B -> b
479/// ");
480///
481/// assert_eq!(p.len(), 2);
482/// ```
483pub fn productions(string: &str) -> Vec<Production> {
484    Production::from_string(string).unwrap()
485}
486
487/// Convenience function for creating visual representation of
488/// a collection of productions, ordered.
489///
490/// # Examples
491/// ```rust
492/// use liblet::production::{production_table, productions};
493///
494/// let p = productions("
495///     A -> B C
496///     B -> b
497/// ");
498/// let result = production_table(p);
499///
500/// assert_eq!(result, "0: A -> B C\n1: B -> b");
501/// ```
502pub fn production_table<I>(productions: I) -> String
503where
504    I: IntoIterator<Item = Production>,
505{
506    productions
507        .into_iter()
508        .enumerate()
509        .map(|(i, p)| format!("{}: {}", i, p))
510        .collect::<Vec<String>>()
511        .join("\n")
512}
513
514#[cfg(test)]
515mod tests {
516
517    use super::*;
518    use crate::symbol::symbol;
519    use std::convert::TryFrom;
520    use std::fmt::Write;
521
522    // struct.Production
523
524    #[test]
525    fn from_string() {
526        let p_check = vec![
527            Production {
528                lhs: vec![symbol("S")],
529                rhs: vec![symbol("A"), symbol("B")],
530            },
531            Production {
532                lhs: vec![symbol("A")],
533                rhs: vec![symbol("a")],
534            },
535            Production {
536                lhs: vec![symbol("A")],
537                rhs: vec![symbol("B")],
538            },
539            Production {
540                lhs: vec![symbol("B")],
541                rhs: vec![symbol("b")],
542            },
543        ];
544
545        assert_eq!(
546            Production::from_string("S -> A B\nA -> a | B\nB -> b").unwrap(),
547            p_check,
548            "Parsed production rules are not those expected"
549        );
550    }
551
552    #[test]
553    fn from_string_error() {
554        let error = TokenizerError::ProductionNoRhs("S".to_string());
555        let result = Production::from_string("S ->\n -> a | B\nB -> b");
556
557        assert!(
558            result.is_err(),
559            "Production from string on test input should return error"
560        );
561        let e = result.unwrap_err();
562        assert_eq!(
563            e,
564            ProductionError::FormatError(error),
565            "Creation of productions from test input returned the wrong error"
566        );
567    }
568
569    #[test]
570    fn from_iter() {
571        let p_check = vec![
572            Production {
573                lhs: vec![symbol("S")],
574                rhs: vec![symbol("A"), symbol("B")],
575            },
576            Production {
577                lhs: vec![symbol("A")],
578                rhs: vec![symbol("a")],
579            },
580            Production {
581                lhs: vec![symbol("B")],
582                rhs: vec![symbol("a")],
583            },
584            Production {
585                lhs: vec![symbol("B")],
586                rhs: vec![symbol("b")],
587            },
588        ];
589
590        assert_eq!(
591            super::productions("S -> A B\nA -> a\nB -> a | b"),
592            p_check,
593            "Created production rules are not those expected"
594        );
595    }
596
597    #[test]
598    fn such_that() {
599        let filter = Production::such_that(ProductionPredicate::LhsEquals(vec![symbol("T")]));
600        let productions = Production::from_string("S -> A | B\nA -> a\nT -> t\nB -> B").unwrap();
601
602        let productions_iter = productions.clone();
603        let mut filtered = productions_iter.iter().filter(filter);
604
605        assert_eq!(
606            filtered.next(),
607            productions.get(3),
608            "Filtered productions on test input should return the T -> t production"
609        );
610        assert_eq!(
611            filtered.next(),
612            None,
613            "Filtered productions on test input should return no more productions"
614        );
615    }
616
617    #[test]
618    fn new() {
619        let p_check = Production {
620            lhs: vec![symbol("S")],
621            rhs: vec![symbol("A"), symbol("B")],
622        };
623
624        assert_eq!(
625            Production::new(p_check.lhs(), p_check.rhs()).unwrap(),
626            p_check,
627            "Created production rule is not the one expected"
628        );
629    }
630
631    #[test]
632    fn new_empty_side_lhs() {
633        let iter = vec![symbol("S")];
634
635        let result = Production::new(vec![], iter);
636        assert!(
637            result.is_err(),
638            "Creation of production rule should return an error"
639        );
640        let e = result.unwrap_err();
641        assert_eq!(
642            e,
643            ProductionError::NoLhs,
644            "Creation of production rule returned the wrong error"
645        );
646    }
647
648    #[test]
649    fn new_empty_side_rhs() {
650        let iter = vec![symbol("S")];
651
652        let result = Production::new(iter, vec![]);
653        assert!(
654            result.is_err(),
655            "Creation of production rule should return an error"
656        );
657        let e = result.unwrap_err();
658        assert_eq!(
659            e,
660            ProductionError::NoRhs,
661            "Creation of production rule returned the wrong error"
662        );
663    }
664
665    #[test]
666    fn new_from_string() {
667        let p_check = Production {
668            lhs: vec![symbol("S")],
669            rhs: vec![symbol("A"), symbol("B")],
670        };
671
672        assert_eq!(
673            Production::new_from_string(vec!["S"], vec!["A", "B"]).unwrap(),
674            p_check,
675            "Created production rule is not the one expected"
676        );
677    }
678
679    #[test]
680    fn new_from_string_error_lhs() {
681        let error = SymbolError::InvalidSymbol("\n".to_string());
682        let result = Production::new_from_string(vec!["\n"], vec!["A", "B"]);
683
684        assert!(
685            result.is_err(),
686            "Created production rule should return error"
687        );
688        let e = result.unwrap_err();
689        assert_eq!(
690            e,
691            ProductionError::SymbolError(error),
692            "Creation of production rule returned the wrong error"
693        );
694    }
695
696    #[test]
697    fn new_from_string_error_rhs() {
698        let error = SymbolError::InvalidSymbol("\n".to_string());
699        let result = Production::new_from_string(vec!["S"], vec!["\n"]);
700
701        assert!(
702            result.is_err(),
703            "Created production rule should return error"
704        );
705        let e = result.unwrap_err();
706        assert_eq!(
707            e,
708            ProductionError::SymbolError(error),
709            "Creation of production rule returned the wrong error"
710        );
711    }
712
713    #[test]
714    fn production_display() {
715        let mut buf = String::new();
716        let p = super::production("A", "B C");
717
718        let result = write!(buf, "{}", p);
719        assert!(result.is_ok());
720        assert_eq!(buf, "A -> B C")
721    }
722
723    #[test]
724    fn production_try_from() {
725        let result = Production::try_from("A -> B");
726        assert!(result.is_ok());
727    }
728
729    #[test]
730    fn production_try_from_multiple() {
731        let string = "A -> B\nA -> C";
732        let result = Production::try_from(string);
733        assert!(result.is_err());
734        let e = result.unwrap_err();
735        assert_eq!(
736            e,
737            ProductionError::FormatError(TokenizerError::ProductionMultiple(string.to_string()))
738        );
739    }
740
741    #[test]
742    fn production_try_from_error() {
743        let string = "A -> B -> C";
744        let result = Production::try_from(string);
745        assert!(result.is_err());
746        let e = result.unwrap_err();
747        assert_eq!(
748            e,
749            ProductionError::FormatError(TokenizerError::ProductionMultipleOneLine(0))
750        );
751    }
752
753    #[test]
754    fn production_try_from_no_productions() {
755        let result = Production::try_from("");
756        assert!(result.is_err());
757        let e = result.unwrap_err();
758        assert_eq!(
759            e,
760            ProductionError::FormatError(TokenizerError::ProductionEmpty("".to_string()))
761        );
762    }
763
764    // enum.ProductionError
765
766    #[test]
767    fn production_error_display_no_lhs() {
768        let mut buf = String::new();
769
770        let result = write!(buf, "{}", ProductionError::NoLhs);
771        assert!(result.is_ok());
772        assert_eq!(buf, "ProductionError: no lhs in production")
773    }
774
775    #[test]
776    fn production_error_display_no_rhs() {
777        let mut buf = String::new();
778
779        let result = write!(buf, "{}", ProductionError::NoRhs);
780        assert!(result.is_ok());
781        assert_eq!(buf, "ProductionError: no rhs in production")
782    }
783
784    #[test]
785    fn production_error_display_symbol_error() {
786        let mut buf = String::new();
787        let error = SymbolError::EmptySymbol;
788
789        let result = write!(buf, "{}", ProductionError::SymbolError(error.clone()));
790        assert!(result.is_ok());
791        assert_eq!(
792            buf,
793            format!("ProductionError: symbol error encountered = {}", error)
794        )
795    }
796
797    #[test]
798    fn production_error_display_format_error() {
799        let mut buf = String::new();
800        let error = TokenizerError::ProductionNoLhs;
801
802        let result = write!(buf, "{}", ProductionError::FormatError(error.clone()));
803        assert!(result.is_ok());
804        assert_eq!(
805            buf,
806            format!(
807                "ProductionError: bad formatted string encountered, error = {}",
808                error
809            )
810        )
811    }
812
813    #[test]
814    fn production_error_source() {
815        assert!(
816            ProductionError::FormatError(TokenizerError::ProductionNoLhs)
817                .source()
818                .is_some()
819        );
820        assert!(ProductionError::SymbolError(SymbolError::EmptySymbol)
821            .source()
822            .is_some());
823    }
824
825    #[test]
826    fn production_error_source_none() {
827        assert!(ProductionError::NoLhs.source().is_none());
828        assert!(ProductionError::NoRhs.source().is_none());
829    }
830
831    // enum.ProductionPredicate
832
833    #[test]
834    fn predicate_lhs_equals() {
835        let predicate = ProductionPredicate::LhsEquals(vec![symbol("T")]);
836
837        assert!(
838            predicate.test(&Production {
839                lhs: vec![symbol("T")],
840                rhs: vec![]
841            }),
842            "Predicate should return true"
843        );
844        assert!(
845            !predicate.test(&Production {
846                lhs: vec![symbol("F")],
847                rhs: vec![]
848            }),
849            "Predicate should return false"
850        );
851    }
852
853    #[test]
854    fn predicate_rhs_equals() {
855        let predicate = ProductionPredicate::RhsEquals(vec![symbol("T")]);
856
857        assert!(
858            predicate.test(&Production {
859                lhs: vec![],
860                rhs: vec![symbol("T")]
861            }),
862            "Predicate should return true"
863        );
864        assert!(
865            !predicate.test(&Production {
866                lhs: vec![],
867                rhs: vec![symbol("F")]
868            }),
869            "Predicate should return false"
870        );
871    }
872
873    #[test]
874    fn predicate_rhs_length_equals() {
875        let predicate = ProductionPredicate::RhsLengthEquals(2);
876
877        assert!(
878            predicate.test(&Production {
879                lhs: vec![],
880                rhs: vec![symbol("T1"), symbol("T2")]
881            }),
882            "Predicate should return true"
883        );
884        assert!(
885            !predicate.test(&Production {
886                lhs: vec![],
887                rhs: vec![symbol("F")]
888            }),
889            "Predicate should return false"
890        );
891    }
892
893    #[test]
894    fn predicate_rhs_is_suffix_of() {
895        let predicate = ProductionPredicate::RhsIsSuffixOf(vec![symbol("T2"), symbol("T3")]);
896
897        assert!(
898            predicate.test(&Production {
899                lhs: vec![],
900                rhs: vec![symbol("T1"), symbol("T2"), symbol("T3")]
901            }),
902            "Predicate should return true"
903        );
904        assert!(
905            !predicate.test(&Production {
906                lhs: vec![],
907                rhs: vec![symbol("F")]
908            }),
909            "Predicate should return false"
910        );
911    }
912
913    #[test]
914    fn production_predicate_display_lhs_equals() {
915        let mut buf = String::new();
916
917        let result = write!(
918            buf,
919            "{}",
920            ProductionPredicate::LhsEquals(vec![symbol("A"), symbol("B")])
921        );
922        assert!(result.is_ok());
923        assert_eq!(buf, format!("ProductionPredicate: lhs == {}", "A B"))
924    }
925
926    #[test]
927    fn production_predicate_display_rhs_equals() {
928        let mut buf = String::new();
929
930        let result = write!(
931            buf,
932            "{}",
933            ProductionPredicate::RhsEquals(vec![symbol("A"), symbol("B")])
934        );
935        assert!(result.is_ok());
936        assert_eq!(buf, format!("ProductionPredicate: rhs == {}", "A B"))
937    }
938
939    #[test]
940    fn production_predicate_display_rhs_is_suffix_of() {
941        let mut buf = String::new();
942
943        let result = write!(
944            buf,
945            "{}",
946            ProductionPredicate::RhsIsSuffixOf(vec![symbol("A"), symbol("B")])
947        );
948        assert!(result.is_ok());
949        assert_eq!(buf, format!("ProductionPredicate: rhs suffix == {}", "A B"))
950    }
951
952    #[test]
953    fn production_predicate_display_rhs_length_equals() {
954        let mut buf = String::new();
955
956        let result = write!(buf, "{}", ProductionPredicate::RhsLengthEquals(2));
957        assert!(result.is_ok());
958        assert_eq!(buf, format!("ProductionPredicate: rhs length == {}", 2))
959    }
960
961    // mod.production
962
963    #[test]
964    fn production() {
965        let p_check = Production {
966            lhs: vec![symbol("S")],
967            rhs: vec![symbol("A"), symbol("B")],
968        };
969
970        assert_eq!(
971            super::production("S", "A B"),
972            p_check,
973            "Created production rule is not the one expected"
974        );
975    }
976
977    #[test]
978    fn productions() {
979        let p_check = vec![
980            Production {
981                lhs: vec![symbol("S")],
982                rhs: vec![symbol("A"), symbol("B")],
983            },
984            Production {
985                lhs: vec![symbol("A")],
986                rhs: vec![symbol("a")],
987            },
988        ];
989
990        assert_eq!(
991            super::productions("S -> A B\nA -> a"),
992            p_check,
993            "Created production rules are not those expected"
994        );
995    }
996
997    #[test]
998    fn production_table() {
999        let p = super::productions(
1000            "
1001            A -> B C
1002            B -> b
1003        ",
1004        );
1005        let result = super::production_table(p);
1006
1007        assert_eq!(result, "0: A -> B C\n1: B -> b");
1008    }
1009}