8 #include <catch2/catch_session.hpp>
9 #include <catch2/catch_test_macros.hpp>
20 using namespace nmodl;
21 using namespace visitor;
23 using namespace test_utils;
36 SymtabVisitor().visit_program(*ast);
37 InlineVisitor().visit_program(*ast);
39 std::vector<DUChain> chains;
40 DefUseAnalyzeVisitor v(*ast->get_symbol_table());
44 chains.reserve(blocks.size());
45 for (
const auto& block: blocks) {
46 chains.push_back(v.analyze(*block, variable));
50 CheckParentVisitor().check_ast(*ast);
55 SCENARIO(
"Perform DefUse analysis on NMODL constructs") {
56 GIVEN(
"global variable usage in assignment statements") {
57 std::string nmodl_text = R
"(
68 std::string expected_text =
69 R"({"DerivativeBlock":[{"name":"D"},{"name":"U"},{"name":"D"}]})";
71 THEN("Def-Use chains for individual usage is printed") {
75 REQUIRE(chains[0].
to_string(
true) == expected_text);
78 REQUIRE(
to_nmodl(*chains[0].chain[0].binary_expression) ==
"tau = 1");
79 REQUIRE(
to_nmodl(*chains[0].chain[1].binary_expression) ==
"tau = 1+tau");
80 REQUIRE(
to_nmodl(*chains[0].chain[2].binary_expression) ==
"tau = 1+tau");
84 GIVEN(
"block with use of verbatim block") {
85 std::string nmodl_text = R
"(
97 std::string expected_text =
98 R"({"DerivativeBlock":[{"name":"U"},{"name":"D"},{"name":"U"}]})";
100 THEN("Verbatim block is considered as use of the variable") {
103 REQUIRE(chains[0].
to_string(
true) == expected_text);
106 REQUIRE(chains[0].chain[0].binary_expression ==
nullptr);
107 REQUIRE(
to_nmodl(*chains[0].chain[1].binary_expression) ==
"tau = 1");
108 REQUIRE(chains[0].chain[2].binary_expression ==
nullptr);
112 GIVEN(
"use of array variables") {
113 std::string nmodl_text = R
"(
123 tau[0] = 1 : tau[0] is defined
124 tau[2] = 1 + tau[1] + tau[2] : tau[1] is used; tau[2] is defined as well as used
125 m[0] = m[1] : m[0] is defined and used on next line; m[1] is used
126 h[1] = m[0] + h[0] : h[0] is used; h[1] is defined
127 o[i] = 1 : o[i] is defined for any i
128 n[i+1] = 1 + n[i] : n[i] is used as well as defined for any i
132 THEN("Def-Use analyser distinguishes variables by array index") {
145 REQUIRE(m0[0].
to_string() == R
"({"DerivativeBlock":[{"name":"D"},{"name":"U"}]})");
146 REQUIRE(to_nmodl(*m0[0].chain[0].binary_expression) == "m[0] = m[1]");
147 REQUIRE(
to_nmodl(*m0[0].chain[1].binary_expression) ==
"h[1] = m[0]+h[0]");
148 REQUIRE(m1[0].
to_string() == R
"({"DerivativeBlock":[{"name":"U"}]})");
149 REQUIRE(to_nmodl(*m1[0].chain[0].binary_expression) == "m[0] = m[1]");
150 REQUIRE(h1[0].
to_string() == R
"({"DerivativeBlock":[{"name":"D"}]})");
151 REQUIRE(to_nmodl(*h1[0].chain[0].binary_expression) == "h[1] = m[0]+h[0]");
152 REQUIRE(tau0[0].
to_string() == R
"({"DerivativeBlock":[{"name":"LD"}]})");
153 REQUIRE(to_nmodl(*tau0[0].chain[0].binary_expression) == "tau[0] = 1");
154 REQUIRE(tau1[0].
to_string() == R
"({"DerivativeBlock":[{"name":"LU"}]})");
155 REQUIRE(to_nmodl(*tau1[0].chain[0].binary_expression) ==
156 "tau[2] = 1+tau[1]+tau[2]");
158 R
"({"DerivativeBlock":[{"name":"LU"},{"name":"LD"}]})");
159 REQUIRE(to_nmodl(*tau2[0].chain[0].binary_expression) ==
160 "tau[2] = 1+tau[1]+tau[2]");
161 REQUIRE(
to_nmodl(*tau2[0].chain[1].binary_expression) ==
162 "tau[2] = 1+tau[1]+tau[2]");
163 REQUIRE(n0[0].
to_string() == R
"({"DerivativeBlock":[{"name":"U"},{"name":"D"}]})");
164 REQUIRE(to_nmodl(*n0[0].chain[0].binary_expression) == "n[i+1] = 1+n[i]");
165 REQUIRE(
to_nmodl(*n0[0].chain[1].binary_expression) ==
"n[i+1] = 1+n[i]");
166 REQUIRE(n1[0].
to_string() == R
"({"DerivativeBlock":[{"name":"U"},{"name":"D"}]})");
167 REQUIRE(to_nmodl(*n1[0].chain[0].binary_expression) == "n[i+1] = 1+n[i]");
168 REQUIRE(
to_nmodl(*n1[0].chain[1].binary_expression) ==
"n[i+1] = 1+n[i]");
169 REQUIRE(o0[0].
to_string() == R
"({"DerivativeBlock":[{"name":"D"}]})");
170 REQUIRE(to_nmodl(*o0[0].chain[0].binary_expression) == "o[i] = 1");
175 GIVEN(
"global variable used in if statement (lhs)") {
176 std::string nmodl_text = R
"(
187 std::string expected_text =
188 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"U"}]}]}]})";
190 THEN("tau is used") {
193 REQUIRE(chains[0].
to_string() == expected_text);
195 REQUIRE(chains[0].chain[0].binary_expression ==
nullptr);
196 REQUIRE(chains[0].chain[0].children[0].binary_expression ==
nullptr);
197 REQUIRE(
to_nmodl(*chains[0].chain[0].children[0].children[0].binary_expression) ==
202 GIVEN(
"global variable used in if statement (rhs)") {
203 std::string nmodl_text = R
"(
214 std::string expected_text =
215 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"U"}]}]}]})";
217 THEN("tau is used") {
220 REQUIRE(chains[0].
to_string() == expected_text);
222 REQUIRE(chains[0].chain[0].binary_expression ==
nullptr);
223 REQUIRE(chains[0].chain[0].children[0].binary_expression ==
nullptr);
224 REQUIRE(
to_nmodl(*chains[0].chain[0].children[0].children[0].binary_expression) ==
229 GIVEN(
"global variable definition in else block") {
230 std::string nmodl_text = R
"(
245 std::string expected_text =
246 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"LD"}]},{"ELSE":[{"name":"D"}]}]}]})";
248 THEN("Def-Use chains should return CD") {
251 REQUIRE(chains[0].
to_string() == expected_text);
256 GIVEN(
"global variable use in if statement + definition and use in if and else blocks") {
257 std::string nmodl_text = R
"(
271 std::string expected_text =
272 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"U"},{"name":"U"},{"name":"D"}]},{"ELSE":[{"name":"U"},{"name":"D"}]}]}]})";
274 THEN("tau is used and then used in its definitions") {
277 REQUIRE(chains[0].
to_string() == expected_text);
278 REQUIRE(chains[0].chain[0].binary_expression ==
nullptr);
279 REQUIRE(chains[0].chain[0].children[0].binary_expression ==
nullptr);
280 REQUIRE(
to_nmodl(*chains[0].chain[0].children[0].children[0].binary_expression) ==
282 REQUIRE(
to_nmodl(*chains[0].chain[0].children[0].children[1].binary_expression) ==
284 REQUIRE(
to_nmodl(*chains[0].chain[0].children[0].children[2].binary_expression) ==
286 REQUIRE(chains[0].chain[0].children[1].binary_expression ==
nullptr);
287 REQUIRE(
to_nmodl(*chains[0].chain[0].children[1].children[0].binary_expression) ==
289 REQUIRE(
to_nmodl(*chains[0].chain[0].children[1].children[1].binary_expression) ==
294 GIVEN(
"global variable usage in else block") {
295 std::string nmodl_text = R
"(
308 std::string expected_text =
309 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"name":"IF"},{"ELSE":[{"name":"U"},{"name":"D"}]}]}]})";
311 THEN("Def-Use chains should return USE") {
314 REQUIRE(chains[0].
to_string() == expected_text);
319 GIVEN(
"local variable usage in else block") {
320 std::string nmodl_text = R
"(
334 std::string expected_text =
335 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"U"}]},{"ELSE":[{"name":"LU"},{"name":"LD"}]}]}]})";
337 THEN("Def-Use chains should return USE because global variables have precedence in eval") {
340 REQUIRE(chains[0].
to_string() == expected_text);
345 GIVEN(
"local variable conditional definition") {
346 std::string nmodl_text = R
"(
359 std::string expected_text =
360 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"LD"}]}]}]})";
362 THEN("Def-Use chains should return USE because global variables have precedence in eval") {
365 REQUIRE(chains[0].
to_string() == expected_text);
370 GIVEN(
"local and range variables usage and definitions") {
371 std::string nmodl_text = R
"(
386 std::string expected_text =
387 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"name":"IF"},{"name":"ELSE"}]}]})";
390 "Def-Use chains should return NONE because the variable we look for is not one of "
394 REQUIRE(chains[0].
to_string() == expected_text);
399 GIVEN(
"Simple check of local and global variables") {
400 std::string nmodl_text = R
"(
415 std::string expected_text_x =
416 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"U"}]}]}]})";
417 std::string expected_text_a =
418 R"({"DerivativeBlock":[{"name":"LD"},{"CONDITIONAL_BLOCK":[{"name":"IF"}]}]})";
419 std::string expected_text_b =
420 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"name":"IF"}]}]})";
421 std::string expected_text_c =
422 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"LD"}]}]}]})";
424 THEN("local and global variables are correctly analyzed") {
428 REQUIRE(chains_x[0].
to_string() == expected_text_x);
433 REQUIRE(chains_a[0].
to_string() == expected_text_a);
438 REQUIRE(chains_b[0].
to_string() == expected_text_b);
443 REQUIRE(chains_c[0].
to_string() == expected_text_c);
448 GIVEN(
"Simple check of assigned variables") {
449 const std::string nmodl_text = R
"(
464 const std::string expected_text_y =
465 R
"({"DerivativeBlock":[{"name":"D"},{"name":"U"},{"name":"D"}]})";
467 THEN("assigned variables are correctly analyzed") {
471 REQUIRE(chains_y[0].
to_string() == expected_text_y);
477 GIVEN(
"global variable definition in if-else block") {
478 std::string nmodl_text = R
"(
493 std::string expected_text =
494 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"D"},{"name":"U"}]},{"ELSE":[{"name":"D"}]}]}]})";
496 THEN("Def-Use chains should return DEF") {
499 REQUIRE(chains[0].
to_string() == expected_text);
504 GIVEN(
"conditional definition in nested block") {
505 std::string nmodl_text = R
"(
522 std::string expected_text =
523 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"D"},{"name":"U"}]}]}]},{"ELSEIF":[{"name":"D"}]}]}]})";
525 THEN("Def-Use chains should return DEF") {
528 REQUIRE(chains[0].
to_string() == expected_text);
533 GIVEN(
"global variable usage in if-elseif-else block") {
534 std::string nmodl_text = R
"(
553 std::string expected_text =
554 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"D"}]}]},{"name":"U"},{"name":"D"},{"CONDITIONAL_BLOCK":[{"name":"IF"},{"ELSEIF":[{"name":"D"}]}]}]})";
556 THEN("Def-Use chains for individual usage is printed") {
559 REQUIRE(chains[0].
to_string() == expected_text);
564 GIVEN(
"global variable used in nested if-elseif-else block") {
565 std::string nmodl_text = R
"(
594 std::string expected_text =
595 R"({"DerivativeBlock":[{"CONDITIONAL_BLOCK":[{"IF":[{"name":"LD"}]}]},{"CONDITIONAL_BLOCK":[{"IF":[{"CONDITIONAL_BLOCK":[{"name":"IF"},{"ELSE":[{"name":"D"}]}]}]},{"ELSEIF":[{"CONDITIONAL_BLOCK":[{"IF":[{"CONDITIONAL_BLOCK":[{"name":"IF"},{"ELSE":[{"name":"U"}]}]}]}]},{"name":"D"}]}]}]})";
597 THEN("Def-Use chains for nested statements calculated") {
600 REQUIRE(chains[0].
to_string() == expected_text);