User Guide
semantic_analysis_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 
10 #include "ast/function_block.hpp"
11 #include "ast/function_call.hpp"
14 #include "ast/procedure_block.hpp"
15 #include "ast/program.hpp"
16 #include "ast/statement_block.hpp"
17 #include "ast/string.hpp"
18 #include "ast/suffix.hpp"
19 #include "ast/table_statement.hpp"
21 #include "utils/logger.hpp"
23 
24 namespace nmodl {
25 namespace visitor {
26 
28  check_fail = false;
30 
31  /// <-- This code is for check 2
32  const auto& suffix_node = collect_nodes(node, {ast::AstNodeType::SUFFIX});
33  if (!suffix_node.empty()) {
34  const auto& suffix = std::dynamic_pointer_cast<const ast::Suffix>(suffix_node[0]);
35  const auto& type = suffix->get_type()->get_node_name();
36  is_point_process = (type == "POINT_PROCESS" || type == "ARTIFICIAL_CELL");
37  }
38  /// -->
39 
40  /// <-- This code is for check 4
41  using namespace symtab::syminfo;
42  const auto& with_prop = NmodlType::read_ion_var | NmodlType::write_ion_var;
43 
44  const auto& sym_table = node.get_symbol_table();
45  assert(sym_table != nullptr);
46 
47  // get all ion variables
48  const auto& ion_variables = sym_table->get_variables_with_properties(with_prop, false);
49 
50  /// make sure ion variables aren't redefined in a `CONSTANT` block.
51  for (const auto& var: ion_variables) {
52  if (var->has_any_property(NmodlType::constant_var)) {
53  logger->critical(
54  fmt::format("SemanticAnalysisVisitor :: ion variable {} from the USEION statement "
55  "can not be re-declared in a CONSTANT block",
56  var->get_name()));
57  check_fail = true;
58  }
59  }
60  /// -->
61 
62  visit_program(node);
63  return check_fail;
64 }
65 
67  /// <-- This code is for check 8
68  const auto& derivative_block_nodes = collect_nodes(node, {ast::AstNodeType::DERIVATIVE_BLOCK});
69  if (derivative_block_nodes.size() > 1) {
70  logger->critical("It is not supported to have several DERIVATIVE blocks");
71  check_fail = true;
72  }
73  /// -->
74  node.visit_children(*this);
75 }
76 
78  /// <-- This code is for check 1
79  in_procedure = true;
80  one_arg_in_procedure_function = node.get_parameters().size() == 1;
81  node.visit_children(*this);
82  in_procedure = false;
83  /// -->
84 }
85 
87  /// <-- This code is for check 1
88  in_function = true;
89  one_arg_in_procedure_function = node.get_parameters().size() == 1;
90  node.visit_children(*this);
91  in_function = false;
92  /// -->
93 }
94 
96  /// <-- This code is a portion of check 9
97  // There are only two contexts where a random_var is allowed. As the first arg of a random
98  // function or as an item in the RANDOM declaration.
99  // Only the former needs checking.
100  bool ok = true;
101  auto name = node.get_node_name();
102 
103  // only check for variables exist in the symbol table (e.g. SUFFIX has type Name but it's not
104  // variable)
105  // if variable is not RANDOM then nothing to check for it
106  auto symbol = program_symtab->lookup(name);
107  if (!symbol || !symbol->has_any_property(symtab::syminfo::NmodlType::random_var)) {
108  return;
109  }
110 
111  auto parent = node.get_parent();
112 
113  // if it's RANDOM var declaration in NEURON block then nothing to do
114  if (parent && parent->is_random_var()) {
115  return;
116  }
117 
118  if (parent && parent->is_var_name()) {
119  parent = parent->get_parent();
120  if (parent && parent->is_function_call()) {
121  auto fname = parent->get_node_name();
122  // if function is a random function then check if the current
123  // name is the function's first argument
124  if (is_random_construct_function(fname)) {
125  auto rfun = dynamic_cast<ast::FunctionCall*>(parent);
126  const auto& arguments = rfun->get_arguments();
127  if (!arguments.empty() && arguments.front()->is_var_name() &&
128  arguments.front()->get_node_name() == name) {
129  // if this is a first argument to function then there
130  // is no problem
131  node.visit_children(*this);
132  return;
133  }
134  }
135  }
136  }
137 
138  // Otherwise, we have an error
139  auto position = node.get_token()->position();
140  logger->critical(
141  fmt::format("SemanticAnalysisVisitor :: RANDOM variable {} at {}"
142  " can be used only as the first arg of a random function",
143  node.get_node_name(),
144  position));
145  check_fail = true;
146 
147  node.visit_children(*this);
148  /// -->
149 }
150 
152  /// <-- This code is a portion of check 9
153  // The first arg of a RANDOM function must be a random_var
154  // Otherwise it's an error
155  auto fname = node.get_node_name();
156  if (is_random_construct_function(fname)) {
157  const auto& arguments = node.get_arguments();
158  if (!arguments.empty()) {
159  auto arg0 = arguments.front();
160  if (arg0->is_var_name()) {
161  auto name = arg0->get_node_name();
162  auto symbol = program_symtab->lookup(name);
163  if (symbol->has_any_property(symtab::syminfo::NmodlType::random_var)) {
164  node.visit_children(*this);
165  return;
166  }
167  }
168  }
169  auto position = node.get_name()->get_token()->position();
170  logger->critical(
171  fmt::format("SemanticAnalysisVisitor :: random function {} at {} :: The first arg must "
172  "be a random variable",
173  fname,
174  position));
175  check_fail = true;
176  }
177  node.visit_children(*this);
178  /// -->
179 }
180 
182  /// <-- This code is for check 1
184  logger->critical(
185  "SemanticAnalysisVisitor :: The procedure or function containing the TABLE statement "
186  "should contains exactly one argument.");
187  check_fail = true;
188  }
189  /// -->
190  /// <-- This code is for check 3
191  const auto& table_vars = tableStmt.get_table_vars();
192  if (in_function && !table_vars.empty()) {
193  logger->critical(
194  "SemanticAnalysisVisitor :: TABLE statement in FUNCTION cannot have a table name "
195  "list.");
196  }
197  if (in_procedure && table_vars.empty()) {
198  logger->critical(
199  "SemanticAnalysisVisitor :: TABLE statement in PROCEDURE must have a table name list.");
200  }
201  /// -->
202 }
203 
205  /// <-- This code is for check 2
206  if (!is_point_process) {
207  logger->warn(
208  "SemanticAnalysisVisitor :: This mod file is not point process but contains a "
209  "destructor.");
210  check_fail = true;
211  }
212  /// -->
213 }
214 
216  /// <-- This code is for check 5
217  for (const auto& n: node.get_variables()) {
218  if (n->get_value()->get_value() != "t") {
219  logger->warn(
220  "SemanticAnalysisVisitor :: '{}' cannot be used as an independent variable, only "
221  "'t' is allowed.",
222  n->get_value()->get_value());
223  }
224  }
225  /// -->
226 }
227 
229  /// <-- This code is for check 7
230  if (node.get_parameters().size() < 1) {
231  logger->critical(
232  "SemanticAnalysisVisitor :: Function table '{}' must have one or more arguments.",
233  node.get_node_name());
234  check_fail = true;
235  }
236  /// -->
237 }
238 
240  /// <-- This code is for check 6
241  if (accel_backend) {
242  logger->error("PROTECT statement is not supported with GPU execution");
243  }
244  if (in_mutex) {
245  logger->warn("SemanticAnalysisVisitor :: Find a PROTECT inside a already locked part.");
246  }
247  /// -->
248 }
249 
251  /// <-- This code is for check 6
252  if (accel_backend) {
253  logger->error("MUTEXLOCK statement is not supported with GPU execution");
254  }
255  if (in_mutex) {
256  logger->warn("SemanticAnalysisVisitor :: Found a MUTEXLOCK inside an already locked part.");
257  }
258  in_mutex = true;
259  /// -->
260 }
261 
263  /// <-- This code is for check 6
264  if (accel_backend) {
265  logger->error("MUTEXUNLOCK statement is not supported with GPU execution");
266  }
267  if (!in_mutex) {
268  logger->warn("SemanticAnalysisVisitor :: Found a MUTEXUNLOCK outside a locked part.");
269  }
270  in_mutex = false;
271  /// -->
272 }
273 
274 } // namespace visitor
275 } // namespace nmodl
nmodl::ast::FunctionCall::get_node_name
std::string get_node_name() const override
Return name of the node.
Definition: ast.cpp:7061
nmodl::ast::AstNodeType::DERIVATIVE_BLOCK
@ DERIVATIVE_BLOCK
type of ast::DerivativeBlock
nmodl::ast::ProcedureBlock::get_parameters
const ArgumentVector & get_parameters() const noexcept override
Getter for member variable ProcedureBlock::parameters.
Definition: procedure_block.hpp:201
nmodl::ast::Name::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:795
nmodl::ast::FunctionBlock
TODO.
Definition: function_block.hpp:39
semantic_analysis_visitor.hpp
nmodl::ast::FunctionTableBlock::get_parameters
const ArgumentVector & get_parameters() const noexcept override
Getter for member variable FunctionTableBlock::parameters.
Definition: function_table_block.hpp:199
nmodl::visitor::SemanticAnalysisVisitor::in_mutex
bool in_mutex
true if we are inside a mutex locked part
Definition: semantic_analysis_visitor.hpp:60
nmodl::visitor::SemanticAnalysisVisitor::in_function
bool in_function
true if we are in a function block
Definition: semantic_analysis_visitor.hpp:56
nmodl::ast::FunctionTableBlock
TODO.
Definition: function_table_block.hpp:39
nmodl::visitor::SemanticAnalysisVisitor::accel_backend
bool accel_backend
true if accelerator backend is used for code generation
Definition: semantic_analysis_visitor.hpp:50
nmodl::ast::FunctionCall::get_name
std::shared_ptr< Name > get_name() const noexcept
Getter for member variable FunctionCall::name.
Definition: function_call.hpp:157
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
function_table_block.hpp
Auto generated AST classes declaration.
nmodl::visitor::SemanticAnalysisVisitor::visit_procedure_block
void visit_procedure_block(const ast::ProcedureBlock &node) override
Store if we are in a procedure and if the arity of this is 1.
Definition: semantic_analysis_visitor.cpp:77
nmodl::ast::MutexLock
Represent MUTEXLOCK statement in NMODL.
Definition: mutex_lock.hpp:38
nmodl::ast::TableStatement
Represents TABLE statement in NMODL.
Definition: table_statement.hpp:39
symbol_properties.hpp
Implement various classes to represent various Symbol properties.
nmodl::ast::FunctionBlock::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:3976
nmodl::ast::ProcedureBlock::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:4173
nmodl::logger
logger_type logger
Definition: logger.cpp:34
nmodl::is_random_construct_function
bool is_random_construct_function(const std::string &name)
Is given name a one of the function for RANDOM construct.
Definition: visitor_utils.cpp:285
procedure_block.hpp
Auto generated AST classes declaration.
nmodl::visitor::SemanticAnalysisVisitor::visit_name
void visit_name(const ast::Name &node) override
Only use of random_var is as first arg in random function.
Definition: semantic_analysis_visitor.cpp:95
nmodl::ast::Name::get_token
const ModToken * get_token() const noexcept override
Return associated token for the current ast node.
Definition: name.hpp:141
nmodl::visitor::SemanticAnalysisVisitor::visit_table_statement
void visit_table_statement(const ast::TableStatement &node) override
Visit a table statement and check that the arity of the block were 1.
Definition: semantic_analysis_visitor.cpp:181
nmodl::visitor::SemanticAnalysisVisitor::program_symtab
symtab::SymbolTable * program_symtab
Definition: semantic_analysis_visitor.hpp:48
nmodl::ModToken::position
std::string position() const
Return position of the token as string.
Definition: modtoken.cpp:14
nmodl::ast::FunctionTableBlock::get_node_name
std::string get_node_name() const override
Return name of the node.
Definition: ast.cpp:3800
nmodl::visitor::SemanticAnalysisVisitor::visit_function_call
void visit_function_call(const ast::FunctionCall &node) override
random function first arg must be random_var
Definition: semantic_analysis_visitor.cpp:151
string.hpp
Auto generated AST classes declaration.
visitor_utils.hpp
Utility functions for visitors implementation.
breakpoint_block.hpp
Auto generated AST classes declaration.
program.hpp
Auto generated AST classes declaration.
statement_block.hpp
Auto generated AST classes declaration.
nmodl::ast::FunctionBlock::get_parameters
const ArgumentVector & get_parameters() const noexcept override
Getter for member variable FunctionBlock::parameters.
Definition: function_block.hpp:201
nmodl::visitor::SemanticAnalysisVisitor::check
bool check(const ast::Program &node)
Definition: semantic_analysis_visitor.cpp:27
nmodl::ast::IndependentBlock::get_variables
const NameVector & get_variables() const noexcept
Getter for member variable IndependentBlock::variables.
Definition: independent_block.hpp:180
nmodl::ast::FunctionCall
TODO.
Definition: function_call.hpp:38
nmodl::visitor::SemanticAnalysisVisitor::in_procedure
bool in_procedure
true if we are in a procedure block
Definition: semantic_analysis_visitor.hpp:54
nmodl::ast::DestructorBlock
Represents a DESTRUCTOR block in the NMODL.
Definition: destructor_block.hpp:53
independent_block.hpp
Auto generated AST classes declaration.
nmodl::ast::ProtectStatement
TODO.
Definition: protect_statement.hpp:38
nmodl::visitor::SemanticAnalysisVisitor::visit_function_block
void visit_function_block(const ast::FunctionBlock &node) override
Store if we are in a function and if the arity of this is 1.
Definition: semantic_analysis_visitor.cpp:86
nmodl::ast::MutexUnlock
Represent MUTEXUNLOCK statement in NMODL.
Definition: mutex_unlock.hpp:38
nmodl::visitor::SemanticAnalysisVisitor::visit_independent_block
void visit_independent_block(const ast::IndependentBlock &node) override
Visit independent block and check if one of the variable is not t.
Definition: semantic_analysis_visitor.cpp:215
nmodl::ast::AstNodeType::SUFFIX
@ SUFFIX
type of ast::Suffix
nmodl::visitor::SemanticAnalysisVisitor::check_fail
bool check_fail
Definition: semantic_analysis_visitor.hpp:46
nmodl::ast::Program::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:12902
suffix.hpp
Auto generated AST classes declaration.
nmodl::visitor::SemanticAnalysisVisitor::visit_mutex_unlock
void visit_mutex_unlock(const ast::MutexUnlock &node) override
Look if MUTEXUNLOCK is outside a locked block.
Definition: semantic_analysis_visitor.cpp:262
nmodl::ast::FunctionCall::get_arguments
const ExpressionVector & get_arguments() const noexcept
Getter for member variable FunctionCall::arguments.
Definition: function_call.hpp:166
nmodl::visitor::SemanticAnalysisVisitor::visit_program
void visit_program(const ast::Program &node) override
Check number of DERIVATIVE blocks.
Definition: semantic_analysis_visitor.cpp:66
nmodl::collect_nodes
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
Definition: visitor_utils.cpp:205
nmodl::visitor::SemanticAnalysisVisitor::visit_protect_statement
void visit_protect_statement(const ast::ProtectStatement &node) override
Look if protect is inside a locked block.
Definition: semantic_analysis_visitor.cpp:239
function_block.hpp
Auto generated AST classes declaration.
function_call.hpp
Auto generated AST classes declaration.
nmodl::visitor::SemanticAnalysisVisitor::visit_function_table_block
void visit_function_table_block(const ast::FunctionTableBlock &node) override
Visit function table to check that number of args > 0.
Definition: semantic_analysis_visitor.cpp:228
nmodl::ast::TableStatement::get_table_vars
const NameVector & get_table_vars() const noexcept
Getter for member variable TableStatement::table_vars.
Definition: table_statement.hpp:166
nmodl::ast::ProcedureBlock
TODO.
Definition: procedure_block.hpp:39
nmodl::ast::FunctionCall::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:7068
nmodl::ast::Name::get_node_name
std::string get_node_name() const override
Return name of the node.
Definition: ast.cpp:791
logger.hpp
Implement logger based on spdlog library.
nmodl::visitor::SemanticAnalysisVisitor::one_arg_in_procedure_function
bool one_arg_in_procedure_function
true if the procedure or the function contains only one argument
Definition: semantic_analysis_visitor.hpp:52
nmodl::visitor::SemanticAnalysisVisitor::is_point_process
bool is_point_process
true if the mod file is of type point process
Definition: semantic_analysis_visitor.hpp:58
nmodl::ast::Program::get_symbol_table
symtab::SymbolTable * get_symbol_table() const override
Return associated symbol table for the current ast node.
Definition: program.hpp:153
nmodl::ast::IndependentBlock
Represents a INDEPENDENT block in the NMODL.
Definition: independent_block.hpp:46
nmodl::visitor::SemanticAnalysisVisitor::visit_destructor_block
void visit_destructor_block(const ast::DestructorBlock &node) override
Visit destructor and check that the file is of type POINT_PROCESS or ARTIFICIAL_CELL.
Definition: semantic_analysis_visitor.cpp:204
nmodl::ast::Name
Represents a name.
Definition: name.hpp:44
nmodl::ast::Program
Represents top level AST node for whole NMODL input.
Definition: program.hpp:39
nmodl::symtab::syminfo::NmodlType::random_var
@ random_var
Randomvar Type.
table_statement.hpp
Auto generated AST classes declaration.
nmodl::symtab::SymbolTable::lookup
std::shared_ptr< Symbol > lookup(const std::string &name) const
check if symbol with given name exist in the current table (but not in parents)
Definition: symbol_table.hpp:199
nmodl::visitor::SemanticAnalysisVisitor::visit_mutex_lock
void visit_mutex_lock(const ast::MutexLock &node) override
Look if MUTEXLOCK is inside a locked block.
Definition: semantic_analysis_visitor.cpp:250
nmodl::ast::Ast::get_parent
virtual Ast * get_parent() const
Parent getter.
Definition: ast.cpp:307
nmodl::ast::Ast::get_node_name
virtual std::string get_node_name() const
Return name of of the node.
Definition: ast.cpp:28