User Guide
semantic_analysis.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"
15 
16 
17 using namespace nmodl;
18 using namespace visitor;
19 using namespace test_utils;
20 
22 
23 //=============================================================================
24 // Procedure/Function inlining tests
25 //=============================================================================
26 
27 bool run_semantic_analysis_visitor(const std::string& text) {
29  const auto& ast = driver.parse_string(text);
30  SymtabVisitor().visit_program(*ast);
31  return SemanticAnalysisVisitor{}.check(*ast);
32 }
33 
34 SCENARIO("TABLE stmt", "[visitor][semantic_analysis]") {
35  GIVEN("Procedure with more than one argument") {
36  std::string nmodl_text = R"(
37  PROCEDURE rates_1(a, b) {
38  TABLE ainf FROM 0 TO 1 WITH 1
39  ainf = 1
40  }
41  )";
42  THEN("fail") {
43  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
44  }
45  }
46  GIVEN("Procedure with exactly one argument") {
47  std::string nmodl_text = R"(
48  PROCEDURE rates_1(a) {
49  TABLE ainf FROM 0 TO 1 WITH 1
50  ainf = 1
51  }
52  )";
53  THEN("pass") {
54  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
55  }
56  }
57  GIVEN("Procedure with less than one argument") {
58  std::string nmodl_text = R"(
59  PROCEDURE rates_1() {
60  TABLE ainf FROM 0 TO 1 WITH 1
61  ainf = 1
62  }
63  )";
64  THEN("fail") {
65  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
66  }
67  }
68  GIVEN("Two procedures which use a table and have the same variable") {
69  std::string nmodl_text = R"(
70  PROCEDURE p1(arg) {
71  TABLE var FROM 0 TO 1 WITH 10
72  var = arg
73  }
74  PROCEDURE p2(arg) {
75  TABLE var FROM 0 TO 1 WITH 10
76  var = 2 * arg
77  }
78  )";
79  THEN("fail") {
80  // This is asserted because `nocmodl` generated code fails to
81  // compile in these cases, not because it must not work.
82  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
83  }
84  }
85 }
86 
87 SCENARIO("Destructor block", "[visitor][semantic_analysis]") {
88  GIVEN("A point-process mod file, with a destructor") {
89  std::string nmodl_text = R"(
90  DESTRUCTOR { : Destructor is before
91  }
92 
93  NEURON {
94  POINT_PROCESS test
95  }
96  )";
97  THEN("pass") {
98  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
99  }
100  }
101  GIVEN("A artifial-cell mod file, with a destructor") {
102  std::string nmodl_text = R"(
103  NEURON {
104  ARTIFICIAL_CELL test
105  }
106 
107  DESTRUCTOR {
108  }
109  )";
110  THEN("pass") {
111  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
112  }
113  }
114  GIVEN("A non point-process mod file, with a destructor") {
115  std::string nmodl_text = R"(
116  NEURON {
117  }
118 
119  DESTRUCTOR {
120  }
121  )";
122  THEN("fail") {
123  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
124  }
125  }
126 }
127 
128 SCENARIO("Ion variable in CONSTANT block", "[visitor][semantic_analysis]") {
129  GIVEN("A mod file with ion variable redeclared in a CONSTANT block") {
130  std::string nmodl_text = R"(
131  NEURON {
132  SUFFIX cdp4Nsp
133  USEION ca READ cao, cai, ica WRITE cai
134  }
135  CONSTANT { cao = 2 (mM) }
136  )";
137  THEN("Semantic analysis fails") {
138  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
139  }
140  }
141 }
142 
143 SCENARIO("INDEPENDENT block", "[visitor][semantic_analysis]") {
144  GIVEN("A mod file with Independent block with only t") {
145  std::string nmodl_text = R"(
146  INDEPENDENT {
147  t FROM 0 TO 1 WITH 100
148  }
149  )";
150  THEN("Semantic analysis succeed") {
151  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
152  }
153  }
154  GIVEN("A mod file with Independent block with something else than t") {
155  std::string nmodl_text = R"(
156  INDEPENDENT {
157  t FROM 0 TO 1 WITH 100
158  u FROM 0 TO 1 WITH 100
159  }
160  )";
161  THEN("Semantic analysis fails") {
162  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
163  }
164  }
165 }
166 
167 SCENARIO("FUNCTION_TABLE block", "[visitor][semantic_analysis]") {
168  GIVEN("A mod file with FUNCTION_TABLE without argument") {
169  std::string nmodl_text = R"(
170  FUNCTION_TABLE ttt()
171  )";
172  THEN("Semantic analysis should fail") {
173  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
174  }
175  }
176  GIVEN("A mod file with FUNCTION_TABLE with at least one argument") {
177  std::string nmodl_text = R"(
178  FUNCTION_TABLE ttt(w (mV))
179  )";
180  THEN("Semantic analysis should success") {
181  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
182  }
183  }
184 }
185 
186 
187 SCENARIO("At most one DERIVATIVE block", "[visitor][semantic_analysis]") {
188  GIVEN("Only one DERIVATIVE block") {
189  std::string nmodl_text = R"(
190  DERIVATIVE states {
191  m' = m/mTau
192  }
193  )";
194  THEN("Semantic analysis should success") {
195  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
196  }
197  }
198  GIVEN("2 DERIVATIVE blocks") {
199  std::string nmodl_text = R"(
200  DERIVATIVE states1 {
201  m' = m/mTau
202  }
203  DERIVATIVE states2 {
204  h' = h/hTau
205  }
206  )";
207  THEN("Semantic analysis should failed") {
208  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
209  }
210  }
211 }
212 
213 SCENARIO("RANDOM Construct", "[visitor][semantic_analysis]") {
214  GIVEN("A mod file with correct RANDOM variable usage") {
215  std::string nmodl_text = R"(
216  NEURON {
217  RANDOM r
218  }
219  PROCEDURE rates() {
220  LOCAL x
221  random_setseq(r, 1)
222  x = 1 + random_negexp(r)
223  x = x + exp(random_negexp(r))
224  }
225  FUNCTION erand() {
226  erand = random_negexp(r)
227  }
228  )";
229  THEN("Semantic analysis should pass") {
230  REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text));
231  }
232  }
233 
234  GIVEN("A mod file with incorrect usage of RANDOM variable as function arguments") {
235  std::string nmodl_text = R"(
236  NEURON {
237  RANDOM r
238  }
239  PROCEDURE rates() {
240  random_setseq(1, r)
241  }
242  )";
243  THEN("Semantic analysis should faial") {
244  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
245  }
246  }
247 
248  GIVEN("A mod file with incorrect usage of RANDOM variable in an expression") {
249  std::string nmodl_text = R"(
250  NEURON {
251  RANDOM r
252  }
253  PROCEDURE rates() {
254  LOCAL x
255  x = r + 1
256  }
257  )";
258  THEN("Semantic analysis should fail") {
259  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
260  }
261  }
262 
263  GIVEN("A mod file with incorrect usage of RANDOM variable in non-random function") {
264  std::string nmodl_text = R"(
265  NEURON {
266  RANDOM r
267  }
268  PROCEDURE rates() {
269  LOCAL x
270  x = exp(r) + 1
271  }
272  )";
273  THEN("Semantic analysis should fail") {
274  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
275  }
276  }
277 }
278 
279 SCENARIO("RANGE and FUNCTION/PROCEDURE block", "[visitor][semantic_analysis]") {
280  GIVEN("A mod file with same RANGE var name and a FUNCTION name") {
281  std::string nmodl_text = R"(
282  NEURON {
283  RANGE f
284  }
285  FUNCTION f(arg) {
286  f = 1
287  }
288  )";
289  THEN("Semantic analysis should fail") {
290  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
291  }
292  }
293  GIVEN("A mod file with same RANGE var name and a PROCEDURE name") {
294  std::string nmodl_text = R"(
295  NEURON {
296  RANGE f
297  }
298  PROCEDURE f(arg) {
299  }
300  )";
301  THEN("Semantic analysis should fail") {
302  REQUIRE(run_semantic_analysis_visitor(nmodl_text));
303  }
304  }
305 }
test_utils.hpp
nmodl::parser::NmodlDriver
Class that binds all pieces together for parsing nmodl file.
Definition: nmodl_driver.hpp:67
semantic_analysis_visitor.hpp
Visitor to check some semantic rules on the AST
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
run_semantic_analysis_visitor
bool run_semantic_analysis_visitor(const std::string &text)
Definition: semantic_analysis.cpp:27
program.hpp
Auto generated AST classes declaration.
driver
nmodl::parser::UnitDriver driver
Definition: parser.cpp:28
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("TABLE stmt", "[visitor][semantic_analysis]")
Definition: semantic_analysis.cpp:34
nmodl_driver.hpp
symtab_visitor.hpp
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.