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;
39 std::vector<DUChain> chains;
44 chains.reserve(blocks.size());
45 for (
const auto& block: blocks) {
46 chains.push_back(
v.analyze(*block, variable));
55 SCENARIO(
"Perform DefUse analysis on NMODL constructs") {
56 GIVEN(
"global variable usage in assignment statements") {
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") {
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") {
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)") {
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);
202 GIVEN(
"global variable used in if statement (rhs)") {
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);
229 GIVEN(
"global variable definition in else block") {
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") {
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);
286 REQUIRE(chains[0].chain[0].
children[1].binary_expression ==
nullptr);
294 GIVEN(
"global variable usage in else block") {
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") {
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") {
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") {
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") {
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") {
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") {
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") {
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") {
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") {
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);
Auto generated AST classes declaration.
Visitor for checking parents of ast nodes
Class that binds all pieces together for parsing nmodl file.
Visitor to return Def-Use chain for a given variable in the block/node
Visitor to inline local procedure and function calls
void visit_program(ast::Program &node) override
visit node of type ast::Program
Concrete visitor for constructing symbol table from AST.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for checking parents of ast nodes
int check_ast(const ast::Ast &node)
A small wrapper to have a nicer call in parser.cpp.
SCENARIO("Perform DefUse analysis on NMODL constructs")
std::vector< DUChain > run_defuse_visitor(const std::string &text, const std::string &variable)
Visitor to return Def-Use chain for a given variable in the block/node
AstNodeType
Enum type for every AST node type.
bool parse_string(const std::string &input)
parser Units provided as string (used for testing)
Visitor to inline local procedure and function calls
static double eval(double *p, int n, Vect *x, Vect *y, char *fcn)
std::string reindent_text(const std::string &text, int indent_level)
Reindent nmodl text for text-to-text comparison.
std::string to_string(DUState state)
DUState to string conversion for pretty-printing.
@ CD
global or local variable is conditionally defined
@ U
global variable is used
@ LD
local variable is defined
@ NONE
variable is not used
@ D
global variable is defined
encapsulates code generation backend implementations
std::vector< std::shared_ptr< const ast::Ast > > collect_nodes(const ast::Ast &node, const std::vector< ast::AstNodeType > &types)
traverse node recursively and collect nodes of given types
std::string to_nmodl(const ast::Ast &node, const std::set< ast::AstNodeType > &exclude_types)
Given AST node, return the NMODL string representation.
Auto generated AST classes declaration.
static double children(void *v)
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.
nmodl::parser::UnitDriver driver