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