diff --git a/src/visitors/semantic_analysis_visitor.cpp b/src/visitors/semantic_analysis_visitor.cpp index 40d6b1667..365fac7ad 100644 --- a/src/visitors/semantic_analysis_visitor.cpp +++ b/src/visitors/semantic_analysis_visitor.cpp @@ -6,6 +6,7 @@ */ #include "visitors/semantic_analysis_visitor.hpp" +#include "ast/breakpoint_block.hpp" #include "ast/function_block.hpp" #include "ast/function_table_block.hpp" #include "ast/independent_block.hpp" @@ -170,5 +171,25 @@ void SemanticAnalysisVisitor::visit_mutex_unlock(const ast::MutexUnlock& /* node /// --> } +void SemanticAnalysisVisitor::visit_breakpoint_block(const ast::BreakpointBlock& node) { + /// <-- This code is for check 8 + solve_block_found_in_this_breakpoint_block = false; + node.visit_children(*this); + solve_block_found_in_this_breakpoint_block = false; + /// --> +} + +void SemanticAnalysisVisitor::visit_solve_block(const ast::SolveBlock& /* node */) { + /// <-- This code is for check 8 + if (solve_block_found_in_this_breakpoint_block) { + logger->critical( + "It is not allowed to have several solve blocks in the same breakpoint block"); + check_fail = true; + } else { + solve_block_found_in_this_breakpoint_block = true; + } + /// --> +} + } // namespace visitor } // namespace nmodl diff --git a/src/visitors/semantic_analysis_visitor.hpp b/src/visitors/semantic_analysis_visitor.hpp index 1deffcb15..f51b10dfe 100644 --- a/src/visitors/semantic_analysis_visitor.hpp +++ b/src/visitors/semantic_analysis_visitor.hpp @@ -31,6 +31,7 @@ * 5. Check if an independent variable is not 't'. * 6. Check that mutex are not badly use * 7. Check than function table got at least one argument. + * 8. Check that at most one solve block is present per breakpoint block. */ #include "ast/ast.hpp" #include "visitors/ast_visitor.hpp" @@ -54,6 +55,8 @@ class SemanticAnalysisVisitor: public ConstAstVisitor { bool is_point_process = false; /// true if we are inside a mutex locked part bool in_mutex = false; + /// true if we already found a solve block + bool solve_block_found_in_this_breakpoint_block = false; /// Store if we are in a procedure and if the arity of this is 1 void visit_procedure_block(const ast::ProcedureBlock& node) override; @@ -82,6 +85,11 @@ class SemanticAnalysisVisitor: public ConstAstVisitor { /// Look if MUTEXUNLOCK is outside a locked block void visit_mutex_unlock(const ast::MutexUnlock& node) override; + void visit_breakpoint_block(const ast::BreakpointBlock& node) override; + + /// Check how many solve block we got + void visit_solve_block(const ast::SolveBlock& node) override; + public: SemanticAnalysisVisitor(bool accel_backend = false) : accel_backend(accel_backend) {} diff --git a/test/unit/visitor/semantic_analysis.cpp b/test/unit/visitor/semantic_analysis.cpp index 23c58d266..0d51aeb75 100644 --- a/test/unit/visitor/semantic_analysis.cpp +++ b/test/unit/visitor/semantic_analysis.cpp @@ -165,3 +165,41 @@ SCENARIO("FUNCTION_TABLE block", "[visitor][semantic_analysis]") { } } } + + +SCENARIO("At most one solve block per breakpoint block", "[visitor][semantic_analysis]") { + GIVEN("A breakpoint block with only one solve block") { + std::string nmodl_text = R"( + BREAKPOINT { + SOLVE dX METHOD cnexp + } + )"; + THEN("Semantic analysis should success") { + REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text)); + } + } + GIVEN("2 breakpoints block with one solve block each") { + std::string nmodl_text = R"( + PROCEDURE foo() { + SOLVE dX METHOD cnexp + } + BREAKPOINT { + SOLVE dY METHOD cnexp + } + )"; + THEN("Semantic analysis should success") { + REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text)); + } + } + GIVEN("A breakpoint block with two solve blocks") { + std::string nmodl_text = R"( + BREAKPOINT { + SOLVE dX METHOD cnexp + SOLVE dY METHOD cnexp + } + )"; + THEN("Semantic analysis should fail") { + REQUIRE(run_semantic_analysis_visitor(nmodl_text)); + } + } +}