diff --git a/src/main.cpp b/src/main.cpp index 5d32b51898..1e2a43871f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -329,6 +329,12 @@ int main(int argc, const char* argv[]) { /// just visit the ast AstVisitor().visit_program(*ast); + /// construct symbol table + { + logger->info("Running symtab visitor"); + SymtabVisitor(update_symtab).visit_program(*ast); + } + /// Check some rules that ast should follow { logger->info("Running semantic analysis visitor"); @@ -337,12 +343,6 @@ int main(int argc, const char* argv[]) { } } - /// construct symbol table - { - logger->info("Running symtab visitor"); - SymtabVisitor(update_symtab).visit_program(*ast); - } - /// use cnexp instead of after_cvode solve method { logger->info("Running CVode to cnexp visitor"); diff --git a/src/visitors/semantic_analysis_visitor.cpp b/src/visitors/semantic_analysis_visitor.cpp index 618200edc3..602b2eb502 100644 --- a/src/visitors/semantic_analysis_visitor.cpp +++ b/src/visitors/semantic_analysis_visitor.cpp @@ -4,6 +4,7 @@ #include "ast/program.hpp" #include "ast/suffix.hpp" #include "ast/table_statement.hpp" +#include "symtab/symbol_properties.hpp" #include "utils/logger.hpp" #include "visitors/visitor_utils.hpp" @@ -22,6 +23,28 @@ bool SemanticAnalysisVisitor::check(const ast::Program& node) { } /// --> + /// <-- This code is for check 4 + using namespace symtab::syminfo; + const auto& with_prop = NmodlType::read_ion_var | NmodlType::write_ion_var; + + const auto& sym_table = node.get_symbol_table(); + assert(sym_table != nullptr); + + // get all ion variables + const auto& ion_variables = sym_table->get_variables_with_properties(with_prop, false); + + /// make sure ion variables aren't redefined in a `CONSTANT` block. + for (const auto& var: ion_variables) { + if (var->has_any_property(NmodlType::constant_var)) { + logger->critical( + fmt::format("SemanticAnalysisVisitor :: ion variable {} from the USEION statement " + "can not be re-declared in a CONSTANT block", + var->get_name())); + check_fail = true; + } + } + /// --> + visit_program(node); return check_fail; } diff --git a/src/visitors/semantic_analysis_visitor.hpp b/src/visitors/semantic_analysis_visitor.hpp index 7d1bbb90e2..35fef8d44b 100644 --- a/src/visitors/semantic_analysis_visitor.hpp +++ b/src/visitors/semantic_analysis_visitor.hpp @@ -25,8 +25,9 @@ * * 1. Check that a function or a procedure containing a TABLE statement contains only one argument * (mandatory in mod2c). - * 2. Check that destructor blocks are only inside mod file that are point_process - * 3. A TABLE statement in functions cannot have name list, and should have one in procedures + * 2. Check that destructor blocks are only inside mod file that are point_process. + * 3. A TABLE statement in functions cannot have name list, and should have one in procedures. + * 4. Check if ion variables from a `USEION` statement are not declared in `CONSTANT` block. */ #include "ast/ast.hpp" #include "visitors/ast_visitor.hpp" diff --git a/test/unit/visitor/semantic_analysis.cpp b/test/unit/visitor/semantic_analysis.cpp index 710cf35224..2e2314c1de 100644 --- a/test/unit/visitor/semantic_analysis.cpp +++ b/test/unit/visitor/semantic_analysis.cpp @@ -11,6 +11,7 @@ #include "parser/nmodl_driver.hpp" #include "test/unit/utils/test_utils.hpp" #include "visitors/semantic_analysis_visitor.hpp" +#include "visitors/symtab_visitor.hpp" using namespace nmodl; @@ -26,6 +27,7 @@ using nmodl::parser::NmodlDriver; bool run_semantic_analysis_visitor(const std::string& text) { NmodlDriver driver; const auto& ast = driver.parse_string(text); + SymtabVisitor().visit_program(*ast); return SemanticAnalysisVisitor{}.check(*ast); } @@ -105,3 +107,18 @@ SCENARIO("Destructor block", "[visitor][semantic_analysis]") { } } } + +SCENARIO("Ion variable in CONSTANT block", "[visitor][semantic_analysis]") { + GIVEN("A mod file with ion variable redeclared in a CONSTANT block") { + std::string nmodl_text = R"( + NEURON { + SUFFIX cdp4Nsp + USEION ca READ cao, cai, ica WRITE cai + } + CONSTANT { cao = 2 (mM) } + )"; + THEN("Semantic analysis fails") { + REQUIRE(run_semantic_analysis_visitor(nmodl_text)); + } + } +}