1 use grammar::parse_tree::Grammar;
2 use lexer::dfa::interpret;
3 use normalize::resolve::resolve;
4 use normalize::NormResult;
5 use parser;
6 use test_util;
7 
validate_grammar(grammar: &str) -> NormResult<Grammar>8 fn validate_grammar(grammar: &str) -> NormResult<Grammar> {
9     let parsed_grammar = parser::parse_grammar(&grammar).expect("parse grammar");
10     let parsed_grammar = resolve(parsed_grammar).expect("resolve");
11     super::validate(parsed_grammar)
12 }
13 
check_err(expected_err: &str, grammar: &str, span: &str)14 fn check_err(expected_err: &str, grammar: &str, span: &str) {
15     let err = validate_grammar(&grammar).unwrap_err();
16     test_util::check_norm_err(expected_err, span, err);
17 }
18 
check_intern_token(grammar: &str, expected_tokens: Vec<(&'static str, &'static str)>)19 fn check_intern_token(grammar: &str, expected_tokens: Vec<(&'static str, &'static str)>) {
20     let parsed_grammar = validate_grammar(&grammar).expect("validate");
21     let intern_token = parsed_grammar.intern_token().expect("intern_token");
22     println!("intern_token: {:?}", intern_token);
23     for (input, expected_user_name) in expected_tokens {
24         let actual_user_name =
25             interpret::interpret(&intern_token.dfa, input).map(|(index, text)| {
26                 let user_name = &intern_token.match_entries[index.index()].user_name;
27                 (user_name.clone(), text)
28             });
29         let actual_user_name = format!("{:?}", actual_user_name);
30         if expected_user_name != actual_user_name {
31             panic!(
32                 "input `{}` matched `{}` but we expected `{}`",
33                 input, actual_user_name, expected_user_name
34             );
35         }
36     }
37 }
38 
39 #[test]
unknown_terminal()40 fn unknown_terminal() {
41     check_err(
42         r#"terminal `"\+"` does not have a pattern defined for it"#,
43         r#"grammar; extern { enum Term { } } X = X "+";"#,
44         r#"                                        ~~~ "#,
45     );
46 }
47 
48 #[test]
unknown_id_terminal()49 fn unknown_id_terminal() {
50     check_err(
51         r#"terminal `"foo"` does not have a pattern defined for it"#,
52         r#"grammar; extern { enum Term { } } X = X "foo";"#,
53         r#"                                        ~~~~~ "#,
54     );
55 }
56 
57 #[test]
tick_input_lifetime_already_declared()58 fn tick_input_lifetime_already_declared() {
59     check_err(
60         r#".*the `'input` lifetime is implicit and cannot be declared"#,
61         r#"grammar<'input>; X = X "foo";"#,
62         r#"~~~~~~~                      "#,
63     );
64 }
65 
66 #[test]
input_parameter_already_declared()67 fn input_parameter_already_declared() {
68     check_err(
69         r#".*the `input` parameter is implicit and cannot be declared"#,
70         r#"grammar(input:u32); X = X "foo";"#,
71         r#"~~~~~~~                         "#,
72     );
73 }
74 
75 #[test]
invalid_regular_expression_unterminated_group()76 fn invalid_regular_expression_unterminated_group() {
77     check_err(
78         r#"unclosed group"#,
79         r#"grammar; X = X r"(123";"#,
80         r#"               ~~~~~~~ "#,
81     );
82 }
83 
84 #[test]
quoted_literals()85 fn quoted_literals() {
86     check_intern_token(
87         r#"grammar; X = X "+" "-" "foo" "(" ")";"#,
88         vec![
89             ("+", r#"Some(("+", "+"))"#),
90             ("-", r#"Some(("-", "-"))"#),
91             ("(", r#"Some(("(", "("))"#),
92             (")", r#"Some((")", ")"))"#),
93             ("foo", r#"Some(("foo", "foo"))"#),
94             ("<", r#"None"#),
95         ],
96     );
97 }
98 
99 #[test]
regex_literals()100 fn regex_literals() {
101     check_intern_token(
102         r#"grammar; X = X r"[a-z]+" r"[0-9]+";"#,
103         vec![
104             ("a", r##"Some((r#"[a-z]+"#, "a"))"##),
105             ("def", r##"Some((r#"[a-z]+"#, "def"))"##),
106             ("1", r##"Some((r#"[0-9]+"#, "1"))"##),
107             ("9123456", r##"Some((r#"[0-9]+"#, "9123456"))"##),
108         ],
109     );
110 }
111 
112 /// Basic test for match mappings.
113 #[test]
match_mappings()114 fn match_mappings() {
115     check_intern_token(
116         r#"grammar; match { r"(?i)begin" => "BEGIN" } else { "abc" => ALPHA } X = "BEGIN" ALPHA;"#,
117         vec![
118             ("BEGIN", r##"Some(("BEGIN", "BEGIN"))"##),
119             ("begin", r##"Some(("BEGIN", "begin"))"##),
120             ("abc", r#"Some((ALPHA, "abc"))"#),
121         ],
122     );
123 }
124 
125 /// Match mappings, exercising precedence. Here the ID regex *would*
126 /// be ambiguous with the begin regex.
127 #[test]
match_precedence()128 fn match_precedence() {
129     check_intern_token(
130         r#"grammar; match { r"(?i)begin" => "BEGIN" } else { r"\w+" => ID } X = ();"#,
131         vec![
132             ("BEGIN", r##"Some(("BEGIN", "BEGIN"))"##),
133             ("begin", r##"Some(("BEGIN", "begin"))"##),
134             ("abc", r#"Some((ID, "abc"))"#),
135         ],
136     );
137 }
138 
139 /// Test that, without a `catch-all`, using unrecognized literals is an error.
140 #[test]
invalid_match_literal()141 fn invalid_match_literal() {
142     check_err(
143         r#"terminal `"foo"` does not have a match mapping defined for it"#,
144         r#"grammar; match { r"(?i)begin" => "BEGIN" } X = "foo";"#,
145         r#"                                               ~~~~~ "#,
146     );
147 }
148 
149 /// Test that, without a `catch-all`, using unrecognized literals is an error.
150 #[test]
invalid_match_regex_literal()151 fn invalid_match_regex_literal() {
152     check_err(
153         r##"terminal `r#"foo"#` does not have a match mapping defined for it"##,
154         r#"grammar; match { r"(?i)begin" => "BEGIN" } X = r"foo";"#,
155         r#"                                               ~~~~~~ "#,
156     );
157 }
158 
159 /// Test that, with a catch-all, the previous two examples work.
160 #[test]
match_catch_all()161 fn match_catch_all() {
162     let grammar = r#"grammar; match { r"(?i)begin" => "BEGIN", _ } X = { "foo", r"foo" };"#;
163     assert!(validate_grammar(&grammar).is_ok())
164 }
165 
166 #[test]
complex_match()167 fn complex_match() {
168     let grammar = r##"
169         grammar;
170         match {
171             "abc"        => "ABC",
172             r"(?i)begin" => BEGIN
173         }
174 
175         pub Query: String = {
176             "ABC" BEGIN => String::from("Success")
177         };
178 "##;
179     assert!(validate_grammar(&grammar).is_ok())
180 }
181 
182 /// Test that overlapping regular expressions are still forbidden within one level
183 /// of a match declaration.
184 #[test]
ambiguity_within_match()185 fn ambiguity_within_match() {
186     check_err(
187         r##"ambiguity detected between the terminal `r#"b"#` and the terminal `r#"\(\?i\)b"#`"##,
188         r#"grammar; match { r"(?i)b" => "B", r"b" => "b" }"#,
189         r#"                                  ~~~~~~~~~~~~ "#,
190     );
191 }
192 
193 /// Test that using the **exact same regular expression** twice is
194 /// forbidden, even across multiple levels of the match expression.
195 /// No good reason to do that.
196 #[test]
same_literal_twice()197 fn same_literal_twice() {
198     check_err(
199         r##"multiple match entries for `r#"\(\?i\)b"#`"##,
200         r#"grammar; match { r"(?i)b" => "B" } else { r"(?i)b" => "b" }"#,
201         r#"                                          ~~~~~~~~~~~~~~~~ "#,
202     );
203 }
204