cfgrammar/yacc/
ast.rs

1use std::{
2    collections::{HashMap, HashSet},
3    error::Error,
4    fmt,
5    str::FromStr,
6};
7
8use indexmap::{IndexMap, IndexSet};
9
10use super::{
11    Precedence, YaccGrammarError, YaccGrammarErrorKind, YaccGrammarWarning, YaccGrammarWarningKind,
12    YaccKind, parser::YaccParser,
13};
14
15use crate::{
16    Span,
17    header::{GrmtoolsSectionParser, HeaderError, HeaderErrorKind, HeaderValue},
18};
19
20/// Any error from the Yacc parser returns an instance of this struct.
21#[derive(Debug, PartialEq, Eq, Clone)]
22pub struct ASTModificationError {
23    kind: YaccGrammarErrorKind,
24}
25
26impl Error for ASTModificationError {}
27
28impl fmt::Display for ASTModificationError {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        write!(f, "{}", self.kind)
31    }
32}
33
34/// Contains a `GrammarAST` structure produced from a grammar source file.
35/// As well as any errors which occurred during the construction of the AST.
36#[derive(Debug, Clone)]
37#[cfg_attr(test, derive(PartialEq))]
38pub struct ASTWithValidityInfo {
39    yacc_kind: YaccKind,
40    ast: GrammarAST,
41    errs: Vec<YaccGrammarError>,
42}
43
44impl ASTWithValidityInfo {
45    /// Parses a source file into an AST, returning an ast and any errors that were
46    /// encountered during the construction of it.  The `ASTWithValidityInfo` can be
47    /// then unused to construct a `YaccGrammar`, which will either produce an
48    /// `Ok(YaccGrammar)` or an `Err` which includes these errors.
49    ///
50    /// This function ignores the `%grmtools` section entirely, assuming that the caller has
51    /// already extracted the `YaccKind` if any.
52    pub fn new(yacc_kind: YaccKind, s: &str) -> Self {
53        let mut errs = Vec::new();
54        let ast = {
55            let mut yp = YaccParser::new(yacc_kind, s);
56            yp.parse().map_err(|e| errs.extend(e)).ok();
57            let mut ast = yp.build();
58            ast.complete_and_validate().map_err(|e| errs.push(e)).ok();
59            ast
60        };
61        ASTWithValidityInfo {
62            ast,
63            errs,
64            yacc_kind,
65        }
66    }
67
68    /// Returns a `GrammarAST` constructed as the result of parsing a source file.
69    /// When errors have occurred and `is_valid` returns false, this AST is the
70    /// subset of the source file which parsed correctly while not encountering
71    /// any errors. As such even when an AST is not valid, it will return an AST.
72    pub fn ast(&self) -> &GrammarAST {
73        &self.ast
74    }
75
76    /// Returns whether any errors where encountered during the
77    /// parsing and validation of the AST during it's construction.
78    pub fn is_valid(&self) -> bool {
79        self.errors().is_empty()
80    }
81
82    /// Returns the `YaccKind` that was used to parse the `GrammarAST`.
83    pub fn yacc_kind(&self) -> YaccKind {
84        self.yacc_kind
85    }
86
87    /// Returns all errors which were encountered during AST construction.
88    pub fn errors(&self) -> &[YaccGrammarError] {
89        self.errs.as_slice()
90    }
91
92    pub fn clone_and_change_start_rule(&self, rule: Rule) -> Result<Self, ASTModificationError> {
93        if self.ast.get_rule(&rule.name.0).is_some() {
94            let mut ret = self.clone();
95            // The `Span`of the `start` field and the `name` field typically differ
96            // in that `start` is the parameter of a `%start` declaration, while
97            // `name` refers to the definition site of the rule itself.
98            //
99            // Lacking a better `Span` we use the definition site, for the `%start` rule here.
100            ret.ast.start = Some(rule.name);
101            Ok(ret)
102        } else {
103            Err(ASTModificationError {
104                kind: YaccGrammarErrorKind::InvalidStartRule(rule.name.0),
105            })
106        }
107    }
108}
109
110impl FromStr for ASTWithValidityInfo {
111    type Err = Vec<YaccGrammarError>;
112    /// Parses the `%grmtools section` expecting it to contain a `yacckind` entry.
113    fn from_str(src: &str) -> Result<Self, Vec<YaccGrammarError>> {
114        let mut errs = Vec::new();
115        let (header, _) = GrmtoolsSectionParser::new(src, true)
116            .parse()
117            .map_err(|mut errs| errs.drain(..).map(|e| e.into()).collect::<Vec<_>>())?;
118        if let Some(HeaderValue(_, yk_val)) = header.get("yacckind") {
119            let yacc_kind = YaccKind::try_from(yk_val).map_err(|e| vec![e.into()])?;
120            let ast = {
121                // We don't want to strip off the header so that span's will be correct.
122                let mut yp = YaccParser::new(yacc_kind, src);
123                yp.parse().map_err(|e| errs.extend(e)).ok();
124                let mut ast = yp.build();
125                ast.complete_and_validate().map_err(|e| errs.push(e)).ok();
126                ast
127            };
128            Ok(ASTWithValidityInfo {
129                ast,
130                errs,
131                yacc_kind,
132            })
133        } else {
134            Err(vec![
135                HeaderError {
136                    kind: HeaderErrorKind::InvalidEntry("yacckind"),
137                    locations: vec![Span::new(0, 0)],
138                }
139                .into(),
140            ])
141        }
142    }
143}
144
145/// An AST representing a grammar. This is built up gradually: when it is finished, the
146/// `complete_and_validate` must be called exactly once in order to finish the set-up. At that
147/// point, any further mutations made to the struct lead to undefined behaviour.
148#[derive(Debug, Clone)]
149#[cfg_attr(test, derive(PartialEq))]
150#[non_exhaustive]
151pub struct GrammarAST {
152    pub start: Option<(String, Span)>,
153    // map from a rule name to indexes into `prods`
154    pub rules: IndexMap<String, Rule>,
155    pub prods: Vec<Production>,
156    // A set of indexes into `tokens` for all tokens in `%token` directives.
157    // e.g. given a `%token a` and a token "b" not specified in any `%token` directive
158    // we have `self.tokens.get_index_of("a") ∈ self.token_directives`. However for
159    // token "b" we have `self.tokens.get_index_of("b") ∉ self.token_directives`.
160    pub token_directives: HashSet<usize>,
161    pub tokens: IndexSet<String>,
162    pub spans: Vec<Span>,
163    pub precs: HashMap<String, (Precedence, Span)>,
164    pub avoid_insert: Option<HashMap<String, Span>>,
165    pub implicit_tokens: Option<HashMap<String, Span>>,
166    // Error pretty-printers,
167    // The first span of the value is the span of the key,
168    // The second span in the value, is the span of the values string.
169    pub epp: HashMap<String, (Span, (String, Span))>,
170    pub expect: Option<(usize, Span)>,
171    pub expectrr: Option<(usize, Span)>,
172    pub parse_param: Option<(String, String)>,
173    pub programs: Option<String>,
174    // The set of symbol names that, if unused in a
175    // grammar, will not cause a warning or error.
176    pub expect_unused: Vec<Symbol>,
177}
178
179#[derive(Debug, Clone)]
180#[cfg_attr(test, derive(Eq, PartialEq))]
181pub struct Rule {
182    pub name: (String, Span),
183    pub pidxs: Vec<usize>, // index into GrammarAST.prod
184    pub actiont: Option<String>,
185}
186
187#[derive(Debug, Clone)]
188#[cfg_attr(test, derive(Eq, PartialEq))]
189pub struct Production {
190    pub symbols: Vec<Symbol>,
191    pub precedence: Option<String>,
192    pub action: Option<String>,
193    pub prod_span: Span,
194}
195
196#[derive(Clone, Debug)]
197#[cfg_attr(test, derive(Eq, PartialEq))]
198pub enum Symbol {
199    Rule(String, Span),
200    Token(String, Span),
201}
202
203/// Specifies an index into a `GrammarAst.tokens` or a `GrammarAST.rules`.
204/// Unlike `cfgrammar::Symbol` it is not parameterized by a `StorageT`.
205#[derive(Eq, PartialEq, Debug, Copy, Clone)]
206pub(crate) enum SymbolIdx {
207    Rule(usize),
208    Token(usize),
209}
210
211impl SymbolIdx {
212    pub(crate) fn symbol(self, ast: &GrammarAST) -> Symbol {
213        match self {
214            SymbolIdx::Rule(idx) => {
215                let (rule_name, rule_span) = &ast.rules[idx].name;
216                Symbol::Rule(rule_name.clone(), *rule_span)
217            }
218            SymbolIdx::Token(idx) => {
219                let tok = &ast.tokens[idx];
220                Symbol::Token(tok.clone(), ast.spans[idx])
221            }
222        }
223    }
224}
225impl fmt::Display for Symbol {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        match *self {
228            Symbol::Rule(ref s, _) => write!(f, "{}", s),
229            Symbol::Token(ref s, _) => write!(f, "{}", s),
230        }
231    }
232}
233
234impl GrammarAST {
235    pub(crate) fn new() -> GrammarAST {
236        GrammarAST {
237            start: None,
238            rules: IndexMap::new(), // Using an IndexMap means that we retain the order
239            // of rules as they're found in the input file.
240            prods: Vec::new(),
241            spans: Vec::new(),
242            tokens: IndexSet::new(),
243            token_directives: HashSet::new(),
244            precs: HashMap::new(),
245            avoid_insert: None,
246            implicit_tokens: None,
247            epp: HashMap::new(),
248            expect: None,
249            expectrr: None,
250            parse_param: None,
251            programs: None,
252            expect_unused: Vec::new(),
253        }
254    }
255
256    pub fn add_rule(&mut self, (name, name_span): (String, Span), actiont: Option<String>) {
257        self.rules.insert(
258            name.clone(),
259            Rule {
260                name: (name, name_span),
261                pidxs: Vec::new(),
262                actiont,
263            },
264        );
265    }
266
267    pub fn add_prod(
268        &mut self,
269        rule_name: String,
270        symbols: Vec<Symbol>,
271        precedence: Option<String>,
272        action: Option<String>,
273        prod_span: Span,
274    ) {
275        self.rules[&rule_name].pidxs.push(self.prods.len());
276        self.prods.push(Production {
277            symbols,
278            precedence,
279            action,
280            prod_span,
281        });
282    }
283
284    #[deprecated(since = "0.10.2", note = "Please use set_programs instead")]
285    pub fn add_programs(&mut self, s: String) {
286        self.set_programs(s);
287    }
288
289    pub fn set_programs(&mut self, s: String) {
290        self.programs = Some(s)
291    }
292
293    pub fn get_rule(&self, key: &str) -> Option<&Rule> {
294        self.rules.get(key)
295    }
296
297    pub fn has_token(&self, s: &str) -> bool {
298        self.tokens.contains(s)
299    }
300
301    /// After the AST has been populated, perform any final operations, and validate the grammar
302    /// checking that:
303    ///   1) The start rule references a rule in the grammar
304    ///   2) Every rule reference references a rule in the grammar
305    ///   3) Every token reference references a declared token
306    ///   4) If a production has a precedence token, then it references a declared token
307    ///   5) Every token declared with %epp matches a known token
308    ///
309    /// If the validation succeeds, None is returned.
310    pub(crate) fn complete_and_validate(&mut self) -> Result<(), YaccGrammarError> {
311        match self.start {
312            None => {
313                return Err(YaccGrammarError {
314                    kind: YaccGrammarErrorKind::NoStartRule,
315                    spans: vec![Span::new(0, 0)],
316                });
317            }
318            Some((ref s, span)) => {
319                if !self.rules.contains_key(s) {
320                    return Err(YaccGrammarError {
321                        kind: YaccGrammarErrorKind::InvalidStartRule(s.clone()),
322                        spans: vec![span],
323                    });
324                }
325            }
326        }
327        for rule in self.rules.values() {
328            for &pidx in &rule.pidxs {
329                let prod = &self.prods[pidx];
330                if let Some(ref n) = prod.precedence {
331                    if !self.tokens.contains(n) {
332                        return Err(YaccGrammarError {
333                            kind: YaccGrammarErrorKind::UnknownToken(n.clone()),
334                            spans: vec![Span::new(0, 0)],
335                        });
336                    }
337                    if !self.precs.contains_key(n) {
338                        return Err(YaccGrammarError {
339                            kind: YaccGrammarErrorKind::NoPrecForToken(n.clone()),
340                            spans: vec![Span::new(0, 0)],
341                        });
342                    }
343                }
344                for sym in &prod.symbols {
345                    match *sym {
346                        Symbol::Rule(ref name, span) => {
347                            if !self.rules.contains_key(name) {
348                                return Err(YaccGrammarError {
349                                    kind: YaccGrammarErrorKind::UnknownRuleRef(name.clone()),
350                                    spans: vec![span],
351                                });
352                            }
353                        }
354                        Symbol::Token(ref name, span) => {
355                            if !self.tokens.contains(name) {
356                                return Err(YaccGrammarError {
357                                    kind: YaccGrammarErrorKind::UnknownToken(name.clone()),
358                                    spans: vec![span],
359                                });
360                            }
361                        }
362                    }
363                }
364            }
365        }
366
367        for (k, (sp, _)) in self.epp.iter() {
368            if self.tokens.contains(k) {
369                continue;
370            }
371            if let Some(ref it) = self.implicit_tokens {
372                if it.contains_key(k) {
373                    continue;
374                }
375            }
376            return Err(YaccGrammarError {
377                kind: YaccGrammarErrorKind::UnknownEPP(k.clone()),
378                spans: vec![*sp],
379            });
380        }
381
382        for sym in &self.expect_unused {
383            match sym {
384                Symbol::Rule(sym_name, sym_span) => {
385                    if self.get_rule(sym_name).is_none() {
386                        return Err(YaccGrammarError {
387                            kind: YaccGrammarErrorKind::UnknownRuleRef(sym_name.clone()),
388                            spans: vec![*sym_span],
389                        });
390                    }
391                }
392                Symbol::Token(sym_name, sym_span) => {
393                    if !self.has_token(sym_name) {
394                        return Err(YaccGrammarError {
395                            kind: YaccGrammarErrorKind::UnknownToken(sym_name.clone()),
396                            spans: vec![*sym_span],
397                        });
398                    }
399                }
400            }
401        }
402        Ok(())
403    }
404
405    pub fn warnings(&self) -> Vec<YaccGrammarWarning> {
406        self.unused_symbols()
407            .map(|symidx| {
408                let (kind, span) = match symidx.symbol(self) {
409                    Symbol::Rule(_, span) => (YaccGrammarWarningKind::UnusedRule, span),
410                    Symbol::Token(_, span) => (YaccGrammarWarningKind::UnusedToken, span),
411                };
412                YaccGrammarWarning {
413                    kind,
414                    spans: vec![span],
415                }
416            })
417            .collect()
418    }
419
420    /// Return the indices of unexpectedly unused rules (relative to ast.rules)
421    /// and tokens (relative to ast.tokens) as `SymbolIdx`s.
422    pub(crate) fn unused_symbols(&self) -> impl Iterator<Item = SymbolIdx> + '_ {
423        let start_rule_name = self.start.as_ref().map(|(name, _)| name.clone());
424        let start_rule = self
425            .rules
426            .iter()
427            .find(|(rule_name, _)| start_rule_name.as_ref() == Some(rule_name));
428        let mut seen_rules = HashSet::new();
429        let mut seen_tokens = HashSet::new();
430        let mut expected_unused_tokens = HashSet::new();
431        let mut expected_unused_rules = HashSet::new();
432        for sym in &self.expect_unused {
433            match sym {
434                Symbol::Rule(sym_name, _) => {
435                    expected_unused_rules.insert(sym_name);
436                }
437                Symbol::Token(sym_name, _) => {
438                    expected_unused_tokens.insert(sym_name);
439                }
440            }
441        }
442        if let Some(implicit_tokens) = self.implicit_tokens.as_ref() {
443            expected_unused_tokens.extend(implicit_tokens.keys())
444        }
445        if let Some((start_name, start_rule)) = start_rule {
446            let mut todo = Vec::new();
447            todo.extend(start_rule.pidxs.iter().copied());
448            seen_rules.insert(start_name);
449
450            while let Some(pidx) = todo.pop() {
451                let prod = &self.prods[pidx];
452                for sym in &prod.symbols {
453                    match sym {
454                        Symbol::Rule(name, _) => {
455                            if seen_rules.insert(name) {
456                                if let Some(rule) = self.rules.get(name) {
457                                    todo.extend(&rule.pidxs);
458                                }
459                            }
460                        }
461                        Symbol::Token(name, _) => {
462                            seen_tokens.insert(name);
463                        }
464                    };
465                }
466            }
467        }
468        self.rules
469            .iter()
470            .enumerate()
471            .filter_map(move |(rule_id, (rule_name, _))| {
472                if expected_unused_rules.contains(rule_name) || seen_rules.contains(rule_name) {
473                    None
474                } else {
475                    Some(SymbolIdx::Rule(rule_id))
476                }
477            })
478            .chain(
479                self.tokens
480                    .iter()
481                    .enumerate()
482                    .filter_map(move |(tok_idx, tok)| {
483                        if expected_unused_tokens.contains(tok) || seen_tokens.contains(tok) {
484                            None
485                        } else {
486                            Some(SymbolIdx::Token(tok_idx))
487                        }
488                    }),
489            )
490    }
491}
492
493#[cfg(test)]
494mod test {
495    use super::{
496        super::{AssocKind, Precedence},
497        GrammarAST, Span, Symbol, YaccGrammarError, YaccGrammarErrorKind,
498    };
499
500    fn rule(n: &str) -> Symbol {
501        Symbol::Rule(n.to_string(), Span::new(0, 0))
502    }
503
504    fn token(n: &str) -> Symbol {
505        Symbol::Token(n.to_string(), Span::new(0, 0))
506    }
507
508    #[test]
509    fn test_empty_grammar() {
510        let mut grm = GrammarAST::new();
511        match grm.complete_and_validate() {
512            Err(YaccGrammarError {
513                kind: YaccGrammarErrorKind::NoStartRule,
514                ..
515            }) => (),
516            _ => panic!("Validation error"),
517        }
518    }
519
520    #[test]
521    fn test_invalid_start_rule() {
522        let mut grm = GrammarAST::new();
523        let empty_span = Span::new(0, 0);
524        grm.start = Some(("A".to_string(), empty_span));
525        grm.add_rule(("B".to_string(), empty_span), None);
526        grm.add_prod("B".to_string(), vec![], None, None, empty_span);
527        match grm.complete_and_validate() {
528            Err(YaccGrammarError {
529                kind: YaccGrammarErrorKind::InvalidStartRule(_),
530                ..
531            }) => (),
532            _ => panic!("Validation error"),
533        }
534    }
535
536    #[test]
537    fn test_valid_start_rule() {
538        let mut grm = GrammarAST::new();
539        let empty_span = Span::new(0, 0);
540        grm.start = Some(("A".to_string(), empty_span));
541        grm.add_rule(("A".to_string(), empty_span), None);
542        grm.add_prod("A".to_string(), vec![], None, None, empty_span);
543        assert!(grm.complete_and_validate().is_ok());
544    }
545
546    #[test]
547    fn test_valid_rule_ref() {
548        let mut grm = GrammarAST::new();
549        let empty_span = Span::new(0, 0);
550        grm.start = Some(("A".to_string(), empty_span));
551        grm.add_rule(("A".to_string(), empty_span), None);
552        grm.add_rule(("B".to_string(), empty_span), None);
553        grm.add_prod("A".to_string(), vec![rule("B")], None, None, empty_span);
554        grm.add_prod("B".to_string(), vec![], None, None, empty_span);
555        assert!(grm.complete_and_validate().is_ok());
556    }
557
558    #[test]
559    fn test_invalid_rule_ref() {
560        let mut grm = GrammarAST::new();
561        let empty_span = Span::new(0, 0);
562        grm.start = Some(("A".to_string(), empty_span));
563        grm.add_rule(("A".to_string(), empty_span), None);
564        grm.add_prod("A".to_string(), vec![rule("B")], None, None, empty_span);
565        match grm.complete_and_validate() {
566            Err(YaccGrammarError {
567                kind: YaccGrammarErrorKind::UnknownRuleRef(_),
568                ..
569            }) => (),
570            _ => panic!("Validation error"),
571        }
572    }
573
574    #[test]
575    fn test_valid_token_ref() {
576        let mut grm = GrammarAST::new();
577        let empty_span = Span::new(0, 0);
578        grm.tokens.insert("b".to_string());
579        grm.start = Some(("A".to_string(), empty_span));
580        grm.add_rule(("A".to_string(), empty_span), None);
581        grm.add_prod("A".to_string(), vec![token("b")], None, None, empty_span);
582        assert!(grm.complete_and_validate().is_ok());
583    }
584
585    #[test]
586    fn test_redefine_rules_as_tokens() {
587        // for now we won't support the YACC feature that allows
588        // to redefine rules as tokens by adding them to '%token'
589        let mut grm = GrammarAST::new();
590        let empty_span = Span::new(0, 0);
591        grm.tokens.insert("b".to_string());
592        grm.start = Some(("A".to_string(), empty_span));
593        grm.add_rule(("A".to_string(), empty_span), None);
594        grm.add_prod("A".to_string(), vec![rule("b")], None, None, empty_span);
595        assert!(grm.complete_and_validate().is_err());
596    }
597
598    #[test]
599    fn test_invalid_token_ref() {
600        let mut grm = GrammarAST::new();
601        let empty_span = Span::new(0, 0);
602        grm.start = Some(("A".to_string(), empty_span));
603        grm.add_rule(("A".to_string(), empty_span), None);
604        grm.add_prod("A".to_string(), vec![token("b")], None, None, empty_span);
605        match grm.complete_and_validate() {
606            Err(YaccGrammarError {
607                kind: YaccGrammarErrorKind::UnknownToken(_),
608                ..
609            }) => (),
610            _ => panic!("Validation error"),
611        }
612    }
613
614    #[test]
615    fn test_invalid_rule_forgotten_token() {
616        let mut grm = GrammarAST::new();
617        let empty_span = Span::new(0, 0);
618        grm.start = Some(("A".to_string(), empty_span));
619        grm.add_rule(("A".to_string(), empty_span), None);
620        grm.add_prod(
621            "A".to_string(),
622            vec![rule("b"), token("b")],
623            None,
624            None,
625            Span::new(0, 2),
626        );
627        match grm.complete_and_validate() {
628            Err(YaccGrammarError {
629                kind: YaccGrammarErrorKind::UnknownRuleRef(_),
630                ..
631            }) => (),
632            _ => panic!("Validation error"),
633        }
634    }
635
636    #[test]
637    fn test_invalid_epp() {
638        let mut grm = GrammarAST::new();
639        let empty_span = Span::new(2, 3);
640        grm.start = Some(("A".to_string(), empty_span));
641        grm.add_rule(("A".to_string(), empty_span), None);
642        grm.add_prod("A".to_string(), vec![], None, None, empty_span);
643        grm.epp
644            .insert("k".to_owned(), (empty_span, ("v".to_owned(), empty_span)));
645        match grm.complete_and_validate() {
646            Err(YaccGrammarError {
647                kind: YaccGrammarErrorKind::UnknownEPP(_),
648                spans,
649            }) if spans.len() == 1 && spans[0] == Span::new(2, 3) => (),
650            _ => panic!("Validation error"),
651        }
652    }
653
654    #[test]
655    fn test_precedence_override() {
656        let mut grm = GrammarAST::new();
657        let empty_span = Span::new(0, 0);
658        grm.precs.insert(
659            "b".to_string(),
660            (
661                Precedence {
662                    level: 1,
663                    kind: AssocKind::Left,
664                },
665                Span::new(0, 0),
666            ),
667        );
668        grm.start = Some(("A".to_string(), empty_span));
669        grm.tokens.insert("b".to_string());
670        grm.add_rule(("A".to_string(), empty_span), None);
671        grm.add_prod(
672            "A".to_string(),
673            vec![token("b")],
674            Some("b".to_string()),
675            None,
676            empty_span,
677        );
678        assert!(grm.complete_and_validate().is_ok());
679    }
680
681    #[test]
682    fn test_invalid_precedence_override() {
683        let mut grm = GrammarAST::new();
684        let empty_span = Span::new(0, 0);
685        grm.start = Some(("A".to_string(), empty_span));
686        grm.add_rule(("A".to_string(), empty_span), None);
687        grm.add_prod(
688            "A".to_string(),
689            vec![token("b")],
690            Some("b".to_string()),
691            None,
692            empty_span,
693        );
694        match grm.complete_and_validate() {
695            Err(YaccGrammarError {
696                kind: YaccGrammarErrorKind::UnknownToken(_),
697                ..
698            }) => (),
699            _ => panic!("Validation error"),
700        }
701        grm.tokens.insert("b".to_string());
702        match grm.complete_and_validate() {
703            Err(YaccGrammarError {
704                kind: YaccGrammarErrorKind::NoPrecForToken(_),
705                ..
706            }) => (),
707            _ => panic!("Validation error"),
708        }
709    }
710
711    #[test]
712    fn test_ast_unused_symbols() {
713        let mut grm = GrammarAST::new();
714        let empty_span = Span::new(0, 0);
715        grm.start = Some(("A".to_string(), empty_span));
716        grm.add_rule(("A".to_string(), empty_span), None);
717        grm.add_prod("A".to_string(), vec![], None, None, empty_span);
718        grm.tokens.insert("b".to_string());
719        grm.spans.push(Span::new(4, 5));
720        grm.add_rule(("B".to_string(), Span::new(1, 2)), None);
721        grm.add_prod("B".to_string(), vec![token("b")], None, None, empty_span);
722
723        assert_eq!(
724            grm.unused_symbols()
725                .map(|sym_idx| sym_idx.symbol(&grm))
726                .collect::<Vec<Symbol>>()
727                .as_slice(),
728            &[
729                Symbol::Rule("B".to_string(), Span::new(1, 2)),
730                Symbol::Token("b".to_string(), Span::new(4, 5))
731            ]
732        )
733    }
734
735    #[test]
736    fn token_rule_confusion_issue_557() {
737        use super::*;
738        use crate::yacc::*;
739        let ast_validity = ASTWithValidityInfo::new(
740            YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
741            r#"
742            %start start
743            %%
744            start: "a" a;
745            a: "c";"#,
746        );
747        assert!(
748            ast_validity.ast().prods[0]
749                .symbols
750                .contains(&Symbol::Rule("a".to_string(), Span::new(64, 65)))
751        );
752        let ast_validity = ASTWithValidityInfo::new(
753            YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
754            r#"
755            %start start
756            %%
757            start: "a" x;
758            x: "c";"#,
759        );
760        assert!(
761            ast_validity.ast().prods[0]
762                .symbols
763                .contains(&Symbol::Rule("x".to_string(), Span::new(64, 65)))
764        );
765        let ast_validity = ASTWithValidityInfo::new(
766            YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
767            r#"
768        %start start
769        %token a
770        %%
771        start: "a" a;
772        "#,
773        );
774        assert_eq!(
775            ast_validity.ast().prods[0].symbols,
776            [
777                Symbol::Token("a".to_string(), Span::new(66, 67)),
778                Symbol::Token("a".to_string(), Span::new(69, 70))
779            ]
780        );
781    }
782
783    #[test]
784    fn test_token_directives() {
785        use super::*;
786        use crate::yacc::*;
787
788        // Testing that `%token a` after `%left "a"` still ends up in
789        let ast_validity = ASTWithValidityInfo::new(
790            YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
791            r#"
792                %left "a"
793                %token a
794                %start start
795                %%
796                start: "a" a "b";
797                "#,
798        );
799        assert!(
800            ast_validity
801                .ast()
802                .token_directives
803                .contains(&ast_validity.ast().tokens.get_index_of("a").unwrap())
804        );
805        assert!(
806            !ast_validity
807                .ast()
808                .token_directives
809                .contains(&ast_validity.ast().tokens.get_index_of("b").unwrap())
810        );
811    }
812
813    #[test]
814    fn clone_ast_changing_start_rule() {
815        use super::*;
816        use crate::yacc::*;
817        let y_src = r#"
818        %start AStart
819        %token A B C
820        %%
821        AStart: A ':' BStart ';';
822        BStart: B ',' C | C ',' B;
823        "#;
824
825        let astart_ast_validity =
826            ASTWithValidityInfo::new(YaccKind::Original(YaccOriginalActionKind::NoAction), &y_src);
827        let bstart_rule = astart_ast_validity.ast().get_rule("BStart").unwrap();
828        let bstart_ast_validity = astart_ast_validity
829            .clone_and_change_start_rule(bstart_rule.clone())
830            .unwrap();
831        assert!(astart_ast_validity.is_valid());
832        assert!(bstart_ast_validity.is_valid());
833        assert_eq!(
834            bstart_ast_validity.ast().start.as_ref(),
835            Some(&bstart_rule.name)
836        );
837    }
838}