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