User Guide
rename.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Blue Brain Project, EPFL.
3  * See the top-level LICENSE file for details.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <catch2/catch_test_macros.hpp>
9 
10 #include "ast/program.hpp"
11 #include "parser/nmodl_driver.hpp"
19 
20 using namespace nmodl;
21 using namespace visitor;
22 using namespace test;
23 using namespace test_utils;
24 
26 
27 
28 //=============================================================================
29 // Variable rename tests
30 //=============================================================================
31 
32 static std::string run_var_rename_visitor(
33  const std::string& text,
34  const std::vector<std::pair<std::string, std::string>>& variables) {
36  const auto& ast = driver.parse_string(text);
37  for (const auto& variable: variables) {
38  RenameVisitor(variable.first, variable.second).visit_program(*ast);
39  }
40  std::stringstream stream;
41  NmodlPrintVisitor(stream).visit_program(*ast);
42 
43  // check that, after visitor rearrangement, parents are still up-to-date
44  CheckParentVisitor().check_ast(*ast);
45 
46  return stream.str();
47 }
48 
49 SCENARIO("Renaming any variable in mod file with RenameVisitor", "[visitor][rename]") {
50  GIVEN("A mod file") {
51  // sample nmodl text
52  std::string input_nmodl_text = R"(
53  NEURON {
54  SUFFIX NaTs2_t
55  USEION na READ ena WRITE ina
56  RANGE gNaTs2_tbar
57  }
58 
59  PARAMETER {
60  gNaTs2_tbar = 0.1 (S/cm2)
61  }
62 
63  STATE {
64  m
65  h
66  }
67 
68  COMMENT
69  m and gNaTs2_tbar remain same here
70  ENDCOMMENT
71 
72  BREAKPOINT {
73  LOCAL gNaTs2_t
74  gNaTs2_t = gNaTs2_tbar*m*m*m*h
75  ina = gNaTs2_t*(v-ena)
76  }
77 
78  FUNCTION mAlpha() {
79  }
80  )";
81 
82  /// expected result after renaming m, gNaTs2_tbar and mAlpha
83  std::string output_nmodl_text = R"(
84  NEURON {
85  SUFFIX NaTs2_t
86  USEION na READ ena WRITE ina
87  RANGE new_gNaTs2_tbar
88  }
89 
90  PARAMETER {
91  new_gNaTs2_tbar = 0.1 (S/cm2)
92  }
93 
94  STATE {
95  mm
96  h
97  }
98 
99  COMMENT
100  m and gNaTs2_tbar remain same here
101  ENDCOMMENT
102 
103  BREAKPOINT {
104  LOCAL gNaTs2_t
105  gNaTs2_t = new_gNaTs2_tbar*mm*mm*mm*h
106  ina = gNaTs2_t*(v-ena)
107  }
108 
109  FUNCTION mBeta() {
110  }
111  )";
112 
113  std::string input = reindent_text(input_nmodl_text);
114  std::string expected_output = reindent_text(output_nmodl_text);
115 
116  THEN("existing variables could be renamed") {
117  std::vector<std::pair<std::string, std::string>> variables = {
118  {"m", "mm"},
119  {"gNaTs2_tbar", "new_gNaTs2_tbar"},
120  {"mAlpha", "mBeta"},
121  };
122  auto result = run_var_rename_visitor(input, variables);
123  REQUIRE(result == expected_output);
124  }
125 
126  THEN("non-existing variables will be ignored") {
127  std::vector<std::pair<std::string, std::string>> variables = {
128  {"unknown_variable", "doesnot_matter"}};
129  auto result = run_var_rename_visitor(input, variables);
130  REQUIRE(result == input);
131  }
132  }
133 }
134 
135 //=============================================================================
136 // Local variable rename tests
137 //=============================================================================
138 
139 std::string run_local_var_rename_visitor(const std::string& text) {
141  const auto& ast = driver.parse_string(text);
142 
143  SymtabVisitor().visit_program(*ast);
144 
145  VerbatimVarRenameVisitor().visit_program(*ast);
146  LocalVarRenameVisitor().visit_program(*ast);
147  std::stringstream stream;
148  NmodlPrintVisitor(stream).visit_program(*ast);
149  return stream.str();
150 }
151 
152 SCENARIO("Renaming with presence of local and global variables in same block",
153  "[visitor][rename]") {
154  GIVEN("A neuron block and procedure with same variable name") {
155  std::string nmodl_text = R"(
156  NEURON {
157  SUFFIX NaTs2_t
158  USEION na READ ena WRITE ina
159  RANGE gNaTs2_tbar
160  }
161 
162  PROCEDURE rates() {
163  LOCAL gNaTs2_tbar
164  gNaTs2_tbar = 2.1 + ena
165  }
166  )";
167 
168  std::string expected_nmodl_text = R"(
169  NEURON {
170  SUFFIX NaTs2_t
171  USEION na READ ena WRITE ina
172  RANGE gNaTs2_tbar
173  }
174 
175  PROCEDURE rates() {
176  LOCAL gNaTs2_tbar_r_0
177  gNaTs2_tbar_r_0 = 2.1+ena
178  }
179  )";
180 
181  THEN("var renaming pass changes only local variables in procedure") {
182  std::string input = reindent_text(nmodl_text);
183  auto expected_result = reindent_text(expected_nmodl_text);
184  auto result = run_local_var_rename_visitor(input);
185  REQUIRE(result == expected_result);
186  }
187  }
188 }
189 
190 SCENARIO("Renaming in the absence of global blocks", "[visitor][rename]") {
191  GIVEN("Procedures containing same variables") {
192  std::string nmodl_text = R"(
193  PROCEDURE rates_1() {
194  LOCAL gNaTs2_tbar
195  gNaTs2_tbar = 2.1+ena
196  }
197 
198  PROCEDURE rates_2() {
199  LOCAL gNaTs2_tbar
200  gNaTs2_tbar = 2.1+ena
201  }
202  )";
203 
204  THEN("nothing gets renamed") {
205  std::string input = reindent_text(nmodl_text);
206  auto result = run_local_var_rename_visitor(input);
207  REQUIRE(result == input);
208  }
209  }
210 }
211 
212 SCENARIO("Variable renaming in nested blocks", "[visitor][rename]") {
213  GIVEN("Mod file containing procedures with nested blocks") {
214  std::string input_nmodl_text = R"(
215  NEURON {
216  SUFFIX NaTs2_t
217  USEION na READ ena WRITE ina
218  RANGE gNaTs2_tbar
219  }
220 
221  PARAMETER {
222  gNaTs2_tbar = 0.1 (S/cm2)
223  tau = 11.1
224  }
225 
226  STATE {
227  m
228  h
229  }
230 
231  BREAKPOINT {
232  LOCAL gNaTs2_t
233  gNaTs2_t = gNaTs2_tbar*m*m*m*h
234  ina = gNaTs2_t*(v-ena)
235  {
236  LOCAL gNaTs2_t, h
237  gNaTs2_t = m + h
238  {
239  LOCAL m
240  m = gNaTs2_t + h
241  {
242  LOCAL m, h
243  VERBATIM
244  _lm = 12
245  ENDVERBATIM
246  }
247  }
248  }
249  }
250 
251  PROCEDURE rates() {
252  LOCAL x, m
253  m = x + gNaTs2_tbar
254  {
255  {
256  LOCAL h, x, gNaTs2_tbar
257  m = h * x * gNaTs2_tbar + tau
258  }
259  }
260  }
261  )";
262 
263  std::string expected_nmodl_text = R"(
264  NEURON {
265  SUFFIX NaTs2_t
266  USEION na READ ena WRITE ina
267  RANGE gNaTs2_tbar
268  }
269 
270  PARAMETER {
271  gNaTs2_tbar = 0.1 (S/cm2)
272  tau = 11.1
273  }
274 
275  STATE {
276  m
277  h
278  }
279 
280  BREAKPOINT {
281  LOCAL gNaTs2_t
282  gNaTs2_t = gNaTs2_tbar*m*m*m*h
283  ina = gNaTs2_t*(v-ena)
284  {
285  LOCAL gNaTs2_t_r_0, h_r_1
286  gNaTs2_t_r_0 = m+h_r_1
287  {
288  LOCAL m_r_1
289  m_r_1 = gNaTs2_t_r_0+h_r_1
290  {
291  LOCAL m_r_0, h_r_0
292  VERBATIM
293  m_r_0 = 12
294  ENDVERBATIM
295  }
296  }
297  }
298  }
299 
300  PROCEDURE rates() {
301  LOCAL x, m_r_2
302  m_r_2 = x+gNaTs2_tbar
303  {
304  {
305  LOCAL h_r_2, x_r_0, gNaTs2_tbar_r_0
306  m_r_2 = h_r_2*x_r_0*gNaTs2_tbar_r_0+tau
307  }
308  }
309  }
310  )";
311 
312  THEN("variables conflicting with global variables get renamed starting from inner block") {
313  std::string input = reindent_text(input_nmodl_text);
314  auto expected_result = reindent_text(expected_nmodl_text);
315  auto result = run_local_var_rename_visitor(input);
316  REQUIRE(result == expected_result);
317  }
318  }
319 }
320 
321 
322 SCENARIO("Renaming in presence of local variable in verbatim block", "[visitor][rename]") {
323  GIVEN("A neuron block and procedure with same variable name") {
324  std::string nmodl_text = R"(
325  NEURON {
326  RANGE gNaTs2_tbar
327  }
328 
329  PROCEDURE rates() {
330  LOCAL gNaTs2_tbar, x
331  VERBATIM
332  _lx = _lgNaTs2_tbar
333  #define my_macro_var _lgNaTs2_tbar*2
334  ENDVERBATIM
335  gNaTs2_tbar = my_macro_var + 1
336  }
337 
338  PROCEDURE alpha() {
339  VERBATIM
340  _p_gNaTs2_tbar = 12
341  ENDVERBATIM
342  }
343  )";
344 
345  std::string expected_nmodl_text = R"(
346  NEURON {
347  RANGE gNaTs2_tbar
348  }
349 
350  PROCEDURE rates() {
351  LOCAL gNaTs2_tbar_r_0, x
352  VERBATIM
353  x = gNaTs2_tbar_r_0
354  #define my_macro_var gNaTs2_tbar_r_0*2
355  ENDVERBATIM
356  gNaTs2_tbar_r_0 = my_macro_var+1
357  }
358 
359  PROCEDURE alpha() {
360  VERBATIM
361  gNaTs2_tbar = 12
362  ENDVERBATIM
363  }
364  )";
365 
366  THEN("var renaming pass changes local & global variable in verbatim block") {
367  std::string input = reindent_text(nmodl_text);
368  auto expected_result = reindent_text(expected_nmodl_text);
369  auto result = run_local_var_rename_visitor(input);
370  REQUIRE(result == expected_result);
371  }
372  }
373 }
test_utils.hpp
nmodl::parser::NmodlDriver
Class that binds all pieces together for parsing nmodl file.
Definition: nmodl_driver.hpp:67
nmodl::test_utils::reindent_text
std::string reindent_text(const std::string &text, int indent_level)
Reindent nmodl text for text-to-text comparison.
Definition: test_utils.cpp:53
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
program.hpp
Auto generated AST classes declaration.
driver
nmodl::parser::UnitDriver driver
Definition: parser.cpp:28
verbatim_var_rename_visitor.hpp
Rename variable in verbatim block.
run_local_var_rename_visitor
std::string run_local_var_rename_visitor(const std::string &text)
Definition: rename.cpp:139
local_var_rename_visitor.hpp
Visitor to rename local variables conflicting with global scope
nmodl::parser::UnitDriver::parse_string
bool parse_string(const std::string &input)
parser Units provided as string (used for testing)
Definition: unit_driver.cpp:40
SCENARIO
SCENARIO("Renaming any variable in mod file with RenameVisitor", "[visitor][rename]")
Definition: rename.cpp:49
run_var_rename_visitor
static std::string run_var_rename_visitor(const std::string &text, const std::vector< std::pair< std::string, std::string >> &variables)
Definition: rename.cpp:32
checkparent_visitor.hpp
Visitor for checking parents of ast nodes
nmodl_driver.hpp
rename_visitor.hpp
Blindly rename given variable to new name
symtab_visitor.hpp
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.
nmodl_visitor.hpp
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.