1 use grammar::repr::*;
2 use lr1::build_states;
3 use lr1::tls::Lr1Tls;
4 use string_cache::DefaultAtom as Atom;
5 use test_util::normalized_grammar;
6 use tls::Tls;
7 
8 use super::{ConflictClassification, ErrorReportingCx};
9 
nt(t: &str) -> NonterminalString10 fn nt(t: &str) -> NonterminalString {
11     NonterminalString(Atom::from(t))
12 }
13 
14 #[test]
priority_conflict()15 fn priority_conflict() {
16     let _tls = Tls::test();
17     let grammar = normalized_grammar(
18         r#"
19 grammar;
20 pub Ty: () = {
21     "int" => (),
22     "bool" => (),
23     <t1:Ty> "->" <t2:Ty> => (),
24 };
25 "#,
26     );
27     let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
28     let err = build_states(&grammar, nt("Ty")).unwrap_err();
29     let mut cx = ErrorReportingCx::new(&grammar, &err.states, &err.conflicts);
30     let conflicts = super::token_conflicts(&err.conflicts);
31     let conflict = &conflicts[0];
32 
33     println!("conflict={:?}", conflict);
34 
35     match cx.classify(conflict) {
36         ConflictClassification::Precedence {
37             shift,
38             reduce,
39             nonterminal,
40         } => {
41             println!(
42                 "shift={:#?}, reduce={:#?}, nonterminal={:?}",
43                 shift, reduce, nonterminal
44             );
45             assert_eq!(shift.symbols.len(), 5); // Ty -> Ty -> Ty
46             assert_eq!(shift.cursor, 3); // Ty -> Ty -> Ty
47             assert_eq!(shift.symbols, reduce.symbols);
48             assert_eq!(shift.cursor, reduce.cursor);
49             assert_eq!(nonterminal, nt("Ty"));
50         }
51         r => panic!("wrong classification {:#?}", r),
52     }
53 }
54 
55 #[test]
expr_braced_conflict()56 fn expr_braced_conflict() {
57     let _tls = Tls::test();
58     let grammar = normalized_grammar(
59         r#"
60 grammar;
61 pub Expr: () = {
62     "Id" => (),
63     "Id" "{" "}" => (),
64     "Expr" "+" "Id" => (),
65     "if" Expr "{" "}" => (),
66 };
67 "#,
68     );
69     let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
70     let err = build_states(&grammar, nt("Expr")).unwrap_err();
71     let mut cx = ErrorReportingCx::new(&grammar, &err.states, &err.conflicts);
72     let conflicts = super::token_conflicts(&err.conflicts);
73     let conflict = &conflicts[0];
74 
75     println!("conflict={:?}", conflict);
76 
77     match cx.classify(conflict) {
78         ConflictClassification::InsufficientLookahead { .. } => {}
79         r => panic!("wrong classification {:#?}", r),
80     }
81 }
82 
83 #[test]
suggest_question_conflict()84 fn suggest_question_conflict() {
85     let _tls = Tls::test();
86     let grammar = normalized_grammar(
87         r#"
88         grammar;
89 
90         pub E: () = {
91             "L",
92             "&" OPT_L E
93         };
94 
95         OPT_L: () = {
96             (),
97             "L"
98         };
99 "#,
100     );
101     let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
102     let err = build_states(&grammar, nt("E")).unwrap_err();
103     let mut cx = ErrorReportingCx::new(&grammar, &err.states, &err.conflicts);
104     let conflicts = super::token_conflicts(&err.conflicts);
105     let conflict = &conflicts[0];
106 
107     println!("conflict={:?}", conflict);
108 
109     match cx.classify(conflict) {
110         ConflictClassification::SuggestQuestion {
111             shift: _,
112             reduce: _,
113             nonterminal,
114             symbol,
115         } => {
116             assert_eq!(nonterminal, nt("OPT_L"));
117             assert_eq!(
118                 symbol,
119                 Symbol::Terminal(TerminalString::quoted(Atom::from("L")))
120             );
121         }
122         r => panic!("wrong classification {:#?}", r),
123     }
124 }
125 
126 #[test]
suggest_inline_conflict()127 fn suggest_inline_conflict() {
128     let _tls = Tls::test();
129     let grammar = normalized_grammar(
130         r##"
131 grammar;
132 
133 pub ImportDecl: () = {
134     "import" <Path> ";" => (),
135     "import" <Path> "." "*" ";" => (),
136 };
137 
138 Path: () = {
139     <head: Ident> <tail: ("." <Ident>)*> => ()
140 };
141 
142 Ident = r#"[a-zA-Z][a-zA-Z0-9]*"#;
143 "##,
144     );
145     let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
146     let err = build_states(&grammar, nt("ImportDecl")).unwrap_err();
147     let mut cx = ErrorReportingCx::new(&grammar, &err.states, &err.conflicts);
148     let conflicts = super::token_conflicts(&err.conflicts);
149     let conflict = &conflicts[0];
150 
151     println!("conflict={:?}", conflict);
152 
153     match cx.classify(conflict) {
154         ConflictClassification::SuggestInline {
155             shift: _,
156             reduce: _,
157             nonterminal,
158         } => {
159             assert_eq!(nonterminal, nt("Path"));
160         }
161         r => panic!("wrong classification {:#?}", r),
162     }
163 }
164 
165 /// This example used to cause an out-of-bounds error.
166 #[test]
issue_249()167 fn issue_249() {
168     let _tls = Tls::test();
169     let grammar = normalized_grammar(
170         r##"
171 grammar;
172 
173 pub Func = StructDecl* VarDecl*;
174 StructDecl = "<" StructParameter* ">";
175 StructParameter = "may_dangle"?;
176 VarDecl = "let";
177 "##,
178     );
179     let _lr1_tls = Lr1Tls::install(grammar.terminals.clone());
180     let err = build_states(&grammar, nt("Func")).unwrap_err();
181     let mut cx = ErrorReportingCx::new(&grammar, &err.states, &err.conflicts);
182     let conflicts = super::token_conflicts(&err.conflicts);
183     for conflict in &conflicts {
184         println!("conflict={:?}", conflict);
185         cx.classify(conflict);
186     }
187 }
188