User Guide
codegen_neuron_cpp_visitor.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 #include <catch2/matchers/catch_matchers_string.hpp>
10 
11 #include "ast/program.hpp"
13 #include "parser/nmodl_driver.hpp"
20 
21 using Catch::Matchers::ContainsSubstring;
22 
23 using namespace nmodl;
24 using namespace visitor;
25 using namespace codegen;
26 
30 
31 /// Helper for creating C codegen visitor
32 std::shared_ptr<CodegenNeuronCppVisitor> create_neuron_cpp_visitor(
33  const std::shared_ptr<ast::Program>& ast,
34  const std::string& /* text */,
35  std::stringstream& ss) {
36  /// construct symbol table
37  SymtabVisitor().visit_program(*ast);
38 
39  /// run all necessary pass
40  InlineVisitor().visit_program(*ast);
41  NeuronSolveVisitor().visit_program(*ast);
42  SolveBlockVisitor().visit_program(*ast);
43  FunctionCallpathVisitor().visit_program(*ast);
44 
45  bool optimize_ion_vars = false;
46  bool enable_cvode = true;
47 
48  /// create C code generation visitor
49  auto cv = std::make_shared<CodegenNeuronCppVisitor>(
50  "_test", ss, "double", optimize_ion_vars, enable_cvode);
51  return cv;
52 }
53 
54 SCENARIO("Check whether PROCEDURE and FUNCTION need setdata call", "[codegen][needsetdata]") {
55  GIVEN("mod file with GLOBAL and RANGE variables used in FUNC and PROC") {
56  std::string input_nmodl = R"(
57  NEURON {
58  SUFFIX test
59  RANGE x
60  GLOBAL s
61  }
62  PARAMETER {
63  s = 2
64  }
65  ASSIGNED {
66  x
67  }
68  PROCEDURE a() {
69  x = get_42()
70  }
71  FUNCTION b() {
72  a()
73  }
74  FUNCTION get_42() {
75  get_42 = 42
76  }
77  )";
78  const auto& ast = NmodlDriver().parse_string(input_nmodl);
79  std::stringstream ss;
80  auto cvisitor = create_neuron_cpp_visitor(ast, input_nmodl, ss);
81  cvisitor->visit_program(*ast);
82  const auto symtab = ast->get_symbol_table();
83  THEN("use_range_ptr_var property is added to needed FUNC and PROC") {
84  auto use_range_ptr_var_funcs = symtab->get_variables_with_properties(
85  NmodlType::use_range_ptr_var);
86  REQUIRE(use_range_ptr_var_funcs.size() == 2);
87  const auto a = symtab->lookup("a");
88  REQUIRE(a->has_any_property(NmodlType::use_range_ptr_var));
89  const auto b = symtab->lookup("b");
90  REQUIRE(b->has_any_property(NmodlType::use_range_ptr_var));
91  const auto get_42 = symtab->lookup("get_42");
92  REQUIRE(!get_42->has_any_property(NmodlType::use_range_ptr_var));
93  }
94  }
95 }
96 
97 std::string create_mod_file_write(const std::string& var) {
98  std::string pattern = R"(
99  NEURON {{
100  SUFFIX test
101  USEION ca WRITE {0}
102  }}
103  ASSIGNED {{
104  {0}
105  }}
106  INITIAL {{
107  {0} = 124.5
108  }}
109  )";
110 
111  return fmt::format(pattern, var);
112 }
113 
114 std::string create_mod_file_read(const std::string& var) {
115  std::string pattern = R"(
116  NEURON {{
117  SUFFIX test
118  USEION ca READ {0}
119  RANGE x
120  }}
121  ASSIGNED {{
122  x
123  {0}
124  }}
125  INITIAL {{
126  x = {0}
127  }}
128  )";
129 
130  return fmt::format(pattern, var);
131 }
132 
133 std::string transpile(const std::string& nmodl) {
134  const auto& ast = NmodlDriver().parse_string(nmodl);
135  std::stringstream ss;
136  auto cvisitor = create_neuron_cpp_visitor(ast, nmodl, ss);
137  cvisitor->visit_program(*ast);
138 
139  return ss.str();
140 }
141 
142 SCENARIO("Write `cao`.", "[codegen]") {
143  GIVEN("mod file that writes to `cao`") {
144  std::string cpp = transpile(create_mod_file_write("cao"));
145 
146  THEN("it contains") {
147  REQUIRE_THAT(cpp, ContainsSubstring("nrn_check_conc_write(_prop, ca_prop, 0);"));
148  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_check_conc_write(_prop, ca_prop, 1);"));
149  REQUIRE_THAT(cpp, ContainsSubstring("nrn_promote(ca_prop, 3, 0);"));
150  REQUIRE_THAT(cpp, ContainsSubstring("nrn_wrote_conc("));
151  }
152  }
153 }
154 
155 SCENARIO("Write `cai`.", "[codegen]") {
156  GIVEN("mod file that writes to `cai`") {
157  std::string cpp = transpile(create_mod_file_write("cai"));
158 
159  THEN("it contains") {
160  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_check_conc_write(_prop, ca_prop, 0);"));
161  REQUIRE_THAT(cpp, ContainsSubstring("nrn_check_conc_write(_prop, ca_prop, 1);"));
162  REQUIRE_THAT(cpp, ContainsSubstring("nrn_promote(ca_prop, 3, 0);"));
163  REQUIRE_THAT(cpp, ContainsSubstring("nrn_wrote_conc("));
164  }
165  }
166 }
167 
168 SCENARIO("Write `eca`.", "[codegen]") {
169  GIVEN("mod file that writes to `eca`") {
170  std::string cpp = transpile(create_mod_file_write("eca"));
171 
172  THEN("it contains") {
173  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_check_conc_write(_prop,"));
174  REQUIRE_THAT(cpp, ContainsSubstring("nrn_promote(ca_prop, 0, 3);"));
175  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_wrote_conc("));
176  }
177  }
178 }
179 
180 SCENARIO("Read `cao`.", "[codegen]") {
181  GIVEN("mod file that reads to `cao`") {
182  std::string cpp = transpile(create_mod_file_read("cao"));
183 
184  THEN("it contains") {
185  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_check_conc_write(_prop,"));
186  REQUIRE_THAT(cpp, ContainsSubstring("nrn_promote(ca_prop, 1, 0);"));
187  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_wrote_conc("));
188  }
189  }
190 }
191 
192 SCENARIO("Read `cai`.", "[codegen]") {
193  GIVEN("mod file that reads to `cai`") {
194  std::string cpp = transpile(create_mod_file_read("cai"));
195 
196  THEN("it contains") {
197  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_check_conc_write(_prop,"));
198  REQUIRE_THAT(cpp, ContainsSubstring("nrn_promote(ca_prop, 1, 0);"));
199  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_wrote_conc("));
200  }
201  }
202 }
203 
204 SCENARIO("Read `eca`.", "[codegen]") {
205  GIVEN("mod file that reads to `eca`") {
206  std::string cpp = transpile(create_mod_file_read("eca"));
207 
208  THEN("it contains") {
209  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_check_conc_write(_prop,"));
210  REQUIRE_THAT(cpp, ContainsSubstring("nrn_promote(ca_prop, 0, 1);"));
211  REQUIRE_THAT(cpp, !ContainsSubstring("nrn_wrote_conc("));
212  }
213  }
214 }
215 
216 SCENARIO("ARTIFICIAL_CELL with `net_send`") {
217  GIVEN("a mod file") {
218  std::string nmodl = R"(
219  NEURON {
220  ARTIFICIAL_CELL test
221  }
222  NET_RECEIVE(w) {
223  net_send(t+1, 1)
224  }
225  )";
226  std::string cpp = transpile(nmodl);
227 
228  THEN("it contains") {
229  REQUIRE_THAT(cpp, ContainsSubstring("artcell_net_send"));
230  }
231  }
232 }
233 
234 SCENARIO("ARTIFICIAL_CELL with `net_move`") {
235  GIVEN("a mod file") {
236  std::string nmodl = R"(
237  NEURON {
238  ARTIFICIAL_CELL test
239  }
240  NET_RECEIVE(w) {
241  net_move(t+1)
242  }
243  )";
244  std::string cpp = transpile(nmodl);
245 
246  THEN("it contains") {
247  REQUIRE_THAT(cpp, ContainsSubstring("artcell_net_move"));
248  }
249  }
250 }
create_mod_file_read
std::string create_mod_file_read(const std::string &var)
Definition: codegen_neuron_cpp_visitor.cpp:114
test_utils.hpp
nmodl::parser::NmodlDriver
Class that binds all pieces together for parsing nmodl file.
Definition: nmodl_driver.hpp:67
create_neuron_cpp_visitor
std::shared_ptr< CodegenNeuronCppVisitor > create_neuron_cpp_visitor(const std::shared_ptr< ast::Program > &ast, const std::string &, std::stringstream &ss)
Helper for creating C codegen visitor.
Definition: codegen_neuron_cpp_visitor.cpp:32
solve_block_visitor.hpp
Replace solve block statements with actual solution node in the AST.
SCENARIO
SCENARIO("Check whether PROCEDURE and FUNCTION need setdata call", "[codegen][needsetdata]")
Definition: codegen_neuron_cpp_visitor.cpp:54
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
create_mod_file_write
std::string create_mod_file_write(const std::string &var)
Definition: codegen_neuron_cpp_visitor.cpp:97
codegen_neuron_cpp_visitor.hpp
Visitor for printing C++ code compatible with legacy api of NEURON
program.hpp
Auto generated AST classes declaration.
nmodl::symtab::syminfo::NmodlType
NmodlType
NMODL variable properties.
Definition: symbol_properties.hpp:116
function_callpath_visitor.hpp
Visitor for traversing FunctionBlock s and ProcedureBlocks through their FunctionCall s
nmodl::parser::NmodlDriver::parse_string
std::shared_ptr< ast::Program > parse_string(const std::string &input)
parser nmodl provided as string (used for testing)
Definition: nmodl_driver.cpp:89
neuron_solve_visitor.hpp
Visitor that solves ODEs using old solvers of NEURON
inline_visitor.hpp
Visitor to inline local procedure and function calls
nmodl_driver.hpp
transpile
std::string transpile(const std::string &nmodl)
Definition: codegen_neuron_cpp_visitor.cpp:133
symtab_visitor.hpp
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.