From 23f27b5638d259beb201da41f46659572dc1a568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 7 Jul 2026 16:39:30 +0200 Subject: [PATCH 01/54] Fixed namespace issues --- cmake/doctest.cmake | 30 +- cmake/libxml2.cmake | 2 +- cmake/sanitizers.cmake | 194 +++++++++++-- include/utap/AbstractBuilder.hpp | 28 +- include/utap/DocumentBuilder.hpp | 8 +- include/utap/ExpressionBuilder.hpp | 24 +- include/utap/PrettyPrinter.hpp | 22 +- include/utap/StatementBuilder.hpp | 8 +- include/utap/TypeChecker.hpp | 2 +- include/utap/builder.hpp | 63 ++-- include/utap/common.hpp | 77 +++-- include/utap/expression.hpp | 23 +- include/utap/property.hpp | 4 +- include/utap/symbols.hpp | 24 +- include/utap/type.hpp | 74 +++-- include/utap/utap.hpp | 18 +- src/AbstractBuilder.cpp | 33 +-- src/DocumentBuilder.cpp | 76 ++--- src/ExpressionBuilder.cpp | 230 +++++++-------- src/FeatureChecker.cpp | 31 +- src/PrettyPrinter.cpp | 48 ++-- src/StatementBuilder.cpp | 71 ++--- src/TypeChecker.cpp | 354 ++++++++++++----------- src/document.cpp | 41 ++- src/expression.cpp | 122 ++++---- src/keywords.hpp | 1 - src/lexer.l | 1 + src/libparser.hpp | 3 +- src/parser.y | 437 ++++++++++++++-------------- src/print.hpp | 10 +- src/property.cpp | 101 ++++--- src/statement.cpp | 10 +- src/symbols.cpp | 36 +-- src/type.cpp | 237 +++++++-------- src/xmlreader.cpp | 444 +++++++++++++++-------------- src/xmlwriter.cpp | 16 +- test/document_fixture.h | 4 +- test/expression_test.cpp | 172 +++++------ test/parser_test.cpp | 1 - test/pretty.cpp | 5 +- test/prettyprint_test.cpp | 10 +- test/statement_test.cpp | 27 +- 42 files changed, 1651 insertions(+), 1471 deletions(-) diff --git a/cmake/doctest.cmake b/cmake/doctest.cmake index 605b3b4a..1a6c34cb 100644 --- a/cmake/doctest.cmake +++ b/cmake/doctest.cmake @@ -1,13 +1,15 @@ -# Ensures that doctest unit testing framework is installed +# Downloads and compiles DocTest unit testing framework include(FetchContent) +#set(FETCHCONTENT_QUIET ON) +#set(FETCHCONTENT_UPDATES_DISCONNECTED ON) FetchContent_Declare(doctest - GIT_REPOSITORY https://github.com/doctest/doctest.git - GIT_TAG v2.4.12 # "main" for latest - GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) - GIT_PROGRESS TRUE # show download progress in Ninja - EXCLUDE_FROM_ALL ON # don't build if not used - FIND_PACKAGE_ARGS 2.4.12) + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG v2.5.2 # "main" for latest + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + EXCLUDE_FROM_ALL ON # don't build if not used + FIND_PACKAGE_ARGS 2.5.2) set(DOCTEST_WITH_TESTS OFF CACHE BOOL "Build tests/examples") set(DOCTEST_WITH_MAIN_IN_STATIC_LIB ON CACHE BOOL "Build a static lib for doctest::doctest_with_main") @@ -17,21 +19,15 @@ set(DOCTEST_USE_STD_HEADERS OFF CACHE BOOL "Use std headers") FetchContent_MakeAvailable(doctest) if(doctest_FOUND) # find_package - message(STATUS "Found doctest: ${doctest_DIR}") + message(STATUS "Found doctest: ${doctest_DIR}") else(doctest_FOUND) # FetchContent - message(STATUS "Fetched doctest: ${doctest_SOURCE_DIR}") + message(STATUS "Fetched doctest: ${doctest_SOURCE_DIR}") endif(doctest_FOUND) if (TARGET doctest::doctest) - message(STATUS " Available target: doctest::doctest") + message(STATUS " Available target: doctest::doctest") endif () if (TARGET doctest::doctest_with_main) - message(STATUS " Available target: doctest::doctest_with_main") -else () - add_library(doctest_with_main INTERFACE) - target_compile_definitions(doctest_with_main INTERFACE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) - target_link_libraries(doctest_with_main INTERFACE doctest::doctest) - add_library(doctest::doctest_with_main ALIAS doctest_with_main) - message(STATUS " Added target: doctest::doctest_with_main") + message(STATUS " Available target: doctest::doctest_with_main") endif () diff --git a/cmake/libxml2.cmake b/cmake/libxml2.cmake index 413b67b4..0c321578 100644 --- a/cmake/libxml2.cmake +++ b/cmake/libxml2.cmake @@ -10,7 +10,7 @@ FetchContent_Declare(LibXml2 GIT_SHALLOW ON GIT_PROGRESS ON EXCLUDE_FROM_ALL ON # don't build if not used - FIND_PACKAGE_ARGS 2.9.14) + FIND_PACKAGE_ARGS 2.13.9) set(LIBXML2_SHARED_LIBS OFF CACHE BOOL "LibXml2 shared libraries") set(LIBXML2_WITH_FTP OFF CACHE BOOL "LibXml2 FTP support") diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake index 8a4d41f1..b516368b 100644 --- a/cmake/sanitizers.cmake +++ b/cmake/sanitizers.cmake @@ -1,43 +1,183 @@ # Various sanitizers (runtime checks) for debugging -# Available with GCC and Clang on Linux and macOS (not on Windows yet) - -option(SSP "Stack Smashing Protector" OFF) # Available on Windows too -option(UBSAN "Undefined Behavior Sanitizer" OFF) -option(ASAN "Address Sanitizer" OFF) -option(LSAN "Leak Sanitizer" OFF) -option(TSAN "Thread Sanitizer" OFF) - -if (SSP) - add_compile_options(-fstack-protector) - add_link_options(-fstack-protector) - message(STATUS "Enable Stack Smashing Protector") +# Use options (e.g. -DASAN=ON) for your *entire* project +# AddressSanitizer also checks for stack abuse and leaks, but StackProtector and LeakSanitizer have less overhead. +# Mutually compatible sanitizers: SSP, UBSAN, LSAN, ASAN +# TSAN is incompatible with UBSAN, LSAN, ASAN +option(CXXWARN "Compiler warnings" OFF) +option(HARDENED "Enable flags which improve security without affecting ABI" OFF) +option(SSP "Stack Smashing Protector (GCC/Clang/AppleClang/MSVC)" OFF) +option(UBSAN "Undefined Behavior Sanitizer (GCC/Clang/AppleClang on Unix)" OFF) +option(LSAN "Leak Sanitizer (GCC/Clang/AppleClang on Unix)" OFF) +option(ASAN "Address Sanitizer (GCC/Clang/AppleClang on Unix, MSVC on Windows)" OFF) +option(MSAN "Memory (initialization) Sanitizer (Clang/AppleClang on Unix)" OFF) +option(TSAN "Thread Sanitizer (GCC/Clang/AppleClang on Unix)" OFF) +option(RTC_C "Runtime Checks for Conversions (MSVC on Windows)" OFF) +option(RTC_S "Runtime Checks for Stack (MSVC on Windows)" OFF) +option(RTC_U "Runtime Checks for Uninitialized (MSVC on Windows)" OFF) + +if (CXXWARN) + if (CMAKE_CXX_COMPILER_ID MATCHES GNU) + add_compile_options(-Wpedantic -Wall -Wextra) + message(STATUS "Enabled compiler warnings") + elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) + add_compile_options(-Wpedantic -Wall -Wextra) + # consider: -Wunsafe-buffer-usage + message(STATUS "Enabled compiler warnings") + elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + add_compile_options(/Wall) + message(STATUS "Enabled compiler warnings") + else() + message(STATUS "Failed to turn on compiler warnings for ${CMAKE_CXX_COMPILER_ID}") + endif () +endif (CXXWARN) + +if(SSP) + add_compile_options(-Wstack-protector -fstack-protector-strong -fstack-clash-protection) + add_link_options(-fstack-protector-strong -fstack-clash-protection) + message(STATUS "Enabled Stack Smashing Protector") +else(SSP) + message(STATUS "Disabled Stack Smashing Protector") endif(SSP) -if (ASAN OR UBSAN OR LSAN OR TSAN) - add_compile_options(-fno-omit-frame-pointer) - add_link_options(-fno-omit-frame-pointer) -endif() +if (HARDENED) + if(CMAKE_CXX_COMPILER_ID MATCHES GNU) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14) + # See `g++ --help=hardened` + add_compile_options(-Whardened -fhardened) + add_link_options(-Whardened -fhardened) + else () + add_compile_definitions(_FORTIFY_SOURCE=3 _GLIBCXX_ASSERTIONS) + add_compile_options(-fvisibility=hidden -ftrivial-auto-var-init=zero -fstack-protector-strong -fstack-clash-protection -fcf-protection=full) + add_link_options(-ftrivial-auto-var-init=zero -fstack-protector-strong -fstack-clash-protection -fcf-protection=full -Wl,-z,relro,-z,now) + endif () + message(STATUS "Enabled Hardened Security") + elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) + # See https://clang.llvm.org/docs/SafeStack.html + # Also https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html + # -fsanitize=cfi depends on libclang-rt-19-dev being installed + add_compile_definitions(_FORTIFY_SOURCE=3 _GLIBCXX_ASSERTIONS) + add_compile_options(-fvisibility=hidden -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -fsanitize=safe-stack) + add_link_options(-fstack-protector-strong -fstack-clash-protection -fcf-protection=full -fsanitize=safe-stack -Wl,-z,relro,-z,now) + # -flto -fsanitize=cfi # breaks in several weird ways + # See https://libcxx.llvm.org/Hardening.html + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_definitions(_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG _LIBCPP_ABI_BOUNDED_ITERATORS) + else () + add_compile_definitions(_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST) + endif () + message(STATUS "Enabled Hardened Security") + elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + add_compile_definitions(_MSVC_STL_HARDENING _STL_VERIFY _MSVC_STL_DESTRUCTOR_TOMBSTONES) + add_compile_options(/analyze /sdl /GS /guard:cf) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_definitions(_ALLOW_RTCc_IN_STL) + add_compile_options(/RTCs /RTCu) # incompatible with /O2, loops: /RTCc + endif () + message(STATUS "Enabled Hardened Security") + else () + message(WARNING "Failed to Harden Security for ${CMAKE_CXX_COMPILER_ID}") + endif () +else (HARDENED) + message(STATUS "Disabled Hardened Security") +endif (HARDENED) -if (UBSAN) +if(UBSAN) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=undefined) + message(STATUS "See MSVC sanitizers: https://learn.microsoft.com/en-us/cpp/sanitizers") + message(STATUS "Consider RTC_C, RTC_S, RTS_U instead") + else() add_compile_options(-fsanitize=undefined) add_link_options(-fsanitize=undefined) - message(STATUS "Enabled Undefined Behavior Sanitizer") + endif() + message(STATUS "Enabled Undefined Behavior Sanitizer") +else(UBSAN) + message(STATUS "Disabled Undefined Behavior Sanitizer") endif(UBSAN) -if (ASAN) +if(LSAN) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=leak) + message(STATUS "See MSVC sanitizers: https://learn.microsoft.com/en-us/cpp/sanitizers") + message(STATUS "Consider ASAN instead") + else() + add_compile_options(-fsanitize=leak) + add_link_options(-fsanitize=leak) + endif() + message(STATUS "Enabled Leak Sanitizer") +else(LSAN) + message(STATUS "Disabled Leak Sanitizer") +endif(LSAN) + +if(ASAN) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=address) + else() add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) - message(STATUS "Enabled Address Sanitizer") + endif() + message(STATUS "Enabled Address Sanitizer") +else(ASAN) + message(STATUS "Disabled Address Sanitizer") endif(ASAN) -if (LSAN) - add_compile_options(-fsanitize=leak) - add_link_options(-fsanitize=leak) - message(STATUS "Enabled Leak Sanitizer") -endif(LSAN) +if(MSAN) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=memory) + else() + add_compile_options(-fsanitize=memory) + add_link_options(-fsanitize=memory) + endif() + message(STATUS "Enabled Memory Sanitizer") +else(MSAN) + message(STATUS "Disabled Memory Sanitizer") +endif(MSAN) -if (TSAN) +if(TSAN) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=thread) + message(STATUS "See MSVC sanitizers: https://learn.microsoft.com/en-us/cpp/sanitizers") + else() add_compile_options(-fsanitize=thread) add_link_options(-fsanitize=thread) - message(STATUS "Enabled Thread Sanitizer") + endif() + message(STATUS "Enabled Thread Sanitizer") +else(TSAN) + message(STATUS "Disabled Thread Sanitizer") endif(TSAN) + +if(UBSAN OR LSAN OR ASAN OR MSAN OR TSAN) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + else() + add_compile_options(-fno-omit-frame-pointer) + add_link_options(-fno-omit-frame-pointer) + endif() +endif() + +if(RTC_C) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/RTCc) + add_compile_definitions(_ALLOW_RTCc_IN_STL) + message(STATUS "Enabled Runtime Check Conversions") + else() + message(WARNING "Runtime Check Conversions are not enabled for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif(RTC_C) + +if(RTC_S) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/RTCs) + message(STATUS "Enabled Runtime Check Stack") + else() + message(WARNING "Runtime Check Stack is not enabled for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif(RTC_S) + +if(RTC_U) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/RTCu) + message(STATUS "Enabled Runtime Check Uninitialized") + else() + message(WARNING "Runtime Check Uninitialized is not enabled for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif(RTC_U) diff --git a/include/utap/AbstractBuilder.hpp b/include/utap/AbstractBuilder.hpp index 1bea1818..c0132b1c 100644 --- a/include/utap/AbstractBuilder.hpp +++ b/include/utap/AbstractBuilder.hpp @@ -121,7 +121,7 @@ class AbstractBuilder : public ParserBuilder // 1 epxr,1sync,1expr void proc_select(std::string_view id) override; void proc_guard() override; - void proc_sync(Constants::Synchronisation type) override; // 1 expr + void proc_sync(Sync type) override; // 1 expr void proc_update() override; void proc_prob() override; /************************************************************ @@ -132,7 +132,7 @@ class AbstractBuilder : public ParserBuilder void instance_name_begin(std::string_view name) override; void instance_name_end(std::string_view name, uint32_t arguments) override; void proc_message(std::string_view from, std::string_view to, const int loc, const bool pch) override; - void proc_message(Constants::Synchronisation type) override; // 1 expr + void proc_message(Sync type) override; // 1 expr void proc_condition(const std::vector& anchors, const int loc, const bool pch, const bool hot) override; void proc_condition() override; // Label @@ -187,12 +187,12 @@ class AbstractBuilder : public ParserBuilder void expr_pre_increment() override; // 1 expr void expr_post_decrement() override; // 1 expr void expr_pre_decrement() override; // 1 expr - void expr_assignment(Constants::Kind op) override; // 2 expr - void expr_unary(Constants::Kind unaryop) override; // 1 expr - void expr_binary(Constants::Kind binaryop) override; // 2 expr - void expr_nary(Constants::Kind, uint32_t num) override; + void expr_assignment(Kind op) override; // 2 expr + void expr_unary(Kind unaryop) override; // 1 expr + void expr_binary(Kind binaryop) override; // 2 expr + void expr_nary(Kind, uint32_t num) override; void expr_scenario(std::string_view name) override; - void expr_ternary(Constants::Kind ternaryop, bool firstMissing) override; // 3 expr + void expr_ternary(Kind ternaryop, bool firstMissing) override; // 3 expr void expr_inline_if() override; // 3 expr void expr_comma() override; // 2 expr void expr_dot(std::string_view) override; // 1 expr @@ -204,15 +204,15 @@ class AbstractBuilder : public ParserBuilder void expr_sum_begin(std::string_view name) override; void expr_sum_end(std::string_view name) override; - void expr_proba_qualitative(Constants::Kind, Constants::Kind, double) override; - void expr_proba_quantitative(Constants::Kind) override; - void expr_proba_compare(Constants::Kind, Constants::Kind) override; + void expr_proba_qualitative(Kind, Kind, double) override; + void expr_proba_quantitative(Kind) override; + void expr_proba_compare(Kind, Kind) override; void expr_proba_expected(std::string_view identifier) override; void expr_simulate(int no_of_exprs, bool = false, int = 0) override; - void expr_builtin_function1(Constants::Kind) override; - void expr_builtin_function2(Constants::Kind) override; - void expr_builtin_function3(Constants::Kind) override; - void expr_optimize_exp(Constants::Kind, PRICETYPE, Constants::Kind) override; + void expr_builtin_function1(Kind) override; + void expr_builtin_function2(Kind) override; + void expr_builtin_function3(Kind) override; + void expr_optimize_exp(Kind, PriceType, Kind) override; void expr_load_strategy() override; void expr_save_strategy(std::string_view strategy_name) override; diff --git a/include/utap/DocumentBuilder.hpp b/include/utap/DocumentBuilder.hpp index 1df120cb..9debdb48 100644 --- a/include/utap/DocumentBuilder.hpp +++ b/include/utap/DocumentBuilder.hpp @@ -94,8 +94,8 @@ class DocumentBuilder : public StatementBuilder Declarations* getCurrentDeclarationBlock(); - Variable* addVariable(Type type, std::string_view name, Expression init, position_t pos) override; - bool addFunction(Type type, std::string_view name, position_t pos) override; + Variable* add_variable(Type type, std::string_view name, Expression init, position_t pos) override; + bool add_function(Type type, std::string_view name, position_t pos) override; void addSelectSymbolToFrame(std::string_view name, Frame&, position_t pos); @@ -122,7 +122,7 @@ class DocumentBuilder : public StatementBuilder void proc_edge_end(std::string_view from = {}, std::string_view to = {}) override; void proc_select(std::string_view id) override; void proc_guard() override; - void proc_sync(Constants::Synchronisation type) override; + void proc_sync(Sync type) override; void proc_update() override; void proc_prob() override; void instantiation_begin(std::string_view, uint32_t, std::string_view) override; @@ -142,7 +142,7 @@ class DocumentBuilder : public StatementBuilder void instance_name_begin(std::string_view name) override; void instance_name_end(std::string_view name, uint32_t arguments) override; void proc_message(std::string_view from, std::string_view to, const int loc, const bool pch) override; - void proc_message(Constants::Synchronisation type) override; + void proc_message(Sync type) override; void proc_condition(const std::vector& anchors, const int loc, const bool pch, const bool hot) override; void proc_condition() override; // Label diff --git a/include/utap/ExpressionBuilder.hpp b/include/utap/ExpressionBuilder.hpp index 6b7a460a..3dc37a53 100644 --- a/include/utap/ExpressionBuilder.hpp +++ b/include/utap/ExpressionBuilder.hpp @@ -199,13 +199,13 @@ class ExpressionBuilder : public AbstractBuilder void expr_pre_increment() override; void expr_post_decrement() override; void expr_pre_decrement() override; - void expr_assignment(Constants::Kind op) override; - void expr_unary(Constants::Kind unaryop) override; - void expr_binary(Constants::Kind binaryop) override; - void expr_nary(Constants::Kind op, uint32_t num) override; + void expr_assignment(Kind op) override; + void expr_unary(Kind unaryop) override; + void expr_binary(Kind binaryop) override; + void expr_nary(Kind op, uint32_t num) override; void expr_scenario(std::string_view name) override; Expression exprScenario(); - void expr_ternary(Constants::Kind ternaryop, bool firstMissing) override; + void expr_ternary(Kind ternaryop, bool firstMissing) override; void expr_inline_if() override; void expr_comma() override; void expr_dot(std::string_view) override; @@ -217,14 +217,14 @@ class ExpressionBuilder : public AbstractBuilder void expr_sum_begin(std::string_view name) override; void expr_sum_end(std::string_view name) override; - void expr_proba_qualitative(Constants::Kind, Constants::Kind, double) override; - void expr_proba_quantitative(Constants::Kind) override; - void expr_proba_compare(Constants::Kind, Constants::Kind) override; + void expr_proba_qualitative(Kind, Kind, double) override; + void expr_proba_quantitative(Kind) override; + void expr_proba_compare(Kind, Kind) override; void expr_proba_expected(std::string_view) override; - void expr_builtin_function1(Constants::Kind) override; - void expr_builtin_function2(Constants::Kind) override; - void expr_builtin_function3(Constants::Kind) override; - void expr_optimize_exp(Constants::Kind, PRICETYPE, Constants::Kind) override; + void expr_builtin_function1(Kind) override; + void expr_builtin_function2(Kind) override; + void expr_builtin_function3(Kind) override; + void expr_optimize_exp(Kind, PriceType, Kind) override; void expr_save_strategy(std::string_view strategy_name) override; void expr_load_strategy() override; diff --git a/include/utap/PrettyPrinter.hpp b/include/utap/PrettyPrinter.hpp index fc5b2f71..21492e5f 100644 --- a/include/utap/PrettyPrinter.hpp +++ b/include/utap/PrettyPrinter.hpp @@ -50,7 +50,7 @@ class PrettyPrinter : public AbstractBuilder uint32_t level{}; void indent(); - void indent(std::string& s); + void indent(std::string& s) const; public: PrettyPrinter(std::ostream& stream); @@ -114,7 +114,7 @@ class PrettyPrinter : public AbstractBuilder void proc_location_init(std::string_view id) override; void proc_select(std::string_view id) override; void proc_guard() override; - void proc_sync(Constants::Synchronisation type) override; + void proc_sync(Sync type) override; void proc_update() override; void proc_edge_begin(std::string_view source, std::string_view target, const bool control); void proc_edge_begin(std::string_view source, std::string_view target, const bool control, @@ -135,12 +135,12 @@ class PrettyPrinter : public AbstractBuilder void expr_pre_increment() override; void expr_post_decrement() override; void expr_pre_decrement() override; - void expr_assignment(Constants::Kind op) override; - void expr_unary(Constants::Kind op) override; - void expr_binary(Constants::Kind op) override; - void expr_nary(Constants::Kind op, uint32_t num) override; + void expr_assignment(Kind op) override; + void expr_unary(Kind op) override; + void expr_binary(Kind op) override; + void expr_nary(Kind op, uint32_t num) override; void expr_scenario(std::string_view name) override; - void expr_ternary(Constants::Kind op, bool firstMissing) override; + void expr_ternary(Kind op, bool firstMissing) override; void expr_inline_if() override; void expr_comma() override; void expr_dot(std::string_view) override; @@ -151,13 +151,13 @@ class PrettyPrinter : public AbstractBuilder void expr_exists_end(std::string_view name) override; void expr_sum_begin(std::string_view name) override; void expr_sum_end(std::string_view name) override; - void expr_proba_quantitative(Constants::Kind) override; + void expr_proba_quantitative(Kind) override; void expr_MITL_diamond(int, int) override; void expr_MITL_box(int, int) override; void expr_simulate(int no_of_expr, bool filter_prop, int max_accept_runs) override; - void expr_builtin_function1(Constants::Kind kind) override; - void expr_builtin_function2(Constants::Kind kind) override; - void expr_builtin_function3(Constants::Kind kind) override; + void expr_builtin_function1(Kind kind) override; + void expr_builtin_function2(Kind kind) override; + void expr_builtin_function3(Kind kind) override; void before_update() override; void after_update() override; void instantiation_begin(std::string_view, uint32_t, std::string_view) override; diff --git a/include/utap/StatementBuilder.hpp b/include/utap/StatementBuilder.hpp index 5d6abec8..c2c8e897 100644 --- a/include/utap/StatementBuilder.hpp +++ b/include/utap/StatementBuilder.hpp @@ -58,11 +58,11 @@ class StatementBuilder : public ExpressionBuilder /** path to libraries*/ std::vector libpaths; - virtual Variable* addVariable(Type type, std::string_view name, Expression init, position_t pos) = 0; - virtual bool addFunction(Type type, std::string_view name, position_t pos) = 0; + virtual Variable* add_variable(Type type, std::string_view name, Expression init, position_t pos) = 0; + virtual bool add_function(Type type, std::string_view name, position_t pos) = 0; - static void collectDependencies(std::set&, const Expression&); - static void collectDependencies(std::set&, const Type&); + static void collect_dependencies(std::set&, const Expression&); + static void collect_dependencies(std::set&, const Type&); public: explicit StatementBuilder(Document&, std::vector libpaths = {}); diff --git a/include/utap/TypeChecker.hpp b/include/utap/TypeChecker.hpp index 889853e4..7b3728f4 100644 --- a/include/utap/TypeChecker.hpp +++ b/include/utap/TypeChecker.hpp @@ -144,7 +144,7 @@ class TypeChecker : public DocumentVisitor, public AbstractStatementVisitor bool checkBound(const Expression& expr); bool checkPredicate(const Expression& expr); bool checkProbBound(const Expression& expr); - bool checkUntilCond(Constants::Kind kind, const Expression& expr); + bool checkUntilCond(Kind kind, const Expression& expr); bool checkMonitoredExpr(const Expression& expr); bool checkPathQuant(const Expression& expr); bool checkAggregationOp(const Expression& expr); diff --git a/include/utap/builder.hpp b/include/utap/builder.hpp index c79b6916..e123377c 100644 --- a/include/utap/builder.hpp +++ b/include/utap/builder.hpp @@ -79,19 +79,6 @@ class TypeException : public std::logic_error class ParserBuilder { public: - /********************************************************************* - * Type prefix which can be applied in front of some type. - */ - enum class TypePrefix : uint8_t { - NONE = 0, - CONST = 1, - URGENT = 2, - BROADCAST = 4, - URGENT_BROADCAST = 6, - SYSTEM_META = 8, - HYBRID = 16 - }; - std::vector lscTemplateNames; virtual ~ParserBuilder() noexcept = default; @@ -253,7 +240,7 @@ class ParserBuilder virtual void proc_edge_end(std::string_view from, std::string_view to) = 0; virtual void proc_select(std::string_view id) = 0; // 1 expr virtual void proc_guard() = 0; // 1 expr - virtual void proc_sync(Constants::Synchronisation type) = 0; // 1 expr + virtual void proc_sync(Sync type) = 0; // 1 expr virtual void proc_update() = 0; // 1 expr virtual void proc_prob() = 0; virtual void proc_branchpoint(std::string_view name) = 0; @@ -265,7 +252,7 @@ class ParserBuilder virtual void instance_name_begin(std::string_view name) = 0; virtual void instance_name_end(std::string_view name, uint32_t arguments) = 0; virtual void proc_message(std::string_view from, std::string_view to, const int loc, const bool pch) = 0; - virtual void proc_message(Constants::Synchronisation type) = 0; + virtual void proc_message(Sync type) = 0; virtual void proc_condition(const std::vector& anchors, const int loc, const bool pch, const bool hot) = 0; virtual void proc_condition() = 0; // 1 expr @@ -330,13 +317,12 @@ class ParserBuilder virtual void expr_pre_increment() = 0; // 1 expr virtual void expr_post_decrement() = 0; // 1 expr virtual void expr_pre_decrement() = 0; // 1 expr - virtual void expr_assignment(Constants::Kind op) = 0; // 2 expr - virtual void expr_unary(Constants::Kind unaryop) = 0; // 1 expr - virtual void expr_binary(Constants::Kind binaryop) = 0; // 2 expr - virtual void expr_nary(Constants::Kind, uint32_t num) = 0; // n expr + virtual void expr_assignment(Kind op) = 0; // 2 expr + virtual void expr_unary(Kind unaryop) = 0; // 1 expr + virtual void expr_binary(Kind binaryop) = 0; // 2 expr + virtual void expr_nary(Kind, uint32_t num) = 0; // n expr virtual void expr_scenario(std::string_view name) = 0; // LSC - virtual void expr_ternary(Constants::Kind ternaryop, - bool firstMissing = false) = 0; // 3 expr + virtual void expr_ternary(Kind ternaryop, bool firstMissing = false) = 0; // 3 expr virtual void expr_inline_if() = 0; // 3 expr virtual void expr_comma() = 0; // 2 expr virtual void expr_dot(std::string_view) = 0; // 1 expr @@ -349,19 +335,17 @@ class ParserBuilder virtual void expr_sum_end(std::string_view name) = 0; // Extensions for SMC: - virtual void expr_proba_qualitative(Constants::Kind, Constants::Kind, double) = 0; ///< estimate Pr - virtual void expr_proba_quantitative(Constants::Kind) = 0; ///< evaluate if Pr >= value - virtual void expr_proba_compare(Constants::Kind, Constants::Kind) = 0; ///< compare two Prs + virtual void expr_proba_qualitative(Kind, Kind, double) = 0; ///< estimate Pr + virtual void expr_proba_quantitative(Kind) = 0; ///< evaluate if Pr >= value + virtual void expr_proba_compare(Kind, Kind) = 0; ///< compare two Prs virtual void expr_proba_expected(std::string_view identifier) = 0; ///< estimate mean value virtual void expr_simulate(int nb_of_exprs, bool filter_prop = false, int max_accepting_runs = 0) = 0; - virtual void expr_builtin_function1(Constants::Kind) = 0; - virtual void expr_builtin_function2(Constants::Kind) = 0; - virtual void expr_builtin_function3(Constants::Kind) = 0; + virtual void expr_builtin_function1(Kind) = 0; + virtual void expr_builtin_function2(Kind) = 0; + virtual void expr_builtin_function3(Kind) = 0; // Extensions for learning: - enum PRICETYPE { TIMEPRICE, EXPRPRICE, PROBAPRICE }; - virtual void expr_optimize_exp(Constants::Kind, PRICETYPE, - Constants::Kind) = 0; ///< minimize/maximize expected value query + virtual void expr_optimize_exp(Kind, PriceType, Kind) = 0; ///< minimize/maximize expected value query virtual void expr_load_strategy() = 0; virtual void expr_save_strategy(std::string_view strategy_name) = 0; @@ -461,8 +445,6 @@ TypeException strategy_not_declared_error(std::string_view name); TypeException unknown_dynamic_template_error(std::string_view name); TypeException shadows_a_variable_warning(std::string_view name); -} // namespace UTAP - /** * Parse a file in the XTA format, reporting the document to the given * implementation of the the ParserBuilder interface and reporting @@ -470,9 +452,9 @@ TypeException shadows_a_variable_warning(std::string_view name); * is used; otherwise the 3.x syntax is used. On success, this * function returns with a positive value. */ -int32_t parse_XTA(FILE*, UTAP::ParserBuilder&, bool newxta); +int32_t parse_XTA(FILE*, ParserBuilder&, bool newxta); -int32_t parse_XTA(const char*, UTAP::ParserBuilder&, bool newxta); +int32_t parse_XTA(const char*, ParserBuilder&, bool newxta); /** * Parse a buffer in the XTA format, reporting the document to the given @@ -481,7 +463,7 @@ int32_t parse_XTA(const char*, UTAP::ParserBuilder&, bool newxta); * is used; otherwise the 3.x syntax is used. On success, this * function returns with a positive value. */ -int32_t parse_XTA(const char*, UTAP::ParserBuilder&, bool newxta, UTAP::XTAPart part, std::string_view xpath); +int32_t parse_XTA(const char*, ParserBuilder&, bool newxta, XTAPart part, std::string_view xpath); /** * Parse a buffer in the XML format, reporting the document to the given @@ -490,7 +472,7 @@ int32_t parse_XTA(const char*, UTAP::ParserBuilder&, bool newxta, UTAP::XTAPart * is used; otherwise the 3.x syntax is used. On success, this * function returns with a positive value. */ -int32_t parse_XML_buffer(const char* buffer, UTAP::ParserBuilder&, bool newxta); +int32_t parse_XML_buffer(const char* buffer, ParserBuilder&, bool newxta); /** * Parse the file with the given name assuming it is in the XML @@ -500,21 +482,22 @@ int32_t parse_XML_buffer(const char* buffer, UTAP::ParserBuilder&, bool newxta); * otherwise the 3.x syntax is used. On success, this function returns * with a positive value. */ -int32_t parse_XML_file(const std::filesystem::path& path, UTAP::ParserBuilder&, bool newxta); +int32_t parse_XML_file(const std::filesystem::path& path, ParserBuilder&, bool newxta); -int32_t parse_XML_fd(int fd, UTAP::ParserBuilder& pb, bool newxta); +int32_t parse_XML_fd(int fd, ParserBuilder& pb, bool newxta); /** * Parse properties from a buffer. The properties are reported using * the given ParserBuilder and errors are reported using the * ErrorHandler. */ -int32_t parse_property(const char* buffer, UTAP::ParserBuilder& aParserBuilder, const std::string& xpath = {}); +int32_t parse_property(const char* buffer, ParserBuilder& aParserBuilder, const std::string& xpath = {}); /** * Parse properties from a file. The properties are reported using the * given ParserBuilder and errors are reported using the ErrorHandler. */ -int32_t parse_property(FILE*, UTAP::ParserBuilder& aParserBuilder); +int32_t parse_property(FILE*, ParserBuilder& aParserBuilder); +} // namespace UTAP #endif /* UTAP_BUILDER_HH */ diff --git a/include/utap/common.hpp b/include/utap/common.hpp index 41eb2b0c..bb210743 100644 --- a/include/utap/common.hpp +++ b/include/utap/common.hpp @@ -1,7 +1,7 @@ // -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- /* libutap - Uppaal Timed Automata Parser. - Copyright (C) 2020 Aalborg University. + Copyright (C) 2020-2026 Aalborg University. Copyright (C) 2002-2006 Uppsala University and Aalborg University. This library is free software; you can redistribute it and/or @@ -24,7 +24,9 @@ #define UTAP_COMMON_HH namespace UTAP { -namespace Constants { + +//TODO: upgrade to `using enum` from C++20 +namespace KindNames { enum Kind { PLUS, MINUS, @@ -289,39 +291,62 @@ enum Kind { DYNAMIC_EVAL, PROCESS_VAR, DOUBLE_INV_GUARD, - }; +} // namespace KindNames +using KindNames::Kind; /********************************************************** * Synchronisations: */ -enum Synchronisation { SYNC_QUE, SYNC_BANG, SYNC_CSP }; -} // namespace Constants +namespace SyncNames { enum Sync { QUE, BANG, CSP }; } +using SyncNames::Sync; /** Type for specifying which XTA part to parse (syntax switch) */ +namespace XTAPartNames { enum XTAPart { - S_XTA, // entire document - S_DECLARATION, - S_LOCAL_DECL, - S_INST, - S_SYSTEM, - S_PARAMETERS, - S_INVARIANT, - S_EXPONENTIAL_RATE, - S_SELECT, - S_GUARD, - S_SYNC, - S_ASSIGN, - S_EXPRESSION, - S_EXPRESSION_LIST, - S_PROPERTY, - S_XTA_PROCESS, - S_PROBABILITY, - /*LSC*/ S_INSTANCE_LINE, - S_MESSAGE, - S_UPDATE, - S_CONDITION + XTA, // entire document + DECLARATION, + LOCAL_DECL, + INST, + SYSTEM, + PARAMETERS, + INVARIANT, + EXPONENTIAL_RATE, + SELECT, + GUARD, + SYNC, + ASSIGN, + EXPRESSION, + EXPRESSION_LIST, + PROPERTY, + XTA_PROCESS, + PROBABILITY, + /*LSC*/ INSTANCE_LINE, + MESSAGE, + UPDATE, + CONDITION +}; +} // namespace XTAPartNS +using XTAPartNames::XTAPart; + +/********************************************************************* + * Type prefix which can be applied in front of some type. + */ +namespace TypePrefixNames { +enum TypePrefix { + NONE = 0, + CONST = 1, + URGENT = 2, + BROADCAST = 4, + URGENT_BROADCAST = 6, + SYSTEM_META = 8, + HYBRID = 16 }; +} // namespace TypePrefixNames +using TypePrefixNames::TypePrefix; + +namespace PriceTypeNames { enum PriceType { TIME, EXPR, PROBA }; } +using PriceTypeNames::PriceType; } // namespace UTAP diff --git a/include/utap/expression.hpp b/include/utap/expression.hpp index 9431f313..8d2d23d1 100644 --- a/include/utap/expression.hpp +++ b/include/utap/expression.hpp @@ -69,10 +69,9 @@ namespace UTAP { class Expression { -private: - struct expression_data; - std::shared_ptr data = nullptr; // PIMPL pattern with cheap/shallow copying - Expression(Constants::Kind, const position_t&); + struct Data; + std::shared_ptr data = nullptr; // PIMPL pattern with cheap/shallow copying + Expression(Kind, const position_t&); public: /// Default constructor creates an empty expression. @@ -98,7 +97,7 @@ class Expression Expression clone_deeper(const Frame& frame, const Frame& select = {}) const; /// Returns the kind of the expression. - Constants::Kind get_kind() const; + Kind get_kind() const; /// Returns the number of subexpression. uint32_t get_size() const; @@ -134,7 +133,7 @@ class Expression bool empty() const; /// Returns the synchronisation type of SYNC operations. - Constants::Synchronisation get_sync() const; + Sync get_sync() const; /// Outputs a textual representation of the expression. std::ostream& print(std::ostream& os, bool old = false) const; @@ -214,7 +213,7 @@ class Expression Expression subst(const Symbol&, Expression) const; /// Precedence of expression type, higher precedence goes before low precedence - static int get_precedence(Constants::Kind); + static int get_precedence(Kind); /// Create a CONSTANT expression. static Expression create_constant(int32_t, position_t = {}); @@ -228,22 +227,22 @@ class Expression static Expression create_identifier(const Symbol&, position_t = {}); /// Create a unary expression - static Expression create_unary(Constants::Kind, Expression, position_t = {}, Type = {}); + static Expression create_unary(Kind, Expression, position_t = {}, Type = {}); /** Create a binary expression */ - static Expression create_binary(Constants::Kind, Expression, Expression, position_t = {}, Type = {}); + static Expression create_binary(Kind, Expression, Expression, position_t = {}, Type = {}); /** Create a ternary expression */ - static Expression create_ternary(Constants::Kind, Expression, Expression, Expression, position_t = {}, Type = {}); + static Expression create_ternary(Kind, Expression, Expression, Expression, position_t = {}, Type = {}); /** Create an n-ary expression */ - static Expression create_nary(Constants::Kind, std::vector sub, position_t = {}, Type = {}); + static Expression create_nary(Kind, std::vector sub, position_t = {}, Type = {}); /** Create a DOT expression */ static Expression create_dot(Expression, int32_t index, position_t = {}, Type = {}); /** Create a SYNC expression */ - static Expression create_sync(Expression, Constants::Synchronisation, position_t = {}); + static Expression create_sync(Expression, Sync, position_t = {}); /** Create a DEADLOCK expression */ static Expression create_deadlock(position_t = {}); diff --git a/include/utap/property.hpp b/include/utap/property.hpp index 06ab0369..e0484d65 100644 --- a/include/utap/property.hpp +++ b/include/utap/property.hpp @@ -208,9 +208,9 @@ class PropertyBuilder : public UTAP::StatementBuilder, public std::enable_shared protected: std::list properties{}; // TigaPropertyBuilder assumes stable list and stores pointers. - UTAP::Variable* addVariable(UTAP::Type type, std::string_view name, UTAP::Expression init, + UTAP::Variable* add_variable(UTAP::Type type, std::string_view name, UTAP::Expression init, UTAP::position_t pos) override; - bool addFunction(UTAP::Type type, std::string_view name, UTAP::position_t pos) override; + bool add_function(UTAP::Type type, std::string_view name, UTAP::position_t pos) override; void typeCheck(UTAP::Expression& expr); bool allowProcessReferences() override; diff --git a/include/utap/symbols.hpp b/include/utap/symbols.hpp index 0abc848c..13a12d39 100644 --- a/include/utap/symbols.hpp +++ b/include/utap/symbols.hpp @@ -59,7 +59,6 @@ class NoParentException : public std::exception */ class Symbol { -private: struct Data; std::shared_ptr data{nullptr}; // pImpl pattern @@ -70,22 +69,15 @@ class Symbol public: /// Default constructor Symbol() = default; - Symbol(const Symbol&) = default; + ~Symbol() noexcept; ///< hidden dtor due to pImpl + Symbol(const Symbol&) = default; ///< light/shallow copy + Symbol& operator=(const Symbol&) = default; ///< light/shallow copy Symbol(Symbol&&) = default; - Symbol& operator=(const Symbol&) = default; Symbol& operator=(Symbol&&) = default; - /// Destructor - ~Symbol() noexcept; - - /// Equality operator - bool operator==(const Symbol&) const; - - /// Inequality operator - bool operator!=(const Symbol&) const; - - /// Less-than operator - bool operator<(const Symbol&) const; + bool operator==(const Symbol& other) const { return data == other.data; } + bool operator!=(const Symbol& other) const { return !(*this == other); } + bool operator<(const Symbol& other) const { return data < other.data; } /// Get frame this symbol belongs to Frame get_frame() const; // TODO: consider removing this method (mostly unused) @@ -159,10 +151,10 @@ class Frame Frame make_sub(); /// Equality operator - bool operator==(const Frame&) const; + bool operator==(const Frame& other) const { return data == other.data; } /// Inequality operator - bool operator!=(const Frame&) const; + bool operator!=(const Frame& other) const { return !(*this == other); } /// Returns the number of symbols in this frame uint32_t get_size() const; diff --git a/include/utap/type.hpp b/include/utap/type.hpp index bb84614e..ce66ab2f 100644 --- a/include/utap/type.hpp +++ b/include/utap/type.hpp @@ -93,23 +93,22 @@ class Symbol; */ class Type { -private: struct type_data; std::shared_ptr data; public: - explicit Type(Constants::Kind kind, const position_t& pos, size_t size); + explicit Type(Kind kind, const position_t& pos, size_t size); /// Default constructor creates a null-type. Type() = default; /// Checks if types are equal - bool operator==(const Type&) const; + bool operator==(const Type& other) const { return data == other.data; } /// Checks if types are not equal - bool operator!=(const Type&) const; + bool operator!=(const Type& other) const { return !(*this == other); } /// Returns the kind of type object. - Constants::Kind get_kind() const; + Kind get_kind() const; /** * Returns the position of the type in the input file. This @@ -121,7 +120,7 @@ class Type uint32_t size() const; /// Less-than operator. - bool operator<(const Type&) const; + bool operator<(const Type& other) const { return data < other.data; } /// Returns the \a i'th child. const Type& operator[](uint32_t) const; @@ -176,72 +175,72 @@ class Type std::string declaration() const; /// Shortcut for is(RANGE). - bool is_range() const { return is(Constants::RANGE); } + bool is_range() const { return is(Kind::RANGE); } /// Shortcut for is(INT). - bool is_integer() const { return is(Constants::INT); } + bool is_integer() const { return is(Kind::INT); } /// Shortcut for is(BOOL). - bool is_boolean() const { return is(Constants::BOOL); } + bool is_boolean() const { return is(Kind::BOOL); } /// Shortcut for is(FUNCTION). - bool is_function() const { return is(Constants::FUNCTION); } + bool is_function() const { return is(Kind::FUNCTION); } /// Shortcut for is(FUNCTION_EXTERNAL). - bool is_function_external() const { return is(Constants::FUNCTION_EXTERNAL); } + bool is_function_external() const { return is(Kind::FUNCTION_EXTERNAL); } /// Shortcut for is(PROCESS). - bool is_process() const { return is(Constants::PROCESS); } + bool is_process() const { return is(Kind::PROCESS); } /// Shortcut for is(PROCESS_SET). - bool is_process_set() const { return is(Constants::PROCESS_SET); } + bool is_process_set() const { return is(Kind::PROCESS_SET); } /// Shortcut for is(LOCATION). - bool is_location() const { return is(Constants::LOCATION); } + bool is_location() const { return is(Kind::LOCATION); } /// Shortcut for is(LOCATION_EXPR). - bool is_location_expr() const { return is(Constants::LOCATION_EXPR); } + bool is_location_expr() const { return is(Kind::LOCATION_EXPR); } /// Shortcut for is(INSTANCELINE). - bool is_instance_line() const { return is(Constants::INSTANCE_LINE); } + bool is_instance_line() const { return is(Kind::INSTANCE_LINE); } /// Shortcut for is(BRANCHPOINT). - bool is_branchpoint() const { return is(Constants::BRANCHPOINT); } + bool is_branchpoint() const { return is(Kind::BRANCHPOINT); } /// Shortcut for is(CHANNEL). - bool is_channel() const { return is(Constants::CHANNEL); } + bool is_channel() const { return is(Kind::CHANNEL); } /// Shortcut for is(ARRAY). - bool is_array() const { return is(Constants::ARRAY); } + bool is_array() const { return is(Kind::ARRAY); } /// Shortcut for is(SCALAR). - bool is_scalar() const { return is(Constants::SCALAR); } + bool is_scalar() const { return is(Kind::SCALAR); } /// Shortcut for is(CLOCK). - bool is_clock() const { return is(Constants::CLOCK); } + bool is_clock() const { return is(Kind::CLOCK); } /// Shortcut for is(RECORD). - bool is_record() const { return is(Constants::RECORD); } + bool is_record() const { return is(Kind::RECORD); } /// Shortcut for is(DIFF). - bool is_diff() const { return is(Constants::DIFF); } + bool is_diff() const { return is(Kind::DIFF); } /// Shortcut for is(VOID_TYPE). - bool is_void() const { return is(Constants::VOID_TYPE); } + bool is_void() const { return is(Kind::VOID_TYPE); } /// Shortcut for is(COST). - bool is_cost() const { return is(Constants::COST); } + bool is_cost() const { return is(Kind::COST); } /// Shortcut for is(DOUBLE). - bool is_double() const { return is(Constants::DOUBLE); } + bool is_double() const { return is(Kind::DOUBLE); } /// Shortcut for is(STRING). - bool is_string() const { return is(Constants::STRING); } + bool is_string() const { return is(Kind::STRING); } /// True if the type is a boolean, integer, or location bool is_integral() const { - using namespace Constants; + using namespace KindNames; return is(INT) || is(BOOL) || is(PROCESS_VAR) || is(LOCATION) || is(LOCATION_EXPR); } @@ -249,33 +248,33 @@ class Type * Returns true if this is an invariant, boolean or * integer. Shortcut for isIntegral() || is(INVARIANT). */ - bool is_invariant() const { return is(Constants::INVARIANT) || is_integral(); } + bool is_invariant() const { return is(Kind::INVARIANT) || is_integral(); } /** * Returns true if this is a guard, invariant, boolean or * integer. Shortcut for is(GUARD) || is_invariant(). */ - bool is_guard() const { return is(Constants::GUARD) || is_invariant(); } + bool is_guard() const { return is(Kind::GUARD) || is_invariant(); } /** * Returns true if this is a probability or integer. Shortcut * for is(PROBABILITY) || isInteger(). */ - bool is_probability() const { return is(Constants::PROBABILITY) || is_integer() || is_double() || is_clock(); } + bool is_probability() const { return is(Kind::PROBABILITY) || is_integer() || is_double() || is_clock(); } /** * Returns true if this is a constraint, guard, invariant, * boolean or integer. Shortcut for is(CONSTRAINT) || * is_guard(). */ - bool is_constraint() const { return is(Constants::CONSTRAINT) || is_guard(); } + bool is_constraint() const { return is(Kind::CONSTRAINT) || is_guard(); } /** * Returns true if this is a formula, constraint, guard, * invariant, boolean or integer. Shortcut for is(FORMULA) || * is_constraint(). */ - bool is_formula() const { return is(Constants::FORMULA) || is_constraint(); } + bool is_formula() const { return is(Kind::FORMULA) || is_constraint(); } /** * Removes any leading prefixes, RANGE, REF and LABEL types @@ -305,7 +304,7 @@ class Type /** Returns true if the type has kind \a kind or if type is a * prefix, RANGE or REF type and the getChild().is(kind) * returns true. */ - bool is(Constants::Kind kind) const; + bool is(Kind kind) const; /** Returns true if two types are compatible for equality operator. * Types are compatible if they are structurally @@ -364,7 +363,7 @@ class Type * could be anything and it is the responsibility of the * caller to make sure that the given kind is a valid prefix. */ - Type create_prefix(Constants::Kind kind, position_t = position_t()) const; + Type create_prefix(Kind kind, position_t = position_t()) const; /// Creates a LABEL. Type create_label(std::string_view, position_t = position_t()) const; @@ -373,7 +372,7 @@ class Type static Type create_range(Type primitive, Expression from, Expression till, position_t = position_t()); /// Creates a primitive type - static Type create_primitive(Constants::Kind, position_t = position_t()); + static Type create_primitive(Kind, position_t = position_t()); /// Creates an array type. static Type create_array(Type sub, Type size, position_t = position_t()); @@ -401,9 +400,8 @@ class Type static Type create_instance(const Frame&, position_t = position_t()); /// Creates a new LSC instance type static Type create_LSC_instance(const Frame&, position_t = position_t()); + friend std::ostream& operator<<(std::ostream&, const Type&); }; } // namespace UTAP -std::ostream& operator<<(std::ostream& o, const UTAP::Type& t); - #endif // UTAP_TYPE_HH diff --git a/include/utap/utap.hpp b/include/utap/utap.hpp index 0c0a3810..c7a0e4bb 100644 --- a/include/utap/utap.hpp +++ b/include/utap/utap.hpp @@ -32,17 +32,21 @@ #include #include -bool parse_XTA(FILE*, UTAP::Document&, bool newxta); -bool parse_XTA(const char* buffer, UTAP::Document&, bool newxta); -int32_t parse_XML_buffer(const char* buffer, UTAP::Document&, bool newxta, +namespace UTAP { + +bool parse_XTA(FILE*, Document&, bool newxta); +bool parse_XTA(const char* buffer, Document&, bool newxta); +int32_t parse_XML_buffer(const char* buffer, Document&, bool newxta, const std::vector& libpaths = {}); -int32_t parse_XML_file(const std::filesystem::path&, UTAP::Document&, bool newxta, +int32_t parse_XML_file(const std::filesystem::path&, Document&, bool newxta, const std::vector& libpaths = {}); -int32_t parse_XML_fd(int fd, UTAP::Document&, bool newxta, const std::vector& libpaths = {}); -UTAP::expression_t parse_expression(const char* buffer, UTAP::Document&, bool); -int32_t write_XML_file(const char* filename, UTAP::Document& doc); +int32_t parse_XML_fd(int fd, Document&, bool newxta, const std::vector& libpaths = {}); +expression_t parse_expression(const char* buffer, Document&, bool); +int32_t write_XML_file(const char* filename, Document& doc); /** returns a string representation of built-in types and constants (see parser.y) */ const char* utap_builtin_declarations(); +} + #endif /* UTAP_HH */ diff --git a/src/AbstractBuilder.cpp b/src/AbstractBuilder.cpp index fbb76c7a..fc70ccdb 100644 --- a/src/AbstractBuilder.cpp +++ b/src/AbstractBuilder.cpp @@ -26,8 +26,7 @@ #include #include -using namespace UTAP; - +namespace UTAP { /* void ParserBuilder::handleWarning(const char* msg, ...) { @@ -118,12 +117,12 @@ void AbstractBuilder::proc_edge_begin(std::string_view from, std::string_view to void AbstractBuilder::proc_edge_end(std::string_view from, std::string_view to) { UNSUPPORTED; } void AbstractBuilder::proc_select(std::string_view id) { UNSUPPORTED; } void AbstractBuilder::proc_guard() { UNSUPPORTED; } -void AbstractBuilder::proc_sync(Constants::Synchronisation type) { UNSUPPORTED; } +void AbstractBuilder::proc_sync(Sync type) { UNSUPPORTED; } void AbstractBuilder::proc_update() { UNSUPPORTED; } void AbstractBuilder::proc_prob() { UNSUPPORTED; } // LSC -void AbstractBuilder::proc_message(Constants::Synchronisation type) { UNSUPPORTED; } +void AbstractBuilder::proc_message(Sync type) { UNSUPPORTED; } void AbstractBuilder::proc_instance_line() { UNSUPPORTED; } void AbstractBuilder::instance_name_begin(std::string_view name) { UNSUPPORTED; } void AbstractBuilder::instance_name_end(std::string_view name, uint32_t arguments) { UNSUPPORTED; } @@ -190,16 +189,16 @@ void AbstractBuilder::expr_post_increment() { UNSUPPORTED; } void AbstractBuilder::expr_pre_increment() { UNSUPPORTED; } void AbstractBuilder::expr_post_decrement() { UNSUPPORTED; } void AbstractBuilder::expr_pre_decrement() { UNSUPPORTED; } -void AbstractBuilder::expr_assignment(Constants::Kind op) { UNSUPPORTED; } -void AbstractBuilder::expr_unary(Constants::Kind unaryop) { UNSUPPORTED; } -void AbstractBuilder::expr_binary(Constants::Kind binaryop) { UNSUPPORTED; } -void AbstractBuilder::expr_nary(Constants::Kind kind, uint32_t num) { UNSUPPORTED; } +void AbstractBuilder::expr_assignment(Kind op) { UNSUPPORTED; } +void AbstractBuilder::expr_unary(Kind unaryop) { UNSUPPORTED; } +void AbstractBuilder::expr_binary(Kind binaryop) { UNSUPPORTED; } +void AbstractBuilder::expr_nary(Kind kind, uint32_t num) { UNSUPPORTED; } // LSC void AbstractBuilder::expr_scenario(std::string_view name) { UNSUPPORTED; } // end LSC -void AbstractBuilder::expr_ternary(Constants::Kind ternaryop, bool firstMissing) { UNSUPPORTED; } +void AbstractBuilder::expr_ternary(Kind ternaryop, bool firstMissing) { UNSUPPORTED; } void AbstractBuilder::expr_inline_if() { UNSUPPORTED; } void AbstractBuilder::expr_comma() { UNSUPPORTED; } void AbstractBuilder::expr_location() { UNSUPPORTED; } @@ -210,12 +209,12 @@ void AbstractBuilder::expr_forall_end(std::string_view name) { UNSUPPORTED; } void AbstractBuilder::expr_sum_begin(std::string_view name) { UNSUPPORTED; } void AbstractBuilder::expr_sum_end(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::expr_builtin_function1(Constants::Kind) { UNSUPPORTED; } -void AbstractBuilder::expr_builtin_function2(Constants::Kind) { UNSUPPORTED; } -void AbstractBuilder::expr_builtin_function3(Constants::Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_builtin_function1(Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_builtin_function2(Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_builtin_function3(Kind) { UNSUPPORTED; } void AbstractBuilder::expr_simulate(int no_of_exprs, bool, int) { UNSUPPORTED; } -void AbstractBuilder::expr_optimize_exp(Constants::Kind, PRICETYPE, Constants::Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_optimize_exp(Kind, PriceType, Kind) { UNSUPPORTED; } void AbstractBuilder::expr_load_strategy() { UNSUPPORTED; } void AbstractBuilder::expr_save_strategy(std::string_view strategy_name) { UNSUPPORTED; } void AbstractBuilder::expr_MITL_formula() { UNSUPPORTED; } @@ -227,9 +226,9 @@ void AbstractBuilder::expr_MITL_next() { UNSUPPORTED; } void AbstractBuilder::expr_MITL_atom() { UNSUPPORTED; } void AbstractBuilder::expr_optimize(int, int, int, int) { UNSUPPORTED; } -void AbstractBuilder::expr_proba_qualitative(Constants::Kind, Constants::Kind, double) { UNSUPPORTED; } -void AbstractBuilder::expr_proba_quantitative(Constants::Kind) { UNSUPPORTED; } -void AbstractBuilder::expr_proba_compare(Constants::Kind, Constants::Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_proba_qualitative(Kind, Kind, double) { UNSUPPORTED; } +void AbstractBuilder::expr_proba_quantitative(Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_proba_compare(Kind, Kind) { UNSUPPORTED; } void AbstractBuilder::expr_proba_expected(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_exists_begin(std::string_view name) { UNSUPPORTED; } void AbstractBuilder::expr_exists_end(std::string_view name) { UNSUPPORTED; } @@ -295,3 +294,5 @@ void AbstractBuilder::expect_resource(std::string_view, std::string_view, std::s void AbstractBuilder::query_results_begin() { UNSUPPORTED; } void AbstractBuilder::query_results_end() { UNSUPPORTED; } void AbstractBuilder::model_option(std::string_view, std::string_view) { UNSUPPORTED; } + +} // namenamespace UTAP \ No newline at end of file diff --git a/src/DocumentBuilder.cpp b/src/DocumentBuilder.cpp index 7bea49f6..aba80c4c 100644 --- a/src/DocumentBuilder.cpp +++ b/src/DocumentBuilder.cpp @@ -28,8 +28,8 @@ #include #include -using namespace UTAP; -using namespace Constants; +namespace UTAP { + using namespace std::string_view_literals; DocumentBuilder::DocumentBuilder(Document& doc, std::vector paths): @@ -39,7 +39,7 @@ DocumentBuilder::DocumentBuilder(Document& doc, std::vectoradd_function(type, name, pos, currentFun); } @@ -61,12 +61,12 @@ Declarations* DocumentBuilder::getCurrentDeclarationBlock() void DocumentBuilder::addSelectSymbolToFrame(std::string_view id, Frame& frame, position_t pos) { auto type = typeFragments.pop(); - if (!type.is(CONSTANT)) { - type = type.create_prefix(CONSTANT); + if (!type.is(Kind::CONSTANT)) { + type = type.create_prefix(Kind::CONSTANT); } if (!type.is_scalar() && !type.is_integer()) { handle_error(TypeException{"$Scalar_set_or_integer_expected"}); - } else if (!type.is(RANGE)) { + } else if (!type.is(Kind::RANGE)) { handle_error(TypeException{"$Range_expected"}); } else { if (auto uid = Symbol{}; resolve(id, uid)) @@ -178,10 +178,10 @@ void DocumentBuilder::proc_location_commit(std::string_view name) { if (auto uid = Symbol{}; !resolve(name, uid) || !uid.get_type().is_location()) { handle_error(TypeException{"$Location_expected"}); - } else if (uid.get_type().is(URGENT)) { + } else if (uid.get_type().is(Kind::URGENT)) { handle_error(TypeException{"$States_cannot_be_committed_and_urgent_at_the_same_time"}); } else { - uid.set_type(uid.get_type().create_prefix(COMMITTED, position)); + uid.set_type(uid.get_type().create_prefix(Kind::COMMITTED, position)); } } @@ -189,10 +189,10 @@ void DocumentBuilder::proc_location_urgent(std::string_view name) { if (auto uid = Symbol{}; !resolve(name, uid) || !uid.get_type().is_location()) { handle_error(TypeException{"$Location_expected"}); - } else if (uid.get_type().is(COMMITTED)) { + } else if (uid.get_type().is(Kind::COMMITTED)) { handle_error(TypeException{"$States_cannot_be_committed_and_urgent_at_the_same_time"}); } else { - uid.set_type(uid.get_type().create_prefix(URGENT, position)); + uid.set_type(uid.get_type().create_prefix(Kind::URGENT, position)); } } @@ -214,18 +214,18 @@ void DocumentBuilder::proc_edge_begin(std::string_view from, std::string_view to !resolve(from, fid) || (!fid.get_type().is_location() && !fid.get_type().is_branchpoint())) { handle_error(TypeException{"$No_such_location_or_branchpoint_(source)"}); push_frame(frames.top().make_sub()); // dummy frame for upcoming popFrame - } else if (auto tid = Symbol{}; - !resolve(to, tid) || (!tid.get_type().is_location() && !tid.get_type().is_branchpoint())) { - handle_error(TypeException{"$No_such_location_or_branchpoint_(destination)"}); - push_frame(frames.top().make_sub()); // dummy frame for upcoming popFrame - } else { - currentEdge = ¤tTemplate->add_edge(fid, tid, control, actname); - currentEdge->guard = make_constant(1); - currentEdge->assign = make_constant(1); - // default "probability" weight is 1. - currentEdge->prob = make_constant(1); - push_frame(currentEdge->select = frames.top().make_sub()); - } + } else if (auto tid = Symbol{}; + !resolve(to, tid) || (!tid.get_type().is_location() && !tid.get_type().is_branchpoint())) { + handle_error(TypeException{"$No_such_location_or_branchpoint_(destination)"}); + push_frame(frames.top().make_sub()); // dummy frame for upcoming popFrame + } else { + currentEdge = ¤tTemplate->add_edge(fid, tid, control, actname); + currentEdge->guard = make_constant(1); + currentEdge->assign = make_constant(1); + // default "probability" weight is 1. + currentEdge->prob = make_constant(1); + push_frame(currentEdge->select = frames.top().make_sub()); + } } void DocumentBuilder::proc_edge_end(std::string_view from, std::string_view to) { frames.pop(); } @@ -241,7 +241,7 @@ void DocumentBuilder::proc_guard() currentEdge->guard = fragments.pop(); } -void DocumentBuilder::proc_sync(Synchronisation type) +void DocumentBuilder::proc_sync(Sync type) { if (currentEdge == nullptr) { handle_error(TypeException("Must be declared inside of an edge")); @@ -281,9 +281,9 @@ void DocumentBuilder::instantiation_begin(std::string_view name, uint32_t parame // Lookup symbol. auto id = Symbol{}; if (!resolve(templ_name, id) || - (id.get_type().get_kind() != INSTANCE && id.get_type().get_kind() != LSC_INSTANCE)) { + (id.get_type().get_kind() != Kind::INSTANCE && id.get_type().get_kind() != Kind::LSC_INSTANCE)) { handle_error(TypeException{"$Not_a_template"}); - } + } // Push parameters to frame stack. auto frame = frames.top().make_sub(); @@ -303,7 +303,7 @@ void DocumentBuilder::instantiation_end(std::string_view name, uint32_t paramete * reported the problem. */ auto id = Symbol{}; - if (resolve(templ_name, id) && (id.get_type().get_kind() == INSTANCE || id.get_type().get_kind() == LSC_INSTANCE)) { + if (resolve(templ_name, id) && (id.get_type().get_kind() == Kind::INSTANCE || id.get_type().get_kind() == Kind::LSC_INSTANCE)) { auto* old_instance = static_cast(id.get_data()); // Check number of arguments. If too many arguments, pop the rest. auto expected = id.get_type().size(); @@ -318,7 +318,7 @@ void DocumentBuilder::instantiation_end(std::string_view name, uint32_t paramete exprs[arguments] = fragments.pop(); arguments = 0; // Create template composition. - Instance& new_instance = (id.get_type().get_kind() == INSTANCE) + Instance& new_instance = (id.get_type().get_kind() == Kind::INSTANCE) ? document.add_instance(name, *old_instance, params, exprs, position) : document.add_LSC_instance(name, *old_instance, params, exprs, position); @@ -331,7 +331,7 @@ void DocumentBuilder::instantiation_end(std::string_view name, uint32_t paramete std::set& restricted = old_instance->restricted; for (uint32_t i = 0; i < expected; i++) { if (restricted.find(old_instance->parameters[i]) != restricted.end()) { - collectDependencies(new_instance.restricted, exprs[i]); + collect_dependencies(new_instance.restricted, exprs[i]); } } } @@ -348,7 +348,7 @@ void DocumentBuilder::process(std::string_view name) throw no_such_process_error(name); auto type = symbol.get_type(); - if (type.get_kind() != INSTANCE) + if (type.get_kind() != Kind::INSTANCE) throw not_a_template_error(symbol.get_name()); if (type.size() > 0) { // FIXME: Check type of unbound parameters @@ -408,10 +408,10 @@ void DocumentBuilder::instance_name(std::string_view name, bool templ) if (templ) { auto instName = std::string{name}; auto templName = instName.substr(0, instName.find('(')); - if (!resolve(templName, uid) || (uid.get_type().get_kind() != INSTANCE)) + if (!resolve(templName, uid) || (uid.get_type().get_kind() != Kind::INSTANCE)) handle_error(not_a_template_error(templName)); } else { - if (resolve(name, uid) && (uid.get_type().get_kind() == INSTANCE)) { + if (resolve(name, uid) && (uid.get_type().get_kind() == Kind::INSTANCE)) { const auto* t = static_cast(uid.get_data()); if (t->parameters.get_size() > 0) { handle_error(TypeException{"$Wrong_number_of_arguments_in_instance_line_name"}); @@ -419,7 +419,7 @@ void DocumentBuilder::instance_name(std::string_view name, bool templ) } } currentInstanceLine->uid = - currentTemplate->frame.add_symbol(name, Type::create_primitive(INSTANCE_LINE), position, currentInstanceLine); + currentTemplate->frame.add_symbol(name, Type::create_primitive(Kind::INSTANCE_LINE), position, currentInstanceLine); } void DocumentBuilder::instance_name_begin(std::string_view name) @@ -440,7 +440,7 @@ void DocumentBuilder::instance_name_end(std::string_view name, uint32_t argument // assert(parameters == params.get_size()); // Lookup symbol. In case of failure, instance_name_begin already reported the problem. - if (auto id = Symbol{}; resolve(name, id) && id.get_type().get_kind() == INSTANCE) { + if (auto id = Symbol{}; resolve(name, id) && id.get_type().get_kind() == Kind::INSTANCE) { auto* old_instance = static_cast(id.get_data()); /* Check number of arguments. If too many arguments, pop the @@ -471,7 +471,7 @@ void DocumentBuilder::instance_name_end(std::string_view name, uint32_t argument std::set& restricted = old_instance->restricted; for (uint32_t i = 0; i < expected; i++) { if (restricted.find(old_instance->parameters[i]) != restricted.end()) { - collectDependencies(currentInstanceLine->restricted, exprs[i]); + collect_dependencies(currentInstanceLine->restricted, exprs[i]); } } } @@ -495,7 +495,7 @@ void DocumentBuilder::proc_message(std::string_view from, std::string_view to, c } } -void DocumentBuilder::proc_message(Synchronisation type) // Label +void DocumentBuilder::proc_message(Sync type) // Label { if (currentMessage != nullptr) currentMessage->label = Expression::create_sync(fragments[0], type, position); @@ -572,7 +572,7 @@ void DocumentBuilder::decl_dynamic_template(std::string_view name) for (uint32_t i = 0; i < params.get_size(); i++) { if (!params[i].get_type().is_integer() && !params[i].get_type().is_boolean() && - !(params[i].get_type().is(REF) && params[i].get_type()[0].is(BROADCAST))) + !(params[i].get_type().is(Kind::REF) && params[i].get_type()[0].is(Kind::BROADCAST))) handle_error(TypeException{"Dynamic template parameters can either be boolean or integer and " "cannot be references"}); } @@ -664,3 +664,5 @@ void DocumentBuilder::model_option(std::string_view key, std::string_view value) handle_error(TypeException{"options tag found without attribute 'key'"}); document.get_options().emplace_back(std::string{key}, std::string{value}); } + +} // namespace UTAP \ No newline at end of file diff --git a/src/ExpressionBuilder.cpp b/src/ExpressionBuilder.cpp index bf1b13ac..6ec4b744 100644 --- a/src/ExpressionBuilder.cpp +++ b/src/ExpressionBuilder.cpp @@ -23,20 +23,20 @@ #include "utap/TypeChecker.hpp" -#include #include #include -#include #include #include #include -using namespace UTAP; -using namespace Constants; +#include + +namespace UTAP { -inline static bool isMITL(const Expression& e) +static bool isMITL(const Expression& e) { switch (e.get_kind()) { + using namespace KindNames; case MITL_FORMULA: case MITL_RELEASE: case MITL_UNTIL: @@ -50,7 +50,7 @@ inline static bool isMITL(const Expression& e) } } -inline static Expression toMITLAtom(const Expression& e) { return Expression::create_unary(MITL_ATOM, e); } +static Expression toMITLAtom(const Expression& e) { return Expression::create_unary(Kind::MITL_ATOM, e); } ExpressionBuilder::ExpressionBuilder(Document& doc): document{doc}, scalar_count{0} { @@ -83,7 +83,7 @@ bool ExpressionBuilder::is_type(std::string_view name) if (!resolve(name, uid)) { return false; } - return uid.get_type().get_kind() == TYPEDEF; + return uid.get_type().get_kind() == Kind::TYPEDEF; } Expression ExpressionBuilder::make_constant(int value) const { return Expression::create_constant(value, position); } @@ -102,15 +102,15 @@ Expression ExpressionBuilder::make_constant(std::string_view value) const Type ExpressionBuilder::apply_prefix(TypePrefix prefix, Type type) { switch (prefix) { - case TypePrefix::CONST: return type.create_prefix(CONSTANT, position); + case TypePrefix::CONST: return type.create_prefix(Kind::CONSTANT, position); case TypePrefix::SYSTEM_META: // Meta in the syntax corresponds to a static variable internally. // Internal "meta" variables correspond to state meta variables. - return type.create_prefix(SYSTEM_META, position); - case TypePrefix::URGENT: return type.create_prefix(URGENT, position); - case TypePrefix::BROADCAST: return type.create_prefix(BROADCAST, position); - case TypePrefix::URGENT_BROADCAST: return type.create_prefix(URGENT, position).create_prefix(BROADCAST, position); - case TypePrefix::HYBRID: return type.create_prefix(HYBRID, position); + return type.create_prefix(Kind::SYSTEM_META, position); + case TypePrefix::URGENT: return type.create_prefix(Kind::URGENT, position); + case TypePrefix::BROADCAST: return type.create_prefix(Kind::BROADCAST, position); + case TypePrefix::URGENT_BROADCAST: return type.create_prefix(Kind::URGENT, position).create_prefix(Kind::BROADCAST, position); + case TypePrefix::HYBRID: return type.create_prefix(Kind::HYBRID, position); default: return type; } } @@ -121,13 +121,13 @@ void ExpressionBuilder::type_pop() { typeFragments.pop(); } void ExpressionBuilder::type_bool(TypePrefix prefix) { - Type type = Type::create_primitive(Constants::BOOL, position); + Type type = Type::create_primitive(Kind::BOOL, position); typeFragments.push(apply_prefix(prefix, type)); } void ExpressionBuilder::type_int(TypePrefix prefix) { - Type type = Type::create_primitive(Constants::INT, position); + Type type = Type::create_primitive(Kind::INT, position); if (prefix != TypePrefix::CONST) { type = Type::create_range(type, make_constant(defaultIntMin), make_constant(defaultIntMax), position); } @@ -137,22 +137,22 @@ void ExpressionBuilder::type_int(TypePrefix prefix) void ExpressionBuilder::type_string(TypePrefix prefix) { if (prefix != TypePrefix::CONST) { - typeFragments.push(Type::create_primitive(VOID_TYPE)); + typeFragments.push(Type::create_primitive(Kind::VOID_TYPE)); throw TypeException("$Strings_should_always_be_const"); } - Type type = Type::create_primitive(Constants::STRING, position); + Type type = Type::create_primitive(Kind::STRING, position); typeFragments.push(apply_prefix(prefix, type)); } void ExpressionBuilder::type_double(TypePrefix prefix) { - Type type = Type::create_primitive(Constants::DOUBLE, position); + Type type = Type::create_primitive(Kind::DOUBLE, position); typeFragments.push(apply_prefix(prefix, type)); } void ExpressionBuilder::type_bounded_int(TypePrefix prefix) { - Type type = Type::create_primitive(Constants::INT, position); + Type type = Type::create_primitive(Kind::INT, position); type = Type::create_range(type, fragments[1], fragments[0], position); fragments.pop(2); typeFragments.push(apply_prefix(prefix, type)); @@ -162,23 +162,23 @@ void ExpressionBuilder::type_channel(TypePrefix prefix) { bool is_broadcast = prefix == TypePrefix::BROADCAST || prefix == TypePrefix::URGENT_BROADCAST; document.add_channel(is_broadcast); - Type type = Type::create_primitive(CHANNEL, position); + Type type = Type::create_primitive(Kind::CHANNEL, position); typeFragments.push(apply_prefix(prefix, type)); } void ExpressionBuilder::type_clock(TypePrefix prefix) { - Type type = Type::create_primitive(CLOCK, position); + Type type = Type::create_primitive(Kind::CLOCK, position); typeFragments.push(apply_prefix(prefix, type)); } void ExpressionBuilder::type_void() { - Type type = Type::create_primitive(VOID_TYPE, position); + Type type = Type::create_primitive(Kind::VOID_TYPE, position); typeFragments.push(type); } -static void collectDependencies(std::set& dependencies, const Expression& expr) +static void collect_dependencies(std::set& dependencies, const Expression& expr) { std::set symbols; expr.collect_possible_reads(symbols); @@ -200,12 +200,12 @@ void ExpressionBuilder::type_scalar(TypePrefix prefix) Expression lower, upper; expr_nat(1); - expr_binary(MINUS); + expr_binary(Kind::MINUS); upper = fragments[0]; lower = make_constant(0); fragments.pop(); - auto type = Type::create_primitive(SCALAR, position); + auto type = Type::create_primitive(Kind::SCALAR, position); type = Type::create_range(type, lower, upper, position); type = apply_prefix(prefix, type); @@ -227,7 +227,7 @@ void ExpressionBuilder::type_scalar(TypePrefix prefix) * Therefore mark all symbols in upper and those that they * depend on as restricted. */ - collectDependencies(currentTemplate->restricted, upper); + collect_dependencies(currentTemplate->restricted, upper); } typeFragments.push(type); } @@ -237,8 +237,8 @@ void ExpressionBuilder::type_name(TypePrefix prefix, std::string_view name) Symbol uid; assert(resolve(name, uid)); - if (!resolve(name, uid) || uid.get_type().get_kind() != TYPEDEF) { - typeFragments.push(Type::create_primitive(VOID_TYPE)); + if (!resolve(name, uid) || uid.get_type().get_kind() != Kind::TYPEDEF) { + typeFragments.push(Type::create_primitive(Kind::VOID_TYPE)); throw TypeException("$Identifier_is_undeclared_or_not_a_type_name"); } @@ -256,21 +256,21 @@ void ExpressionBuilder::type_name(TypePrefix prefix, std::string_view name) void ExpressionBuilder::expr_true() { Expression expr = make_constant(1); - expr.set_type(Type::create_primitive(Constants::BOOL)); + expr.set_type(Type::create_primitive(Kind::BOOL)); fragments.push(expr); } void ExpressionBuilder::expr_false() { Expression expr = make_constant(0); - expr.set_type(Type::create_primitive(Constants::BOOL)); + expr.set_type(Type::create_primitive(Kind::BOOL)); fragments.push(expr); } void ExpressionBuilder::expr_double(double d) { Expression expr = Expression::create_double(d, position); - expr.set_type(Type::create_primitive(Constants::DOUBLE)); + expr.set_type(Type::create_primitive(Kind::DOUBLE)); fragments.push(expr); } @@ -317,15 +317,15 @@ void ExpressionBuilder::expr_call_end(uint32_t n) * function or a processset. */ switch (id.get_type().get_kind()) { - case FUNCTION_EXTERNAL: - case FUNCTION: + case Kind::FUNCTION_EXTERNAL: + case Kind::FUNCTION: if (expr.size() != id.get_type().size()) handle_error(TypeException{"$Wrong_number_of_arguments"}); - e = Expression::create_nary(id.get_type().get_kind() == FUNCTION ? FUN_CALL : FUN_CALL_EXT, expr, position, + e = Expression::create_nary(id.get_type().get_kind() == Kind::FUNCTION ? Kind::FUN_CALL : Kind::FUN_CALL_EXT, expr, position, id.get_type()[0]); break; - case PROCESS_SET: + case Kind::PROCESS_SET: if (expr.size() - 1 != id.get_type().size()) handle_error(TypeException{"$Wrong_number_of_arguments"}); instance = static_cast(id.get_symbol().get_data()); @@ -346,7 +346,7 @@ void ExpressionBuilder::expr_call_end(uint32_t n) e.set_type(type); for (size_t i = 1; i < expr.size(); ++i) { type = type.get_sub(); - e = Expression::create_binary(ARRAY, e, expr[i], position, type); + e = Expression::create_binary(Kind::ARRAY, e, expr[i], position, type); } break; @@ -375,28 +375,28 @@ void ExpressionBuilder::expr_array() element = Type(); } - fragments.push(Expression::create_binary(ARRAY, var, index, position, element)); + fragments.push(Expression::create_binary(Kind::ARRAY, var, index, position, element)); } // 1 expr void ExpressionBuilder::expr_post_increment() { - fragments[0] = Expression::create_unary(POST_INCREMENT, fragments[0], position); + fragments[0] = Expression::create_unary(Kind::POST_INCREMENT, fragments[0], position); } void ExpressionBuilder::expr_pre_increment() { - fragments[0] = Expression::create_unary(PRE_INCREMENT, fragments[0], position, fragments[0].get_type()); + fragments[0] = Expression::create_unary(Kind::PRE_INCREMENT, fragments[0], position, fragments[0].get_type()); } void ExpressionBuilder::expr_post_decrement() // 1 expr { - fragments[0] = Expression::create_unary(POST_DECREMENT, fragments[0], position); + fragments[0] = Expression::create_unary(Kind::POST_DECREMENT, fragments[0], position); } void ExpressionBuilder::expr_pre_decrement() { - fragments[0] = Expression::create_unary(PRE_DECREMENT, fragments[0], position, fragments[0].get_type()); + fragments[0] = Expression::create_unary(Kind::PRE_DECREMENT, fragments[0], position, fragments[0].get_type()); } void ExpressionBuilder::expr_builtin_function1(Kind kind) @@ -432,11 +432,11 @@ void ExpressionBuilder::expr_assignment(Kind op) // 2 expr void ExpressionBuilder::expr_unary(Kind unaryop) // 1 expr { switch (unaryop) { - case PLUS: + case Kind::PLUS: /* Unary plus can be ignored */ break; - case MINUS: - unaryop = UNARY_MINUS; + case Kind::MINUS: + unaryop = Kind::UNARY_MINUS; [[fallthrough]]; default: fragments[0] = Expression::create_unary(unaryop, fragments[0], position, fragments[0].get_type()); } @@ -444,7 +444,7 @@ void ExpressionBuilder::expr_unary(Kind unaryop) // 1 expr void ExpressionBuilder::expr_binary(Kind binaryop) // 2 expr { - Kind mitlop = (binaryop == AND ? MITL_CONJ : MITL_DISJ); + Kind mitlop = (binaryop == Kind::AND ? Kind::MITL_CONJ : Kind::MITL_DISJ); Kind op = binaryop; Expression left = fragments[1]; Expression right = fragments[0]; @@ -482,8 +482,8 @@ void ExpressionBuilder::expr_scenario(std::string_view name) auto check [[maybe_unused]] = resolve(name, uid); assert(check); auto scen = Expression::create_identifier(uid); - auto expr = Expression::create_unary(SCENARIO, scen, position); - fragments.push(Expression::create_unary(SCENARIO, scen, position)); + auto expr = Expression::create_unary(Kind::SCENARIO, scen, position); + fragments.push(Expression::create_unary(Kind::SCENARIO, scen, position)); } Expression ExpressionBuilder::exprScenario() @@ -495,14 +495,14 @@ Expression ExpressionBuilder::exprScenario() auto i = obs.get_type().find_index_of("lmin"); Expression left = Expression::create_dot(obs, i.value_or(-1), position, - Type::create_primitive(Constants::BOOL)); // std::cout << left << std::endl; + Type::create_primitive(Kind::BOOL)); // std::cout << left << std::endl; obs = Expression::create_identifier(uid); i = obs.get_type().find_index_of("lmax"); Expression right = Expression::create_dot(obs, i.value_or(-1), position, - Type::create_primitive(Constants::BOOL)); // std::cout << right << std::endl; - return Expression::create_binary(SCENARIO2, left, right, position); + Type::create_primitive(Kind::BOOL)); // std::cout << right << std::endl; + return Expression::create_binary(Kind::SCENARIO2, left, right, position); } void ExpressionBuilder::expr_ternary(Kind ternaryop, bool firstMissing) // 3 expr @@ -521,7 +521,7 @@ void ExpressionBuilder::expr_inline_if() Expression e = fragments[0]; fragments.pop(3); - fragments.push(Expression::create_ternary(INLINE_IF, c, t, e, position)); + fragments.push(Expression::create_ternary(Kind::INLINE_IF, c, t, e, position)); } void ExpressionBuilder::expr_comma() @@ -529,7 +529,7 @@ void ExpressionBuilder::expr_comma() Expression e1 = fragments[1]; Expression e2 = fragments[0]; fragments.pop(2); - fragments.push(Expression::create_binary(COMMA, e1, e2, position, e2.get_type())); + fragments.push(Expression::create_binary(Kind::COMMA, e1, e2, position, e2.get_type())); } void ExpressionBuilder::expr_location() @@ -540,7 +540,7 @@ void ExpressionBuilder::expr_location() // TODO: create a separate type for location expressions and get rid of magical constants // we use special max-value to denote this special "meta-variable" expr = Expression::create_dot(expr, std::numeric_limits::max(), position, - Type::create_primitive(Constants::LOCATION_EXPR)); + Type::create_primitive(Kind::LOCATION_EXPR)); } else { handle_error(not_a_process_error(expr.str(true))); } @@ -565,14 +565,14 @@ void ExpressionBuilder::expr_dot(std::string_view id) if (!i) { handle_error(has_no_such_member_error(id)); } else if (type.get_sub(*i).is_location()) { - expr = Expression::create_dot(expr, *i, position, Type::create_primitive(Constants::BOOL)); + expr = Expression::create_dot(expr, *i, position, Type::create_primitive(Kind::BOOL)); } else { type = type.get_sub(*i).rename(process->templ->uid.get_name() + "::", name.get_name() + "::"); for (const auto& [s, e] : process->mapping) type = type.subst(s, e); expr = Expression::create_dot(expr, *i, position, type); } - } else if (type.is(PROCESS_VAR)) { + } else if (type.is(Kind::PROCESS_VAR)) { Symbol uid; // temporarily set the frame to that of its associated template if (dynamicFrames.find(expr.get_symbol().get_name()) == dynamicFrames.end()) { @@ -588,10 +588,10 @@ void ExpressionBuilder::expr_dot(std::string_view id) Expression identifier = Expression::create_identifier(uid, position); expr = (Expression::create_nary( - DYNAMIC_EVAL, {identifier, expr}, position, + Kind::DYNAMIC_EVAL, {identifier, expr}, position, identifier.get_type().is_location() - ? Type::create_primitive(Constants::BOOL, position) - : identifier.get_type())); // Type::createPrimitive (Constants::BOOL,position))); + ? Type::create_primitive(Kind::BOOL, position) + : identifier.get_type())); // Type::createPrimitive (Kind::BOOL,position))); } else { handle_error(is_not_a_struct_error(expr.str(true))); } @@ -603,8 +603,8 @@ void ExpressionBuilder::expr_forall_begin(std::string_view name) Type type = typeFragments[0]; typeFragments.pop(); - if (!type.is(CONSTANT)) { - type = type.create_prefix(CONSTANT); + if (!type.is(Kind::CONSTANT)) { + type = type.create_prefix(Kind::CONSTANT); } push_frame(frames.top().make_sub()); @@ -622,7 +622,7 @@ void ExpressionBuilder::expr_forall_end(std::string_view name) * but the identifier expression will maintain a reference to the * symbol so it will not be deallocated. */ - fragments[0] = Expression::create_binary(FORALL, Expression::create_identifier(frames.top()[0], position), + fragments[0] = Expression::create_binary(Kind::FORALL, Expression::create_identifier(frames.top()[0], position), fragments[0], position); pop_frame(); } @@ -636,7 +636,7 @@ void ExpressionBuilder::expr_exists_end(std::string_view name) * but the identifier expression will maintain a reference to the * symbol so it will not be deallocated. */ - fragments[0] = Expression::create_binary(EXISTS, Expression::create_identifier(frames.top()[0], position), + fragments[0] = Expression::create_binary(Kind::EXISTS, Expression::create_identifier(frames.top()[0], position), fragments[0], position); pop_frame(); } @@ -650,32 +650,32 @@ void ExpressionBuilder::expr_sum_end(std::string_view name) * but the identifier expression will maintain a reference to the * symbol so it will not be deallocated. */ - fragments[0] = Expression::create_binary(SUM, Expression::create_identifier(frames.top()[0], position), + fragments[0] = Expression::create_binary(Kind::SUM, Expression::create_identifier(frames.top()[0], position), fragments[0], position); pop_frame(); } -void ExpressionBuilder::expr_proba_qualitative(Constants::Kind pathType, Constants::Kind comp, double probBound) +void ExpressionBuilder::expr_proba_qualitative(Kind pathType, Kind comp, double probBound) { - auto invert = (comp == LE); + auto invert = (comp == Kind::LE); auto& boundTypeOrBoundedExpr = fragments[3]; auto& bound = fragments[2]; auto& runs = fragments[1]; auto& predicate = fragments[0]; auto args = std::vector{runs, boundTypeOrBoundedExpr, bound, - invert ? Expression::create_unary(NOT, predicate, position) : predicate, + invert ? Expression::create_unary(Kind::NOT, predicate, position) : predicate, Expression::create_double(invert ? 1.0 - probBound : probBound, position)}; fragments.pop(4); - fragments.push(Expression::create_nary(invert ? (pathType == BOX ? PROBA_MIN_DIAMOND : PROBA_MIN_BOX) - : (pathType == BOX ? PROBA_MIN_BOX : PROBA_MIN_DIAMOND), + fragments.push(Expression::create_nary(invert ? (pathType == Kind::BOX ? Kind::PROBA_MIN_DIAMOND : Kind::PROBA_MIN_BOX) + : (pathType == Kind::BOX ? Kind::PROBA_MIN_BOX : Kind::PROBA_MIN_DIAMOND), std::move(args), position)); } -void ExpressionBuilder::expr_optimize_exp(Constants::Kind kind, PRICETYPE ptype, Constants::Kind goal_type) +void ExpressionBuilder::expr_optimize_exp(Kind kind, PriceType ptype, Kind goal_type) { - if (goal_type != Constants::DIAMOND) + if (goal_type != Kind::DIAMOND) handle_error(TypeException{"$Wrong_path_quantifier"}); auto boundVar = fragments[4]; @@ -685,21 +685,21 @@ void ExpressionBuilder::expr_optimize_exp(Constants::Kind kind, PRICETYPE ptype, auto goal = fragments[0]; if (!discrete.is_true() && !cont.is_true()) { - discrete.set_type(Type::create_primitive(LIST, position)); - cont.set_type(Type::create_primitive(LIST, position)); + discrete.set_type(Type::create_primitive(Kind::LIST, position)); + cont.set_type(Type::create_primitive(Kind::LIST, position)); } Expression price; Expression level = make_constant(0); size_t nb = 4; switch (ptype) { - case TIMEPRICE: // use time + case PriceType::TIME: // use time price = make_constant(1); break; - case EXPRPRICE: // user-provided expression + case PriceType::EXPR: // user-provided expression price = fragments[5]; ++nb; break; - case PROBAPRICE: price = goal; break; + case PriceType::PROBA: price = goal; break; default: handle_error(TypeException{"$Unknown_price_type"}); } @@ -716,20 +716,20 @@ void ExpressionBuilder::expr_load_strategy() Expression cont = fragments[1]; Expression strat = fragments[0]; if (!discrete.is_true() && !cont.is_true()) { - discrete.set_type(Type::create_primitive(LIST, position)); - cont.set_type(Type::create_primitive(LIST, position)); + discrete.set_type(Type::create_primitive(Kind::LIST, position)); + cont.set_type(Type::create_primitive(Kind::LIST, position)); } fragments.pop(3); - fragments.push(Expression::create_ternary(LOAD_STRAT, strat, discrete, cont, position)); + fragments.push(Expression::create_ternary(Kind::LOAD_STRAT, strat, discrete, cont, position)); } void ExpressionBuilder::expr_save_strategy(std::string_view strategy_name) { assert(fragments.size() == 1); - fragments[0] = Expression::create_binary(SAVE_STRAT, fragments[0], make_constant(strategy_name), position); + fragments[0] = Expression::create_binary(Kind::SAVE_STRAT, fragments[0], make_constant(strategy_name), position); } -void ExpressionBuilder::expr_proba_quantitative(Constants::Kind pathType) +void ExpressionBuilder::expr_proba_quantitative(Kind pathType) { auto& boundTypeOrBoundedExpr = fragments[4]; auto& bound = fragments[3]; @@ -739,10 +739,10 @@ void ExpressionBuilder::expr_proba_quantitative(Constants::Kind pathType) auto args = std::vector{runs, boundTypeOrBoundedExpr, bound, predicate, untilCond}; fragments.pop(5); - fragments.push(Expression::create_nary((pathType == BOX ? PROBA_BOX : PROBA_DIAMOND), std::move(args), position)); + fragments.push(Expression::create_nary((pathType == Kind::BOX ? Kind::PROBA_BOX : Kind::PROBA_DIAMOND), std::move(args), position)); } -void ExpressionBuilder::expr_proba_compare(Constants::Kind pathType1, Constants::Kind pathType2) +void ExpressionBuilder::expr_proba_compare(Kind pathType1, Kind pathType2) { auto& boundTypeOrBoundedExpr1 = fragments[7]; auto& bound1 = fragments[6]; @@ -761,7 +761,7 @@ void ExpressionBuilder::expr_proba_compare(Constants::Kind pathType1, Constants: boundTypeOrBoundedExpr2, bound2, make_constant(pathType2), predicate2}; fragments.pop(8); - fragments.push(Expression::create_nary(PROBA_CMP, std::move(args), position)); + fragments.push(Expression::create_nary(Kind::PROBA_CMP, std::move(args), position)); } void ExpressionBuilder::expr_proba_expected(std::string_view aggregatingOp) @@ -782,7 +782,7 @@ void ExpressionBuilder::expr_proba_expected(std::string_view aggregatingOp) auto args = std::vector{runs, boundTypeOrBoundedExpr, bound, make_constant(aggOpId), expression}; fragments.pop(4); - fragments.push(Expression::create_nary(PROBA_EXP, std::move(args), position)); + fragments.push(Expression::create_nary(Kind::PROBA_EXP, std::move(args), position)); } void ExpressionBuilder::expr_simulate(int nbExpr, bool hasReach, int numberOfAcceptingRuns) @@ -797,7 +797,7 @@ void ExpressionBuilder::expr_simulate(int nbExpr, bool hasReach, int numberOfAcc auto& bound = fragments[1 + offset]; auto runs = fragments[0 + offset]; - if (runs.get_kind() == CONSTANT && runs.get_type().is_integer() && runs.get_value() < 0) + if (runs.get_kind() == Kind::CONSTANT && runs.get_type().is_integer() && runs.get_value() < 0) runs = make_constant(1); auto args = std::vector{}; @@ -816,9 +816,9 @@ void ExpressionBuilder::expr_simulate(int nbExpr, bool hasReach, int numberOfAcc fragments.pop(offset + 3); if (hasReach) - fragments.push(Expression::create_nary(SIMULATEREACH, std::move(args), position)); + fragments.push(Expression::create_nary(Kind::SIMULATEREACH, std::move(args), position)); else - fragments.push(Expression::create_nary(SIMULATE, std::move(args), position)); + fragments.push(Expression::create_nary(Kind::SIMULATE, std::move(args), position)); } void ExpressionBuilder::expr_MITL_formula() @@ -826,7 +826,7 @@ void ExpressionBuilder::expr_MITL_formula() Expression mitl = fragments[0]; if (!isMITL(mitl)) mitl = toMITLAtom(mitl); - Expression form = Expression::create_unary(MITL_FORMULA, mitl, position); + Expression form = Expression::create_unary(Kind::MITL_FORMULA, mitl, position); fragments.pop(); fragments.push(form); } @@ -842,7 +842,7 @@ void ExpressionBuilder::expr_MITL_until(int low, int high) auto lowd = make_constant(low); auto highd = make_constant(high); auto args = std::vector{left, lowd, highd, right}; - Expression form = Expression::create_nary(MITL_UNTIL, std::move(args), position); + Expression form = Expression::create_nary(Kind::MITL_UNTIL, std::move(args), position); fragments.pop(2); fragments.push(form); } @@ -859,20 +859,20 @@ void ExpressionBuilder::expr_MITL_release(int low, int high) auto highd = make_constant(high); auto args = std::vector{left, lowd, highd, right}; fragments.pop(2); - fragments.push(Expression::create_nary(MITL_RELEASE, std::move(args), position)); + fragments.push(Expression::create_nary(Kind::MITL_RELEASE, std::move(args), position)); } /*transform the diamond <>[low,high]phi into a (true U[low,high] phi) structure */ void ExpressionBuilder::expr_MITL_diamond(int low, int high) { - auto left = Expression::create_unary(MITL_ATOM, make_constant(1)); + auto left = Expression::create_unary(Kind::MITL_ATOM, make_constant(1)); auto right = fragments[0]; if (!isMITL(right)) right = toMITLAtom(right); auto lowd = make_constant(low); auto highd = make_constant(high); auto args = std::vector{left, lowd, highd, right}; - Expression form = Expression::create_nary(MITL_UNTIL, std::move(args), position); + Expression form = Expression::create_nary(Kind::MITL_UNTIL, std::move(args), position); fragments.pop(1); fragments.push(form); } @@ -880,14 +880,14 @@ void ExpressionBuilder::expr_MITL_diamond(int low, int high) /*transform the diamond [][low,high]phi into a (false R[low,high] phi) structure */ void ExpressionBuilder::expr_MITL_box(int low, int high) { - auto left = Expression::create_unary(MITL_ATOM, make_constant(0)); + auto left = Expression::create_unary(Kind::MITL_ATOM, make_constant(0)); auto right = fragments[0]; if (!isMITL(right)) right = toMITLAtom(right); auto lowd = make_constant(low); auto highd = make_constant(high); auto args = std::vector{left, lowd, highd, right}; - Expression form = Expression::create_nary(MITL_RELEASE, std::move(args), position); + Expression form = Expression::create_nary(Kind::MITL_RELEASE, std::move(args), position); fragments.pop(1); fragments.push(form); } @@ -896,7 +896,7 @@ void ExpressionBuilder::expr_MITL_disj() { auto& left = fragments[1]; auto& right = fragments[0]; - Expression form = Expression::create_binary(MITL_DISJ, left, right, position); + Expression form = Expression::create_binary(Kind::MITL_DISJ, left, right, position); fragments.pop(2); fragments.push(form); } @@ -906,7 +906,7 @@ void ExpressionBuilder::expr_MITL_conj() auto left = fragments[1]; auto right = fragments[0]; fragments.pop(2); - fragments.push(Expression::create_binary(MITL_CONJ, left, right, position)); + fragments.push(Expression::create_binary(Kind::MITL_CONJ, left, right, position)); } void ExpressionBuilder::expr_MITL_next() @@ -915,7 +915,7 @@ void ExpressionBuilder::expr_MITL_next() if (!isMITL(next)) next = toMITLAtom(next); fragments.pop(); - fragments.push(Expression::create_unary(MITL_NEXT, next, position)); + fragments.push(Expression::create_unary(Kind::MITL_NEXT, next, position)); } void ExpressionBuilder::expr_MITL_atom() @@ -923,7 +923,7 @@ void ExpressionBuilder::expr_MITL_atom() Expression atom = fragments[0]; if (!isMITL(atom)) { fragments.pop(); - fragments.push(Expression::create_unary(MITL_ATOM, atom, position)); + fragments.push(Expression::create_unary(Kind::MITL_ATOM, atom, position)); } } @@ -934,7 +934,7 @@ void ExpressionBuilder::expr_spawn(int n) for (auto i = 0; i <= n; ++i) exprs[i] = fragments[n - i]; fragments.pop(n + 1); - fragments.push(Expression::create_nary(SPAWN, std::move(exprs), position, id.get_type())); + fragments.push(Expression::create_nary(Kind::SPAWN, std::move(exprs), position, id.get_type())); } void ExpressionBuilder::expr_exit() { fragments.push(Expression::create_exit(position)); } @@ -942,15 +942,15 @@ void ExpressionBuilder::expr_exit() { fragments.push(Expression::create_exit(pos void ExpressionBuilder::expr_numof() { Expression id = fragments[0]; - Type t = Type::create_primitive(Constants::INT, position); + Type t = Type::create_primitive(Kind::INT, position); fragments.pop(); - fragments.push(Expression::create_unary(NUMOF, id, position, t)); + fragments.push(Expression::create_unary(Kind::NUMOF, id, position, t)); } void ExpressionBuilder::expr_forall_dynamic_begin(std::string_view name, std::string_view temp) { push_frame(frames.top().make_sub()); - frames.top().add_symbol(name, Type::create_primitive(PROCESS_VAR, position), position); + frames.top().add_symbol(name, Type::create_primitive(Kind::PROCESS_VAR, position), position); Template* templ = document.find_dynamic_template(temp); if (templ == nullptr) throw unknown_dynamic_template_error(temp); @@ -967,7 +967,7 @@ void ExpressionBuilder::expr_forall_dynamic_end(std::string_view name) auto identifier = Expression::create_identifier(frames.top()[0], position); bool mitl = isMITL(expr); if (mitl) { - if (expr.get_kind() == MITL_ATOM) { + if (expr.get_kind() == Kind::MITL_ATOM) { expr = expr.get(0).clone(); mitl = false; } @@ -975,15 +975,15 @@ void ExpressionBuilder::expr_forall_dynamic_end(std::string_view name) auto exprs = std::vector{identifier, process, expr}; fragments.pop(2); - fragments.push(Expression::create_nary((mitl ? MITL_FORALL : FORALL_DYNAMIC), std::move(exprs), position, - Type::create_primitive(Constants::BOOL, position))); + fragments.push(Expression::create_nary((mitl ? Kind::MITL_FORALL : Kind::FORALL_DYNAMIC), std::move(exprs), position, + Type::create_primitive(Kind::BOOL, position))); pop_frame(); pop_dynamic_frame_of(name); } void ExpressionBuilder::expr_exists_dynamic_begin(std::string_view name, std::string_view temp) { push_frame(frames.top().make_sub()); - frames.top().add_symbol(name, Type::create_primitive(Constants::PROCESS_VAR, position), position); + frames.top().add_symbol(name, Type::create_primitive(Kind::PROCESS_VAR, position), position); if (Template* templ = document.find_dynamic_template(temp); templ == nullptr) throw unknown_dynamic_template_error(temp); else { @@ -999,15 +999,15 @@ void ExpressionBuilder::expr_exists_dynamic_end(std::string_view name) Expression identifier = Expression::create_identifier(frames.top()[0], position); bool mitl = isMITL(expr); if (mitl) { - if (expr.get_kind() == MITL_ATOM) { + if (expr.get_kind() == Kind::MITL_ATOM) { expr = expr.get(0).clone(); mitl = false; } } auto exprs = std::vector{identifier, process, expr}; fragments.pop(2); - fragments.push(Expression::create_nary((mitl ? MITL_EXISTS : EXISTS_DYNAMIC), std::move(exprs), position, - Type::create_primitive(Constants::BOOL, position))); + fragments.push(Expression::create_nary((mitl ? Kind::MITL_EXISTS : Kind::EXISTS_DYNAMIC), std::move(exprs), position, + Type::create_primitive(Kind::BOOL, position))); pop_frame(); pop_dynamic_frame_of(name); } @@ -1015,7 +1015,7 @@ void ExpressionBuilder::expr_exists_dynamic_end(std::string_view name) void ExpressionBuilder::expr_sum_dynamic_begin(std::string_view name, std::string_view temp) { push_frame(frames.top().make_sub()); - frames.top().add_symbol(name, Type::create_primitive(Constants::PROCESS_VAR, position), position); + frames.top().add_symbol(name, Type::create_primitive(Kind::PROCESS_VAR, position), position); Template* templ = document.find_dynamic_template(temp); if (templ == nullptr) throw unknown_dynamic_template_error(temp); @@ -1030,7 +1030,7 @@ void ExpressionBuilder::expr_sum_dynamic_end(std::string_view name) Expression identifier = Expression::create_identifier(frames.top()[0], position); auto exprs = std::vector{identifier, process, expr}; fragments.pop(2); - fragments.push(Expression::create_nary(SUM_DYNAMIC, std::move(exprs), position, expr.get_type())); + fragments.push(Expression::create_nary(Kind::SUM_DYNAMIC, std::move(exprs), position, expr.get_type())); pop_frame(); pop_dynamic_frame_of(name); } @@ -1038,7 +1038,7 @@ void ExpressionBuilder::expr_sum_dynamic_end(std::string_view name) void ExpressionBuilder::expr_foreach_dynamic_begin(std::string_view name, std::string_view temp) { push_frame(frames.top().make_sub()); - frames.top().add_symbol(name, Type::create_primitive(Constants::PROCESS_VAR, position), position); + frames.top().add_symbol(name, Type::create_primitive(Kind::PROCESS_VAR, position), position); if (auto* t = document.find_dynamic_template(temp); t != nullptr) { // dynamicFrames [name]=document->find_dynamic_template(temp)->frame; push_dynamic_frame_of(document.find_dynamic_template(temp), name); @@ -1053,8 +1053,8 @@ void ExpressionBuilder::expr_foreach_dynamic_end(std::string_view name) auto identifier = Expression::create_identifier(frames.top()[0], position); auto exprs = std::vector{identifier, process, expr}; fragments.pop(2); - fragments.push(Expression::create_nary(FOREACH_DYNAMIC, std::move(exprs), position, - Type::create_primitive(Constants::INT, position))); + fragments.push(Expression::create_nary(Kind::FOREACH_DYNAMIC, std::move(exprs), position, + Type::create_primitive(Kind::INT, position))); pop_frame(); pop_dynamic_frame_of(name); } @@ -1071,3 +1071,5 @@ void ExpressionBuilder::pop_dynamic_frame_of(std::string_view name) if (auto it = dynamicFrames.find(name); it != dynamicFrames.end()) dynamicFrames.erase(it); } + +} // namespace UTAP \ No newline at end of file diff --git a/src/FeatureChecker.cpp b/src/FeatureChecker.cpp index b9cf7f83..41ef120f 100644 --- a/src/FeatureChecker.cpp +++ b/src/FeatureChecker.cpp @@ -27,7 +27,7 @@ #include -using namespace UTAP; +namespace UTAP { FeatureChecker::FeatureChecker(Document& document) { @@ -62,14 +62,15 @@ void FeatureChecker::visit_edge(Edge& edge) void FeatureChecker::visit_guard(Expression& guard) { switch (guard.get_kind()) { - case Constants::LT: - case Constants::LE: - case Constants::EQ: + using namespace KindNames; + case LT: + case LE: + case EQ: for (auto i = 0u; i < guard.get_size(); ++i) { if (guard.get(i).uses_fp()) supported_methods.symbolic = false; } - [[fallthrough]]; + [[fallthrough]]; default: break; } } @@ -77,11 +78,11 @@ void FeatureChecker::visit_guard(Expression& guard) void FeatureChecker::visit_assignment(Expression& ass) { switch (ass.get_kind()) { - case Constants::ASSIGN: + case Kind::ASSIGN: if (ass.uses_fp() && !ass.uses_hybrid()) supported_methods.symbolic = false; break; - case Constants::COMMA: + case Kind::COMMA: for (auto i = 0u; i < ass.get_size(); ++i) visit_assignment(ass.get(i)); break; @@ -104,16 +105,16 @@ void FeatureChecker::visit_location(Location& location) */ bool FeatureChecker::is_rate_disallowed_in_symbolic(const Expression& e) { - if (e.get_kind() == Constants::EQ) { + if (e.get_kind() == Kind::EQ) { assert(e.get_size() >= 2); Expression rate; Expression clock; - if (e.get(0).get_kind() == Constants::RATE) { + if (e.get(0).get_kind() == Kind::RATE) { clock = e.get(0); rate = e.get(1); - } else if (e.get(1).get_kind() == Constants::RATE) { + } else if (e.get(1).get_kind() == Kind::RATE) { clock = e.get(1); rate = e.get(0); } else { @@ -121,10 +122,10 @@ bool FeatureChecker::is_rate_disallowed_in_symbolic(const Expression& e) } // rates over hybrid clocks are allowed, because they are ignored/abstracted in symbolic analysis - if (clock.get(0).get_symbol().get_type().is(Constants::HYBRID)) + if (clock.get(0).get_symbol().get_type().is(Kind::HYBRID)) return false; - if (rate.get_kind() != Constants::CONSTANT) + if (rate.get_kind() != Kind::CONSTANT) return false; if (rate.get_value() != 0 && rate.get_value() != 1) return true; // NOLINT(readability-simplify-boolean-expr) @@ -132,7 +133,7 @@ bool FeatureChecker::is_rate_disallowed_in_symbolic(const Expression& e) return false; } - if (e.get_kind() == Constants::AND) { + if (e.get_kind() == Kind::AND) { for (uint32_t i = 0; i < e.get_size(); ++i) { if (is_rate_disallowed_in_symbolic(e.get(i))) return true; @@ -146,7 +147,9 @@ void FeatureChecker::visit_frame(const Frame& frame) { for (uint32_t i = 0; i < frame.get_size(); ++i) { Type t = frame.get_symbol(i).get_type(); - if (t.is_channel() && !t.is(Constants::BROADCAST)) + if (t.is_channel() && !t.is(Kind::BROADCAST)) supported_methods.stochastic = false; } } + +} // namespace UTAP \ No newline at end of file diff --git a/src/PrettyPrinter.cpp b/src/PrettyPrinter.cpp index f68f8154..0a4c93a0 100644 --- a/src/PrettyPrinter.cpp +++ b/src/PrettyPrinter.cpp @@ -31,26 +31,26 @@ #include #include -using namespace UTAP; -using namespace UTAP::Constants; +namespace UTAP { + using namespace std::string_literals; using namespace std::string_view_literals; -static inline const std::string& label(AbstractBuilder::TypePrefix prefix) +static const std::string& label(TypePrefix prefix) { static const std::string labels[] = {"", "const ", "urgent ", "", "broadcast ", "", "urgent broadcast ", "", "meta "}; - return labels[(uint8_t)prefix]; + return labels[static_cast(prefix)]; } -static inline std::string pop_back(std::vector& vs) +static std::string pop_back(std::vector& vs) { auto res = std::move(vs.back()); vs.pop_back(); return res; } -static inline std::string pop_top(std::stack& ss) +static std::string pop_top(std::stack& ss) { auto res = std::move(ss.top()); ss.pop(); @@ -59,16 +59,15 @@ static inline std::string pop_top(std::stack& ss) void PrettyPrinter::indent() { - for (uint32_t i = 0; i < level; i++) { + for (uint32_t i = 0; i < level; i++) *o.top() << '\t'; - } } -void PrettyPrinter::indent(std::string& s) +void PrettyPrinter::indent(std::string& s) const { - for (uint32_t i = 0; i < level; i++) { + s.reserve(s.size() + level); + for (uint32_t i = 0; i < level; i++) s += '\t'; - } } PrettyPrinter::PrettyPrinter(std::ostream& stream) @@ -205,7 +204,7 @@ void PrettyPrinter::expr_nary(Kind kind, uint32_t num) { auto opString = std::string{}; switch (kind) { - case LIST: opString = ", "; break; + case Kind::LIST: opString = ", "; break; default: throw TypeException{"Invalid operator"}; } @@ -492,12 +491,12 @@ void PrettyPrinter::proc_select(std::string_view id) void PrettyPrinter::proc_guard() { guard = static_cast(st.size()); } -void PrettyPrinter::proc_sync(Synchronisation type) +void PrettyPrinter::proc_sync(Sync type) { switch (type) { - case SYNC_QUE: st.back() += '?'; break; - case SYNC_BANG: st.back() += '!'; break; - case SYNC_CSP: + case Sync::QUE: st.back() += '?'; break; + case Sync::BANG: st.back() += '!'; break; + case Sync::CSP: // no append break; } @@ -722,10 +721,10 @@ static std::string_view get_builtin_fun_name(Kind kind) "random_poisson", "random_tri", "random_weibull"}; - static_assert(RANDOM_WEIBULL_F - ABS_F + 1 == sizeof(funNames) / sizeof(funNames[0]), + static_assert(Kind::RANDOM_WEIBULL_F - Kind::ABS_F + 1 == sizeof(funNames) / sizeof(funNames[0]), "Builtin function name list is wrong"); - assert(ABS_F <= kind && kind <= RANDOM_WEIBULL_F); - return funNames[kind - ABS_F]; + assert(Kind::ABS_F <= kind && kind <= Kind::RANDOM_WEIBULL_F); + return funNames[kind - Kind::ABS_F]; } void PrettyPrinter::expr_builtin_function1(Kind kind) @@ -752,6 +751,7 @@ void PrettyPrinter::expr_assignment(Kind op) auto lhs = pop_back(st); st.emplace_back(); switch (op) { + using namespace KindNames; case ASSIGN: st.back() = '(' + lhs + " = " + rhs + ')'; break; case ASS_PLUS: st.back() = '(' + lhs + " += " + rhs + ')'; break; case ASS_MINUS: st.back() = '(' + lhs + " -= " + rhs + ')'; break; @@ -772,6 +772,7 @@ void PrettyPrinter::expr_unary(Kind op) auto exp = pop_back(st); st.emplace_back(); switch (op) { + using namespace KindNames; case MINUS: st.back() = '-' + exp; break; case NOT: st.back() = '!' + exp; break; case PLUS: st.back() = '+' + exp; break; @@ -789,6 +790,7 @@ void PrettyPrinter::expr_binary(Kind op) auto exp1 = pop_back(st); st.emplace_back(); switch (op) { + using namespace KindNames; case PO_CONTROL: st.back() = exp1 + " control: " + exp2; break; case CONTROL_TOPT_DEF1: st.back() = "control_t*(" + exp1 + "): " + exp2; break; case PLUS: st.back() = '(' + exp1 + " + " + exp2 + ')'; break; @@ -824,8 +826,8 @@ void PrettyPrinter::expr_ternary(Kind op, bool firstMissing) auto exp1 = (firstMissing) ? "1"s : pop_back(st); st.emplace_back(); switch (op) { - case CONTROL_TOPT: st.back() = "control_t*(" + exp1 + "," + exp2 + "): " + exp3; break; - case SMC_CONTROL: st.back() = "control[" + exp1 + "<=" + exp2 + "]: " + exp3; break; + case Kind::CONTROL_TOPT: st.back() = "control_t*(" + exp1 + "," + exp2 + "): " + exp3; break; + case Kind::SMC_CONTROL: st.back() = "control[" + exp1 + "<=" + exp2 + "]: " + exp3; break; default: throw TypeException{"Invalid operator"}; } } @@ -964,7 +966,7 @@ void PrettyPrinter::exprProba2(bool isTimedBound, int type) st.push_back(ss.str()); } */ -void PrettyPrinter::expr_proba_quantitative(Constants::Kind type) +void PrettyPrinter::expr_proba_quantitative(Kind type) { const auto pred2 = pop_back(st); const auto pred1 = pop_back(st); @@ -1045,3 +1047,5 @@ void PrettyPrinter::query_comment(std::string_view comment) *o.top() << "/* Comment: " << comment << " */\n"; } void PrettyPrinter::query_end() { *o.top() << "/** Query end. */\n"; } + +} // namespace UTAP \ No newline at end of file diff --git a/src/StatementBuilder.cpp b/src/StatementBuilder.cpp index 3596aecd..4f6b8972 100644 --- a/src/StatementBuilder.cpp +++ b/src/StatementBuilder.cpp @@ -34,8 +34,7 @@ #include #endif -using namespace UTAP; -using namespace Constants; +namespace UTAP { StatementBuilder::StatementBuilder(Document& doc, std::vector libpaths): ExpressionBuilder{doc}, libpaths{std::move(libpaths)} @@ -44,7 +43,7 @@ StatementBuilder::StatementBuilder(Document& doc, std::vectorlibpaths.emplace(this->libpaths.begin(), ""); } -void StatementBuilder::collectDependencies(std::set& dependencies, const Expression& expr) +void StatementBuilder::collect_dependencies(std::set& dependencies, const Expression& expr) { auto symbols = std::set{}; expr.collect_possible_reads(symbols); @@ -66,16 +65,16 @@ void StatementBuilder::collectDependencies(std::set& dependencies, const } } -void StatementBuilder::collectDependencies(std::set& dependencies, const Type& type) +void StatementBuilder::collect_dependencies(std::set& dependencies, const Type& type) { - if (type.get_kind() == RANGE) { + if (type.get_kind() == Kind::RANGE) { auto [lower, upper] = type.get_range(); - collectDependencies(dependencies, lower); - collectDependencies(dependencies, upper); - collectDependencies(dependencies, type[0]); + collect_dependencies(dependencies, lower); + collect_dependencies(dependencies, upper); + collect_dependencies(dependencies, type[0]); } else { for (uint32_t i = 0; i < type.size(); ++i) - collectDependencies(dependencies, type[i]); + collect_dependencies(dependencies, type[i]); } } @@ -88,7 +87,7 @@ void StatementBuilder::type_array_of_size(uint32_t n) expr_nat(0); fragments.push(expr); expr_nat(1); - expr_binary(MINUS); + expr_binary(Kind::MINUS); type_bounded_int(TypePrefix::NONE); type_array_of_type(n + 1); } @@ -104,10 +103,10 @@ void StatementBuilder::type_array_of_type(uint32_t n) * processes. */ if (currentTemplate != nullptr) { - collectDependencies(currentTemplate->restricted, size); + collect_dependencies(currentTemplate->restricted, size); } - if ((!size.is_integer() && !size.is_scalar()) || !size.is(RANGE)) { + if ((!size.is_integer() && !size.is_scalar()) || !size.is(Kind::RANGE)) { handle_error(TypeException{"$Array_must_be_defined_over_an_integer_range_or_a_scalar_set"}); } } @@ -135,7 +134,7 @@ void StatementBuilder::struct_field(std::string_view name) auto type = typeFragments.pop(); // Constant fields are not allowed - if (type.is(CONSTANT)) + if (type.is(Kind::CONSTANT)) handle_error(TypeException{"$Constant_fields_not_allowed_in_struct"}); fields.push_back(type); @@ -171,7 +170,7 @@ static bool initialisable(Type type) { type = type.strip(); switch (type.get_kind()) { - case RECORD: + case Kind::RECORD: for (auto i = 0u; i < type.size(); i++) { if (!initialisable(type[i])) { return false; @@ -179,12 +178,12 @@ static bool initialisable(Type type) } return true; - case ARRAY: + case Kind::ARRAY: if (type.get_array_size().is_scalar()) { return false; } return initialisable(type.get_sub()); - case STRING: return true; + case Kind::STRING: return true; default: return type.is_integral() || type.is_clock() || type.is_double(); } } @@ -192,15 +191,15 @@ static bool initialisable(Type type) static bool mustInitialise(const Type& type) { const auto k = type.get_kind(); - assert(k != FUNCTION); - assert(k != FUNCTION_EXTERNAL); - assert(k != PROCESS); - assert(k != INSTANCE); - assert(k != LSC_INSTANCE); + assert(k != Kind::FUNCTION); + assert(k != Kind::FUNCTION_EXTERNAL); + assert(k != Kind::PROCESS); + assert(k != Kind::INSTANCE); + assert(k != Kind::LSC_INSTANCE); switch (k) { - case CONSTANT: return true; - case RECORD: + case Kind::CONSTANT: return true; + case Kind::RECORD: for (auto i = 0u; i < type.size(); i++) { if (mustInitialise(type[i])) { return true; @@ -243,7 +242,7 @@ void StatementBuilder::decl_var(std::string_view name, bool hasInit) } // Add variable to document - addVariable(type, name, init, position_t()); + add_variable(type, name, init, position_t()); } // Array and struct initialisers are represented as expressions having @@ -284,13 +283,13 @@ Expression StatementBuilder::make_initialiser(const Type& type, const Expression { if (type.is_assignment_compatible(init.get_type(), true)) { return init; - } else if (type.is_array() && init.get_kind() == LIST) { + } else if (type.is_array() && init.get_kind() == Kind::LIST) { auto subtype = type.get_sub(); auto result = std::vector(init.get_size()); for (uint32_t i = 0; i < init.get_type().size(); i++) result[i] = make_initialiser(subtype, init[i]); - return Expression::create_nary(LIST, result, init.get_position(), type); - } else if (type.is_record() && init.get_kind() == LIST) { + return Expression::create_nary(Kind::LIST, result, init.get_position(), type); + } else if (type.is_record() && init.get_kind() == Kind::LIST) { /* In order to access the record labels we have to strip any * prefixes and labels from the record type. */ @@ -317,7 +316,7 @@ Expression StatementBuilder::make_initialiser(const Type& type, const Expression } result[current] = make_initialiser(type.get_sub(current), init[i]); } - return Expression::create_nary(LIST, result, init.get_position(), type); + return Expression::create_nary(Kind::LIST, result, init.get_position(), type); } document.add_error(init.get_position(), "$Invalid_initialiser", type.str()); return init; @@ -343,7 +342,7 @@ void StatementBuilder::decl_init_list(uint32_t num) fields[i].set_type(type[0]); } // Create list expression - fragments.push(Expression::create_nary(LIST, fields, position, Type::create_record(types, labels, position))); + fragments.push(Expression::create_nary(Kind::LIST, fields, position, Type::create_record(types, labels, position))); } /******************************************************************** @@ -353,7 +352,7 @@ void StatementBuilder::decl_parameter(std::string_view name, bool ref) { Type type = typeFragments.pop(); if (ref) - type = type.create_prefix(REF); + type = type.create_prefix(Kind::REF); params.add_symbol(name, type, position); } @@ -383,7 +382,7 @@ void StatementBuilder::decl_func_begin(std::string_view name) labels.push_back(param.get_name()); } auto type = Type::create_function(return_type, types, labels, position); - if (!addFunction(type, name, {})) + if (!add_function(type, name, {})) handle_error(duplicate_definition_error(name)); /* We maintain a stack of frames. As the function has a local @@ -473,7 +472,7 @@ void StatementBuilder::decl_external_func(std::string_view name, std::string_vie } auto type = Type::create_external_function(return_type, types, labels, position); - if (!addFunction(type, alias, position_t())) + if (!add_function(type, alias, position_t())) handle_error(duplicate_definition_error(alias)); push_frame(frames.top().make_sub()); params.move_to(frames.top()); // params is emptied here @@ -520,15 +519,15 @@ void StatementBuilder::iteration_begin(std::string_view name) { Type type = typeFragments.pop(); // The iterator cannot be modified. - if (!type.is(CONSTANT)) { - type = type.create_prefix(CONSTANT); + if (!type.is(Kind::CONSTANT)) { + type = type.create_prefix(Kind::CONSTANT); } // The iteration statement has a local scope for the iterator. push_frame(frames.top().make_sub()); // Add variable. - Variable* variable = addVariable(type, name, Expression(), position_t()); + Variable* variable = add_variable(type, name, Expression(), position_t()); /* Create a new statement for the loop. We need to already create * this here as the statement is the only thing that can keep the @@ -621,3 +620,5 @@ void StatementBuilder::expr_call_begin() handle_error(TypeException{"$Recursion_is_not_allowed"}); } } + +} // namespace UTAP \ No newline at end of file diff --git a/src/TypeChecker.cpp b/src/TypeChecker.cpp index 5d116336..ffed12ab 100644 --- a/src/TypeChecker.cpp +++ b/src/TypeChecker.cpp @@ -38,19 +38,19 @@ #include #include #include + #include // size_t #include // uint32_t #include -using namespace UTAP; -using namespace Constants; +namespace UTAP { using namespace std::string_literals; namespace { // anonymous namespace is preferred over `static` (C++ standard) /// The following are simple helper functions for testing the type of expressions. -bool is_cost(const Expression& expr) { return expr.get_type().is(COST); } +bool is_cost(const Expression& expr) { return expr.get_type().is(Kind::COST); } bool is_void(const Expression& expr) { return expr.get_type().is_void(); } @@ -69,7 +69,7 @@ bool is_string(Expression expr) bool is_integer(const Expression& expr) { return expr.get_type().is_integer(); } -bool is_const_integer(const Expression& expr) { return expr.get_kind() == CONSTANT && is_integer(expr); } +bool is_const_integer(const Expression& expr) { return expr.get_kind() == Kind::CONSTANT && is_integer(expr); } bool is_default_int(const Type& type) { @@ -93,7 +93,7 @@ bool is_double_value(const Expression& expr) { return is_double(expr) || is_cloc bool is_number(const Expression& expr) { return is_double_value(expr) || is_integral(expr); } -bool is_const_double(const Expression& expr) { return expr.get_kind() == CONSTANT && is_double(expr); } +bool is_const_double(const Expression& expr) { return expr.get_kind() == Kind::CONSTANT && is_double(expr); } bool is_invariant(const Expression& expr) { return expr.get_type().is_invariant(); } @@ -107,7 +107,7 @@ bool is_formula(const Expression& expr) { return expr.get_type().is_formula(); } bool is_formula_list(const Expression& expr) { - if (expr.get_kind() != LIST) { + if (expr.get_kind() != Kind::LIST) { return false; } @@ -129,12 +129,12 @@ bool hasStrictLowerBound(const Expression& expr) } switch (expr.get_kind()) { - case LT: // int < clock + case Kind::LT: // int < clock if (is_integral(expr[0]) && is_clock(expr[1])) { return true; } break; - case GT: // clock > int + case Kind::GT: // clock > int if (is_clock(expr[0]) && is_integral(expr[1])) { return true; } @@ -155,12 +155,12 @@ bool hasStrictUpperBound(const Expression& expr) } switch (expr.get_kind()) { - case GT: // int > clock + case Kind::GT: // int > clock if (is_integral(expr[0]) && is_clock(expr[1])) { return true; } break; - case LT: // clock < int + case Kind::LT: // clock < int if (is_clock(expr[0]) && is_integral(expr[1])) { return true; } @@ -176,7 +176,7 @@ bool hasStrictUpperBound(const Expression& expr) * Returns true iff type is a valid invariant. A valid invariant is * either an invariant expression or an integer expression. */ -bool is_invariant_wr(const Expression& expr) { return is_invariant(expr) || (expr.get_type().is(INVARIANT_WR)); } +bool is_invariant_wr(const Expression& expr) { return is_invariant(expr) || (expr.get_type().is(Kind::INVARIANT_WR)); } /** * Returns true if values of this type can be assigned. This is the @@ -186,13 +186,14 @@ bool is_invariant_wr(const Expression& expr) { return is_invariant(expr) || (exp bool is_assignable(const Type& type) { switch (type.get_kind()) { - case Constants::INT: - case Constants::BOOL: - case Constants::DOUBLE: - case Constants::STRING: - case Constants::CLOCK: - case Constants::COST: - case Constants::SCALAR: return true; + using namespace KindNames; + case INT: + case BOOL: + case DOUBLE: + case STRING: + case CLOCK: + case COST: + case SCALAR: return true; case ARRAY: return is_assignable(type[0]); @@ -490,6 +491,7 @@ Error clock_difference_is_not_supported(const Expression& expr) bool is_game_property(const Expression& expr) { switch (expr.get_kind()) { + using namespace KindNames; case CONTROL: case SMC_CONTROL: case EF_CONTROL: @@ -503,7 +505,7 @@ bool is_game_property(const Expression& expr) bool has_MITL_in_quantified_sub(const Expression& expr) { - bool hasIt = (expr.get_kind() == MITL_FORALL || expr.get_kind() == MITL_EXISTS); + bool hasIt = (expr.get_kind() == Kind::MITL_FORALL || expr.get_kind() == Kind::MITL_EXISTS); if (!hasIt) { for (uint32_t i = 0; i < expr.get_size(); i++) { hasIt |= has_MITL_in_quantified_sub(expr.get(i)); @@ -514,7 +516,7 @@ bool has_MITL_in_quantified_sub(const Expression& expr) bool has_spawn_or_exit(const Expression& expr) { - bool hasIt = (expr.get_kind() == SPAWN || expr.get_kind() == EXIT); + bool hasIt = (expr.get_kind() == Kind::SPAWN || expr.get_kind() == Kind::EXIT); if (!hasIt) { for (uint32_t i = 0; i < expr.get_size(); i++) { hasIt |= has_spawn_or_exit(expr.get(i)); @@ -526,7 +528,8 @@ bool has_spawn_or_exit(const Expression& expr) bool valid_return_type(const Type& type) { switch (type.get_kind()) { - case Constants::RECORD: + using namespace KindNames; + case RECORD: for (uint32_t i = 0; i < type.size(); i++) { if (!valid_return_type(type[i])) { return false; @@ -534,13 +537,13 @@ bool valid_return_type(const Type& type) } return true; - case Constants::RANGE: - case Constants::LABEL: return valid_return_type(type[0]); + case RANGE: + case LABEL: return valid_return_type(type[0]); - case Constants::INT: - case Constants::BOOL: - case Constants::SCALAR: - case Constants::DOUBLE: return true; + case INT: + case BOOL: + case SCALAR: + case DOUBLE: return true; default: return false; } @@ -570,7 +573,7 @@ void CompileTimeComputableValues::visit_instance(Instance& temp) { for (const auto& param : temp.parameters) { const Type& type = param.get_type(); - if (!type.is(REF) && type.is_constant() && !type.is_double()) { + if (!type.is(Kind::REF) && type.is_constant() && !type.is_double()) { variables.insert(param); } } @@ -602,24 +605,24 @@ void RateDecomposer::decompose(const Expression& expr, bool inforall) assert(is_invariant_wr(expr)); if (is_invariant(expr)) { - if (expr.get_kind() == Constants::LT) { + if (expr.get_kind() == Kind::LT) { hasStrictInvariant = true; // Strict upper bounds only. } if (!inforall) { invariant = invariant.empty() ? expr - : invariant = Expression::create_binary(AND, invariant, expr, expr.get_position(), - Type::create_primitive(INVARIANT)); + : invariant = Expression::create_binary(Kind::AND, invariant, expr, expr.get_position(), + Type::create_primitive(Kind::INVARIANT)); } - } else if (expr.get_kind() == AND) { + } else if (expr.get_kind() == Kind::AND) { decompose(expr[0], inforall); decompose(expr[1], inforall); - } else if (expr.get_kind() == EQ) { + } else if (expr.get_kind() == Kind::EQ) { Expression left; Expression right; - assert((expr[0].get_type().get_kind() == RATE) ^ (expr[1].get_type().get_kind() == RATE)); + assert((expr[0].get_type().get_kind() == Kind::RATE) ^ (expr[1].get_type().get_kind() == Kind::RATE)); - if (expr[0].get_type().get_kind() == RATE) { + if (expr[0].get_type().get_kind() == Kind::RATE) { left = expr[0][0]; right = expr[1]; } else { @@ -634,19 +637,19 @@ void RateDecomposer::decompose(const Expression& expr, bool inforall) hasClockRates = true; if (!inforall) { invariant = invariant.empty() ? expr - : Expression::create_binary(AND, invariant, expr, expr.get_position(), - Type::create_primitive(INVARIANT_WR)); + : Expression::create_binary(Kind::AND, invariant, expr, expr.get_position(), + Type::create_primitive(Kind::INVARIANT_WR)); } } } else { - assert(expr.get_type().is(INVARIANT_WR)); - assert(expr.get_kind() == FORALL); + assert(expr.get_type().is(Kind::INVARIANT_WR)); + assert(expr.get_kind() == Kind::FORALL); // Enter the forall to look for clock rates but don't // record them, rather the forall expression. decompose(expr[1], true); invariant = invariant.empty() ? expr - : invariant = Expression::create_binary(AND, invariant, expr, expr.get_position(), - Type::create_primitive(INVARIANT_WR)); + : invariant = Expression::create_binary(Kind::AND, invariant, expr, expr.get_position(), + Type::create_primitive(Kind::INVARIANT_WR)); } } @@ -682,9 +685,9 @@ void TypeChecker::handleError(TypeError error) */ void TypeChecker::checkIgnoredValue(const Expression& expr) { - if (!expr.changes_any_variable() && expr.get_kind() != FUN_CALL_EXT) { + if (!expr.changes_any_variable() && expr.get_kind() != Kind::FUN_CALL_EXT) { handleWarning(expression_has_no_effect(expr)); - } else if (expr.get_kind() == COMMA && !expr[1].changes_any_variable() && expr[1].get_kind() != FUN_CALL_EXT) { + } else if (expr.get_kind() == Kind::COMMA && !expr[1].changes_any_variable() && expr[1].get_kind() != Kind::FUN_CALL_EXT) { handleWarning(expression_has_no_effect(expr[1])); } } @@ -728,6 +731,7 @@ void TypeChecker::checkType(const Type& type, bool initialisable, bool inStruct) Type size; switch (type.get_kind()) { + using namespace KindNames; case LABEL: checkType(type[0], initialisable, inStruct); break; case URGENT: @@ -806,14 +810,14 @@ void TypeChecker::checkType(const Type& type, bool initialisable, bool inStruct) checkType(type.get_sub(i), true, true); } break; - case Constants::STRING: + case STRING: if (inStruct) handleError(cannot_be_inside_struct(type)); - break; - case Constants::CLOCK: break; - case Constants::DOUBLE: break; - case Constants::INT: break; - case Constants::BOOL: break; + break; + case CLOCK: break; + case DOUBLE: break; + case INT: break; + case BOOL: break; default: if (initialisable) @@ -838,7 +842,7 @@ void TypeChecker::visit_doc_after(Document& doc) } // Check index expressions - while (expr.get_kind() == ARRAY) { + while (expr.get_kind() == Kind::ARRAY) { if (!isCompileTimeComputable(expr[1])) handleError(must_be_computable_at_compile_time(expr[1])); else if (i.head.changes_any_variable()) @@ -860,7 +864,7 @@ void TypeChecker::visit_doc_after(Document& doc) handleError(channel_expected(expr)); // Check index expressions - while (expr.get_kind() == ARRAY) { + while (expr.get_kind() == Kind::ARRAY) { if (!isCompileTimeComputable(expr[1])) handleError(must_be_computable_at_compile_time(expr[1])); else if (j.second.changes_any_variable()) @@ -929,7 +933,7 @@ void TypeChecker::visit_io_decl(IODecl& iodecl) handleError(channel_expected(expr)); // Check index expressions - while (expr.get_kind() == ARRAY) { + while (expr.get_kind() == Kind::ARRAY) { if (!isCompileTimeComputable(expr[1])) handleError(must_be_computable_at_compile_time(expr[1])); else if (expr.changes_any_variable()) @@ -950,7 +954,7 @@ void TypeChecker::visit_io_decl(IODecl& iodecl) handleError(channel_expected(expr)); // Check index expressions - while (expr.get_kind() == ARRAY) { + while (expr.get_kind() == Kind::ARRAY) { if (!isCompileTimeComputable(expr[1])) handleError(must_be_computable_at_compile_time(expr[1])); else if (expr.changes_any_variable()) @@ -967,7 +971,7 @@ void TypeChecker::visit_process(Instance& process) // Unbound parameters of processes must be either scalars or bounded integers. const Symbol& parameter = process.parameters[i]; const Type& type = parameter.get_type(); - if (!(type.is_scalar() || type.is_range()) || type.is(REF) || is_default_int(type)) + if (!(type.is_scalar() || type.is_range()) || type.is(Kind::REF) || is_default_int(type)) handleError(free_param_must_be_int_or_scalar(parameter)); /* Unbound parameters must not be used either directly or indirectly in any array size declarations. * I.e. they must not be restricted. */ @@ -1024,7 +1028,7 @@ void TypeChecker::visit_location(Location& loc) } if (!loc.exp_rate.empty()) { if (auto& expr = loc.exp_rate; checkExpression(expr)) { - if (!is_integral(expr) && expr.get_kind() != FRACTION && !expr.get_type().is_double()) { + if (!is_integral(expr) && expr.get_kind() != Kind::FRACTION && !expr.get_type().is_double()) { handleError(number_expected(expr)); } } @@ -1071,8 +1075,8 @@ void TypeChecker::visit_edge(Edge& edge) handleError(must_be_side_effect_free(edge.sync)); else { const bool hasClockGuard = !edge.guard.empty() && !is_integral(edge.guard); - const bool isUrgent = channel.is(URGENT); - const bool receivesBroadcast = channel.is(BROADCAST) && edge.sync.get_sync() == SYNC_QUE; + const bool isUrgent = channel.is(Kind::URGENT); + const bool receivesBroadcast = channel.is(Kind::BROADCAST) && edge.sync.get_sync() == Sync::QUE; if (isUrgent && hasClockGuard) { document.set_urgent_transition(); @@ -1111,25 +1115,25 @@ void TypeChecker::visit_edge(Edge& edge) switch (syncUsed) { case 0: switch (edge.sync.get_sync()) { - case SYNC_BANG: - case SYNC_QUE: syncUsed = 1; break; - case SYNC_CSP: syncUsed = 2; break; + case Sync::BANG: + case Sync::QUE: syncUsed = 1; break; + case Sync::CSP: syncUsed = 2; break; } break; case 1: switch (edge.sync.get_sync()) { - case SYNC_BANG: - case SYNC_QUE: + case Sync::BANG: + case Sync::QUE: // ok break; - case SYNC_CSP: syncUsed = -1; break; + case Sync::CSP: syncUsed = -1; break; } break; case 2: switch (edge.sync.get_sync()) { - case SYNC_BANG: - case SYNC_QUE: syncUsed = -1; break; - case SYNC_CSP: + case Sync::BANG: + case Sync::QUE: syncUsed = -1; break; + case Sync::CSP: // ok break; } @@ -1143,11 +1147,11 @@ void TypeChecker::visit_edge(Edge& edge) if (refinementWarnings) { switch (edge.sync.get_sync()) { - case SYNC_BANG: + case Sync::BANG: if (edge.control) handleWarning(outputs_should_be_uncontrollable(edge.sync)); break; - case SYNC_QUE: + case Sync::QUE: if (!edge.control) handleWarning(inputs_should_be_controllable(edge.sync)); break; @@ -1262,7 +1266,7 @@ void TypeChecker::visit_instance(Instance& instance) // - Constant reference with computable argument // - Reference parameter with unique lhs argument // If none of the cases are true, then we generate an error - const bool ref = parameter.get_type().is(REF); + const bool ref = parameter.get_type().is(Kind::REF); const bool constant = parameter.get_type().is_constant(); const bool computable = isCompileTimeComputable(argument); @@ -1270,7 +1274,7 @@ void TypeChecker::visit_instance(Instance& instance) (ref && constant && !computable)) { handleError(incompatible_argument(argument)); continue; - } + } checkParameterCompatible(parameter.get_type(), argument); } @@ -1282,7 +1286,7 @@ void TypeChecker::visitProperty(Expression& expr) if (expr.changes_any_variable()) { handleError(must_be_side_effect_free(expr)); } - if (expr.get_kind() == LOAD_STRAT || expr.get_kind() == SAVE_STRAT) { + if (expr.get_kind() == Kind::LOAD_STRAT || expr.get_kind() == Kind::SAVE_STRAT) { if (!expr.get(0).get_type().is_string()) handleError(loadStrategy_and_saveStrategy_expect_string(expr)); return; @@ -1305,12 +1309,12 @@ void TypeChecker::visitProperty(Expression& expr) } } */ - } else if (auto k = expr.get_kind(); k != SUP_VAR && k != INF_VAR && k != BOUNDS_VAR && k != SCENARIO && - k != PROBA_MIN_BOX && k != PROBA_MIN_DIAMOND && k != PROBA_BOX && - k != PROBA_DIAMOND && k != PROBA_EXP && k != PROBA_CMP && k != SIMULATE && - k != SIMULATEREACH && k != MITL_FORMULA && - k != MIN_EXP && // ALREADY CHECKED IN PARSE - k != MAX_EXP) // ALREADY CHECKED IN PARSE + } else if (auto k = expr.get_kind(); k != Kind::SUP_VAR && k != Kind::INF_VAR && k != Kind::BOUNDS_VAR && k != Kind::SCENARIO && + k != Kind::PROBA_MIN_BOX && k != Kind::PROBA_MIN_DIAMOND && k != Kind::PROBA_BOX && + k != Kind::PROBA_DIAMOND && k != Kind::PROBA_EXP && k != Kind::PROBA_CMP && k != Kind::SIMULATE && + k != Kind::SIMULATEREACH && k != Kind::MITL_FORMULA && + k != Kind::MIN_EXP && // ALREADY CHECKED IN PARSE + k != Kind::MAX_EXP) // ALREADY CHECKED IN PARSE { for (uint32_t i = 0; i < expr.get_size(); i++) { // No nesting except for constraints @@ -1318,13 +1322,13 @@ void TypeChecker::visitProperty(Expression& expr) handleError(nested_path_quntifiers_not_supported(expr[i])); } } - if (expr.get_kind() == PO_CONTROL) { + if (expr.get_kind() == Kind::PO_CONTROL) { /* Observations on clock constraints are limited to be * weak for lower bounds and strict for upper bounds. */ checkObservationConstraints(expr); } - if (has_MITL_in_quantified_sub(expr) && expr.get_kind() != MITL_FORMULA) + if (has_MITL_in_quantified_sub(expr) && expr.get_kind() != Kind::MITL_FORMULA) handleError(mitl_inside_forall_or_exists_in_non_mitl(expr)); } } @@ -1355,8 +1359,8 @@ bool TypeChecker::checkAssignmentExpression(Expression& expr) handleError(invalid_assignment(expr)); return false; } - - if (expr.get_kind() != FUN_CALL_EXT && (expr.get_kind() != CONSTANT || expr.get_value() != 1)) { + const auto k = expr.get_kind(); + if (k != Kind::FUN_CALL_EXT && (k != Kind::CONSTANT || expr.get_value() != 1)) { checkIgnoredValue(expr); } @@ -1382,18 +1386,18 @@ void TypeChecker::checkObservationConstraints(const Expression& expr) bool invalid = false; switch (expr.get_kind()) { - case LT: // int < clock - case GE: // int >= clock + case Kind::LT: // int < clock + case Kind::GE: // int >= clock invalid = is_integral(expr[0]) && is_clock(expr[1]); break; - case LE: // clock <= int - case GT: // clock > int + case Kind::LE: // clock <= int + case Kind::GT: // clock > int invalid = is_clock(expr[0]) && is_integral(expr[1]); break; - case EQ: // clock == int || int == clock - case NEQ: // clock != int || int != clock + case Kind::EQ: // clock == int || int == clock + case Kind::NEQ: // clock != int || int != clock invalid = (is_clock(expr[0]) && is_integral(expr[1])) || (is_integral(expr[0]) && is_clock(expr[1])); break; @@ -1405,16 +1409,16 @@ void TypeChecker::checkObservationConstraints(const Expression& expr) else { switch (expr.get_kind()) // No clock differences. { - case LT: - case LE: - case GT: - case GE: - case EQ: - case NEQ: + case Kind::LT: + case Kind::LE: + case Kind::GT: + case Kind::GE: + case Kind::EQ: + case Kind::NEQ: if ((is_clock(expr[0]) && is_clock(expr[1])) || (is_diff(expr[0]) && is_integer(expr[1])) || (is_integer(expr[0]) && is_diff(expr[1]))) { handleError(clock_difference_is_not_supported(expr)); - } + } break; default:; @@ -1501,7 +1505,7 @@ int32_t TypeChecker::visit_iteration_statement(RangeStatement& stat) */ if (!type.is_scalar() && !type.is_integer()) handleError(scalar_or_integer_expected(type)); - else if (!type.is(RANGE)) + else if (!type.is(Kind::RANGE)) handleError(range_expected(type)); return stat.stat->accept(*this); @@ -1585,7 +1589,7 @@ int32_t TypeChecker::visit_return_statement(ReturnStatement& stat) */ bool TypeChecker::isParameterCompatible(const Type& paramType, const Expression& arg) const { - const bool ref = paramType.is(REF); + const bool ref = paramType.is(Kind::REF); const bool constant = paramType.is_constant(); const bool lvalue = isModifiableLValue(arg); const Type& argType = arg.get_type(); @@ -1627,7 +1631,7 @@ Expression TypeChecker::checkInitialiser(const Type& type, const Expression& ini { if (type.is_assignment_compatible(init.get_type(), true)) { return init; - } else if (type.is_array() && init.get_kind() == LIST) { + } else if (type.is_array() && init.get_kind() == Kind::LIST) { auto subtype = type.get_sub(); auto result = std::vector(init.get_size()); for (uint32_t i = 0; i < init.get_type().size(); i++) { @@ -1635,8 +1639,8 @@ Expression TypeChecker::checkInitialiser(const Type& type, const Expression& ini handleError(field_name_not_allowed_in_array_init(init[i])); checkInitialiser(subtype, init[i]); } - return Expression::create_nary(LIST, result, init.get_position(), type); - } else if (type.is_record() && init.get_kind() == LIST) { + return Expression::create_nary(Kind::LIST, result, init.get_position(), type); + } else if (type.is_record() && init.get_kind() == Kind::LIST) { /* In order to access the record labels we have to strip any * prefixes and labels from the record type. */ @@ -1670,7 +1674,7 @@ Expression TypeChecker::checkInitialiser(const Type& type, const Expression& ini break; } } - return Expression::create_nary(LIST, result, init.get_position(), type); + return Expression::create_nary(Kind::LIST, result, init.get_position(), type); } handleError(invalid_initializer(init)); return init; @@ -1683,7 +1687,7 @@ Type TypeChecker::getInlineIfCommonType(const Type& t1, const Type& t2) const else if (t2.is_record()) return t2; else if ((t1.is_clock() && !t2.is_clock()) || (!t1.is_clock() && t2.is_clock())) - return Type{DOUBLE, {}, 0}; + return Type{Kind::DOUBLE, {}, 0}; else if (t1.is_assignment_compatible(t2)) return t1; else if (t2.is_assignment_compatible(t1)) @@ -1733,6 +1737,7 @@ bool TypeChecker::checkExpression(Expression& expr) // Type arg2; // Type arg3; switch (expr.get_kind()) { + using namespace KindNames; // It is possible to have DOT expressions as data.x // with data being an array of struct. The type checker // is broken and trying @@ -1741,8 +1746,8 @@ bool TypeChecker::checkExpression(Expression& expr) // This should be fixed one day. /* case DOT: - if (expr[0].get_type().is_process(Constants::PROCESS) || - expr[0].get_type().is(Constants::RECORD)) + if (expr[0].get_type().is_process(PROCESS) || + expr[0].get_type().is(RECORD)) { return true; } @@ -1756,7 +1761,7 @@ bool TypeChecker::checkExpression(Expression& expr) if (is_integral(expr[2]) || is_double_value(expr[2])) { type = expr[2].get_type(); } else if (is_invariant(expr[2]) || is_guard(expr[2])) { - type = Type::create_primitive(Constants::DOUBLE_INV_GUARD); + type = Type::create_primitive(DOUBLE_INV_GUARD); } else { handleError(invalid_sum(expr)); return false; @@ -1764,43 +1769,43 @@ bool TypeChecker::checkExpression(Expression& expr) break; case FRACTION: if (is_integral(expr[0]) && is_integral(expr[1])) - type = Type::create_primitive(Constants::FRACTION); + type = Type::create_primitive(FRACTION); break; case PLUS: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } else if ((is_integer(expr[0]) && is_clock(expr[1])) || (is_clock(expr[0]) && is_integer(expr[1]))) { type = Type::create_primitive(CLOCK); } else if ((is_diff(expr[0]) && is_integer(expr[1])) || (is_integer(expr[0]) && is_diff(expr[1]))) { type = Type::create_primitive(DIFF); } else if (is_number(expr[0]) && is_number(expr[1])) { // SMC extension. - type = Type::create_primitive(Constants::DOUBLE); + type = Type::create_primitive(DOUBLE); } break; case MINUS: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } else if (is_clock(expr[0]) && is_integer(expr[1])) - // removed "|| is_integer(expr[0].type) && is_clock(expr[1].type)" - // in order to be able to convert into ClockGuards + // removed "|| is_integer(expr[0].type) && is_clock(expr[1].type)" + // in order to be able to convert into ClockGuards { type = Type::create_primitive(CLOCK); } else if ((is_diff(expr[0]) && is_integer(expr[1])) || (is_integer(expr[0]) && is_diff(expr[1])) || (is_clock(expr[0]) && is_clock(expr[1]))) { type = Type::create_primitive(DIFF); - } else if (is_number(expr[0]) && is_number(expr[1])) { - // SMC extension. - // x-y with that semantic should be written x+(-y) - type = Type::create_primitive(Constants::DOUBLE); - } + } else if (is_number(expr[0]) && is_number(expr[1])) { + // SMC extension. + // x-y with that semantic should be written x+(-y) + type = Type::create_primitive(DOUBLE); + } break; case AND: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if (is_invariant(expr[0]) && is_invariant(expr[1])) { type = Type::create_primitive(INVARIANT); } else if (is_invariant_wr(expr[0]) && is_invariant_wr(expr[1])) { @@ -1816,7 +1821,7 @@ bool TypeChecker::checkExpression(Expression& expr) case OR: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if (is_integral(expr[0]) && is_invariant(expr[1])) { type = Type::create_primitive(INVARIANT); } else if (is_invariant(expr[0]) && is_integral(expr[1])) { @@ -1836,7 +1841,7 @@ bool TypeChecker::checkExpression(Expression& expr) case XOR: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } break; @@ -1861,13 +1866,13 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(template_only_declared_and_undefined(expr)); return false; } - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); break; } case NUMOF: { if (document.find_dynamic_template(expr[0].get_symbol().get_name()) != nullptr) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } else { handleError(not_dynamic_template(expr)); return false; @@ -1882,7 +1887,7 @@ bool TypeChecker::checkExpression(Expression& expr) return false; } - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); break; } @@ -1890,18 +1895,18 @@ bool TypeChecker::checkExpression(Expression& expr) case LT: case LE: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if ((is_clock(expr[0]) && is_clock(expr[1])) || (is_clock(expr[0]) && is_bound(expr[1])) || (is_clock(expr[1]) && is_bound(expr[0])) || (is_diff(expr[0]) && is_bound(expr[1])) || (is_bound(expr[0]) && is_diff(expr[1]))) { type = Type::create_primitive(INVARIANT); - } else if (is_number(expr[0]) && is_clock(expr[1])) { - type = Type::create_primitive(GUARD); - } else if (is_clock(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(GUARD); - } else if (is_number(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(Constants::BOOL); - } + } else if (is_number(expr[0]) && is_clock(expr[1])) { + type = Type::create_primitive(GUARD); + } else if (is_clock(expr[0]) && is_number(expr[1])) { + type = Type::create_primitive(GUARD); + } else if (is_number(expr[0]) && is_number(expr[1])) { + type = Type::create_primitive(BOOL); + } break; case EQ: @@ -1911,43 +1916,43 @@ bool TypeChecker::checkExpression(Expression& expr) (is_number(expr[0]) && is_clock(expr[1])) || (is_diff(expr[0]) && is_number(expr[1])) || (is_number(expr[0]) && is_diff(expr[1]))) { type = Type::create_primitive(GUARD); - } else if (expr[0].get_type().is_equality_compatible(expr[1].get_type())) { - type = Type::create_primitive(Constants::BOOL); - } else if ((expr[0].get_type().is(RATE) && (is_integral(expr[1]) || is_double_value(expr[1]))) || - ((is_integral(expr[0]) || is_double_value(expr[0])) && expr[1].get_type().is(RATE))) { - type = Type::create_primitive(INVARIANT_WR); - } else if (is_number(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(Constants::BOOL); - } + } else if (expr[0].get_type().is_equality_compatible(expr[1].get_type())) { + type = Type::create_primitive(BOOL); + } else if ((expr[0].get_type().is(RATE) && (is_integral(expr[1]) || is_double_value(expr[1]))) || + ((is_integral(expr[0]) || is_double_value(expr[0])) && expr[1].get_type().is(RATE))) { + type = Type::create_primitive(INVARIANT_WR); + } else if (is_number(expr[0]) && is_number(expr[1])) { + type = Type::create_primitive(BOOL); + } break; case NEQ: if (expr[0].get_type().is_equality_compatible(expr[1].get_type())) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if ((is_clock(expr[0]) && is_clock(expr[1])) || (is_clock(expr[0]) && is_integer(expr[1])) || (is_integer(expr[0]) && is_clock(expr[1])) || (is_diff(expr[0]) && is_integer(expr[1])) || (is_integer(expr[0]) && is_diff(expr[1]))) { type = Type::create_primitive(CONSTRAINT); - } else if (is_number(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(Constants::BOOL); - } + } else if (is_number(expr[0]) && is_number(expr[1])) { + type = Type::create_primitive(BOOL); + } break; case GE: case GT: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if ((is_clock(expr[0]) && is_clock(expr[1])) || (is_integer(expr[0]) && is_clock(expr[1])) || (is_integer(expr[1]) && is_clock(expr[0])) || (is_diff(expr[0]) && is_integer(expr[1])) || (is_integer(expr[0]) && is_diff(expr[1]))) { type = Type::create_primitive(INVARIANT); - } else if (is_number(expr[0]) && is_clock(expr[1])) { - type = Type::create_primitive(GUARD); - } else if (is_clock(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(GUARD); - } else if (is_number(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(Constants::BOOL); - } + } else if (is_number(expr[0]) && is_clock(expr[1])) { + type = Type::create_primitive(GUARD); + } else if (is_clock(expr[0]) && is_number(expr[1])) { + type = Type::create_primitive(GUARD); + } else if (is_number(expr[0]) && is_number(expr[1])) { + type = Type::create_primitive(BOOL); + } break; case MULT: @@ -1956,9 +1961,9 @@ bool TypeChecker::checkExpression(Expression& expr) case MIN: case MAX: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } else if (is_number(expr[0]) && is_number(expr[1])) { - type = Type::create_primitive(Constants::DOUBLE); + type = Type::create_primitive(DOUBLE); } break; @@ -1969,13 +1974,13 @@ bool TypeChecker::checkExpression(Expression& expr) case BIT_LSHIFT: case BIT_RSHIFT: if (is_integral(expr[0]) && is_integral(expr[1])) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } break; case NOT: if (is_integral(expr[0])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if (is_constraint(expr[0])) { type = Type::create_primitive(CONSTRAINT); } @@ -1983,9 +1988,9 @@ bool TypeChecker::checkExpression(Expression& expr) case UNARY_MINUS: if (is_integral(expr[0])) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } else if (is_number(expr[0])) { - type = Type::create_primitive(Constants::DOUBLE); + type = Type::create_primitive(DOUBLE); } break; @@ -2044,7 +2049,7 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(integer_expected(expr)); return false; } - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); break; case FMA_F: @@ -2111,7 +2116,7 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(number_expected(expr[0])); return false; } - type = Type::create_primitive(Constants::DOUBLE); + type = Type::create_primitive(DOUBLE); break; case LDEXP_F: @@ -2123,7 +2128,7 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(number_expected(expr[0])); return false; } - type = Type::create_primitive(Constants::DOUBLE); + type = Type::create_primitive(DOUBLE); break; case ABS_F: @@ -2132,7 +2137,7 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(integer_expected(expr[0])); return false; } - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); break; case ILOGB_F: @@ -2141,7 +2146,7 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(number_expected(expr[0])); return false; } - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); break; case IS_FINITE_F: @@ -2154,7 +2159,7 @@ bool TypeChecker::checkExpression(Expression& expr) handleError(number_expected(expr[0])); return false; } - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); break; case INLINE_IF: @@ -2226,7 +2231,7 @@ bool TypeChecker::checkExpression(Expression& expr) checkType(expr[0].get_symbol().get_type()); if (is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if (is_invariant(expr[1])) { type = Type::create_primitive(INVARIANT); } else if (is_invariant_wr(expr[1])) { @@ -2249,7 +2254,7 @@ bool TypeChecker::checkExpression(Expression& expr) checkType(expr[0].get_symbol().get_type()); if (is_integral(expr[1])) { - type = Type::create_primitive(Constants::BOOL); + type = Type::create_primitive(BOOL); } else if (is_constraint(expr[1])) { type = Type::create_primitive(CONSTRAINT); } else { @@ -2266,9 +2271,9 @@ bool TypeChecker::checkExpression(Expression& expr) checkType(expr[0].get_symbol().get_type()); if (is_integral(expr[1])) { - type = Type::create_primitive(Constants::INT); + type = Type::create_primitive(INT); } else if (is_number(expr[1])) { - type = Type::create_primitive(Constants::DOUBLE); + type = Type::create_primitive(DOUBLE); } else { handleError(number_expected(expr[1])); } @@ -2350,11 +2355,11 @@ bool TypeChecker::checkExpression(Expression& expr) } for (uint32_t i = 3; i < nb; ++i) { if (!is_integral(expr[i]) && !is_clock(expr[i]) && !is_double_value(expr[i]) && - !expr[i].get_type().is(Constants::DOUBLE_INV_GUARD) && !is_constraint(expr[i]) && + !expr[i].get_type().is(DOUBLE_INV_GUARD) && !is_constraint(expr[i]) && !expr[i].get_type().is_record() && !expr[i].get_type().is_array()) { handleError(integer_or_clock_expected(expr[i])); return false; - } + } if (expr[i].changes_any_variable()) { handleError(must_be_side_effect_free(expr[i])); return false; @@ -2531,6 +2536,7 @@ bool TypeChecker::checkExpression(Expression& expr) bool TypeChecker::isModifiableLValue(const Expression& expr) const { switch (expr.get_kind()) { + using namespace KindNames; case IDENTIFIER: return expr.get_type().is_mutable(); case DOT: @@ -2580,6 +2586,7 @@ bool TypeChecker::isModifiableLValue(const Expression& expr) const bool TypeChecker::isLValue(const Expression& expr) const { switch (expr.get_kind()) { + using namespace KindNames; case IDENTIFIER: case PRE_INCREMENT: case PRE_DECREMENT: @@ -2620,6 +2627,7 @@ bool TypeChecker::isLValue(const Expression& expr) const bool TypeChecker::isUniqueReference(const Expression& expr) const { switch (expr.get_kind()) { + using namespace KindNames; case IDENTIFIER: return true; case DOT: return isUniqueReference(expr[0]); @@ -2700,7 +2708,7 @@ int32_t parse_XML_fd(int fd, Document& doc, bool newxta, const std::vector #endif -using namespace UTAP; -using namespace Constants; +namespace UTAP { // Explicit instantiations to generate implementation template std::string Stringify::str() const @@ -56,7 +55,6 @@ std::string StringifyIndent::str(const std::string& indent) const return os.str(); } -namespace UTAP { // Explicit instantiations to generate implementation template struct Stringify; template struct Stringify; template struct Stringify; @@ -65,7 +63,6 @@ template struct Stringify; template struct Stringify; template struct Stringify; template struct Stringify; -} // namespace UTAP std::ostream& Location::print(std::ostream& os) const { @@ -148,7 +145,7 @@ std::ostream& Declarations::print_constants(std::ostream& os) const if (!variables.empty()) { bool first = true; for (const auto& variable : variables) { - if (variable.uid.get_type().get_kind() == CONSTANT) { + if (variable.uid.get_type().get_kind() == Kind::CONSTANT) { if (first) { os << "// constants\n"; first = false; @@ -164,7 +161,7 @@ std::ostream& Declarations::print_typedefs(std::ostream& os) const { bool first = true; for (const auto& symbol : frame) { - if (symbol.get_type().get_kind() == TYPEDEF) { + if (symbol.get_type().get_kind() == Kind::TYPEDEF) { if (first) { os << "// type definitions\n"; first = false; @@ -180,7 +177,7 @@ std::ostream& Declarations::print_variables(std::ostream& os, bool global) const if (!variables.empty()) { os << "// variables\n"; for (const auto& var : variables) - if (var.uid.get_type().get_kind() != CONSTANT) + if (var.uid.get_type().get_kind() != Kind::CONSTANT) var.print(os) << ";\n"; } return os; @@ -255,7 +252,7 @@ Location& Template::add_location(std::string_view name, Expression inv, Expressi { bool duplicate = frame.contains(name); auto& loc = locations.emplace_back(); - loc.uid = frame.add_symbol(name, Type::create_primitive(LOCATION), pos, &loc); + loc.uid = frame.add_symbol(name, Type::create_primitive(Kind::LOCATION), pos, &loc); loc.nr = static_cast(locations.size() - 1); loc.invariant = std::move(inv); loc.exp_rate = std::move(er); @@ -270,7 +267,7 @@ Branchpoint& Template::add_branchpoint(std::string_view name, position_t pos) { bool duplicate = frame.contains(name); auto& branchpoint = branchpoints.emplace_back(); - branchpoint.uid = frame.add_symbol(name, Type::create_primitive(BRANCHPOINT), pos, &branchpoint); + branchpoint.uid = frame.add_symbol(name, Type::create_primitive(Kind::BRANCHPOINT), pos, &branchpoint); branchpoint.bpNr = static_cast(branchpoints.size() - 1); if (duplicate) throw duplicate_definition_error(name); @@ -946,7 +943,7 @@ static void visit(DocumentVisitor& visitor, Frame& frame) { for (uint32_t i = 0; i < frame.get_size(); ++i) { Type type = frame[i].get_type(); - if (type.get_kind() == TYPEDEF) { + if (type.get_kind() == Kind::TYPEDEF) { visitor.visit_typedef(frame[i]); continue; } @@ -954,21 +951,21 @@ static void visit(DocumentVisitor& visitor, Frame& frame) void* data = frame[i].get_data(); type = type.strip_array(); // TODO: use visitor dispatch to recover the type - if ((type.is(Constants::INT) || type.is(Constants::STRING) || type.is(Constants::DOUBLE) || - type.is(Constants::BOOL) || type.is(CLOCK) || type.is(CHANNEL) || type.is(SCALAR) || - type.get_kind() == RECORD) && + if ((type.is(Kind::INT) || type.is(Kind::STRING) || type.is(Kind::DOUBLE) || + type.is(Kind::BOOL) || type.is(Kind::CLOCK) || type.is(Kind::CHANNEL) || type.is(Kind::SCALAR) || + type.get_kind() == Kind::RECORD) && data != nullptr) // <--- ignore parameters { visitor.visit_variable(*static_cast(data)); - } else if (type.is(LOCATION)) { + } else if (type.is(Kind::LOCATION)) { visitor.visit_location(*static_cast(data)); - } else if (type.is(LOCATION_EXPR)) { + } else if (type.is(Kind::LOCATION_EXPR)) { visitor.visit_location(*static_cast(data)); - } else if (type.is(FUNCTION)) { + } else if (type.is(Kind::FUNCTION)) { visitor.visit_function(*static_cast(data)); - } else if (type.is(FUNCTION_EXTERNAL)) { + } else if (type.is(Kind::FUNCTION_EXTERNAL)) { // we cannot look inside a external function, skip. - } else if (type.is(INSTANCE_LINE)) { + } else if (type.is(Kind::INSTANCE_LINE)) { visitor.visit_instance_line(*static_cast(data)); } } @@ -1002,11 +999,11 @@ void Document::accept(DocumentVisitor& visitor) for (auto i = 0u; i < global.frame.get_size(); ++i) { const Type type = global.frame[i].get_type().strip_array(); void* data = global.frame[i].get_data(); - if (type.is(PROCESS) || type.is(PROCESS_SET)) { + if (type.is(Kind::PROCESS) || type.is(Kind::PROCESS_SET)) { visitor.visit_process(*static_cast(data)); - } else if (type.is(INSTANCE)) { + } else if (type.is(Kind::INSTANCE)) { visitor.visit_instance(*static_cast(data)); - } else if (type.is(LSC_INSTANCE)) { + } else if (type.is(Kind::LSC_INSTANCE)) { visitor.visit_instance(*static_cast(data)); } } @@ -1083,3 +1080,5 @@ void Document::set_supported_methods(const SupportedMethods& supported_methods) { this->supported_methods = supported_methods; } + +} // namespace UTAP diff --git a/src/expression.cpp b/src/expression.cpp index 4da1b43d..e2ca152a 100644 --- a/src/expression.cpp +++ b/src/expression.cpp @@ -43,23 +43,22 @@ #include // uint32_t #include -using namespace UTAP; -using namespace Constants; +namespace UTAP { -struct Expression::expression_data : public std::enable_shared_from_this +struct Expression::Data : std::enable_shared_from_this { position_t position; ///< The position of the expression Kind kind; ///< The kind of the node - std::variant value; + std::variant value; Symbol symbol; ///< The symbol of the node Type type; ///< The type of the expression std::vector sub; ///< Subexpressions - expression_data(const position_t& p, Kind kind, int32_t value): position{p}, kind{kind}, value{value} {} + Data(const position_t& p, Kind kind, int32_t value): position{p}, kind{kind}, value{value} {} }; -Expression::Expression(Kind kind, const position_t& pos) { data = std::make_shared(pos, kind, 0); } +Expression::Expression(Kind kind, const position_t& pos) { data = std::make_shared(pos, kind, 0); } Expression Expression::clone() const { @@ -130,7 +129,7 @@ Expression Expression::subst(const Symbol& symbol, Expression expr) const { if (empty()) { return *this; - } else if (get_kind() == IDENTIFIER && get_symbol() == symbol) { + } else if (get_kind() == Kind::IDENTIFIER && get_symbol() == symbol) { return expr; } else if (get_size() == 0) { return *this; @@ -160,10 +159,11 @@ bool Expression::uses_fp() const if (empty()) { return false; } - if (data->type.is(Constants::DOUBLE)) { + if (data->type.is(Kind::DOUBLE)) { return true; } switch (data->kind) { + using namespace KindNames; case FABS_F: case FMOD_F: case FMA_F: @@ -243,7 +243,7 @@ bool Expression::uses_hybrid() const if (empty()) { return false; } - if (get_type().is(HYBRID)) { + if (get_type().is(Kind::HYBRID)) { return true; } const auto n = get_size(); @@ -278,6 +278,7 @@ bool Expression::is_dynamic() const return false; } else { switch (data->kind) { + using namespace KindNames; case SPAWN: case NUMOF: case EXIT: @@ -310,6 +311,7 @@ uint32_t Expression::get_size() const return 0; switch (data->kind) { + using namespace KindNames; case MINUS: case PLUS: case MULT: @@ -506,7 +508,7 @@ int32_t Expression::get_value() const { assert(data); assert(data->type.is_integral()); - assert(data->kind == CONSTANT || data->kind == IDENTIFIER || data->kind == VAR_INDEX); + assert(data->kind == Kind::CONSTANT || data->kind == Kind::IDENTIFIER || data->kind == Kind::VAR_INDEX); const auto value = std::get(data->value); return data->type.is_integer() ? value : (value != 0 ? 1 : 0); } @@ -521,29 +523,29 @@ int32_t Expression::get_record_label_index() const double Expression::get_double_value() const { assert(data); - assert(data->kind == CONSTANT); - assert(data->type.is(Constants::DOUBLE)); + assert(data->kind == Kind::CONSTANT); + assert(data->type.is(Kind::DOUBLE)); return std::get(data->value); } int32_t Expression::get_index() const { assert(data); - assert(data->kind == DOT); + assert(data->kind == Kind::DOT); return std::get(data->value); } -Synchronisation Expression::get_sync() const +Sync Expression::get_sync() const { assert(data); - assert(data->kind == SYNC); - return std::get(data->value); + assert(data->kind == Kind::SYNC); + return std::get(data->value); } std::string_view Expression::get_string_value() const { assert(data); - assert(data->kind == CONSTANT); + assert(data->kind == Kind::CONSTANT); assert(data->type.is_string()); return std::get(data->value).str(); } @@ -551,7 +553,7 @@ std::string_view Expression::get_string_value() const size_t Expression::get_string_index() const { assert(data); - assert(data->kind == CONSTANT); + assert(data->kind == Kind::CONSTANT); assert(data->type.is_string()); return std::get(data->value).index(); } @@ -584,7 +586,7 @@ bool Expression::empty() const { return data == nullptr; } bool Expression::is_true() const { - return data == nullptr || (get_type().is_integral() && data->kind == CONSTANT && get_value() == 1); + return data == nullptr || (get_type().is_integral() && data->kind == Kind::CONSTANT && get_value() == 1); } /** @@ -614,7 +616,7 @@ bool Expression::equal(const Expression& e) const if (get_size() != e.get_size() || data->kind != e.data->kind || !std::visit(ValueTypeEquality{}, data->value, e.data->value) || data->symbol != e.data->symbol) { return false; - } + } for (uint32_t i = 0; i < get_size(); i++) { if (!data->sub[i].equal(e[i])) { @@ -638,6 +640,7 @@ Symbol Expression::get_symbol() const assert(data); switch (get_kind()) { + using namespace KindNames; case IDENTIFIER: return data->symbol; case DOT: return get(0).get_symbol(); @@ -681,6 +684,7 @@ void Expression::get_symbols(std::set& symbols) const } switch (get_kind()) { + using namespace KindNames; case IDENTIFIER: symbols.insert(data->symbol); break; case DOT: get(0).get_symbols(symbols); break; @@ -728,7 +732,7 @@ bool Expression::is_reference_to(const std::set& symbols) const bool Expression::contains_deadlock() const { - if (get_kind() == UTAP::Constants::DEADLOCK) + if (get_kind() == Kind::DEADLOCK) return true; if (data) for (const auto& subexp : data->sub) @@ -763,6 +767,7 @@ int Expression::get_precedence() const { return get_precedence(data->kind); } int Expression::get_precedence(Kind kind) { switch (kind) { + using namespace KindNames; case PLUS: case MINUS: return 70; @@ -964,8 +969,8 @@ int Expression::get_precedence(Kind kind) std::ostream& Expression::print_bound_type(std::ostream& os, const Expression& e) const { - if (e.get_kind() == CONSTANT) { - assert(e.get_type().is(Constants::INT)); // Encoding used here. + if (e.get_kind() == Kind::CONSTANT) { + assert(e.get_type().is(Kind::INT)); // Encoding used here. if (e.get_value() == 0) { os << "#"; @@ -1041,10 +1046,10 @@ static const char* get_builtin_fun_name(Kind kind) "random_poisson", "random_tri", "random_weibull"}; - static_assert(RANDOM_WEIBULL_F - ABS_F + 1 == std::size(funNames), "Builtin function name list is wrong"); - assert(ABS_F <= kind); - assert(kind <= RANDOM_WEIBULL_F); - return funNames[kind - ABS_F]; + static_assert(Kind::RANDOM_WEIBULL_F - Kind::ABS_F + 1 == std::size(funNames), "Builtin function name list is wrong"); + assert(Kind::ABS_F <= kind); + assert(kind <= Kind::RANDOM_WEIBULL_F); + return funNames[kind - Kind::ABS_F]; } static inline std::ostream& embrace_strict(std::ostream& os, bool old, const Expression& expr, int precedence) @@ -1080,6 +1085,7 @@ std::ostream& Expression::print(std::ostream& os, bool old) const uint32_t nb; switch (data->kind) { + using namespace KindNames; case PROBA_MIN_BOX: flag = true; [[fallthrough]]; case PROBA_MIN_DIAMOND: os << "Pr["; @@ -1360,9 +1366,9 @@ std::ostream& Expression::print(std::ostream& os, bool old) const case SYNC: get(0).print(os, old); switch (get_sync()) { - case SYNC_QUE: os << '?'; break; - case SYNC_BANG: os << '!'; break; - case SYNC_CSP: + case Sync::QUE: os << '?'; break; + case Sync::BANG: os << '!'; break; + case Sync::CSP: // no append break; } @@ -1461,7 +1467,7 @@ std::ostream& Expression::print(std::ostream& os, bool old) const os << "minE("; get(3).print(os, old); os << ")["; - if (get(0).get_kind() == Constants::CONSTANT) { + if (get(0).get_kind() == CONSTANT) { if (bool is_step_bound = (get(0).get_value() == 0); is_step_bound) os << "#"; } else { @@ -1470,10 +1476,10 @@ std::ostream& Expression::print(std::ostream& os, bool old) const os << "<="; get(1).print(os, old); os << "]"; - if (auto features1 = get(5); features1.get_kind() == Constants::LIST) { + if (auto features1 = get(5); features1.get_kind() == LIST) { features1.print(os << " {", old); os << "} -> {"; - if (auto features2 = get(6); features2.get_kind() == Constants::LIST) + if (auto features2 = get(6); features2.get_kind() == LIST) features2.print(os, old); os << "}"; } @@ -1485,7 +1491,7 @@ std::ostream& Expression::print(std::ostream& os, bool old) const os << "maxE("; get(3).print(os, old); os << ")["; - if (get(0).get_kind() == Constants::CONSTANT) { + if (get(0).get_kind() == CONSTANT) { if (bool is_step_bound = (get(0).get_value() == 0); is_step_bound) os << "#"; } else { @@ -1494,10 +1500,10 @@ std::ostream& Expression::print(std::ostream& os, bool old) const os << "<="; get(1).print(os, old); os << "]"; - if (auto features1 = get(5); features1.get_kind() == Constants::LIST) { + if (auto features1 = get(5); features1.get_kind() == LIST) { features1.print(os << " {", old); os << "} -> {"; - if (auto features2 = get(6); features2.get_kind() == Constants::LIST) + if (auto features2 = get(6); features2.get_kind() == LIST) features2.print(os, old); os << "}"; } @@ -1628,7 +1634,7 @@ std::ostream& Expression::print(std::ostream& os, bool old) const break; case TYPEDEF: os << "typedef"; break; - // Types - Not applicable in expression printing + // Types - Not applicable in expression printing case RANGE: case RECORD: case REF: @@ -1663,7 +1669,7 @@ std::ostream& Expression::print(std::ostream& os, bool old) const case BRANCHPOINT: case PROBABILITY: case DOUBLE_INV_GUARD: - // Deprecated LSC features + // Deprecated LSC features case SCENARIO: case SCENARIO2: case INSTANCE_LINE: @@ -1671,7 +1677,7 @@ std::ostream& Expression::print(std::ostream& os, bool old) const case CONDITION: case UPDATE: case LSC_INSTANCE: - // Deprecated probability feature + // Deprecated probability feature case PMAX: case UNKNOWN: break; @@ -1703,6 +1709,7 @@ void Expression::collect_possible_writes(std::set& symbols) const } switch (get_kind()) { + using namespace KindNames; case ASSIGN: case ASS_PLUS: case ASS_MINUS: @@ -1735,7 +1742,7 @@ void Expression::collect_possible_writes(std::set& symbols) const get(i).get_symbols(symbols); } } - } + } break; default: break; @@ -1751,6 +1758,7 @@ void Expression::collect_possible_reads(std::set& symbols, bool collectR get(i).collect_possible_reads(symbols); switch (get_kind()) { + using namespace KindNames; case IDENTIFIER: symbols.insert(get_symbol()); break; case FUN_CALL: { @@ -1794,47 +1802,47 @@ void Expression::collect_possible_reads(std::set& symbols, bool collectR Expression Expression::create_constant(int32_t value, position_t pos) { - auto expr = Expression{CONSTANT, pos}; + auto expr = Expression{Kind::CONSTANT, pos}; expr.data->value = value; - expr.data->type = Type::create_primitive(Constants::INT); + expr.data->type = Type::create_primitive(Kind::INT); return expr; } Expression Expression::create_var_index(int32_t value, position_t pos) { - auto expr = Expression{VAR_INDEX, pos}; + auto expr = Expression{Kind::VAR_INDEX, pos}; expr.data->value = value; - expr.data->type = Type::create_primitive(Constants::INT); + expr.data->type = Type::create_primitive(Kind::INT); return expr; } Expression Expression::create_exit(position_t pos) { - auto expr = Expression{EXIT, pos}; + auto expr = Expression{Kind::EXIT, pos}; expr.data->value = 0; - expr.data->type = Type::create_primitive(Constants::VOID_TYPE); + expr.data->type = Type::create_primitive(Kind::VOID_TYPE); return expr; } Expression Expression::create_double(double value, position_t pos) { - auto expr = Expression{CONSTANT, pos}; + auto expr = Expression{Kind::CONSTANT, pos}; expr.data->value = value; - expr.data->type = Type::create_primitive(Constants::DOUBLE); + expr.data->type = Type::create_primitive(Kind::DOUBLE); return expr; } Expression Expression::create_string(StringIndex str, position_t pos) { - auto expr = Expression{CONSTANT, pos}; + auto expr = Expression{Kind::CONSTANT, pos}; expr.data->value = str; - expr.data->type = Type::create_primitive(Constants::STRING); + expr.data->type = Type::create_primitive(Kind::STRING); return expr; } Expression Expression::create_identifier(const Symbol& symbol, position_t pos) { - auto expr = Expression{IDENTIFIER, pos}; + auto expr = Expression{Kind::IDENTIFIER, pos}; expr.data->symbol = symbol; if (symbol != Symbol()) { expr.data->type = symbol.get_type(); @@ -1884,16 +1892,16 @@ Expression Expression::create_ternary(Kind kind, Expression e1, Expression e2, E Expression Expression::create_dot(Expression e, int32_t idx, position_t pos, Type type) { - auto expr = Expression{DOT, pos}; + auto expr = Expression{Kind::DOT, pos}; expr.data->value = idx; expr.data->sub.push_back(std::move(e)); expr.data->type = std::move(type); return expr; } -Expression Expression::create_sync(Expression e, Synchronisation s, position_t pos) +Expression Expression::create_sync(Expression e, Sync s, position_t pos) { - auto expr = Expression{SYNC, pos}; + auto expr = Expression{Kind::SYNC, pos}; expr.data->value = s; expr.data->sub.push_back(std::move(e)); return expr; @@ -1901,7 +1909,9 @@ Expression Expression::create_sync(Expression e, Synchronisation s, position_t p Expression Expression::create_deadlock(position_t pos) { - auto expr = Expression{DEADLOCK, pos}; - expr.data->type = Type::create_primitive(CONSTRAINT); + auto expr = Expression{Kind::DEADLOCK, pos}; + expr.data->type = Type::create_primitive(Kind::CONSTRAINT); return expr; } + +} // nanamespace UTAP \ No newline at end of file diff --git a/src/keywords.hpp b/src/keywords.hpp index ed2ccf78..31945ca8 100644 --- a/src/keywords.hpp +++ b/src/keywords.hpp @@ -25,7 +25,6 @@ #include "libparser.hpp" #include -#include // uint32_t namespace UTAP { struct Keyword diff --git a/src/lexer.l b/src/lexer.l index 2142d7cb..df70cee7 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -174,6 +174,7 @@ idchr [a-zA-Z0-9_$#] "#" { return T_HASH; } "location" { return T_LOCATION; } {alpha}{idchr}* { + using namespace UTAP; const auto utap_string = std::string{utap_text}; const auto* keyword_ptr = find_keyword(utap_string); if (keyword_ptr) { diff --git a/src/libparser.hpp b/src/libparser.hpp index 9f3b17ba..1537dd9b 100644 --- a/src/libparser.hpp +++ b/src/libparser.hpp @@ -28,6 +28,8 @@ #include #include +namespace UTAP { + // The maximum length is 4000 (see error message) + 1 for the // terminating \0. constexpr auto MAXLEN = 4001u; @@ -57,7 +59,6 @@ constexpr Syntax operator&(const Syntax& s1, const Syntax& s2) return Syntax{static_cast(s1) & static_cast(s2)}; } -namespace UTAP { /** * Help class used by the lexer, parser and xmlreader to keep * track of the current position. diff --git a/src/parser.y b/src/parser.y index 37908e02..d8bed8be 100644 --- a/src/parser.y +++ b/src/parser.y @@ -51,8 +51,16 @@ #include #include // strlen -using namespace UTAP; -using namespace Constants; +using UTAP::position_t; +using UTAP::Syntax; +using UTAP::ParserBuilder; +using UTAP::tracker; +using UTAP::TypeException; +using UTAP::MAXLEN; +using UTAP::Kind; +using UTAP::Sync; +using UTAP::XTAPart; +using UTAP::PriceType; #define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ @@ -302,7 +310,7 @@ const char* utap_msg(const char *msg) %union { bool flag; int number; - ParserBuilder::TypePrefix prefix; + UTAP::TypePrefix prefix; Kind kind; char string[MAXLEN]; double floating; @@ -648,56 +656,56 @@ TypeId: Type: T_TYPENAME { - CALL(@1, @1, type_name(ParserBuilder::TypePrefix::NONE, $1)); + CALL(@1, @1, type_name(UTAP::TypePrefix::NONE, $1)); } | TypePrefix T_TYPENAME { CALL(@1, @2, type_name($1, $2)); } | T_STRUCT '{' FieldDeclList '}' { - CALL(@1, @4, type_struct(ParserBuilder::TypePrefix::NONE, $3)); + CALL(@1, @4, type_struct(UTAP::TypePrefix::NONE, $3)); } | TypePrefix T_STRUCT '{' FieldDeclList '}' { CALL(@1, @5, type_struct($1, $4)); } | T_STRUCT '{' error '}' { - CALL(@1, @4, type_struct(ParserBuilder::TypePrefix::NONE, 0)); + CALL(@1, @4, type_struct(UTAP::TypePrefix::NONE, 0)); } | TypePrefix T_STRUCT '{' error '}' { - CALL(@1, @5, type_struct(ParserBuilder::TypePrefix::NONE, 0)); + CALL(@1, @5, type_struct(UTAP::TypePrefix::NONE, 0)); } | T_BOOL { - CALL(@1, @1, type_bool(ParserBuilder::TypePrefix::NONE)); + CALL(@1, @1, type_bool(UTAP::TypePrefix::NONE)); } | TypePrefix T_BOOL { CALL(@1, @2, type_bool($1)); } - | T_DOUBLE { CALL(@1, @1, type_double(ParserBuilder::TypePrefix::NONE)); } + | T_DOUBLE { CALL(@1, @1, type_double(UTAP::TypePrefix::NONE)); } | TypePrefix T_DOUBLE { CALL(@1, @2, type_double($1)); } - | T_STRING { CALL(@1, @1, type_string(ParserBuilder::TypePrefix::NONE)); } + | T_STRING { CALL(@1, @1, type_string(UTAP::TypePrefix::NONE)); } | TypePrefix T_STRING { CALL(@1, @2, type_string($1)); } - | T_INT { CALL(@1, @1, type_int(ParserBuilder::TypePrefix::NONE)); } + | T_INT { CALL(@1, @1, type_int(UTAP::TypePrefix::NONE)); } | TypePrefix T_INT { CALL(@1, @2, type_int($1)); } | T_INT '[' Expression ',' Expression ']' { - CALL(@1, @6, type_bounded_int(ParserBuilder::TypePrefix::NONE)); + CALL(@1, @6, type_bounded_int(UTAP::TypePrefix::NONE)); } | TypePrefix T_INT '[' Expression ',' Expression ']' { CALL(@1, @7, type_bounded_int($1)); } | T_CHAN { - CALL(@1, @1, type_channel(ParserBuilder::TypePrefix::NONE)); + CALL(@1, @1, type_channel(UTAP::TypePrefix::NONE)); } | TypePrefix T_CHAN { CALL(@1, @2, type_channel($1)); } - | T_CLOCK { CALL(@1, @1, type_clock(ParserBuilder::TypePrefix::NONE)); } - | T_HYBRID T_CLOCK { CALL(@1, @1, type_clock(ParserBuilder::TypePrefix::HYBRID)); } + | T_CLOCK { CALL(@1, @1, type_clock(UTAP::TypePrefix::NONE)); } + | T_HYBRID T_CLOCK { CALL(@1, @1, type_clock(UTAP::TypePrefix::HYBRID)); } | T_VOID { CALL(@1, @1, type_void()); } | T_SCALAR '[' Expression ']' { - CALL(@1, @4, type_scalar(ParserBuilder::TypePrefix::NONE)); + CALL(@1, @4, type_scalar(UTAP::TypePrefix::NONE)); } | TypePrefix T_SCALAR '[' Expression ']' { CALL(@1, @5, type_scalar($1)); @@ -749,11 +757,11 @@ FieldDeclId: ; TypePrefix: - T_URGENT { $$ = ParserBuilder::TypePrefix::URGENT; } - | T_BROADCAST { $$ = ParserBuilder::TypePrefix::BROADCAST; } - | T_URGENT T_BROADCAST { $$ = ParserBuilder::TypePrefix::URGENT_BROADCAST; } - | T_CONST { $$ = ParserBuilder::TypePrefix::CONST; } - | T_META { $$ = ParserBuilder::TypePrefix::SYSTEM_META; } + T_URGENT { $$ = UTAP::TypePrefix::URGENT; } + | T_BROADCAST { $$ = UTAP::TypePrefix::BROADCAST; } + | T_URGENT T_BROADCAST { $$ = UTAP::TypePrefix::URGENT_BROADCAST; } + | T_CONST { $$ = UTAP::TypePrefix::CONST; } + | T_META { $$ = UTAP::TypePrefix::SYSTEM_META; } ; /********************************************************************* @@ -903,28 +911,28 @@ Sync: SyncExpr: Expression { - CALL(@1, @1, proc_sync(SYNC_CSP)); + CALL(@1, @1, proc_sync(Sync::CSP)); } | Expression T_EXCLAM { - CALL(@1, @2, proc_sync(SYNC_BANG)); + CALL(@1, @2, proc_sync(Sync::BANG)); } | Expression error T_EXCLAM { - CALL(@1, @2, proc_sync(SYNC_BANG)); + CALL(@1, @2, proc_sync(Sync::BANG)); } | Expression '?' { - CALL(@1, @2, proc_sync(SYNC_QUE)); + CALL(@1, @2, proc_sync(Sync::QUE)); } | Expression error '?' { - CALL(@1, @2, proc_sync(SYNC_QUE)); + CALL(@1, @2, proc_sync(Sync::QUE)); } ; MessExpr: Expression { - CALL(@1, @1, proc_message(SYNC_QUE)); + CALL(@1, @1, proc_message(Sync::QUE)); } | Expression error { - CALL(@1, @1, proc_message(SYNC_QUE)); + CALL(@1, @1, proc_message(Sync::QUE)); } ; @@ -981,7 +989,7 @@ UStateList: ExpRate: Expression | Expression ':' Expression { - CALL(@1,@3, expr_binary(FRACTION)); + CALL(@1,@3, expr_binary(Kind::FRACTION)); }; /********************************************************************** @@ -1190,61 +1198,61 @@ Expression: CALL(@1, @2, expr_unary($1)); } %prec UOPERATOR | Expression T_LT Expression { - CALL(@1, @3, expr_binary(LT)); + CALL(@1, @3, expr_binary(Kind::LT)); } | Expression T_LEQ Expression { - CALL(@1, @3, expr_binary(LE)); + CALL(@1, @3, expr_binary(Kind::LE)); } | Expression T_EQ Expression { - CALL(@1, @3, expr_binary(EQ)); + CALL(@1, @3, expr_binary(Kind::EQ)); } | Expression T_NEQ Expression { - CALL(@1, @3, expr_binary(NEQ)); + CALL(@1, @3, expr_binary(Kind::NEQ)); } | Expression T_GT Expression { - CALL(@1, @3, expr_binary(GT)); + CALL(@1, @3, expr_binary(Kind::GT)); } | Expression T_GEQ Expression { - CALL(@1, @3, expr_binary(GE)); + CALL(@1, @3, expr_binary(Kind::GE)); } | Expression T_PLUS Expression { - CALL(@1, @3, expr_binary(PLUS)); + CALL(@1, @3, expr_binary(Kind::PLUS)); } | Expression T_MINUS Expression { - CALL(@1, @3, expr_binary(MINUS)); + CALL(@1, @3, expr_binary(Kind::MINUS)); } | Expression T_MULT Expression { - CALL(@1, @3, expr_binary(MULT)); + CALL(@1, @3, expr_binary(Kind::MULT)); } | Expression T_DIV Expression { - CALL(@1, @3, expr_binary(DIV)); + CALL(@1, @3, expr_binary(Kind::DIV)); } | Expression T_MOD Expression { - CALL(@1, @3, expr_binary(MOD)); + CALL(@1, @3, expr_binary(Kind::MOD)); } | Expression T_POWOP Expression { - CALL(@1, @3, expr_binary(POW)); + CALL(@1, @3, expr_binary(Kind::POW)); } | Expression '&' Expression { - CALL(@1, @3, expr_binary(BIT_AND)); + CALL(@1, @3, expr_binary(Kind::BIT_AND)); } | Expression T_OR Expression { - CALL(@1, @3, expr_binary(BIT_OR)); + CALL(@1, @3, expr_binary(Kind::BIT_OR)); } | Expression T_XOR Expression { - CALL(@1, @3, expr_binary(BIT_XOR)); + CALL(@1, @3, expr_binary(Kind::BIT_XOR)); } | Expression T_LSHIFT Expression { - CALL(@1, @3, expr_binary(BIT_LSHIFT)); + CALL(@1, @3, expr_binary(Kind::BIT_LSHIFT)); } | Expression T_RSHIFT Expression { - CALL(@1, @3, expr_binary(BIT_RSHIFT)); + CALL(@1, @3, expr_binary(Kind::BIT_RSHIFT)); } | Expression T_BOOL_AND Expression { - CALL(@1, @3, expr_binary(AND)); + CALL(@1, @3, expr_binary(Kind::AND)); } | Expression T_BOOL_OR Expression { - CALL(@1, @3, expr_binary(OR)); + CALL(@1, @3, expr_binary(Kind::OR)); } | Expression '?' Expression ':' Expression { CALL(@1, @5, expr_inline_if()); @@ -1256,30 +1264,30 @@ Expression: CALL(@1, @3, expr_dot($3)); } | Expression T_APOS { - CALL(@1, @2, expr_unary(RATE)); + CALL(@1, @2, expr_unary(Kind::RATE)); } | T_DEADLOCK { CALL(@1, @1, expr_deadlock()); } | Expression T_KW_IMPLY { - CALL(@1, @1, expr_unary(NOT)); + CALL(@1, @1, expr_unary(Kind::NOT)); } Expression { - CALL(@3, @3, expr_binary(OR)); + CALL(@3, @3, expr_binary(Kind::OR)); } | Expression T_KW_AND Expression { - CALL(@1, @3, expr_binary(AND)); + CALL(@1, @3, expr_binary(Kind::AND)); } | Expression T_KW_OR Expression { - CALL(@1, @3, expr_binary(OR)); + CALL(@1, @3, expr_binary(Kind::OR)); } | Expression T_KW_XOR Expression { - CALL(@1, @3, expr_binary(XOR)); + CALL(@1, @3, expr_binary(Kind::XOR)); } | Expression T_MIN Expression { - CALL(@1, @3, expr_binary(MIN)); + CALL(@1, @3, expr_binary(Kind::MIN)); } | Expression T_MAX Expression { - CALL(@1, @3, expr_binary(MAX)); + CALL(@1, @3, expr_binary(Kind::MAX)); } | T_SUM '(' Id ':' Type ')' { CALL(@1, @6, expr_sum_begin($3)); @@ -1348,95 +1356,95 @@ Assignment: AssignOp: /* = += -= /= %= &= |= ^= <<= >>= */ - T_ASSIGNMENT { $$ = ASSIGN; } - | T_ASSPLUS { $$ = ASS_PLUS; } - | T_ASSMINUS { $$ = ASS_MINUS; } - | T_ASSDIV { $$ = ASS_DIV; } - | T_ASSMOD { $$ = ASS_MOD; } - | T_ASSMULT { $$ = ASS_MULT; } - | T_ASSAND { $$ = ASS_AND; } - | T_ASSOR { $$ = ASS_OR; } - | T_ASSXOR { $$ = ASS_XOR; } - | T_ASSLSHIFT { $$ = ASS_LSHIFT; } - | T_ASSRSHIFT { $$ = ASS_RSHIFT; } + T_ASSIGNMENT { $$ = Kind::ASSIGN; } + | T_ASSPLUS { $$ = Kind::ASS_PLUS; } + | T_ASSMINUS { $$ = Kind::ASS_MINUS; } + | T_ASSDIV { $$ = Kind::ASS_DIV; } + | T_ASSMOD { $$ = Kind::ASS_MOD; } + | T_ASSMULT { $$ = Kind::ASS_MULT; } + | T_ASSAND { $$ = Kind::ASS_AND; } + | T_ASSOR { $$ = Kind::ASS_OR; } + | T_ASSXOR { $$ = Kind::ASS_XOR; } + | T_ASSLSHIFT { $$ = Kind::ASS_LSHIFT; } + | T_ASSRSHIFT { $$ = Kind::ASS_RSHIFT; } ; UnaryOp: /* - + ! */ - T_MINUS { $$ = MINUS; } - | T_PLUS { $$ = PLUS; } - | T_EXCLAM { $$ = NOT; } - | T_KW_NOT { $$ = NOT; } + T_MINUS { $$ = Kind::MINUS; } + | T_PLUS { $$ = Kind::PLUS; } + | T_EXCLAM { $$ = Kind::NOT; } + | T_KW_NOT { $$ = Kind::NOT; } ; BuiltinFunction1: - T_ABS { $$ = ABS_F; } - | T_FABS { $$ = FABS_F; } - | T_EXP { $$ = EXP_F; } - | T_EXP2 { $$ = EXP2_F; } - | T_EXPM1 { $$ = EXPM1_F; } - | T_LN { $$ = LN_F; } - | T_LOG { $$ = LOG_F; } - | T_LOG10 { $$ = LOG10_F; } - | T_LOG2 { $$ = LOG2_F; } - | T_LOG1P { $$ = LOG1P_F; } - | T_SQRT { $$ = SQRT_F; } - | T_CBRT { $$ = CBRT_F; } - | T_SIN { $$ = SIN_F; } - | T_COS { $$ = COS_F; } - | T_TAN { $$ = TAN_F; } - | T_ASIN { $$ = ASIN_F; } - | T_ACOS { $$ = ACOS_F; } - | T_ATAN { $$ = ATAN_F; } - | T_SINH { $$ = SINH_F; } - | T_COSH { $$ = COSH_F; } - | T_TANH { $$ = TANH_F; } - | T_ASINH { $$ = ASINH_F; } - | T_ACOSH { $$ = ACOSH_F; } - | T_ATANH { $$ = ATANH_F; } - | T_ERF { $$ = ERF_F; } - | T_ERFC { $$ = ERFC_F; } - | T_TGAMMA { $$ = TGAMMA_F; } - | T_LGAMMA { $$ = LGAMMA_F; } - | T_CEIL { $$ = CEIL_F; } - | T_FLOOR { $$ = FLOOR_F; } - | T_TRUNC { $$ = TRUNC_F; } - | T_ROUND { $$ = ROUND_F; } - | T_FINT { $$ = FINT_F; } - | T_ILOGB { $$ = ILOGB_F; } - | T_LOGB { $$ = LOGB_F; } - | T_FPCLASSIFY { $$ = FP_CLASSIFY_F; } - | T_ISFINITE { $$ = IS_FINITE_F; } - | T_ISINF { $$ = IS_INF_F; } - | T_ISNAN { $$ = IS_NAN_F; } - | T_ISNORMAL { $$ = IS_NORMAL_F; } - | T_SIGNBIT { $$ = SIGNBIT_F; } - | T_ISUNORDERED { $$ = IS_UNORDERED_F; } - | T_RANDOM { $$ = RANDOM_F; } - | T_RANDOM_POISSON { $$ = RANDOM_POISSON_F; } + T_ABS { $$ = Kind::ABS_F; } + | T_FABS { $$ = Kind::FABS_F; } + | T_EXP { $$ = Kind::EXP_F; } + | T_EXP2 { $$ = Kind::EXP2_F; } + | T_EXPM1 { $$ = Kind::EXPM1_F; } + | T_LN { $$ = Kind::LN_F; } + | T_LOG { $$ = Kind::LOG_F; } + | T_LOG10 { $$ = Kind::LOG10_F; } + | T_LOG2 { $$ = Kind::LOG2_F; } + | T_LOG1P { $$ = Kind::LOG1P_F; } + | T_SQRT { $$ = Kind::SQRT_F; } + | T_CBRT { $$ = Kind::CBRT_F; } + | T_SIN { $$ = Kind::SIN_F; } + | T_COS { $$ = Kind::COS_F; } + | T_TAN { $$ = Kind::TAN_F; } + | T_ASIN { $$ = Kind::ASIN_F; } + | T_ACOS { $$ = Kind::ACOS_F; } + | T_ATAN { $$ = Kind::ATAN_F; } + | T_SINH { $$ = Kind::SINH_F; } + | T_COSH { $$ = Kind::COSH_F; } + | T_TANH { $$ = Kind::TANH_F; } + | T_ASINH { $$ = Kind::ASINH_F; } + | T_ACOSH { $$ = Kind::ACOSH_F; } + | T_ATANH { $$ = Kind::ATANH_F; } + | T_ERF { $$ = Kind::ERF_F; } + | T_ERFC { $$ = Kind::ERFC_F; } + | T_TGAMMA { $$ = Kind::TGAMMA_F; } + | T_LGAMMA { $$ = Kind::LGAMMA_F; } + | T_CEIL { $$ = Kind::CEIL_F; } + | T_FLOOR { $$ = Kind::FLOOR_F; } + | T_TRUNC { $$ = Kind::TRUNC_F; } + | T_ROUND { $$ = Kind::ROUND_F; } + | T_FINT { $$ = Kind::FINT_F; } + | T_ILOGB { $$ = Kind::ILOGB_F; } + | T_LOGB { $$ = Kind::LOGB_F; } + | T_FPCLASSIFY { $$ = Kind::FP_CLASSIFY_F; } + | T_ISFINITE { $$ = Kind::IS_FINITE_F; } + | T_ISINF { $$ = Kind::IS_INF_F; } + | T_ISNAN { $$ = Kind::IS_NAN_F; } + | T_ISNORMAL { $$ = Kind::IS_NORMAL_F; } + | T_SIGNBIT { $$ = Kind::SIGNBIT_F; } + | T_ISUNORDERED { $$ = Kind::IS_UNORDERED_F; } + | T_RANDOM { $$ = Kind::RANDOM_F; } + | T_RANDOM_POISSON { $$ = Kind::RANDOM_POISSON_F; } ; BuiltinFunction2: - T_FMOD { $$ = FMOD_F; } - | T_FMAX { $$ = FMAX_F; } - | T_FMIN { $$ = FMIN_F; } - | T_FDIM { $$ = FDIM_F; } - | T_POW { $$ = POW_F; } - | T_HYPOT { $$ = HYPOT_F; } - | T_ATAN2 { $$ = ATAN2_F; } - | T_LDEXP { $$ = LDEXP_F; } - | T_NEXTAFTER { $$ = NEXT_AFTER_F; } - | T_COPYSIGN { $$ = COPY_SIGN_F; } - | T_RANDOM_ARCSINE { $$ = RANDOM_ARCSINE_F; } - | T_RANDOM_BETA { $$ = RANDOM_BETA_F; } - | T_RANDOM_GAMMA { $$ = RANDOM_GAMMA_F; } - | T_RANDOM_NORMAL { $$ = RANDOM_NORMAL_F; } - | T_RANDOM_WEIBULL { $$ = RANDOM_WEIBULL_F; } + T_FMOD { $$ = Kind::FMOD_F; } + | T_FMAX { $$ = Kind::FMAX_F; } + | T_FMIN { $$ = Kind::FMIN_F; } + | T_FDIM { $$ = Kind::FDIM_F; } + | T_POW { $$ = Kind::POW_F; } + | T_HYPOT { $$ = Kind::HYPOT_F; } + | T_ATAN2 { $$ = Kind::ATAN2_F; } + | T_LDEXP { $$ = Kind::LDEXP_F; } + | T_NEXTAFTER { $$ = Kind::NEXT_AFTER_F; } + | T_COPYSIGN { $$ = Kind::COPY_SIGN_F; } + | T_RANDOM_ARCSINE { $$ = Kind::RANDOM_ARCSINE_F; } + | T_RANDOM_BETA { $$ = Kind::RANDOM_BETA_F; } + | T_RANDOM_GAMMA { $$ = Kind::RANDOM_GAMMA_F; } + | T_RANDOM_NORMAL { $$ = Kind::RANDOM_NORMAL_F; } + | T_RANDOM_WEIBULL { $$ = Kind::RANDOM_WEIBULL_F; } ; BuiltinFunction3: - T_FMA { $$ = FMA_F; } - | T_RANDOM_TRI { $$ = RANDOM_TRI_F; } + T_FMA { $$ = Kind::FMA_F; } + | T_RANDOM_TRI { $$ = Kind::RANDOM_TRI_F; } ; ArgList: @@ -1467,7 +1475,7 @@ OldDeclaration: OldVarDecl: VariableDecl | T_OLDCONST { - CALL(@1, @1, type_int(ParserBuilder::TypePrefix::CONST)); + CALL(@1, @1, type_int(UTAP::TypePrefix::CONST)); } OldConstDeclIdList ';' { CALL(@1, @3, type_pop()); } @@ -1554,12 +1562,12 @@ OldProcParam: OldProcConstParam: T_OLDCONST { - CALL(@1, @1, type_int(ParserBuilder::TypePrefix::CONST)); + CALL(@1, @1, type_int(UTAP::TypePrefix::CONST)); } NonTypeId ArrayDecl { CALL(@3, @4, decl_parameter($3, false)); } | OldProcConstParam ',' { - CALL(@1, @1, type_int(ParserBuilder::TypePrefix::CONST)); + CALL(@1, @1, type_int(UTAP::TypePrefix::CONST)); } NonTypeId ArrayDecl { CALL(@4, @5, decl_parameter($4, false)); } @@ -1598,7 +1606,7 @@ OldInvariant: | Expression error ',' { } | OldInvariant ',' Expression { - CALL(@1, @3, expr_binary(AND)); + CALL(@1, @3, expr_binary(Kind::AND)); } ; @@ -1645,19 +1653,19 @@ OldGuard: OldGuardList: Expression | OldGuardList ',' Expression { - CALL(@1, @3, expr_binary(AND)); + CALL(@1, @3, expr_binary(Kind::AND)); } ; /* TIGA-SMC Expanded syntax */ ExpQuantifier: - T_MINEXP { $$ = MIN_EXP;} - | T_MAXEXP { $$ = MAX_EXP;}; + T_MINEXP { $$ = Kind::MIN_EXP;} + | T_MAXEXP { $$ = Kind::MAX_EXP;}; ExpPrQuantifier: - T_MINPR { $$ = MIN_EXP;} - | T_MAXPR { $$ = MAX_EXP;}; + T_MINPR { $$ = Kind::MIN_EXP;} + | T_MAXPR { $$ = Kind::MAX_EXP;}; SubjectionList: // do not allow multiple subjections for the time being /*Id ',' SubjectionList { @@ -1698,30 +1706,30 @@ BoolOrKWAnd: SubProperty: T_AF Expression { - CALL(@1, @2, expr_unary(AF)); + CALL(@1, @2, expr_unary(Kind::AF)); } | T_AG '(' Expression BoolOrKWAnd T_AF Expression ')' { - CALL(@5, @6, expr_unary(AF)); - CALL(@3, @6, expr_binary(AND)); - CALL(@1, @7, expr_unary(AG)); + CALL(@5, @6, expr_unary(Kind::AF)); + CALL(@3, @6, expr_binary(Kind::AND)); + CALL(@1, @7, expr_unary(Kind::AG)); } | T_AG Expression { - CALL(@1, @2, expr_unary(AG)); + CALL(@1, @2, expr_unary(Kind::AG)); } | T_EF Expression { - CALL(@1, @2, expr_unary(EF)); + CALL(@1, @2, expr_unary(Kind::EF)); } | T_EG Expression { - CALL(@1, @2, expr_unary(EG)); + CALL(@1, @2, expr_unary(Kind::EG)); } | Expression T_LEADS_TO Expression { - CALL(@1, @3, expr_binary(LEADS_TO)); + CALL(@1, @3, expr_binary(Kind::LEADS_TO)); } | 'A' '[' Expression 'U' Expression ']' { - CALL(@1, @6, expr_binary(A_UNTIL)); + CALL(@1, @6, expr_binary(Kind::A_UNTIL)); } | 'A' '[' Expression 'W' Expression ']' { - CALL(@1, @6, expr_binary(A_WEAK_UNTIL)); + CALL(@1, @6, expr_binary(Kind::A_WEAK_UNTIL)); } ; @@ -1733,37 +1741,37 @@ Features: { AssignablePropperty: T_CONTROL ':' SubProperty Subjection { - CALL(@1, @3, expr_unary(CONTROL)); + CALL(@1, @3, expr_unary(Kind::CONTROL)); CALL(@1, @3, property()); } | T_CONTROL_T T_MULT '(' Expression ',' Expression ')' ':' SubProperty { - CALL(@1, @9, expr_ternary(CONTROL_TOPT)); + CALL(@1, @9, expr_ternary(Kind::CONTROL_TOPT)); CALL(@1, @9, property()); } | T_CONTROL_T T_MULT '(' Expression ')' ':' SubProperty { - CALL(@1, @7, expr_binary(CONTROL_TOPT_DEF1)); + CALL(@1, @7, expr_binary(Kind::CONTROL_TOPT_DEF1)); CALL(@1, @7, property()); } | T_CONTROL_T T_MULT ':' SubProperty { - CALL(@1, @4, expr_unary(CONTROL_TOPT_DEF2)); + CALL(@1, @4, expr_unary(Kind::CONTROL_TOPT_DEF2)); CALL(@1, @4, property()); } | T_EF T_CONTROL ':' SubProperty Subjection { - CALL(@1, @4, expr_unary(EF_CONTROL)); + CALL(@1, @4, expr_unary(Kind::EF_CONTROL)); CALL(@1, @4, property()); } | BracketExprList T_CONTROL ':' SubProperty Subjection { - CALL(@1, @4, expr_binary(PO_CONTROL)); + CALL(@1, @4, expr_binary(Kind::PO_CONTROL)); CALL(@1, @4, property()); } | ExpQuantifier '(' Expression ')' '[' BoundType ']' Features ':' PathType Expression Subjection Imitation { - CALL(@1, @12, expr_optimize_exp($1, ParserBuilder::EXPRPRICE, $10)); + CALL(@1, @12, expr_optimize_exp($1, PriceType::EXPR, $10)); CALL(@1, @9, property()); } | ExpPrQuantifier '[' BoundType ']' Features ':' PathType Expression Subjection Imitation { - CALL(@1, @9, expr_optimize_exp($1, ParserBuilder::TIMEPRICE, $7)); + CALL(@1, @9, expr_optimize_exp($1, PriceType::TIME, $7)); CALL(@1, @6, property()); } | T_LOAD_STRAT Features '(' Expression ')' { @@ -1772,8 +1780,8 @@ AssignablePropperty: } ; /** - | T_MINPR { $$ = MIN_PR;} - | T_MAXPR { $$ = MAX_PR;} + | T_MINPR { $$ = Kind::MIN_PR;} + | T_MAXPR { $$ = Kind::MAX_PR;} */ PropertyExpr: @@ -1781,7 +1789,7 @@ PropertyExpr: CALL(@1, @1, property()); } | T_PMAX Expression { // Deprecated, comes from old uppaal-prob. - CALL(@1, @2, expr_unary(PMAX)); + CALL(@1, @2, expr_unary(Kind::PMAX)); CALL(@1, @2, property()); } | AssignablePropperty @@ -1801,7 +1809,7 @@ PropertyExpr: CALL(@1, @7, property()); } | T_PROBA SMCBounds '(' Expression 'U' Expression ')' Subjection { - CALL(@1, @8, expr_proba_quantitative(DIAMOND)); + CALL(@1, @8, expr_proba_quantitative(Kind::DIAMOND)); CALL(@1, @8, property()); } | T_PROBA SMCBounds '(' PathType Expression ')' T_GEQ @@ -1874,18 +1882,18 @@ BoundType: ; CmpGLE: - T_GEQ { $$ = GE; } - | T_LEQ { $$ = LE; } + T_GEQ { $$ = Kind::GE; } + | T_LEQ { $$ = Kind::LE; } ; PathType: - T_BOX { $$ = BOX; } - | T_DIAMOND { $$ = DIAMOND; } + T_BOX { $$ = Kind::BOX; } + | T_DIAMOND { $$ = Kind::DIAMOND; } ; BracketExprList: '{' ExpressionList '}' { - CALL(@1, @3, expr_nary(LIST,$2)); + CALL(@1, @3, expr_nary(Kind::LIST,$2)); }; /* There is an ExprList but it's not a list, rather @@ -1934,18 +1942,18 @@ Property: | StrategyAssignment | PropertyExpr | SupPrefix NonEmptyExpressionList Subjection { - CALL(@1, @2, expr_nary(LIST,$2)); - CALL(@1, @2, expr_binary(SUP_VAR)); + CALL(@1, @2, expr_nary(Kind::LIST,$2)); + CALL(@1, @2, expr_binary(Kind::SUP_VAR)); CALL(@1, @2, property()); } | InfPrefix NonEmptyExpressionList Subjection { - CALL(@1, @2, expr_nary(LIST,$2)); - CALL(@1, @2, expr_binary(INF_VAR)); + CALL(@1, @2, expr_nary(Kind::LIST,$2)); + CALL(@1, @2, expr_binary(Kind::INF_VAR)); CALL(@1, @2, property()); } | BoundsPrefix NonEmptyExpressionList Subjection { - CALL(@1, @2, expr_nary(LIST,$2)); - CALL(@1, @2, expr_binary(BOUNDS_VAR)); + CALL(@1, @2, expr_nary(Kind::LIST,$2)); + CALL(@1, @2, expr_binary(Kind::BOUNDS_VAR)); CALL(@1, @2, property()); }; @@ -1965,74 +1973,75 @@ static void setStartToken(XTAPart part, bool newxta) { switch (part) { - case S_XTA: + using namespace UTAP::XTAPartNames; + case XTA: syntax_token = newxta ? T_NEW : T_OLD; break; - case S_DECLARATION: + case DECLARATION: syntax_token = newxta ? T_NEW_DECLARATION : T_OLD_DECLARATION; break; - case S_LOCAL_DECL: + case LOCAL_DECL: syntax_token = newxta ? T_NEW_LOCAL_DECL : T_OLD_LOCAL_DECL; break; - case S_INST: + case INST: syntax_token = newxta ? T_NEW_INST : T_OLD_INST; break; - case S_SYSTEM: + case SYSTEM: syntax_token = T_NEW_SYSTEM; break; - case S_PARAMETERS: + case PARAMETERS: syntax_token = newxta ? T_NEW_PARAMETERS : T_OLD_PARAMETERS; break; - case S_INVARIANT: + case INVARIANT: syntax_token = newxta ? T_NEW_INVARIANT : T_OLD_INVARIANT; break; - case S_EXPONENTIAL_RATE: + case EXPONENTIAL_RATE: syntax_token = T_EXPONENTIAL_RATE; break; - case S_SELECT: + case SELECT: syntax_token = T_NEW_SELECT; break; - case S_GUARD: + case GUARD: syntax_token = newxta ? T_NEW_GUARD : T_OLD_GUARD; break; - case S_SYNC: + case SYNC: syntax_token = T_NEW_SYNC; break; - case S_ASSIGN: + case ASSIGN: syntax_token = newxta ? T_NEW_ASSIGN : T_OLD_ASSIGN; break; - case S_EXPRESSION: + case EXPRESSION: syntax_token = T_EXPRESSION; break; - case S_EXPRESSION_LIST: + case EXPRESSION_LIST: syntax_token = T_EXPRESSION_LIST; break; - case S_PROPERTY: + case PROPERTY: syntax_token = T_PROPERTY; break; - case S_XTA_PROCESS: + case XTA_PROCESS: syntax_token = T_XTA_PROCESS; break; - case S_PROBABILITY: + case PROBABILITY: syntax_token = T_PROBABILITY; break; // LSC - case S_INSTANCE_LINE: + case INSTANCE_LINE: syntax_token = T_INSTANCE_LINE; break; - case S_MESSAGE: + case MESSAGE: syntax_token = T_MESSAGE; break; - case S_UPDATE: + case UPDATE: syntax_token = T_UPDATE; break; - case S_CONDITION: + case CONDITION: syntax_token = T_CONDITION; break; } } -static int32_t parse_XTA(ParserBuilder& aParserBuilder, +static int32_t builder_parse_XTA(ParserBuilder& aParserBuilder, bool newxta, XTAPart part, std::string_view xpath) { // Select syntax @@ -2055,11 +2064,11 @@ static int32_t parse_XTA(ParserBuilder& aParserBuilder, return res; } -static int32_t parse_property(ParserBuilder& aParserBuilder, std::string_view xpath) +static int32_t builder_parse_property(ParserBuilder& aParserBuilder, std::string_view xpath) { // Select syntax syntax = Syntax::PROPERTY; - setStartToken(S_PROPERTY, false); + setStartToken(UTAP::XTAPart::PROPERTY, false); // Set parser builder ch = &aParserBuilder; @@ -2070,14 +2079,7 @@ static int32_t parse_property(ParserBuilder& aParserBuilder, std::string_view xp return (utap_parse() != 0) ? -1 : 0; } -int32_t parse_XTA(const char *str, ParserBuilder& builder, - bool newxta, XTAPart part, std::string_view xpath) -{ - utap__scan_string(str); - int32_t res = parse_XTA(builder, newxta, part, xpath); - utap__delete_buffer(YY_CURRENT_BUFFER); - return res; -} +namespace UTAP { const char* utap_builtin_declarations() { static const char* res = @@ -2115,19 +2117,28 @@ static const char* res = return res; } +int32_t parse_XTA(const char *str, ParserBuilder& builder, + bool newxta, XTAPart part, std::string_view xpath) +{ + utap__scan_string(str); + int32_t res = builder_parse_XTA(builder, newxta, part, xpath); + utap__delete_buffer(YY_CURRENT_BUFFER); + return res; +} + int32_t parse_XTA(const char *str, ParserBuilder& builder, bool newxta) { if (newxta) - parse_XTA(utap_builtin_declarations(), builder, newxta, S_DECLARATION, ""); - return parse_XTA(str, builder, newxta, S_XTA, ""); + parse_XTA(utap_builtin_declarations(), builder, newxta, XTAPart::DECLARATION, ""); + return parse_XTA(str, builder, newxta, XTAPart::XTA, ""); } int32_t parse_XTA(FILE *file, ParserBuilder& builder, bool newxta) { if (newxta) - parse_XTA(utap_builtin_declarations(), builder, newxta, S_DECLARATION, ""); + parse_XTA(utap_builtin_declarations(), builder, newxta, XTAPart::DECLARATION, ""); utap__switch_to_buffer(utap__create_buffer(file, YY_BUF_SIZE)); - int res = parse_XTA(builder, newxta, S_XTA, ""); + int res = builder_parse_XTA(builder, newxta, XTAPart::XTA, ""); utap__delete_buffer(YY_CURRENT_BUFFER); return res; } @@ -2135,7 +2146,7 @@ int32_t parse_XTA(FILE *file, ParserBuilder& builder, bool newxta) int32_t parse_property(const char *str, ParserBuilder& aParserBuilder, const std::string& xpath) { utap__scan_string(str); - int32_t res = parse_property(aParserBuilder, xpath); + int32_t res = builder_parse_property(aParserBuilder, xpath); utap__delete_buffer(YY_CURRENT_BUFFER); return res; } @@ -2143,7 +2154,9 @@ int32_t parse_property(const char *str, ParserBuilder& aParserBuilder, const std int32_t parse_property(FILE *file, ParserBuilder& aParserBuilder) { utap__switch_to_buffer(utap__create_buffer(file, YY_BUF_SIZE)); - int32_t res = parse_property(aParserBuilder, ""); + int32_t res = builder_parse_property(aParserBuilder, ""); utap__delete_buffer(YY_CURRENT_BUFFER); return res; } + +} // namespace UTAP \ No newline at end of file diff --git a/src/print.hpp b/src/print.hpp index c46bfc73..40fc7e1e 100644 --- a/src/print.hpp +++ b/src/print.hpp @@ -28,8 +28,8 @@ template std::ostream& print_infix(std::ostream& os, const View& view, std::string_view delim = ",") { - auto b = std::begin(view), e = std::end(view); - if (b != e) { + const auto e = std::end(view); + if (auto b = std::begin(view); b != e) { os << *b; while (++b != e) os << delim << *b; @@ -49,10 +49,10 @@ std::ostream& print_infix(std::ostream& os, const View& view, std::string_view d template std::ostream& print_infix_p(std::ostream& os, const View& view, Print&& print, std::string_view delim = ",") { - auto b = std::begin(view), e = std::end(view); - using E = typename std::iterator_traits::value_type; + const auto e = std::end(view); + using E = typename std::iterator_traits::value_type; static_assert(std::is_invocable_v, "print should accept istream and range element"); - if (b != e) { + if (auto b = std::begin(view); b != e) { print(os, *b); while (++b != e) print(os << delim, *b); diff --git a/src/property.cpp b/src/property.cpp index 5e3d9bf0..b980b32c 100644 --- a/src/property.cpp +++ b/src/property.cpp @@ -29,10 +29,7 @@ #include #include -using UTAP::Expression; - -using namespace UTAP::Constants; -using namespace UTAP; +namespace UTAP { void PropertyBuilder::typeCheck(Expression& expr) { tc.visitProperty(expr); } @@ -116,27 +113,27 @@ void PropertyBuilder::property() if ( // expr.get_kind() == AF || // expr.get_kind() == EG || // expr.get_kind() == LEADS_TO || - expr.get_kind() == SCENARIO || expr.get_kind() == SCENARIO2) { - throw UTAP::TypeException("$Cannot_handle_this_formula_for_models_with_priorities_or_guarded_broadcast_" - "receivers"); - } + expr.get_kind() == Kind::SCENARIO || expr.get_kind() == Kind::SCENARIO2) { + throw TypeException{"$Cannot_handle_this_formula_for_models_with_priorities_or_guarded_broadcast_" + "receivers"}; + } // Undid Marius' change. // The error says clearly for models with priorities or guarded broadcast. // The reason is theoretical. There is no proof of correctness for either of these // cases. In fact they are very similar because they are both based on partitioning // the states with subtractions. This was added in rev. 4528. if (/*document->has_priority_declaration() &&*/ expr.contains_deadlock()) - throw UTAP::TypeException( + throw TypeException{ "$Cannot_handle_deadlock_predicate_for_models_with_priorities_or_guarded_broadcast_" - "receivers"); + "receivers"}; } - if (expr.get_kind() != EF && expr.get_kind() != AG && expr.contains_deadlock()) { - throw UTAP::TypeException("$Cannot_handle_this_deadlock_predicate"); + if (expr.get_kind() != Kind::EF && expr.get_kind() != Kind::AG && expr.contains_deadlock()) { + throw TypeException{"$Cannot_handle_this_deadlock_predicate"}; } if (document.has_dynamic_templates() && !isSMC(&expr)) - throw UTAP::TypeException("Dynamic templates are only supported for SMC queries"); + throw TypeException{"Dynamic templates are only supported for SMC queries"}; /* Compile expression. */ properties.emplace_back(document.find_position(position.start).line, properties.size(), expr); @@ -148,6 +145,7 @@ void PropertyBuilder::property() static bool symbolicProperty(const Expression& expr) { switch (expr.get_kind()) { + using namespace KindNames; case EF: case EG: case AF: @@ -181,6 +179,7 @@ void PropertyBuilder::typeProperty(Expression expr) // NOLINT bool prob = false; switch (expr.get_kind()) { + using namespace KindNames; case EF: properties.back().type = quant_t::EE; break; case EG: properties.back().type = quant_t::EG; break; case AF: properties.back().type = quant_t::AE; break; @@ -226,20 +225,19 @@ void PropertyBuilder::typeProperty(Expression expr) // NOLINT prob = true; break; case MITL_FORMULA: properties.back().type = quant_t::Mitl; break; - default: throw UTAP::TypeException("$Invalid_property_type"); prob = true; + default: throw TypeException{"$Invalid_property_type"}; } if (prob) { if (document.has_priority_declaration()) - throw UTAP::TypeException("Priorities are not supported"); + throw TypeException{"Priorities are not supported"}; if (!document.all_broadcast()) - throw UTAP::TypeException("All channels must be broadcast"); + throw TypeException{"All channels must be broadcast"}; } if (document.get_sync_used() == 2 && document.has_priority_declaration()) - throw UTAP::TypeException("CSP synchronization is not implemented with priorities."); + throw TypeException{"CSP synchronization is not implemented with priorities."}; if (symbolicProperty(expr) && (expr.uses_hybrid() || expr.uses_fp())) - throw UTAP::TypeException("Symbolic verification and synthesis exclude usage of doubles and hybrid clocks in " - "properties."); + throw TypeException{"Symbolic verification and synthesis exclude usage of doubles and hybrid clocks in properties."}; } void PropertyBuilder::scenario(std::string_view name) @@ -248,7 +246,7 @@ void PropertyBuilder::scenario(std::string_view name) if (!resolve(name, symbol)) throw std::runtime_error("$No_such_scenario: " + std::string{name}); Type type = symbol.get_type(); - if (type.get_kind() != LSC_INSTANCE) + if (type.get_kind() != Kind::LSC_INSTANCE) throw std::runtime_error("$Not_a_LSC_template: " + symbol.get_name()); } @@ -268,7 +266,7 @@ void PropertyBuilder::parse(FILE* file) parse_property(file, *this); } -void PropertyBuilder::parse(const char* buf, const std::string& xpath, const UTAP::Options& options) +void PropertyBuilder::parse(const char* buf, const std::string& xpath, const Options& options) { size_t num_props = properties.size(); parse_property(buf, *this, xpath); @@ -278,21 +276,22 @@ void PropertyBuilder::parse(const char* buf, const std::string& xpath, const UTA properties.back().options = options; } -Variable* PropertyBuilder::addVariable(Type type, std::string_view name, Expression init, position_t pos) +Variable* PropertyBuilder::add_variable(Type type, std::string_view name, Expression init, position_t pos) { - throw UTAP::NotSupportedException("addVariable is not supported"); + throw NotSupportedException("add_variable is not supported"); } -bool PropertyBuilder::addFunction(Type type, std::string_view name, position_t pos) +bool PropertyBuilder::add_function(Type type, std::string_view name, position_t pos) { - throw UTAP::NotSupportedException("addFunction is not supported"); + throw NotSupportedException("add_function is not supported"); } -bool PropertyBuilder::isSMC(UTAP::Expression* expr) +bool PropertyBuilder::isSMC(Expression* expr) { if (expr == nullptr) expr = &(fragments[0]); - Kind k = expr->get_kind(); + const Kind k = expr->get_kind(); + using namespace KindNames; return (k == PMAX || k == PROBA_MIN_BOX || k == PROBA_MIN_DIAMOND || k == PROBA_BOX || k == PROBA_DIAMOND || k == PROBA_CMP || k == PROBA_EXP || k == SIMULATE || k == SIMULATEREACH || k == MITL_FORMULA || k == MIN_EXP || k == MAX_EXP); @@ -310,6 +309,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) _imitation = nullptr; switch (expr.get_kind()) { + using namespace KindNames; case LOAD_STRAT: properties.back().result_type = NonZoneStrategy; properties.back().type = quant_t::strategy_load; @@ -335,7 +335,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) switch (expr[2].get_kind()) { case A_UNTIL: properties.back().type = quant_t::control_SMC_AUntil; break; case AF: properties.back().type = quant_t::control_SMC_AF; break; - default: throw UTAP::TypeException("$Invalid_control_synthesis_property_type"); + default: throw TypeException{"$Invalid_control_synthesis_property_type"}; } break; @@ -361,7 +361,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) } break; case A_WEAK_UNTIL: properties.back().type = quant_t::control_AWeakUntil; break; - default: throw UTAP::TypeException("$Invalid_control_synthesis_property_type"); + default: throw TypeException{"$Invalid_control_synthesis_property_type"}; } break; @@ -374,7 +374,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) case A_UNTIL: properties.back().type = quant_t::EF_control_AUntil; break; case AG: properties.back().type = quant_t::EF_control_AG; break; case A_WEAK_UNTIL: properties.back().type = quant_t::EF_control_AWeakUntil; break; - default: throw UTAP::TypeException("$Invalid_control_synthesis_property_type"); + default: throw TypeException{"$Invalid_control_synthesis_property_type"}; } break; @@ -386,7 +386,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) case AG: properties.back().type = quant_t::PO_control_AG; break; case A_UNTIL: properties.back().type = quant_t::PO_control_AUntil; break; case A_WEAK_UNTIL: properties.back().type = quant_t::PO_control_AWeakUntil; break; - default: throw UTAP::TypeException("$Invalid_control_synthesis_property_type"); + default: throw TypeException{"$Invalid_control_synthesis_property_type"}; } break; @@ -396,7 +396,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) switch (expr[2].get_kind()) { case AF: properties.back().type = quant_t::control_opt_AF; break; case A_UNTIL: properties.back().type = quant_t::control_opt_AUntil; break; - default: throw UTAP::TypeException("$Invalid_type_of_time_optimal_control_synthesis_property"); + default: throw TypeException{"$Invalid_type_of_time_optimal_control_synthesis_property"}; } break; @@ -406,7 +406,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) switch (expr[1].get_kind()) { case AF: properties.back().type = quant_t::control_opt_Def1_AF; break; case A_UNTIL: properties.back().type = quant_t::control_opt_Def1_AUntil; break; - default: throw UTAP::TypeException("$Invalid_type_of_time_optimal_control_synthesis_property"); + default: throw TypeException{"$Invalid_type_of_time_optimal_control_synthesis_property"}; } break; @@ -417,7 +417,7 @@ void TigaPropertyBuilder::typeProperty(Expression expr) switch (expr.get_kind()) { case AF: properties.back().type = quant_t::control_opt_Def2_AF; break; case A_UNTIL: properties.back().type = quant_t::control_opt_Def2_AUntil; break; - default: throw UTAP::TypeException("$Invalid_type_of_time_optimal_control_synthesis_property"); + default: throw TypeException{"$Invalid_type_of_time_optimal_control_synthesis_property"}; } break; @@ -425,41 +425,38 @@ void TigaPropertyBuilder::typeProperty(Expression expr) } if (prob && !document.all_broadcast()) - throw UTAP::TypeException("All channels must be broadcast"); + throw TypeException{"All channels must be broadcast"}; if (potigaProp && document.has_strict_lower_bound_on_controllable_edges()) - throw UTAP::TypeException("$(PO)TIGA_properties_cannot_be_checked_for_systems_with_strict_lower_bounds_in_" - "guards"); + throw TypeException{"$(PO)TIGA_properties_cannot_be_checked_for_systems_with_strict_lower_bounds_in_guards"}; if (titiga && document.has_priority_declaration()) // FIXME: always false - throw UTAP::TypeException("$Priorities_are_not_yet_supported_in_TIGA"); + throw TypeException{"$Priorities_are_not_yet_supported_in_TIGA"}; if (!prob) { // FIXME: always true if (document.has_strict_invariants()) - throw UTAP::TypeException("$TIGA_properties_cannot_be_checked_for_systems_with_strict_invariants"); + throw TypeException{"$TIGA_properties_cannot_be_checked_for_systems_with_strict_invariants"}; // Stop-watches are now checked on-the-fly for SMC compatibility. // if (document->has_stop_watch(()) - // throw UTAP::TypeException("$Stop_watches_are_not_yet_supported_in_TIGA"); + // throw TypeException{"$Stop_watches_are_not_yet_supported_in_TIGA"); } } void TigaPropertyBuilder::strategy_declaration(std::string_view id) { - const std::string name = std::string(id); - if (auto it = declarations.find(name); it != declarations.end()) { - declarations.erase(it); - handle_warning(UTAP::duplicate_definition_error(name)); - } - declarations.emplace(name, &properties.back()); + if (auto it = declarations.find(id); it != declarations.end()) { + declarations.erase(it); + handle_warning(duplicate_definition_error(id)); + } + declarations.emplace(std::string(id), &properties.back()); if (!properties.empty()) // this happens when the model and the query file do not correspond. - properties.back().declaration = name; + properties.back().declaration = id; } void TigaPropertyBuilder::subjection(std::string_view id) { - std::string name = std::string(id); - if (auto it = declarations.find(name); it != declarations.end()) + if (auto it = declarations.find(id); it != declarations.end()) subjections.push_back(it->second); else - handle_error(UTAP::strategy_not_declared_error(name)); + handle_error(strategy_not_declared_error(id)); } void TigaPropertyBuilder::imitation(std::string_view id) @@ -467,10 +464,12 @@ void TigaPropertyBuilder::imitation(std::string_view id) if (auto it = declarations.find(id); it != declarations.end()) _imitation = it->second; else - handle_error(UTAP::strategy_not_declared_error(id)); + handle_error(strategy_not_declared_error(id)); } void TigaPropertyBuilder::expr_optimize(int, int, int, int) { // nothing for now } + +} // namespace UTAP \ No newline at end of file diff --git a/src/statement.cpp b/src/statement.cpp index ed6fa190..6a8f97e9 100644 --- a/src/statement.cpp +++ b/src/statement.cpp @@ -26,7 +26,7 @@ #include #include -using namespace UTAP; +namespace UTAP { std::string Statement::to_string(const std::string& indent) const { @@ -364,7 +364,7 @@ class CollectChangesVisitor final : public ExpressionVisitor CollectChangesVisitor() = default; }; -std::set UTAP::collect_changes(Statement& stat) +std::set collect_changes(Statement& stat) { auto visitor = CollectChangesVisitor{}; stat.accept(visitor); @@ -381,7 +381,7 @@ class CollectDependenciesVisitor final : public ExpressionVisitor CollectDependenciesVisitor() = default; }; -std::set UTAP::collect_dependencies(Statement& stat) +std::set collect_dependencies(Statement& stat) { auto visitor = CollectDependenciesVisitor{}; stat.accept(visitor); @@ -402,9 +402,11 @@ class CollectDynamicExpressions final : public ExpressionVisitor CollectDynamicExpressions() = default; }; -std::vector UTAP::collect_dynamic_expressions(Statement& stat) +std::vector collect_dynamic_expressions(Statement& stat) { auto visitor = CollectDynamicExpressions{}; stat.accept(visitor); return std::move(visitor.expressions); } + +} // namespace UTAP \ No newline at end of file diff --git a/src/symbols.cpp b/src/symbols.cpp index 4c91615c..06a75dfd 100644 --- a/src/symbols.cpp +++ b/src/symbols.cpp @@ -25,18 +25,16 @@ #include "utap/range.hpp" #include -#include #include #include #include // The base types -using namespace UTAP; -using namespace Constants; +namespace UTAP { ////////////////////////////////////////////////////////////////////////// -struct Symbol::Data : public std::enable_shared_from_this +struct Symbol::Data : std::enable_shared_from_this { Frame::Data* frame = nullptr; // Uncounted pointer to containing frame // TODO: consider removing Type type; // The type of the symbol @@ -53,16 +51,8 @@ Symbol::Symbol(Frame& frame, Type type, std::string_view name, position_t positi data = std::make_shared(frame.data.get(), std::move(type), user, name, position); } -/* Destructor */ Symbol::~Symbol() noexcept = default; -bool Symbol::operator==(const Symbol& symbol) const { return data == symbol.data; } - -/* Inequality operator */ -bool Symbol::operator!=(const Symbol& symbol) const { return data != symbol.data; } - -bool Symbol::operator<(const Symbol& symbol) const { return data < symbol.data; } - /* Get frame this symbol belongs to */ Frame Symbol::get_frame() const { return Frame(data->frame); } @@ -84,13 +74,15 @@ const std::string& Symbol::get_name() const { return data->name; } void Symbol::set_name(std::string name) { data->name = std::move(name); } -std::ostream& operator<<(std::ostream& o, const UTAP::Symbol& t) { return o << t.get_type() << " " << t.get_name(); } +std::ostream& operator<<(std::ostream& o, const Symbol& t) +{ + return o << t.get_type() << " " << t.get_name(); +} ////////////////////////////////////////////////////////////////////////// -struct Frame::Data : public std::enable_shared_from_this +struct Frame::Data : std::enable_shared_from_this { - // bool hasParent; // True if there is a parent Data* parent; // The parent frame data std::vector symbols; // The symbols in the frame std::map> mapping; // Mapping from names to indices @@ -103,12 +95,6 @@ Frame::Frame(Data* frame) { data = frame->shared_from_this(); } /* Destructor */ Frame::~Frame() noexcept = default; -/* Equality operator */ -bool Frame::operator==(const Frame& frame) const { return data == frame.data; } - -/* Inequality operator */ -bool Frame::operator!=(const Frame& frame) const { return data != frame.data; } - /* Returns the number of symbols in this frame */ uint32_t Frame::get_size() const { return static_cast(data->symbols.size()); } bool Frame::empty() const { return data->symbols.empty(); } @@ -244,14 +230,16 @@ Frame Frame::make_sub() return Frame{data.get()}; } -std::ostream& operator<<(std::ostream& os, const UTAP::Frame& t) +std::ostream& operator<<(std::ostream& os, const Frame& t) { os << "{"; - auto b = std::begin(t), e = std::end(t); - if (b != e) { + const auto e = std::end(t); + if (auto b = std::begin(t); b != e) { os << *b; while (++b != e) os << ", " << *b; } return os << "}"; } + +} // namespace UTAP \ No newline at end of file diff --git a/src/type.cpp b/src/type.cpp index 32e4728c..4beccd79 100644 --- a/src/type.cpp +++ b/src/type.cpp @@ -31,10 +31,9 @@ #include #include // std::pair -using namespace UTAP; -using namespace Constants; +namespace UTAP { -struct child_t +struct Child { std::string label; Type child; @@ -45,7 +44,7 @@ struct Type::type_data Kind kind; // Kind of type object position_t position; // Position in the input file Expression expr; // - std::vector children; + std::vector children; type_data(Kind kind, position_t position): kind{kind}, position{position} {} }; @@ -55,12 +54,6 @@ Type::Type(Kind kind, const position_t& pos, size_t size) data->children.resize(size); } -bool Type::operator==(const Type& type) const { return data == type.data; } - -bool Type::operator!=(const Type& type) const { return data != type.data; } - -bool Type::operator<(const Type& type) const { return data < type.data; } - uint32_t Type::size() const { assert(data); @@ -98,59 +91,60 @@ std::optional Type::find_index_of(std::string_view label) const return {}; } -Kind Type::get_kind() const { return unknown() ? UNKNOWN : data->kind; } +Kind Type::get_kind() const { return unknown() ? Kind::UNKNOWN : data->kind; } bool Type::is_prefix() const { switch (get_kind()) { - case Constants::FRACTION: - case Constants::UNKNOWN: - case Constants::VOID_TYPE: - case Constants::CLOCK: - case Constants::INT: - case Constants::DOUBLE: - case Constants::BOOL: - case Constants::STRING: - case Constants::SCALAR: - case Constants::LOCATION: - case Constants::LOCATION_EXPR: - case Constants::BRANCHPOINT: - case Constants::CHANNEL: - case Constants::COST: - case Constants::INVARIANT: - case Constants::INVARIANT_WR: - case Constants::GUARD: - case Constants::DIFF: - case Constants::CONSTRAINT: - case Constants::FORMULA: - case Constants::ARRAY: - case Constants::RECORD: - case Constants::PROCESS: - case Constants::PROCESS_SET: - case Constants::FUNCTION: - case Constants::FUNCTION_EXTERNAL: - case Constants::INSTANCE: - case Constants::RANGE: - case Constants::REF: - case Constants::TYPEDEF: - case Constants::LABEL: - case Constants::RATE: - case Constants::INSTANCE_LINE: // LSC - case Constants::MESSAGE: // LSC - case Constants::CONDITION: // LSC - case Constants::UPDATE: // LSC - case Constants::LSC_INSTANCE: // LSC + using namespace KindNames; + case FRACTION: + case UNKNOWN: + case VOID_TYPE: + case CLOCK: + case INT: + case DOUBLE: + case BOOL: + case STRING: + case SCALAR: + case LOCATION: + case LOCATION_EXPR: + case BRANCHPOINT: + case CHANNEL: + case COST: + case INVARIANT: + case INVARIANT_WR: + case GUARD: + case DIFF: + case CONSTRAINT: + case FORMULA: + case ARRAY: + case RECORD: + case PROCESS: + case PROCESS_SET: + case FUNCTION: + case FUNCTION_EXTERNAL: + case INSTANCE: + case RANGE: + case REF: + case TYPEDEF: + case LABEL: + case RATE: + case INSTANCE_LINE: // LSC + case MESSAGE: // LSC + case CONDITION: // LSC + case UPDATE: // LSC + case LSC_INSTANCE: // LSC return false; default: return true; } } -bool Type::unknown() const { return data == nullptr || data->kind == UNKNOWN; } +bool Type::unknown() const { return data == nullptr || data->kind == Kind::UNKNOWN; } bool Type::is(Kind kind) const { - using namespace Constants; + using namespace KindNames; const auto k = get_kind(); if (k == PROCESS_VAR) { return kind == PROCESS_VAR; @@ -166,72 +160,71 @@ Type Type::get_sub() const { assert(is_array()); const auto k = get_kind(); - if (k == REF || get_kind() == LABEL) { + if (k == Kind::REF || get_kind() == Kind::LABEL) { return get(0).get_sub(); - } else if (is_prefix()) { + } + if (is_prefix()) { return get(0).get_sub().create_prefix(k); - } else { - return get(0); } + return get(0); } Type Type::get_sub(uint32_t i) const { assert(is_record() || is_process()); const auto k = get_kind(); - if (k == REF || k == LABEL) { + if (k == Kind::REF || k == Kind::LABEL) { return get(0).get_sub(i); - } else if (is_prefix()) { + } + if (is_prefix()) { return get(0).get_sub(i).create_prefix(k); - } else { - return get(i); } + return get(i); } const Type& Type::get_array_size() const { const auto k = get_kind(); - if (is_prefix() || k == REF || k == LABEL) { + if (is_prefix() || k == Kind::REF || k == Kind::LABEL) { return get(0).get_array_size(); - } else { - assert(k == ARRAY); - return get(1); } + assert(k == Kind::ARRAY); + return get(1); } uint32_t Type::get_record_size() const { - if (const auto k = get_kind(); is_prefix() || k == REF || k == LABEL) { + const auto k = get_kind(); + if (is_prefix() || k == Kind::REF || k == Kind::LABEL) { return get(0).get_record_size(); - } else { - assert(k == RECORD); - return size(); } + assert(k == Kind::RECORD); + return size(); } const std::string& Type::get_record_label(uint32_t i) const { static const auto location = std::string{"location"}; - if (const auto k = get_kind(); is_prefix() || k == REF || k == LABEL) { + const auto k = get_kind(); + if (is_prefix() || k == Kind::REF || k == Kind::LABEL) { return get(0).get_record_label(i); - } else if (i == static_cast(std::numeric_limits::max())) { + } + if (i == static_cast(std::numeric_limits::max())) { // TODO: create a separate type for location expressions // TODO: get rid of magical constants return location; - } else { - assert(k == RECORD || k == PROCESS); - return get_label(i); } + assert(k == Kind::RECORD || k == Kind::PROCESS); + return get_label(i); } std::pair Type::get_range() const { - assert(is(RANGE)); - if (get_kind() == RANGE) { + assert(is(Kind::RANGE)); + if (get_kind() == Kind::RANGE) { return std::make_pair(get(1).get_expression(), get(2).get_expression()); - } else { - return get(0).get_range(); } + return get(0).get_range(); } const Expression& Type::get_expression() const @@ -243,7 +236,7 @@ const Expression& Type::get_expression() const Type Type::strip() const { const auto k = get_kind(); - if (is_prefix() || k == RANGE || k == REF || k == LABEL) { + if (is_prefix() || k == Kind::RANGE || k == Kind::REF || k == Kind::LABEL) { return get(0).strip(); } else { return *this; @@ -253,7 +246,7 @@ Type Type::strip() const Type Type::strip_array() const { Type type = strip(); - while (type.get_kind() == ARRAY) { + while (type.get_kind() == Kind::ARRAY) { type = type.get(0).strip(); } return type; @@ -267,7 +260,7 @@ Type Type::rename(const std::string& from, const std::string& to) const type.data->children[i].child = get(i).rename(from, to); type.data->children[i].label = get_label(i); } - if (get_kind() == LABEL && get_label(0) == from) { + if (get_kind() == Kind::LABEL && get_label(0) == from) { type.data->children[0].label = to; } return type; @@ -291,6 +284,7 @@ const position_t& Type::get_position() const { return data->position; } bool Type::is_constant() const { switch (get_kind()) { + using namespace KindNames; case FUNCTION: case FUNCTION_EXTERNAL: case PROCESS: @@ -299,7 +293,7 @@ bool Type::is_constant() const case CONSTANT: return true; case RECORD: return std::all_of(data->children.begin(), data->children.end(), - [](const child_t& c) { return c.child.is_constant(); }); + [](const Child& c) { return c.child.is_constant(); }); default: return size() > 0 && get(0).is_constant(); } } @@ -307,6 +301,7 @@ bool Type::is_constant() const bool Type::is_mutable() const { switch (get_kind()) { + using namespace KindNames; case FUNCTION: case FUNCTION_EXTERNAL: case PROCESS: @@ -315,7 +310,7 @@ bool Type::is_mutable() const case CONSTANT: return false; case RECORD: return std::all_of(data->children.begin(), data->children.end(), - [](const child_t& c) { return c.child.is_mutable(); }); + [](const Child& c) { return c.child.is_mutable(); }); default: return size() == 0 || get(0).is_mutable(); } } @@ -324,11 +319,11 @@ bool Type::is_equality_compatible(const Type& other) const { if (is_integral() && other.is_integral()) { return true; - } else if (is(PROCESS_VAR) && other.is(PROCESS_VAR)) { + } + if (is(Kind::PROCESS_VAR) && other.is(Kind::PROCESS_VAR)) { return true; - } else { - return is_equivalent(other); } + return is_equivalent(other); } bool Type::is_assignment_compatible(const Type& rhs, bool init) const @@ -337,7 +332,8 @@ bool Type::is_assignment_compatible(const Type& rhs, bool init) const (is_double() && (rhs.is_double() || rhs.is_integral()))) : ((is_clock() || is_double()) && (rhs.is_integral() || rhs.is_double() || rhs.is_clock()))) { return true; - } else if (is_integral() && rhs.is_integral()) { + } + if (is_integral() && rhs.is_integral()) { return true; } return is_equivalent(rhs); @@ -353,9 +349,9 @@ bool Type::is_inline_if_compatible(const Type& t1, const Type& t2) const int Type::channel_capability() const { assert(is_channel()); - if (is(URGENT)) + if (is(Kind::URGENT)) return 0; - if (is(BROADCAST)) + if (is(Kind::BROADCAST)) return 1; return 2; } @@ -365,38 +361,47 @@ int Type::channel_capability() const */ bool Type::is_same_scalar(const Type& o) const { - if (get_kind() == REF || get_kind() == CONSTANT || get_kind() == SYSTEM_META) { + if (get_kind() == Kind::REF || get_kind() == Kind::CONSTANT || get_kind() == Kind::SYSTEM_META) { return (*this)[0].is_same_scalar(o); - } else if (o.get_kind() == EF || o.get_kind() == CONSTANT || o.get_kind() == SYSTEM_META) { + } + if (o.get_kind() == Kind::EF || o.get_kind() == Kind::CONSTANT || o.get_kind() == Kind::SYSTEM_META) { return is_same_scalar(o[0]); - } else if (get_kind() == LABEL && o.get_kind() == LABEL) { + } + if (get_kind() == Kind::LABEL && o.get_kind() == Kind::LABEL) { return get_label(0) == o.get_label(0) && (*this)[0].is_same_scalar(o[0]); - } else if (get_kind() == SCALAR && o.get_kind() == SCALAR) { + } + if (get_kind() == Kind::SCALAR && o.get_kind() == Kind::SCALAR) { return true; - } else if (get_kind() == RANGE && o.get_kind() == RANGE) { + } + if (get_kind() == Kind::RANGE && o.get_kind() == Kind::RANGE) { return (*this)[0].is_same_scalar(o[0]) && get_range().first.equal(o.get_range().first) && get_range().second.equal(o.get_range().second); - } else { - return false; } + return false; } bool Type::is_equivalent(const Type& o) const { if (is_integer() && o.is_integer()) { - return !is(RANGE) || !o.is(RANGE) || + return !is(Kind::RANGE) || !o.is(Kind::RANGE) || (get_range().first.equal(o.get_range().first) && get_range().second.equal(o.get_range().second)); - } else if (is_clock() && o.is_clock()) { + } + if (is_clock() && o.is_clock()) { return true; - } else if (is_scalar() && o.is_scalar()) { + } + if (is_scalar() && o.is_scalar()) { return is_same_scalar(o); - } else if (is_double() && o.is_double()) { + } + if (is_double() && o.is_double()) { return true; - } else if (is_boolean() && o.is_boolean()) { + } + if (is_boolean() && o.is_boolean()) { return true; - } else if (is_channel() && o.is_channel()) { + } + if (is_channel() && o.is_channel()) { return channel_capability() == o.channel_capability(); - } else if (is_record() && o.is_record()) { + } + if (is_record() && o.is_record()) { const auto size = get_record_size(); if (const auto oSize = o.get_record_size(); size == oSize) { for (uint32_t i = 0; i < size; ++i) { @@ -423,10 +428,10 @@ bool Type::is_equivalent(const Type& o) const Type Type::create_range(Type type, Expression lower, Expression upper, position_t pos) { - auto t = Type{RANGE, pos, 3}; + auto t = Type{Kind::RANGE, pos, 3}; t.data->children[0].child = std::move(type); - t.data->children[1].child = Type{UNKNOWN, pos, 0}; - t.data->children[2].child = Type{UNKNOWN, pos, 0}; + t.data->children[1].child = Type{Kind::UNKNOWN, pos, 0}; + t.data->children[2].child = Type{Kind::UNKNOWN, pos, 0}; t[1].data->expr = std::move(lower); t[2].data->expr = std::move(upper); return t; @@ -435,7 +440,7 @@ Type Type::create_range(Type type, Expression lower, Expression upper, position_ Type Type::create_record(const std::vector& types, const std::vector& labels, position_t pos) { assert(types.size() == labels.size()); - auto type = Type{RECORD, pos, types.size()}; + auto type = Type{Kind::RECORD, pos, types.size()}; for (size_t i = 0; i < types.size(); i++) { type.data->children[i].child = types[i]; type.data->children[i].label = labels[i]; @@ -447,7 +452,7 @@ Type Type::create_function(Type ret, const std::vector& parameters, const position_t pos) { assert(parameters.size() == labels.size()); - auto type = Type{FUNCTION, pos, parameters.size() + 1}; + auto type = Type{Kind::FUNCTION, pos, parameters.size() + 1}; type.data->children[0].child = std::move(ret); for (size_t i = 0; i < parameters.size(); i++) { type.data->children[i + 1].child = parameters[i]; @@ -460,7 +465,7 @@ Type Type::create_external_function(Type ret, const std::vector& parameter const std::vector& labels, position_t pos) { assert(parameters.size() == labels.size()); - auto type = Type{FUNCTION_EXTERNAL, pos, parameters.size() + 1}; + auto type = Type{Kind::FUNCTION_EXTERNAL, pos, parameters.size() + 1}; type.data->children[0].child = std::move(ret); for (size_t i = 0; i < parameters.size(); i++) { type.data->children[i + 1].child = parameters[i]; @@ -471,7 +476,7 @@ Type Type::create_external_function(Type ret, const std::vector& parameter Type Type::create_array(Type sub, Type size, position_t pos) { - auto type = Type{ARRAY, pos, 2}; + auto type = Type{Kind::ARRAY, pos, 2}; type.data->children[0].child = std::move(sub); type.data->children[1].child = std::move(size); return type; @@ -479,7 +484,7 @@ Type Type::create_array(Type sub, Type size, position_t pos) Type Type::create_typedef(std::string label, Type type, position_t pos) { - auto t = Type{TYPEDEF, pos, 1}; + auto t = Type{Kind::TYPEDEF, pos, 1}; t.data->children[0].label = std::move(label); t.data->children[0].child = std::move(type); return t; @@ -487,7 +492,7 @@ Type Type::create_typedef(std::string label, Type type, position_t pos) Type Type::create_instance(const Frame& parameters, position_t pos) { - auto type = Type{INSTANCE, pos, parameters.get_size()}; + auto type = Type{Kind::INSTANCE, pos, parameters.get_size()}; for (auto i = 0u; i < parameters.get_size(); ++i) { type.data->children[i].child = parameters[i].get_type(); type.data->children[i].label = parameters[i].get_name(); @@ -497,7 +502,7 @@ Type Type::create_instance(const Frame& parameters, position_t pos) Type Type::create_LSC_instance(const Frame& parameters, position_t pos) { - auto type = Type{LSC_INSTANCE, pos, parameters.get_size()}; + auto type = Type{Kind::LSC_INSTANCE, pos, parameters.get_size()}; for (auto i = 0u; i < parameters.get_size(); ++i) { type.data->children[i].child = parameters[i].get_type(); type.data->children[i].label = parameters[i].get_name(); @@ -507,7 +512,7 @@ Type Type::create_LSC_instance(const Frame& parameters, position_t pos) Type Type::create_process(const Frame& frame, position_t pos) { - auto type = Type{PROCESS, pos, frame.get_size()}; + auto type = Type{Kind::PROCESS, pos, frame.get_size()}; for (auto i = 0u; i < frame.get_size(); ++i) { type.data->children[i].child = frame[i].get_type(); type.data->children[i].label = frame[i].get_name(); @@ -517,7 +522,7 @@ Type Type::create_process(const Frame& frame, position_t pos) Type Type::create_process_set(const Type& instance, position_t pos) { - auto type = Type{PROCESS_SET, pos, instance.size()}; + auto type = Type{Kind::PROCESS_SET, pos, instance.size()}; for (auto i = 0u; i < instance.size(); ++i) { type.data->children[i].child = instance[i]; type.data->children[i].label = instance.get_label(i); @@ -536,7 +541,7 @@ Type Type::create_prefix(Kind kind, position_t pos) const Type Type::create_label(std::string_view label, position_t pos) const { - auto type = Type{LABEL, pos, 1}; + auto type = Type{Kind::LABEL, pos, 1}; type.data->children[0].child = *this; type.data->children[0].label = label; return type; @@ -552,6 +557,7 @@ std::ostream& Type::print(std::ostream& os) const os << "("; switch (get_kind()) { + using namespace KindNames; case UNKNOWN: os << "unknown"; break; case RANGE: os << "range"; break; case ARRAY: os << "array"; break; @@ -586,7 +592,7 @@ std::ostream& Type::print(std::ostream& os) const case LOCATION_EXPR: case LOCATION: os << "location"; break; case BRANCHPOINT: os << "branchpoint"; break; - // LSC + // LSC case INSTANCE_LINE: os << "instance line"; break; case MESSAGE: os << "message"; break; case CONDITION: os << "condition"; break; @@ -620,6 +626,7 @@ std::ostream& Type::print_declaration(std::ostream& os) const auto typeDef = false; switch (get_kind()) { + using namespace KindNames; case UNKNOWN: kind = "unknown"; break; case RANGE: range = true; break; case ARRAY: array = true; break; @@ -699,3 +706,5 @@ std::string Type::declaration() const } std::ostream& operator<<(std::ostream& os, const Type& t) { return t.print(os); } + +} // namespace UTAP \ No newline at end of file diff --git a/src/xmlreader.cpp b/src/xmlreader.cpp index b384e033..9f605c97 100644 --- a/src/xmlreader.cpp +++ b/src/xmlreader.cpp @@ -45,7 +45,7 @@ #include namespace UTAP { -enum class tag_t { +enum class Tag { NTA, PROJECT, IMPORTS, @@ -95,50 +95,50 @@ enum class tag_t { }; // clang-format off - static const auto tag_map = std::unordered_map{ - {"nta", tag_t::NTA}, - {"project", tag_t::PROJECT}, - {"imports", tag_t::IMPORTS}, - {"declaration", tag_t::DECLARATION}, - {"template", tag_t::TEMPLATE}, - {"instantiation", tag_t::INSTANTIATION}, - {"system", tag_t::SYSTEM}, - {"name", tag_t::NAME}, - {"parameter", tag_t::PARAMETER}, - {"location", tag_t::LOCATION}, - {"init", tag_t::INIT}, - {"transition", tag_t::TRANSITION}, - {"urgent", tag_t::URGENT}, - {"committed", tag_t::COMMITTED}, - {"branchpoint", tag_t::BRANCHPOINT}, - {"source", tag_t::SOURCE}, - {"target", tag_t::TARGET}, - {"label", tag_t::LABEL}, - {"nail", tag_t::NAIL}, - {"lsc", tag_t::LSC}, - {"type", tag_t::TYPE}, - {"mode", tag_t::MODE}, - {"yloccoord", tag_t::YLOCCOORD}, - {"lsclocation", tag_t::LSCLOCATION}, - {"prechart", tag_t::PRECHART}, - {"instance", tag_t::INSTANCE}, - {"temperature", tag_t::TEMPERATURE}, - {"message", tag_t::MESSAGE}, - {"condition", tag_t::CONDITION}, - {"update", tag_t::UPDATE}, - {"anchor", tag_t::ANCHOR}, - {"queries", tag_t::QUERIES}, - {"query", tag_t::QUERY}, - {"formula", tag_t::FORMULA}, - {"comment", tag_t::COMMENT}, - {"option", tag_t::OPTION}, - {"resource", tag_t::RESOURCE}, - {"expect", tag_t::EXPECT}, - {"result", tag_t::RESULT}, - {"details", tag_t::DETAILS}, - {"samples", tag_t::SAMPLES}, - {"plot", tag_t::PLOT}, - {"series", tag_t::SERIES} + static const auto tag_map = std::unordered_map{ + {"nta", Tag::NTA}, + {"project", Tag::PROJECT}, + {"imports", Tag::IMPORTS}, + {"declaration", Tag::DECLARATION}, + {"template", Tag::TEMPLATE}, + {"instantiation", Tag::INSTANTIATION}, + {"system", Tag::SYSTEM}, + {"name", Tag::NAME}, + {"parameter", Tag::PARAMETER}, + {"location", Tag::LOCATION}, + {"init", Tag::INIT}, + {"transition", Tag::TRANSITION}, + {"urgent", Tag::URGENT}, + {"committed", Tag::COMMITTED}, + {"branchpoint", Tag::BRANCHPOINT}, + {"source", Tag::SOURCE}, + {"target", Tag::TARGET}, + {"label", Tag::LABEL}, + {"nail", Tag::NAIL}, + {"lsc", Tag::LSC}, + {"type", Tag::TYPE}, + {"mode", Tag::MODE}, + {"yloccoord", Tag::YLOCCOORD}, + {"lsclocation", Tag::LSCLOCATION}, + {"prechart", Tag::PRECHART}, + {"instance", Tag::INSTANCE}, + {"temperature", Tag::TEMPERATURE}, + {"message", Tag::MESSAGE}, + {"condition", Tag::CONDITION}, + {"update", Tag::UPDATE}, + {"anchor", Tag::ANCHOR}, + {"queries", Tag::QUERIES}, + {"query", Tag::QUERY}, + {"formula", Tag::FORMULA}, + {"comment", Tag::COMMENT}, + {"option", Tag::OPTION}, + {"resource", Tag::RESOURCE}, + {"expect", Tag::EXPECT}, + {"result", Tag::RESULT}, + {"details", Tag::DETAILS}, + {"samples", Tag::SAMPLES}, + {"plot", Tag::PLOT}, + {"series", Tag::SERIES} }; // clang-format on @@ -148,25 +148,25 @@ enum class tag_t { */ static bool is_blank(std::string_view str) { return std::all_of(str.cbegin(), str.cend(), ::isspace); } -static inline bool is_blank(const xmlChar* str) { return is_blank((const char*)str); } +static bool is_blank(const xmlChar* str) { return is_blank((const char*)str); } -static inline bool is_alpha(char c) { return std::isalpha(c) != 0 || c == '_'; } +static bool is_alpha(char c) { return std::isalpha(c) != 0 || c == '_'; } static bool is_id_char(char c) { return std::isalnum(c) != 0 || c == '_' || c == '$' || c == '#'; } -struct id_expected_error : std::logic_error +struct IdExpectedError : std::logic_error { - id_expected_error(): std::logic_error{"Identifier expected"} {} + IdExpectedError(): std::logic_error{"Identifier expected"} {} }; -struct invalid_id_error : std::logic_error +struct InvalidIdError : std::logic_error { - invalid_id_error(): std::logic_error{"Invalid identifier"} {} + InvalidIdError(): std::logic_error{"Invalid identifier"} {} }; -struct xpath_corrupt_error : std::logic_error +struct XPathCorruptError : std::logic_error { - xpath_corrupt_error(): std::logic_error{"XPath is corrupted"} {} + XPathCorruptError(): std::logic_error{"XPath is corrupted"} {} }; std::string_view trim(std::string_view text) @@ -192,12 +192,12 @@ static std::string_view symbol(std::string_view text) { text = trim(text); if (text.empty()) - throw id_expected_error{}; + throw IdExpectedError{}; if (!is_alpha(text[0])) - throw id_expected_error{}; + throw IdExpectedError{}; for (const auto& c : text) if (!is_id_char(c)) - throw invalid_id_error{}; + throw InvalidIdError{}; return text; } @@ -210,81 +210,80 @@ static std::string_view symbol(std::string_view text) */ class Path { -private: - std::list> path; + std::list> path; public: Path() { path.emplace_back(); }; - void push(tag_t tag) + void push(Tag tag) { path.back().push_back(tag); path.emplace_back(); } - tag_t pop() + Tag pop() { path.pop_back(); return path.back().back(); } - [[nodiscard]] std::string str(tag_t tag = tag_t::NONE) const; + [[nodiscard]] std::string str(Tag tag = Tag::NONE) const; }; -static inline size_t count(const std::vector& level, tag_t tag) +static size_t count(const std::vector& level, Tag tag) { return static_cast(std::count(std::begin(level), std::end(level), tag)); } /** Returns the XPath encoding of the current path. */ -[[nodiscard]] std::string Path::str(tag_t tag) const +[[nodiscard]] std::string Path::str(Tag tag) const { std::ostringstream str; for (auto&& level : path) { if (level.empty()) break; switch (level.back()) { - case tag_t::NTA: str << "/nta"; break; - case tag_t::PROJECT: str << "/project"; break; - case tag_t::IMPORTS: str << "/imports"; break; - case tag_t::DECLARATION: str << "/declaration"; break; - case tag_t::TEMPLATE: str << "/template[" << count(level, tag_t::TEMPLATE) << "]"; break; - case tag_t::INSTANTIATION: str << "/instantiation"; break; - case tag_t::SYSTEM: str << "/system"; break; - case tag_t::NAME: str << "/name"; break; - case tag_t::PARAMETER: str << "/parameter"; break; - case tag_t::LOCATION: str << "/location[" << count(level, tag_t::LOCATION) << "]"; break; - case tag_t::BRANCHPOINT: str << "/branchpoint[" << count(level, tag_t::BRANCHPOINT) << "]"; break; - case tag_t::INIT: str << "/init"; break; - case tag_t::TRANSITION: str << "/transition[" << count(level, tag_t::TRANSITION) << "]"; break; - case tag_t::LABEL: str << "/label[" << count(level, tag_t::LABEL) << "]"; break; - case tag_t::URGENT: str << "/urgent"; break; - case tag_t::COMMITTED: str << "/committed"; break; - case tag_t::SOURCE: str << "/source"; break; - case tag_t::TARGET: str << "/target"; break; - case tag_t::NAIL: str << "/nail[" << count(level, tag_t::NAIL) << "]"; break; - case tag_t::LSC: str << "/lscTemplate[" << count(level, tag_t::LSC) << "]"; break; - case tag_t::TYPE: str << "/type"; break; - case tag_t::MODE: str << "/mode"; break; - case tag_t::YLOCCOORD: str << "/ylocoord[" << count(level, tag_t::YLOCCOORD) << "]"; break; - case tag_t::LSCLOCATION: str << "/lsclocation"; break; - case tag_t::PRECHART: str << "/prechart"; break; - case tag_t::INSTANCE: str << "/instance[" << count(level, tag_t::INSTANCE) << "]"; break; - case tag_t::TEMPERATURE: str << "/temperature[" << count(level, tag_t::TEMPERATURE) << "]"; break; - case tag_t::MESSAGE: str << "/message[" << count(level, tag_t::MESSAGE) << "]"; break; - case tag_t::CONDITION: str << "/condition[" << count(level, tag_t::CONDITION) << "]"; break; - case tag_t::UPDATE: str << "/update[" << count(level, tag_t::UPDATE) << "]"; break; - case tag_t::ANCHOR: str << "/anchor[" << count(level, tag_t::ANCHOR) << "]"; break; - case tag_t::QUERIES: str << "/queries"; break; - case tag_t::QUERY: str << "/query[" << count(level, tag_t::QUERY) << "]"; break; - case tag_t::FORMULA: str << "/formula"; break; - case tag_t::COMMENT: str << "/comment"; break; - case tag_t::OPTION: str << "/option"; break; - case tag_t::RESOURCE: str << "/resource"; break; - case tag_t::EXPECT: str << "/expect"; break; - case tag_t::RESULT: str << "/result"; break; - case tag_t::DETAILS: str << "/details"; break; - case tag_t::SAMPLES: str << "/samples"; break; + case Tag::NTA: str << "/nta"; break; + case Tag::PROJECT: str << "/project"; break; + case Tag::IMPORTS: str << "/imports"; break; + case Tag::DECLARATION: str << "/declaration"; break; + case Tag::TEMPLATE: str << "/template[" << count(level, Tag::TEMPLATE) << "]"; break; + case Tag::INSTANTIATION: str << "/instantiation"; break; + case Tag::SYSTEM: str << "/system"; break; + case Tag::NAME: str << "/name"; break; + case Tag::PARAMETER: str << "/parameter"; break; + case Tag::LOCATION: str << "/location[" << count(level, Tag::LOCATION) << "]"; break; + case Tag::BRANCHPOINT: str << "/branchpoint[" << count(level, Tag::BRANCHPOINT) << "]"; break; + case Tag::INIT: str << "/init"; break; + case Tag::TRANSITION: str << "/transition[" << count(level, Tag::TRANSITION) << "]"; break; + case Tag::LABEL: str << "/label[" << count(level, Tag::LABEL) << "]"; break; + case Tag::URGENT: str << "/urgent"; break; + case Tag::COMMITTED: str << "/committed"; break; + case Tag::SOURCE: str << "/source"; break; + case Tag::TARGET: str << "/target"; break; + case Tag::NAIL: str << "/nail[" << count(level, Tag::NAIL) << "]"; break; + case Tag::LSC: str << "/lscTemplate[" << count(level, Tag::LSC) << "]"; break; + case Tag::TYPE: str << "/type"; break; + case Tag::MODE: str << "/mode"; break; + case Tag::YLOCCOORD: str << "/ylocoord[" << count(level, Tag::YLOCCOORD) << "]"; break; + case Tag::LSCLOCATION: str << "/lsclocation"; break; + case Tag::PRECHART: str << "/prechart"; break; + case Tag::INSTANCE: str << "/instance[" << count(level, Tag::INSTANCE) << "]"; break; + case Tag::TEMPERATURE: str << "/temperature[" << count(level, Tag::TEMPERATURE) << "]"; break; + case Tag::MESSAGE: str << "/message[" << count(level, Tag::MESSAGE) << "]"; break; + case Tag::CONDITION: str << "/condition[" << count(level, Tag::CONDITION) << "]"; break; + case Tag::UPDATE: str << "/update[" << count(level, Tag::UPDATE) << "]"; break; + case Tag::ANCHOR: str << "/anchor[" << count(level, Tag::ANCHOR) << "]"; break; + case Tag::QUERIES: str << "/queries"; break; + case Tag::QUERY: str << "/query[" << count(level, Tag::QUERY) << "]"; break; + case Tag::FORMULA: str << "/formula"; break; + case Tag::COMMENT: str << "/comment"; break; + case Tag::OPTION: str << "/option"; break; + case Tag::RESOURCE: str << "/resource"; break; + case Tag::EXPECT: str << "/expect"; break; + case Tag::RESULT: str << "/result"; break; + case Tag::DETAILS: str << "/details"; break; + case Tag::SAMPLES: str << "/samples"; break; default: /* Strange tag on stack */ - throw xpath_corrupt_error{}; + throw XPathCorruptError{}; } if (level.back() == tag) { break; @@ -299,11 +298,10 @@ static inline size_t count(const std::vector& level, tag_t tag) */ class XMLReader { -private: - using elementmap_t = std::map; + using ElementMap = std::map; using xmlTextReader_ptr = std::unique_ptr; xmlTextReader_ptr reader; /**< The underlying xmlTextReader */ - elementmap_t names; /**< Map from id to name */ + ElementMap names; /**< Map from id to name */ ParserBuilder& parser; /**< The parser builder to which to push the model. */ bool newxta; /**< True if we should use new syntax. */ Path path; @@ -312,7 +310,7 @@ class XMLReader std::string currentType; /**< type of the current LSC template */ std::string currentMode; /**< mode of the current LSC template */ - [[nodiscard]] tag_t getElement() const; + [[nodiscard]] Tag getElement() const; /** Reads an attribute value of the currently parsed tag with manual deallocation. * @param name the name of the XML tag attribute * @return the value of the attribute, remember to xmlFree() it! @@ -327,10 +325,10 @@ class XMLReader bool isEmpty() const; int getNodeType() const; void read(); - bool begin(tag_t, bool skipEmpty = true); - bool end(tag_t); + bool begin(Tag, bool skipEmpty = true); + bool end(Tag); /** skips the content until tag is closed and then looks ahead */ - void close(tag_t tag) + void close(Tag tag) { if (!isEmpty()) { while (!end(tag)) @@ -340,14 +338,14 @@ class XMLReader } /** calls fn zero or one times unless closing tag is found */ template - void zero_or_one(tag_t closing_tag, Fn&& fn) + void zero_or_one(Tag closing_tag, Fn&& fn) { if (!end(closing_tag)) fn(); } /** calls fn zero or more times unless closing tag is found */ template - void zero_or_more(tag_t closing_tag, Fn&& fn) + void zero_or_more(Tag closing_tag, Fn&& fn) { while (!end(closing_tag) && fn()) ; @@ -375,7 +373,7 @@ class XMLReader bool init(); /** Parse optional name tag. */ std::string name(bool instanceLine = false); - std::string readString(tag_t tag, bool instanceLine = false); + std::string readString(Tag tag, bool instanceLine = false); std::string readText(bool instanceLine = false); int readNumber(); /** Parse obligatory source tag. */ @@ -445,13 +443,13 @@ int XMLReader::getNodeType() const { return xmlTextReaderNodeType(reader.get()); * Returns the tag of the current element. Throws an exception if * the tag is not known. */ -tag_t XMLReader::getElement() const +Tag XMLReader::getElement() const { const char* element = (const char*)xmlTextReaderConstLocalName(reader.get()); const auto tag = tag_map.find(element); if (tag == std::end(tag_map)) { /* Unknown element. */ - return tag_t::NONE; + return Tag::NONE; } return tag->second; } @@ -483,7 +481,7 @@ bool XMLReader::isEmpty() const * given tag. If skipEmpty is true, empty elements with the given * tag are ignored. */ -bool XMLReader::begin(tag_t tag, bool skipEmpty) +bool XMLReader::begin(Tag tag, bool skipEmpty) { for (;;) { int node_type = getNodeType(); @@ -491,12 +489,12 @@ bool XMLReader::begin(tag_t tag, bool skipEmpty) read(); node_type = getNodeType(); } - tag_t elem = getElement(); + Tag elem = getElement(); if (elem != tag) { // if the tag was not recognized, try skipping over it until // an end element is found with unknown tag. - if (elem == tag_t::NONE) { - end(tag_t::NONE); + if (elem == Tag::NONE) { + end(Tag::NONE); read(); continue; } @@ -513,7 +511,7 @@ bool XMLReader::begin(tag_t tag, bool skipEmpty) * @return True - if tag found * Ignores whitespace */ -bool UTAP::XMLReader::end(UTAP::tag_t tag) +bool XMLReader::end(Tag tag) { int node_type = getNodeType(); // Ignore whitespace @@ -562,10 +560,10 @@ int XMLReader::parse(const xmlChar* text, XTAPart syntax) bool XMLReader::declaration() { - if (begin(tag_t::DECLARATION)) { + if (begin(Tag::DECLARATION)) { read(); if (getNodeType() == XML_READER_TYPE_TEXT) { - parse(xmlTextReaderConstValue(reader.get()), S_DECLARATION); + parse(xmlTextReaderConstValue(reader.get()), XTAPart::DECLARATION); } return true; } @@ -574,7 +572,18 @@ bool XMLReader::declaration() bool XMLReader::label(bool required, const std::string& s_kind) { - if (begin(tag_t::LABEL)) { + static const auto xta_map = std::map{ + {"invariant", XTAPart::INVARIANT}, + {"select", XTAPart::SELECT}, + {"guard", XTAPart::GUARD}, + {"synchronisation", XTAPart::SYNC}, + {"assignment", XTAPart::ASSIGN}, + {"probability", XTAPart::PROBABILITY}, + {"message", XTAPart::MESSAGE}, + {"update", XTAPart::UPDATE}, + {"condition", XTAPart::CONDITION}, + }; + if (begin(Tag::LABEL)) { /* Get kind attribute. */ char* kind = getAttribute("kind"); if (kind == nullptr) @@ -583,17 +592,13 @@ bool XMLReader::label(bool required, const std::string& s_kind) /* Read the text and push it to the parser. */ if (getNodeType() == XML_READER_TYPE_TEXT) { const xmlChar* text = xmlTextReaderConstValue(reader.get()); - static const auto map = std::map{ - {"invariant", S_INVARIANT}, {"select", S_SELECT}, {"guard", S_GUARD}, - {"synchronisation", S_SYNC}, {"assignment", S_ASSIGN}, {"probability", S_PROBABILITY}, - {"message", S_MESSAGE}, {"update", S_UPDATE}, {"condition", S_CONDITION}, - }; - if (auto part = map.find(kind); part != map.end()) + if (auto part = xta_map.find(kind); part != xta_map.end()) parse(text, part->second); } xmlFree(kind); return true; - } else if (required) { + } + if (required) { tracker.setPath(&parser, path.str()); if (s_kind == "message") // LSC parser.handle_error(TypeException{"$Message_label_is_required"}); @@ -608,7 +613,7 @@ bool XMLReader::label(bool required, const std::string& s_kind) int XMLReader::invariant() { int result = -1; - if (begin(tag_t::LABEL)) { + if (begin(Tag::LABEL)) { /* Get kind attribute. */ char* kind = getAttribute("kind"); if (kind == nullptr) @@ -621,10 +626,10 @@ int XMLReader::invariant() // This is a terrible mess but it's too badly designed // to fix at this moment. if (kind_sv == "invariant") { - if (parse(text, S_INVARIANT) == 0) + if (parse(text, XTAPart::INVARIANT) == 0) result = 0; } else if (kind_sv == "exponentialrate") { - if (parse(text, S_EXPONENTIAL_RATE) == 0) + if (parse(text, XTAPart::EXPONENTIAL_RATE) == 0) result = 1; } } @@ -635,7 +640,7 @@ int XMLReader::invariant() std::string XMLReader::name(bool instanceLine) { - std::string text = readString(tag_t::NAME, instanceLine); + std::string text = readString(Tag::NAME, instanceLine); if (instanceLine && text.empty()) parser.handle_error(TypeException{"$Instance_name_is_required"}); return text; @@ -687,7 +692,7 @@ int XMLReader::readNumber() return -1; } -std::string XMLReader::readString(tag_t tag, bool instanceLine) +std::string XMLReader::readString(Tag tag, bool instanceLine) { if (begin(tag)) { read(); @@ -696,14 +701,14 @@ std::string XMLReader::readString(tag_t tag, bool instanceLine) return ""; } -std::string XMLReader::type() { return readString(tag_t::TYPE); } +std::string XMLReader::type() { return readString(Tag::TYPE); } -std::string XMLReader::mode() { return readString(tag_t::MODE); } +std::string XMLReader::mode() { return readString(Tag::MODE); } int XMLReader::lscLocation() { int n = -1; - if (begin(tag_t::LSCLOCATION)) { + if (begin(Tag::LSCLOCATION)) { n = readNumber(); } if (n == -1) @@ -713,7 +718,7 @@ int XMLReader::lscLocation() bool XMLReader::committed() { - if (begin(tag_t::COMMITTED, false)) { + if (begin(Tag::COMMITTED, false)) { read(); return true; } @@ -722,7 +727,7 @@ bool XMLReader::committed() bool XMLReader::urgent() { - if (begin(tag_t::URGENT, false)) { + if (begin(Tag::URGENT, false)) { read(); return true; } @@ -734,9 +739,9 @@ bool XMLReader::location() bool l_invariant = false; bool l_exponentialRate = false; - if (begin(tag_t::LOCATION, false)) { + if (begin(Tag::LOCATION, false)) { try { - std::string l_path = path.str(tag_t::LOCATION); + std::string l_path = path.str(Tag::LOCATION); /* Extract ID attribute. */ auto l_id = getAttributeStr("id"); if (is_blank(l_id)) @@ -745,7 +750,7 @@ bool XMLReader::location() /* Get name of the location. */ std::string l_name = name(); /* Read the invariant. */ - while (begin(tag_t::LABEL)) { + while (begin(Tag::LABEL)) { int res = invariant(); l_invariant |= res == 0; l_exponentialRate |= res == 1; @@ -786,9 +791,9 @@ bool XMLReader::location() /** Parse optional instance. */ bool XMLReader::instance() { - if (begin(tag_t::INSTANCE, false)) { + if (begin(Tag::INSTANCE, false)) { try { - auto i_path = path.str(tag_t::INSTANCE); + auto i_path = path.str(Tag::INSTANCE); /* Extract ID attribute. */ auto i_id = getAttributeStr("id"); read(); @@ -813,7 +818,7 @@ bool XMLReader::instance() tracker.increment(&parser, 1); /* Push instance to parser builder. */ parser.proc_instance_line(); - parse((xmlChar*)i_name.c_str(), S_INSTANCE_LINE); + parse((xmlChar*)i_name.c_str(), XTAPart::INSTANCE_LINE); } catch (TypeException& e) { parser.handle_error(e); } @@ -825,7 +830,7 @@ bool XMLReader::instance() /** Parse optional yloccoord */ bool XMLReader::yloccoord() { - if (begin(tag_t::YLOCCOORD, false)) { + if (begin(Tag::YLOCCOORD, false)) { read(); // used only for the GUI return true; } @@ -834,7 +839,7 @@ bool XMLReader::yloccoord() std::string XMLReader::temperature() { - if (begin(tag_t::TEMPERATURE, false)) { + if (begin(Tag::TEMPERATURE, false)) { read(); /* Get the temperature of the condition */ return readText(); @@ -844,9 +849,9 @@ std::string XMLReader::temperature() bool XMLReader::prechart() { - if (begin(tag_t::PRECHART, false)) { + if (begin(Tag::PRECHART, false)) { try { - std::string p_path = path.str(tag_t::PRECHART); + std::string p_path = path.str(Tag::PRECHART); /* Get the bottom location number */ read(); bottomPrechart = lscLocation(); @@ -860,19 +865,18 @@ bool XMLReader::prechart() parser.handle_error(e); } return true; - } else { - bottomPrechart = -1; - parser.prechart_set(false); } + bottomPrechart = -1; + parser.prechart_set(false); return false; } bool XMLReader::message() { - if (begin(tag_t::MESSAGE)) { + if (begin(Tag::MESSAGE)) { /* Add dummy position mapping to the message element. */ try { - std::string m_path = path.str(tag_t::MESSAGE); + std::string m_path = path.str(Tag::MESSAGE); read(); std::string from = source(); std::string to = target(); @@ -894,9 +898,9 @@ bool XMLReader::message() bool XMLReader::condition() { - if (begin(tag_t::CONDITION)) { + if (begin(Tag::CONDITION)) { try { - std::string c_path = path.str(tag_t::CONDITION); + std::string c_path = path.str(Tag::CONDITION); read(); std::vector instance_anchors = anchors(); @@ -920,9 +924,9 @@ bool XMLReader::condition() bool XMLReader::update() { - if (begin(tag_t::UPDATE)) { + if (begin(Tag::UPDATE)) { try { - std::string u_path = path.str(tag_t::UPDATE); + std::string u_path = path.str(Tag::UPDATE); // location = atoi((char*)xmlTextReaderGetAttribute(reader, (const xmlChar*)"y")); // pch = (location < bottomPrechart); read(); @@ -944,9 +948,9 @@ bool XMLReader::update() bool XMLReader::branchpoint() { - if (begin(tag_t::BRANCHPOINT, false)) { + if (begin(Tag::BRANCHPOINT, false)) { try { - std::string b_path = path.str(tag_t::BRANCHPOINT); + std::string b_path = path.str(Tag::BRANCHPOINT); auto b_id = getAttributeStr("id"); if (is_blank(b_id)) { throw TypeException{"Branchpoint must have a unique \"id\" attribute"}; @@ -977,7 +981,7 @@ bool XMLReader::branchpoint() bool XMLReader::init() { - if (begin(tag_t::INIT, false)) { + if (begin(Tag::INIT, false)) { /* Get reference attribute. */ char* ref = getAttribute("ref"); /* Find location name for the reference. */ @@ -994,9 +998,8 @@ bool XMLReader::init() xmlFree(ref); read(); return true; - } else { - parser.handle_error(TypeException{"$Missing_initial_location"}); } + parser.handle_error(TypeException{"$Missing_initial_location"}); return false; } @@ -1011,29 +1014,29 @@ std::string XMLReader::reference(const std::string& attributeName) std::string XMLReader::source() { - if (begin(tag_t::SOURCE, false)) + if (begin(Tag::SOURCE, false)) return reference("ref"); throw TypeException{"Missing source element"}; } std::string XMLReader::target() { - if (begin(tag_t::TARGET, false)) + if (begin(Tag::TARGET, false)) return reference("ref"); throw TypeException{"Missing target element"}; } std::string XMLReader::anchor() { - if (begin(tag_t::ANCHOR, false)) + if (begin(Tag::ANCHOR, false)) return reference("instanceid"); throw TypeException{"Missing anchor element"}; } std::vector XMLReader::anchors() { - std::vector res; - while (begin(tag_t::ANCHOR, false)) + auto res = std::vector{}; + while (begin(Tag::ANCHOR, false)) res.push_back(reference("instanceid")); if (res.empty()) throw TypeException{"Missing anchor element"}; @@ -1042,7 +1045,7 @@ std::vector XMLReader::anchors() bool XMLReader::transition() { - if (begin(tag_t::TRANSITION)) { + if (begin(Tag::TRANSITION)) { /* Add dummy position mapping to the transition element. */ try { char* type = getAttribute("controllable"); @@ -1060,7 +1063,7 @@ bool XMLReader::transition() parser.proc_edge_begin(from.c_str(), to.c_str(), control, actname.c_str()); while (label()) ; - while (begin(tag_t::NAIL)) + while (begin(Tag::NAIL)) read(); parser.proc_edge_end(from.c_str(), to.c_str()); } catch (TypeException& e) { @@ -1074,10 +1077,10 @@ bool XMLReader::transition() int XMLReader::parameter() { int count = 0; - if (begin(tag_t::PARAMETER)) { + if (begin(Tag::PARAMETER)) { read(); if (getNodeType() == XML_READER_TYPE_TEXT) { - count = parse(xmlTextReaderConstValue(reader.get()), S_PARAMETERS); + count = parse(xmlTextReaderConstValue(reader.get()), XTAPart::PARAMETERS); } } return count; @@ -1085,8 +1088,8 @@ int XMLReader::parameter() bool XMLReader::templ() { - if (begin(tag_t::TEMPLATE)) { - auto t_path = std::make_shared(path.str(tag_t::TEMPLATE)); + if (begin(Tag::TEMPLATE)) { + auto t_path = std::make_shared(path.str(Tag::TEMPLATE)); read(); try { /* Get the name and the parameters of the template. */ @@ -1127,8 +1130,8 @@ bool XMLReader::templ() bool XMLReader::lscTempl() { - if (begin(tag_t::LSC)) { - std::string t_path = path.str(tag_t::LSC); + if (begin(Tag::LSC)) { + std::string t_path = path.str(Tag::LSC); read(); try { /* Get the name and the parameters of the template. */ @@ -1172,12 +1175,12 @@ bool XMLReader::lscTempl() bool XMLReader::instantiation() { - if (begin(tag_t::INSTANTIATION, false)) { + if (begin(Tag::INSTANTIATION, false)) { const auto* text = (const xmlChar*)""; read(); if (getNodeType() == XML_READER_TYPE_TEXT) text = xmlTextReaderConstValue(reader.get()); - parse(text, S_INST); + parse(text, XTAPart::INST); return true; } return false; @@ -1185,7 +1188,7 @@ bool XMLReader::instantiation() void XMLReader::system() { - if (begin(tag_t::SYSTEM, false)) { + if (begin(Tag::SYSTEM, false)) { const auto* text = (const xmlChar*)""; read(); auto nodeType = getNodeType(); @@ -1195,16 +1198,16 @@ void XMLReader::system() // bison doesn't manage to properly set the position of errors, // leading to nonsense error placements. if (nodeType == XML_READER_TYPE_END_ELEMENT || is_blank(text)) { - tracker.setPath(&parser, path.str(tag_t::SYSTEM)); + tracker.setPath(&parser, path.str(Tag::SYSTEM)); tracker.increment(&parser, 1); parser.handle_error(TypeException{"$syntax_error: $unexpected $end"}); - close(tag_t::SYSTEM); + close(Tag::SYSTEM); return; } - parse(text, S_SYSTEM); - close(tag_t::SYSTEM); + parse(text, XTAPart::SYSTEM); + close(Tag::SYSTEM); } else { - std::string s = (nta) ? path.str(tag_t::NTA) : path.str(tag_t::PROJECT); + std::string s = (nta) ? path.str(Tag::NTA) : path.str(Tag::PROJECT); tracker.setPath(&parser, s); tracker.increment(&parser, 1); parser.handle_error(TypeException{"$Missing_system_tag"}); @@ -1213,28 +1216,28 @@ void XMLReader::system() bool XMLReader::queries() { - if (begin(tag_t::QUERIES, false)) { + if (begin(Tag::QUERIES, false)) { read(); - zero_or_one(tag_t::QUERIES, [this] { return model_options(); }); - zero_or_more(tag_t::QUERIES, [this] { return query(); }); - close(tag_t::QUERIES); + zero_or_one(Tag::QUERIES, [this] { return model_options(); }); + zero_or_more(Tag::QUERIES, [this] { return query(); }); + close(Tag::QUERIES); return true; } return false; } bool XMLReader::query() { - if (begin(tag_t::QUERY, false)) { + if (begin(Tag::QUERY, false)) { if (!isEmpty()) { read(); parser.query_begin(); - zero_or_one(tag_t::QUERY, [this] { return formula(); }); - zero_or_one(tag_t::QUERY, [this] { return comment(); }); - zero_or_more(tag_t::QUERY, [this] { return option(); }); - zero_or_one(tag_t::QUERY, [this] { return expectation(); }); - zero_or_more(tag_t::QUERY, [this] { return result(); }); + zero_or_one(Tag::QUERY, [this] { return formula(); }); + zero_or_one(Tag::QUERY, [this] { return comment(); }); + zero_or_more(Tag::QUERY, [this] { return option(); }); + zero_or_one(Tag::QUERY, [this] { return expectation(); }); + zero_or_more(Tag::QUERY, [this] { return result(); }); parser.query_end(); - close(tag_t::QUERY); + close(Tag::QUERY); } else read(); // look ahead next tag return true; @@ -1243,13 +1246,13 @@ bool XMLReader::query() } bool XMLReader::formula() { - if (begin(tag_t::FORMULA, false)) { + if (begin(Tag::FORMULA, false)) { if (!isEmpty()) { read(); const auto* text = xmlTextReaderConstValue(reader.get()); if (text != nullptr) - parser.query_formula((const char*)text, path.str(tag_t::FORMULA)); - close(tag_t::FORMULA); + parser.query_formula((const char*)text, path.str(Tag::FORMULA)); + close(Tag::FORMULA); } else read(); return true; @@ -1258,13 +1261,13 @@ bool XMLReader::formula() } bool XMLReader::comment() { - if (begin(tag_t::COMMENT, false)) { + if (begin(Tag::COMMENT, false)) { if (!isEmpty()) { read(); const auto* text = xmlTextReaderConstValue(reader.get()); if (text != nullptr) parser.query_comment((const char*)text); - close(tag_t::COMMENT); + close(Tag::COMMENT); } else read(); return true; @@ -1274,13 +1277,13 @@ bool XMLReader::comment() bool XMLReader::option() { - if (begin(tag_t::OPTION, false)) { + if (begin(Tag::OPTION, false)) { char* key = getAttribute("key"); char* value = getAttribute("value"); parser.query_options(key, value); xmlFree(key); xmlFree(value); - close(tag_t::OPTION); + close(Tag::OPTION); return true; } return false; @@ -1288,26 +1291,26 @@ bool XMLReader::option() bool XMLReader::expectation() { - if (begin(tag_t::EXPECT, false)) { + if (begin(Tag::EXPECT, false)) { if (!isEmpty()) { parser.expectation_begin(); char* outcome = getAttribute("outcome"); char* type = getAttribute("type"); char* value = getAttribute("value"); parser.expectation_value(outcome, type, value); - zero_or_more(tag_t::EXPECT, [this] { - if (begin(tag_t::RESOURCE, false)) { + zero_or_more(Tag::EXPECT, [this] { + if (begin(Tag::RESOURCE, false)) { auto type = getAttributeStr("type"); auto value = getAttributeStr("value"); auto unit = getAttributeStr("unit"); parser.expect_resource(type.c_str(), value.c_str(), unit.c_str()); - close(tag_t::RESOURCE); + close(Tag::RESOURCE); return true; } return false; }); parser.expectation_end(); - close(tag_t::EXPECT); + close(Tag::EXPECT); } else read(); return true; @@ -1316,8 +1319,8 @@ bool XMLReader::expectation() } bool XMLReader::result() { - if (begin(tag_t::RESULT, false)) { - close(tag_t::RESULT); + if (begin(Tag::RESULT, false)) { + close(Tag::RESULT); return true; } return false; @@ -1325,11 +1328,11 @@ bool XMLReader::result() void XMLReader::project() { - if (!begin(tag_t::NTA) && !begin(tag_t::PROJECT)) + if (!begin(Tag::NTA) && !begin(Tag::PROJECT)) throw TypeException{"$Missing_nta_or_project_tag"}; - nta = begin(tag_t::NTA); // "nta" or "project"? + nta = begin(Tag::NTA); // "nta" or "project"? if (newxta) - parse((const xmlChar*)utap_builtin_declarations(), S_DECLARATION); + parse((const xmlChar*)utap_builtin_declarations(), XTAPart::DECLARATION); read(); declaration(); while (templ()) @@ -1338,25 +1341,22 @@ void XMLReader::project() ; instantiation(); system(); - if ((nta && !end(tag_t::NTA)) || (!nta && !end(tag_t::PROJECT))) + if ((nta && !end(Tag::NTA)) || (!nta && !end(Tag::PROJECT))) queries(); parser.done(); } bool XMLReader::model_options() { - while (begin(tag_t::OPTION)) { + while (begin(Tag::OPTION)) { read(); char* key = getAttribute("key"); char* value = getAttribute("value"); parser.model_option(key, value); - close(tag_t::OPTION); + close(Tag::OPTION); } return true; } -} // namespace UTAP - -using namespace UTAP; int32_t parse_XML_fd(int fd, ParserBuilder& pb, bool newxta) { @@ -1442,3 +1442,5 @@ static std::string getXMLElement(const char *xmlBuffer, const std::string &path) } } */ + +} // namespace UTAP diff --git a/src/xmlwriter.cpp b/src/xmlwriter.cpp index dad63b65..69e065fa 100644 --- a/src/xmlwriter.cpp +++ b/src/xmlwriter.cpp @@ -29,7 +29,6 @@ #include // strlen namespace UTAP { -using namespace Constants; static constexpr auto MY_ENCODING = "utf-8"; // xml encoding static constexpr auto ERR_STATE_COLOR = "#ff6666"; // pink @@ -197,10 +196,10 @@ void XMLWriter::location(const Location& loc) label("exponentialrate", loc.exp_rate.str(), x, y); } // "committed" or "urgent" element - if (loc.uid.get_type().is(COMMITTED)) { + if (loc.uid.get_type().is(Kind::COMMITTED)) { startElement("committed"); endElement(); - } else if (loc.uid.get_type().is(URGENT)) { + } else if (loc.uid.get_type().is(Kind::URGENT)) { startElement("urgent"); endElement(); } @@ -427,17 +426,16 @@ xmlChar* ConvertInput(const char* in, const char* encoding) return out; } -} // namespace UTAP - -int32_t write_XML_file(const char* filename, UTAP::Document& doc) +int32_t write_XML_file(const char* filename, Document& doc) { /* Create a new XmlWriter for filename, with no compression. */ auto* writer = xmlNewTextWriterFilename(filename, 0); if (writer == nullptr) { - throw UTAP::XMLWriterError{"construction"}; + throw XMLWriterError{"construction"}; } - UTAP::XMLWriter(writer, doc).project(); - + XMLWriter{writer, doc}.project(); // xmlFreeTextWriter(writer); return 0; } + +} // namespace UTAP diff --git a/test/document_fixture.h b/test/document_fixture.h index 585d7eb0..175bdf2a 100644 --- a/test/document_fixture.h +++ b/test/document_fixture.h @@ -153,12 +153,12 @@ class QueryBuilder : public UTAP::StatementBuilder void strategy_declaration(std::string_view strategy_name) override {} void typecheck() { checker.checkExpression(query); } [[nodiscard]] UTAP::Expression getQuery() const { return query; } - UTAP::Variable* addVariable(UTAP::Type type, std::string_view name, UTAP::Expression init, + UTAP::Variable* add_variable(UTAP::Type type, std::string_view name, UTAP::Expression init, UTAP::position_t pos) override { throw UTAP::NotSupportedException(__FUNCTION__); } - bool addFunction(UTAP::Type type, std::string_view name, UTAP::position_t pos) override + bool add_function(UTAP::Type type, std::string_view name, UTAP::position_t pos) override { throw UTAP::NotSupportedException(__FUNCTION__); } diff --git a/test/expression_test.cpp b/test/expression_test.cpp index 181f46c9..ea01594f 100644 --- a/test/expression_test.cpp +++ b/test/expression_test.cpp @@ -28,40 +28,41 @@ TEST_SUITE_BEGIN("Expressions"); TEST_CASE("Expression") { using UTAP::Type; - using exp_t = UTAP::Expression; - const auto i_prim_type = Type::create_primitive(UTAP::Constants::INT); - REQUIRE(i_prim_type.get_kind() == UTAP::Constants::INT); - const auto d_prim_type = Type::create_primitive(UTAP::Constants::DOUBLE); - REQUIRE(d_prim_type.get_kind() == UTAP::Constants::DOUBLE); + using UTAP::Kind; + using Exp = UTAP::Expression; + const auto i_prim_type = Type::create_primitive(Kind::INT); + REQUIRE(i_prim_type.get_kind() == Kind::INT); + const auto d_prim_type = Type::create_primitive(Kind::DOUBLE); + REQUIRE(d_prim_type.get_kind() == Kind::DOUBLE); - const auto i0 = exp_t::create_constant(0); - REQUIRE(i0.get_kind() == UTAP::Constants::CONSTANT); - const auto i2 = exp_t::create_constant(2); - REQUIRE(i2.get_kind() == UTAP::Constants::CONSTANT); - CHECK(i2.get_type().get_kind() == UTAP::Constants::INT); + const auto i0 = Exp::create_constant(0); + REQUIRE(i0.get_kind() == Kind::CONSTANT); + const auto i2 = Exp::create_constant(2); + REQUIRE(i2.get_kind() == Kind::CONSTANT); + CHECK(i2.get_type().get_kind() == Kind::INT); CHECK(i2.get_value() == 2); - const auto i5 = exp_t::create_constant(5); - REQUIRE(i5.get_kind() == UTAP::Constants::CONSTANT); - CHECK(i5.get_type().get_kind() == UTAP::Constants::INT); + const auto i5 = Exp::create_constant(5); + REQUIRE(i5.get_kind() == Kind::CONSTANT); + CHECK(i5.get_type().get_kind() == Kind::INT); CHECK(i5.get_value() == 5); - const auto d3 = exp_t::create_double(3.0); - REQUIRE(d3.get_kind() == UTAP::Constants::CONSTANT); - CHECK(d3.get_type().get_kind() == UTAP::Constants::DOUBLE); + const auto d3 = Exp::create_double(3.0); + REQUIRE(d3.get_kind() == Kind::CONSTANT); + CHECK(d3.get_type().get_kind() == Kind::DOUBLE); CHECK(d3.get_double_value() == 3.0); - const auto d1_2 = exp_t::create_double(0.5); - REQUIRE(d1_2.get_kind() == UTAP::Constants::CONSTANT); - CHECK(d1_2.get_type().get_kind() == UTAP::Constants::DOUBLE); + const auto d1_2 = Exp::create_double(0.5); + REQUIRE(d1_2.get_kind() == Kind::CONSTANT); + CHECK(d1_2.get_type().get_kind() == Kind::DOUBLE); CHECK(d1_2.get_double_value() == 0.5); SUBCASE("Types") { const auto i02 = Type::create_range(i_prim_type, i0, i2); - REQUIRE(i02.get_kind() == UTAP::Constants::RANGE); + REQUIRE(i02.get_kind() == Kind::RANGE); const auto i02_range = i02.get_range(); CHECK(i02_range.first == i0); CHECK(i02_range.second == i2); const auto i25 = Type::create_range(i_prim_type, i2, i5); - REQUIRE(i25.get_kind() == UTAP::Constants::RANGE); + REQUIRE(i25.get_kind() == Kind::RANGE); const auto i25_range = i25.get_range(); CHECK(i25_range.first == i2); CHECK(i25_range.second == i5); @@ -70,55 +71,55 @@ TEST_CASE("Expression") SUBCASE("Operator precedence") { - using namespace UTAP::Constants; + using namespace UTAP::KindNames; // Follow table at https://en.cppreference.com/w/cpp/language/operator_precedence - REQUIRE(exp_t::get_precedence(POST_INCREMENT) == exp_t::get_precedence(POST_DECREMENT)); - REQUIRE(exp_t::get_precedence(POST_INCREMENT) >= exp_t::get_precedence(FUN_CALL)); - REQUIRE(exp_t::get_precedence(FUN_CALL) == exp_t::get_precedence(FUN_CALL_EXT)); - REQUIRE(exp_t::get_precedence(FUN_CALL) >= exp_t::get_precedence(ARRAY)); - REQUIRE(exp_t::get_precedence(ARRAY) >= exp_t::get_precedence(DOT)); - REQUIRE(exp_t::get_precedence(ARRAY) > exp_t::get_precedence(NOT)); - REQUIRE(exp_t::get_precedence(DOT) >= exp_t::get_precedence(RATE)); - REQUIRE(exp_t::get_precedence(PRE_INCREMENT) == exp_t::get_precedence(NOT)); - REQUIRE(exp_t::get_precedence(PRE_INCREMENT) == exp_t::get_precedence(PRE_DECREMENT)); - REQUIRE(exp_t::get_precedence(PRE_INCREMENT) >= exp_t::get_precedence(UNARY_MINUS)); - REQUIRE(exp_t::get_precedence(UNARY_MINUS) >= exp_t::get_precedence(NOT)); - REQUIRE(exp_t::get_precedence(NOT) > exp_t::get_precedence(POW)); - REQUIRE(exp_t::get_precedence(POW) > exp_t::get_precedence(MULT)); - REQUIRE(exp_t::get_precedence(MULT) == exp_t::get_precedence(DIV)); - REQUIRE(exp_t::get_precedence(MULT) == exp_t::get_precedence(MOD)); - REQUIRE(exp_t::get_precedence(MULT) > exp_t::get_precedence(PLUS)); - REQUIRE(exp_t::get_precedence(PLUS) == exp_t::get_precedence(MINUS)); - REQUIRE(exp_t::get_precedence(PLUS) > exp_t::get_precedence(BIT_LSHIFT)); - REQUIRE(exp_t::get_precedence(BIT_LSHIFT) == exp_t::get_precedence(BIT_RSHIFT)); - REQUIRE(exp_t::get_precedence(BIT_LSHIFT) > exp_t::get_precedence(LT)); - REQUIRE(exp_t::get_precedence(LT) == exp_t::get_precedence(LE)); - REQUIRE(exp_t::get_precedence(LT) == exp_t::get_precedence(GT)); - REQUIRE(exp_t::get_precedence(LT) == exp_t::get_precedence(GE)); - REQUIRE(exp_t::get_precedence(LT) > exp_t::get_precedence(EQ)); - REQUIRE(exp_t::get_precedence(EQ) == exp_t::get_precedence(NEQ)); - REQUIRE(exp_t::get_precedence(NEQ) > exp_t::get_precedence(BIT_AND)); - REQUIRE(exp_t::get_precedence(BIT_AND) > exp_t::get_precedence(BIT_XOR)); - REQUIRE(exp_t::get_precedence(BIT_XOR) > exp_t::get_precedence(BIT_OR)); - REQUIRE(exp_t::get_precedence(BIT_OR) > exp_t::get_precedence(AND)); - REQUIRE(exp_t::get_precedence(AND) > exp_t::get_precedence(OR)); - REQUIRE(exp_t::get_precedence(AND) > exp_t::get_precedence(ASSIGN)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_PLUS)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_MINUS)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_MULT)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_DIV)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_MOD)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_LSHIFT)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_RSHIFT)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_AND)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_OR)); - REQUIRE(exp_t::get_precedence(ASSIGN) == exp_t::get_precedence(ASS_XOR)); - REQUIRE(exp_t::get_precedence(ASSIGN) > exp_t::get_precedence(COMMA)); + REQUIRE(Exp::get_precedence(POST_INCREMENT) == Exp::get_precedence(POST_DECREMENT)); + REQUIRE(Exp::get_precedence(POST_INCREMENT) >= Exp::get_precedence(FUN_CALL)); + REQUIRE(Exp::get_precedence(FUN_CALL) == Exp::get_precedence(FUN_CALL_EXT)); + REQUIRE(Exp::get_precedence(FUN_CALL) >= Exp::get_precedence(ARRAY)); + REQUIRE(Exp::get_precedence(ARRAY) >= Exp::get_precedence(DOT)); + REQUIRE(Exp::get_precedence(ARRAY) > Exp::get_precedence(NOT)); + REQUIRE(Exp::get_precedence(DOT) >= Exp::get_precedence(RATE)); + REQUIRE(Exp::get_precedence(PRE_INCREMENT) == Exp::get_precedence(NOT)); + REQUIRE(Exp::get_precedence(PRE_INCREMENT) == Exp::get_precedence(PRE_DECREMENT)); + REQUIRE(Exp::get_precedence(PRE_INCREMENT) >= Exp::get_precedence(UNARY_MINUS)); + REQUIRE(Exp::get_precedence(UNARY_MINUS) >= Exp::get_precedence(NOT)); + REQUIRE(Exp::get_precedence(NOT) > Exp::get_precedence(POW)); + REQUIRE(Exp::get_precedence(POW) > Exp::get_precedence(MULT)); + REQUIRE(Exp::get_precedence(MULT) == Exp::get_precedence(DIV)); + REQUIRE(Exp::get_precedence(MULT) == Exp::get_precedence(MOD)); + REQUIRE(Exp::get_precedence(MULT) > Exp::get_precedence(PLUS)); + REQUIRE(Exp::get_precedence(PLUS) == Exp::get_precedence(MINUS)); + REQUIRE(Exp::get_precedence(PLUS) > Exp::get_precedence(BIT_LSHIFT)); + REQUIRE(Exp::get_precedence(BIT_LSHIFT) == Exp::get_precedence(BIT_RSHIFT)); + REQUIRE(Exp::get_precedence(BIT_LSHIFT) > Exp::get_precedence(LT)); + REQUIRE(Exp::get_precedence(LT) == Exp::get_precedence(LE)); + REQUIRE(Exp::get_precedence(LT) == Exp::get_precedence(GT)); + REQUIRE(Exp::get_precedence(LT) == Exp::get_precedence(GE)); + REQUIRE(Exp::get_precedence(LT) > Exp::get_precedence(EQ)); + REQUIRE(Exp::get_precedence(EQ) == Exp::get_precedence(NEQ)); + REQUIRE(Exp::get_precedence(NEQ) > Exp::get_precedence(BIT_AND)); + REQUIRE(Exp::get_precedence(BIT_AND) > Exp::get_precedence(BIT_XOR)); + REQUIRE(Exp::get_precedence(BIT_XOR) > Exp::get_precedence(BIT_OR)); + REQUIRE(Exp::get_precedence(BIT_OR) > Exp::get_precedence(AND)); + REQUIRE(Exp::get_precedence(AND) > Exp::get_precedence(OR)); + REQUIRE(Exp::get_precedence(AND) > Exp::get_precedence(ASSIGN)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_PLUS)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_MINUS)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_MULT)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_DIV)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_MOD)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_LSHIFT)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_RSHIFT)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_AND)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_OR)); + REQUIRE(Exp::get_precedence(ASSIGN) == Exp::get_precedence(ASS_XOR)); + REQUIRE(Exp::get_precedence(ASSIGN) > Exp::get_precedence(COMMA)); } SUBCASE("Unary") { - using namespace UTAP::Constants; + using namespace UTAP::KindNames; const auto ops = {// clang-format off UNARY_MINUS, NOT, DOT, /*SYNC,*/ PRE_INCREMENT, POST_INCREMENT, PRE_DECREMENT, POST_DECREMENT, RATE, ABS_F, FABS_F, EXP_F, EXP2_F, EXPM1_F, LN_F, LOG_F, LOG10_F, LOG2_F, LOG1P_F, SQRT_F, @@ -128,20 +129,20 @@ TEST_CASE("Expression") SIGNBIT_F, IS_UNORDERED_F, RANDOM_F, RANDOM_POISSON_F }; // clang-format on for (const auto& op : ops) { - REQUIRE(exp_t::get_precedence(op) > 0); - const auto op_i0 = exp_t::create_unary(op, i0); + REQUIRE(Exp::get_precedence(op) > 0); + const auto op_i0 = Exp::create_unary(op, i0); CHECK(op_i0.get_kind() == op); REQUIRE(op_i0.get_size() == 1); CHECK(op_i0.get(0) == i0); - const auto op_i2 = exp_t::create_unary(op, i2); + const auto op_i2 = Exp::create_unary(op, i2); CHECK(op_i2.get_kind() == op); REQUIRE(op_i2.get_size() == 1); CHECK(op_i2.get(0) == i2); - const auto op_d3 = exp_t::create_unary(op, d3); + const auto op_d3 = Exp::create_unary(op, d3); CHECK(op_d3.get_kind() == op); REQUIRE(op_d3.get_size() == 1); CHECK(op_d3.get(0) == d3); - const auto op_d1_2 = exp_t::create_unary(op, d1_2); + const auto op_d1_2 = Exp::create_unary(op, d1_2); CHECK(op_d1_2.get_kind() == op); REQUIRE(op_d1_2.get_size() == 1); CHECK(op_d1_2.get(0) == d1_2); @@ -149,7 +150,7 @@ TEST_CASE("Expression") } SUBCASE("Binary") { - using namespace UTAP::Constants; + using namespace UTAP::KindNames; const auto ops = { // clang-format off MINUS, PLUS, MULT, DIV, MOD, BIT_AND, BIT_OR, BIT_XOR, BIT_LSHIFT, BIT_RSHIFT, @@ -160,13 +161,13 @@ TEST_CASE("Expression") RANDOM_ARCSINE_F, RANDOM_BETA_F, RANDOM_GAMMA_F, RANDOM_NORMAL_F, RANDOM_WEIBULL_F, }; // clang-format on for (const auto& op : ops) { - REQUIRE(exp_t::get_precedence(op) > 0); - const auto op_i25 = exp_t::create_binary(op, i2, i5); + REQUIRE(Exp::get_precedence(op) > 0); + const auto op_i25 = Exp::create_binary(op, i2, i5); CHECK(op_i25.get_kind() == op); REQUIRE(op_i25.get_size() == 2); CHECK(op_i25.get(0) == i2); CHECK(op_i25.get(1) == i5); - const auto op_d3_2 = exp_t::create_binary(op, d3, d1_2); + const auto op_d3_2 = Exp::create_binary(op, d3, d1_2); CHECK(op_d3_2.get_kind() == op); REQUIRE(op_d3_2.get_size() == 2); CHECK(op_d3_2.get(0) == d3); @@ -177,27 +178,28 @@ TEST_CASE("Expression") TEST_CASE("Expression to string conversion") { - using exp_t = UTAP::Expression; - const auto i2 = exp_t::create_constant(2); - const auto i3 = exp_t::create_constant(3); - const auto i5 = exp_t::create_constant(5); - const auto i7 = exp_t::create_constant(7); + using UTAP::Kind; + using Exp = UTAP::Expression; + const auto i2 = Exp::create_constant(2); + const auto i3 = Exp::create_constant(3); + const auto i5 = Exp::create_constant(5); + const auto i7 = Exp::create_constant(7); SUBCASE("Addition and multiplication") { - const auto add1 = exp_t::create_binary(UTAP::Constants::PLUS, i2, i3); + const auto add1 = Exp::create_binary(Kind::PLUS, i2, i3); CHECK(add1.str() == "2 + 3"); - const auto add2 = exp_t::create_binary(UTAP::Constants::PLUS, i5, i7); + const auto add2 = Exp::create_binary(Kind::PLUS, i5, i7); CHECK(add2.str() == "5 + 7"); - const auto mult = exp_t::create_binary(UTAP::Constants::MULT, add1, add2); + const auto mult = Exp::create_binary(Kind::MULT, add1, add2); CHECK(mult.str() == "(2 + 3) * (5 + 7)"); } SUBCASE("Multiplication and power") { - const auto m1 = exp_t::create_binary(UTAP::Constants::MULT, i2, i3); + const auto m1 = Exp::create_binary(Kind::MULT, i2, i3); CHECK(m1.str() == "2 * 3"); - const auto m2 = exp_t::create_binary(UTAP::Constants::MULT, i5, i7); + const auto m2 = Exp::create_binary(Kind::MULT, i5, i7); CHECK(m2.str() == "5 * 7"); - const auto p = exp_t::create_binary(UTAP::Constants::POW, m1, m2); + const auto p = Exp::create_binary(Kind::POW, m1, m2); CHECK(p.str() == "(2 * 3) ** (5 * 7)"); } } diff --git a/test/parser_test.cpp b/test/parser_test.cpp index 080b01aa..149660ce 100644 --- a/test/parser_test.cpp +++ b/test/parser_test.cpp @@ -136,7 +136,6 @@ TEST_CASE("SMC bounds in queries") TEST_CASE("Parsing implicit goals for learning queries") { - using Constants::Kind; auto doc = read_document("simpleSystem.xml"); auto builder = QueryBuilder(doc); const auto& errs = doc.get_errors(); diff --git a/test/pretty.cpp b/test/pretty.cpp index ba4279c4..13b015c0 100644 --- a/test/pretty.cpp +++ b/test/pretty.cpp @@ -27,9 +27,6 @@ #include #include -using namespace std; -using namespace UTAP::Constants; - static bool newSyntax = (getenv("UPPAAL_OLD_SYNTAX") == nullptr); /** @@ -49,7 +46,7 @@ int main(int argc, char* argv[]) if (!std::filesystem::is_regular_file(path)) throw std::runtime_error("Path is not a regular file: " + path.string()); - auto pretty = UTAP::PrettyPrinter{cout}; + auto pretty = UTAP::PrettyPrinter{std::cout}; if (path.extension() == ".xml") { parse_XML_file(path, pretty, newSyntax); diff --git a/test/prettyprint_test.cpp b/test/prettyprint_test.cpp index 956fe0cf..b7e845e9 100644 --- a/test/prettyprint_test.cpp +++ b/test/prettyprint_test.cpp @@ -256,7 +256,7 @@ TEST_CASE("Probability compare pretty print") auto query = f.parse_query("Pr[<=20] (<> true) >= Pr[<=5]([] false)").intermediate; REQUIRE_MESSAGE(errs.empty(), errs.front().msg); - REQUIRE(query.get_kind() == UTAP::Constants::PROBA_CMP); + REQUIRE(query.get_kind() == Kind::PROBA_CMP); CHECK(query.str() == "Pr[<=20] (<> true) >= Pr[<=5] ([] false)"); } @@ -268,12 +268,12 @@ TEST_CASE("Simulate pretty prints") auto query1 = f.parse_query("simulate[<=20;1000] {5, true} : 100 : true").intermediate; REQUIRE_MESSAGE(errs.empty(), errs.front().msg); - REQUIRE(query1.get_kind() == UTAP::Constants::SIMULATEREACH); + REQUIRE(query1.get_kind() == Kind::SIMULATEREACH); CHECK(query1.str() == "simulate[<=20; 1000] {5, true} : 100 : true"); auto query2 = f.parse_query("simulate[#<=10;500] {25, false}").intermediate; REQUIRE_MESSAGE(errs.empty(), errs.front().msg); - REQUIRE(query2.get_kind() == UTAP::Constants::SIMULATE); + REQUIRE(query2.get_kind() == Kind::SIMULATE); CHECK(query2.str() == "simulate[#<=10; 500] {25, false}"); } @@ -355,9 +355,9 @@ TEST_CASE("Chaining disjunctive conjunctions with outer conjunction") TEST_CASE("Post incrementing an identifier should not require parenthesis") { auto frame = Frame::make(); - auto test_symbol = frame.add_symbol("foo", Type::create_primitive(Constants::INT), {}); + auto test_symbol = frame.add_symbol("foo", Type::create_primitive(Kind::INT), {}); auto id = Expression::create_identifier(test_symbol); - auto expr = Expression::create_unary(Constants::POST_INCREMENT, id); + auto expr = Expression::create_unary(Kind::POST_INCREMENT, id); CHECK(expr.str() == "foo++"); } diff --git a/test/statement_test.cpp b/test/statement_test.cpp index ae109510..a1f81c18 100644 --- a/test/statement_test.cpp +++ b/test/statement_test.cpp @@ -39,7 +39,8 @@ TEST_CASE("Empty") TEST_CASE("Composite") { - auto int_type = Type::create_primitive(UTAP::Constants::INT); + using UTAP::Kind; + auto int_type = Type::create_primitive(Kind::INT); auto global = Frame::make(); auto var_a = global.add_symbol("a", int_type, {}); auto id_a = Expression::create_identifier(var_a); @@ -49,24 +50,24 @@ TEST_CASE("Composite") SUBCASE("Trivial") { auto val2 = Expression::create_constant(2); - auto plus = Expression::create_binary(Constants::PLUS, val1, val2); + auto plus = Expression::create_binary(Kind::PLUS, val1, val2); SUBCASE("Assignment") { - auto assign = Expression::create_binary(UTAP::Constants::ASSIGN, id_a, plus); + auto assign = Expression::create_binary(Kind::ASSIGN, id_a, plus); auto s = ExprStatement{assign}; CHECK(s.returns() == false); CHECK(s.to_string(indent) == indent + "a = 1 + 2;"); } SUBCASE("Equality") { - auto equal = Expression::create_binary(UTAP::Constants::EQ, id_a, plus); + auto equal = Expression::create_binary(Kind::EQ, id_a, plus); auto s = ExprStatement{equal}; CHECK(s.returns() == false); CHECK(s.to_string(indent) == indent + "a == 1 + 2;"); } SUBCASE("Assertion") { - auto equal = Expression::create_binary(UTAP::Constants::EQ, id_a, plus); + auto equal = Expression::create_binary(Kind::EQ, id_a, plus); auto s = AssertStatement{equal}; CHECK(s.returns() == false); CHECK(s.to_string(indent) == indent + "assert(a == 1 + 2);"); @@ -76,10 +77,10 @@ TEST_CASE("Composite") { auto var_i = global.add_symbol("i", int_type); auto id_i = Expression::create_identifier(var_i); - auto step_i = Expression::create_unary(UTAP::Constants::PRE_INCREMENT, id_i); - auto step_a = Expression::create_binary(UTAP::Constants::ASS_PLUS, id_a, id_i); - auto cond_i = Expression::create_binary(UTAP::Constants::LT, id_i, val5); - auto cond_a = Expression::create_binary(UTAP::Constants::LT, id_a, val5); + auto step_i = Expression::create_unary(Kind::PRE_INCREMENT, id_i); + auto step_a = Expression::create_binary(Kind::ASS_PLUS, id_a, id_i); + auto cond_i = Expression::create_binary(Kind::LT, id_i, val5); + auto cond_a = Expression::create_binary(Kind::LT, id_a, val5); SUBCASE("If") { auto s = @@ -90,7 +91,7 @@ TEST_CASE("Composite") } SUBCASE("For loop") { - auto init = Expression::create_binary(UTAP::Constants::ASSIGN, id_i, val0); + auto init = Expression::create_binary(Kind::ASSIGN, id_i, val0); auto s = ForStatement{init, cond_i, step_i, std::make_unique(step_a)}; CHECK(s.returns() == false); CHECK(s.to_string(indent) == indent + "for (i = 0; i < 5; ++i)\n"s + indent + INDENT + "a += i;\n"); @@ -113,7 +114,7 @@ TEST_CASE("Composite") auto int_0_5 = Type::create_range(int_type, val0, val5); auto var_i = global.add_symbol("i", int_0_5); auto id_i = Expression::create_identifier(var_i); - auto comp = Expression::create_binary(UTAP::Constants::ASS_PLUS, id_a, id_i); + auto comp = Expression::create_binary(Kind::ASS_PLUS, id_a, id_i); auto s = RangeStatement{var_i, global, std::make_unique(comp)}; CHECK(s.returns() == false); CHECK(s.to_string(indent) == indent + "for (i : int[0,5])\n"s + indent + INDENT + "a += i;\n"); @@ -122,8 +123,8 @@ TEST_CASE("Composite") { auto var_i = global.add_symbol("i", int_type); auto id_i = Expression::create_identifier(var_i); - auto e1 = Expression::create_binary(UTAP::Constants::ASS_PLUS, id_a, id_i); - auto e2 = Expression::create_unary(UTAP::Constants::PRE_INCREMENT, id_a); + auto e1 = Expression::create_binary(Kind::ASS_PLUS, id_a, id_i); + auto e2 = Expression::create_unary(Kind::PRE_INCREMENT, id_a); auto s = BlockStatement{global}; CHECK(s.returns() == true); CHECK(s.to_string(indent) == "{\n" + indent + "}"); From d0e52ddff0e1af85567e898b68a2d7f27ecfdc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 07:23:12 +0000 Subject: [PATCH 02/54] Moved executable utilities to src from test, added a check for external library file before executing external function test --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 17 ++++ {test => src}/featurechecker.cpp | 0 {test => src}/pretty.cpp | 0 {test => src}/syntaxcheck.cpp | 0 test/CMakeLists.txt | 130 ++++++++++++++----------------- test/parser_test.cpp | 9 +++ 7 files changed, 86 insertions(+), 72 deletions(-) rename {test => src}/featurechecker.cpp (100%) rename {test => src}/pretty.cpp (100%) rename {test => src}/syntaxcheck.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2cc6157..5019dc5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,8 +51,8 @@ add_subdirectory(src) if (UTAP_TESTS) include(cmake/doctest.cmake) enable_testing() + add_subdirectory(test) endif(UTAP_TESTS) -add_subdirectory(test) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/UTAPConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY SameMajorVersion) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e8dda9c..d8017a69 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,3 +78,20 @@ target_include_directories(UTAP target_link_libraries(UTAP PRIVATE LibXml2::LibXml2 ${CMAKE_DL_LIBS} ${LIBXML_WINLIBS}) add_library(UTAP::UTAP ALIAS UTAP) # in case the library is not exported-imported + +add_executable(pretty pretty.cpp) +target_link_libraries(pretty PRIVATE UTAP) + +add_executable(syntaxcheck syntaxcheck.cpp) +target_link_libraries(syntaxcheck PRIVATE UTAP) + +add_executable(featurecheck featurechecker.cpp) +target_link_libraries(featurecheck PRIVATE UTAP) + +if (UTAP_STATIC) + target_link_options(pretty PRIVATE -static) + target_link_options(syntaxcheck PRIVATE -static) + target_link_options(featurecheck PRIVATE -static) +endif (UTAP_STATIC) + +install(TARGETS pretty syntaxcheck featurecheck) diff --git a/test/featurechecker.cpp b/src/featurechecker.cpp similarity index 100% rename from test/featurechecker.cpp rename to src/featurechecker.cpp diff --git a/test/pretty.cpp b/src/pretty.cpp similarity index 100% rename from test/pretty.cpp rename to src/pretty.cpp diff --git a/test/syntaxcheck.cpp b/src/syntaxcheck.cpp similarity index 100% rename from test/syntaxcheck.cpp rename to src/syntaxcheck.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d10fe210..98400cec 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,71 +1,59 @@ -add_executable(pretty pretty.cpp) -target_link_libraries(pretty PRIVATE UTAP) - -add_executable(syntaxcheck syntaxcheck.cpp) -target_link_libraries(syntaxcheck PRIVATE UTAP) - -add_executable(featurecheck featurechecker.cpp) -target_link_libraries(featurecheck PRIVATE UTAP) - -if (UTAP_STATIC) - target_link_options(pretty PRIVATE -static) - target_link_options(syntaxcheck PRIVATE -static) - target_link_options(featurecheck PRIVATE -static) -endif (UTAP_STATIC) - -install(TARGETS pretty syntaxcheck featurecheck) - -if(UTAP_TESTS) - #target_compile_definitions(MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/models") - add_executable(expression_test expression_test.cpp) - target_link_libraries(expression_test PRIVATE UTAP doctest::doctest_with_main) - add_test(NAME expression_test COMMAND expression_test) - - add_executable(statement_test statement_test.cpp) - target_link_libraries(statement_test PRIVATE UTAP doctest::doctest_with_main) - add_test(NAME statement_test COMMAND statement_test) - - add_library(external_fn SHARED external_fn.cpp) - target_compile_features(external_fn PUBLIC cxx_std_11) - - add_executable(parser_test parser_test.cpp) - target_compile_definitions(parser_test PRIVATE MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/models") - target_link_libraries(parser_test PRIVATE UTAP doctest::doctest_with_main) - add_dependencies(parser_test external_fn) - add_test(NAME parser_test COMMAND parser_test) - - add_executable(featurechecker_test featurechecker_test.cpp) - target_compile_definitions(featurechecker_test PRIVATE MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/models") - target_link_libraries(featurechecker_test PRIVATE UTAP doctest::doctest_with_main) - add_test(NAME featurechecker_test COMMAND featurechecker_test) - - add_executable(range_test range_test.cpp) - target_link_libraries(range_test PRIVATE UTAP doctest::doctest_with_main) - add_test(NAME range_test COMMAND range_test) - - add_executable(typechecker_test typechecker_test.cpp) - target_compile_definitions(typechecker_test PRIVATE MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/models") - target_link_libraries(typechecker_test PRIVATE UTAP doctest::doctest_with_main) - add_test(NAME typechecker_test COMMAND typechecker_test) - - add_executable(prettyprint_test prettyprint_test.cpp) - target_compile_definitions(prettyprint_test PRIVATE MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/models") - target_link_libraries(prettyprint_test PRIVATE UTAP doctest::doctest_with_main) - add_test(NAME prettyprint_test COMMAND prettyprint_test) - - add_executable(example_test example_test.cpp) - target_link_libraries(example_test PRIVATE UTAP) - add_test(NAME example_train_gate COMMAND example_test ${PROJECT_SOURCE_DIR}/examples/train-gate.xml ${PROJECT_SOURCE_DIR}/examples/train-gate-out.xml) - - if (UTAP_STATIC) - target_link_options(expression_test PRIVATE -static) - target_link_options(statement_test PRIVATE -static) - target_link_options(parser_test PRIVATE -static) - target_link_options(featurechecker_test PRIVATE -static) - target_link_options(range_test PRIVATE -static) - target_link_options(typechecker_test PRIVATE -static) - target_link_options(prettyprint_test PRIVATE -static) - target_link_options(example_test PRIVATE -static) - message(STATUS "Enabled Unit Tests -static") - endif (UTAP_STATIC) -endif(UTAP_TESTS) +set(MODELS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/models") +set(EXAMPLES_PATH "${PROJECT_SOURCE_DIR}/examples") + +add_executable(expression_test expression_test.cpp) +target_link_libraries(expression_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME expression_test COMMAND expression_test) + +add_executable(statement_test statement_test.cpp) +target_link_libraries(statement_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME statement_test COMMAND statement_test) + +add_library(external_fn SHARED external_fn.cpp) +target_compile_features(external_fn PUBLIC cxx_std_11) + +add_executable(parser_test parser_test.cpp) +target_compile_definitions(parser_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(parser_test PRIVATE UTAP doctest::doctest_with_main) +add_dependencies(parser_test external_fn) +add_test(NAME parser_test COMMAND parser_test WORKING_DIRECTORY $) + +add_executable(featurechecker_test featurechecker_test.cpp) +target_compile_definitions(featurechecker_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(featurechecker_test PRIVATE UTAP + doctest::doctest_with_main) +add_test(NAME featurechecker_test COMMAND featurechecker_test) + +add_executable(range_test range_test.cpp) +target_link_libraries(range_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME range_test COMMAND range_test) + +add_executable(typechecker_test typechecker_test.cpp) +target_compile_definitions( + typechecker_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(typechecker_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME typechecker_test COMMAND typechecker_test) + +add_executable(prettyprint_test prettyprint_test.cpp) +target_compile_definitions( + prettyprint_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(prettyprint_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME prettyprint_test COMMAND prettyprint_test) + +add_executable(example_test example_test.cpp) +target_link_libraries(example_test PRIVATE UTAP) +add_test(NAME example_train_gate + COMMAND example_test "${EXAMPLES_PATH}/train-gate.xml" + "${EXAMPLES_PATH}/train-gate-out.xml") + +if(UTAP_STATIC) + target_link_options(expression_test PRIVATE -static) + target_link_options(statement_test PRIVATE -static) + target_link_options(parser_test PRIVATE -static) + target_link_options(featurechecker_test PRIVATE -static) + target_link_options(range_test PRIVATE -static) + target_link_options(typechecker_test PRIVATE -static) + target_link_options(prettyprint_test PRIVATE -static) + target_link_options(example_test PRIVATE -static) + message(STATUS "Enabled Unit Tests -static") +endif(UTAP_STATIC) diff --git a/test/parser_test.cpp b/test/parser_test.cpp index 149660ce..0d207e7b 100644 --- a/test/parser_test.cpp +++ b/test/parser_test.cpp @@ -48,6 +48,15 @@ TEST_CASE("Power expressions") TEST_CASE("External functions") { using namespace UTAP; + auto libpath = std::filesystem::current_path(); + if constexpr (is(OS::Linux)) { + libpath /= "libexternal_fn.so"; + } else if constexpr (is(OS::Linux)) { + libpath /= "libexternal_fn.dylib"; + } else if constexpr (is(OS::Windows)) { + libpath /= "libexternal_fn.dll"; + } + REQUIRE_MESSAGE(exists(libpath), ("expecting library at " + libpath.string())); auto doc = read_document("external_fn.xml"); const auto& errs = doc.get_errors(); REQUIRE(errs.size() == 3); From 2047021c86f4e135681f67c935a0f7fd4ea778df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 07:34:27 +0000 Subject: [PATCH 03/54] Added CMake presets for regular, sanitized and quick builds --- CMakePresets.json | 152 ++++++++++++++++++++++ cmake/CommonPresets.json | 274 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 426 insertions(+) create mode 100644 CMakePresets.json create mode 100644 cmake/CommonPresets.json diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..cec8e6c6 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,152 @@ +{ + "version": 8, + "cmakeMinimumRequired": { + "major": 3, + "minor": 28, + "patch": 0 + }, + "include": ["cmake/CommonPresets.json"], + "configurePresets": [ + { + "name": "quick", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-quick", + "cacheVariables": { + "UTAP_TESTS": { "type": "BOOL", "value": "OFF" }, + "UTAP_CLANG_TIDY": { "type": "BOOL", "value": "OFF" } + }, + "displayName": "Configure quick build (no testing) for Debug and Release" + }, + { + "name": "quick-san", + "inherits": "multi-san", + "binaryDir": "${sourceDir}/build-quick-san", + "cacheVariables": { + "UTAP_TESTS": { "type": "bool", "value": "OFF" }, + "UTAP_CLANG_TIDY": { "type": "bool", "value": "OFF" } + }, + "displayName": "Configure quick build (no testing) with Sanitizers for Debug and Release" + } + ], + "buildPresets": [ + { + "name": "quick-release", + "inherits": "release", + "configurePreset": "quick", + "displayName": "Build quick (no testing) for Release" + }, + { + "name": "quick-debug", + "inherits": "debug", + "configurePreset": "quick", + "displayName": "Build quick (no testing) for Debug" + }, + { + "name": "quick-release-san", + "inherits": "release-san", + "configurePreset": "quick-san", + "displayName": "Build quick (no testing) with Sanitizers and Release" + }, + { + "name": "quick-debug-san", + "inherits": "debug-san", + "configurePreset": "quick-san", + "displayName": "Build quick (no testing) with Sanitizers and Debug" + } + ], + "testPresets": [], + "workflowPresets": [ + { + "name": "quick", + "displayName": "Configure and Build quick (no testing) for Debug and Release", + "steps": [ + { + "type": "configure", + "name": "quick" + }, + { + "type": "build", + "name": "quick-release" + }, + { + "type": "build", + "name": "quick-debug" + } + ] + }, + { + "name": "quick-release", + "displayName": "Configure and Build quick (no testing) for Release", + "steps": [ + { + "type": "configure", + "name": "quick" + }, + { + "type": "build", + "name": "quick-release" + } + ] + }, + { + "name": "quick-debug", + "displayName": "Configure and Build quick (no testing) for Debug", + "steps": [ + { + "type": "configure", + "name": "quick" + }, + { + "type": "build", + "name": "quick-debug" + } + ] + }, + { + "name": "quick-san", + "displayName": "Configure and Build quick (no testing) with Sanitizers for Debug and Release", + "steps": [ + { + "type": "configure", + "name": "quick-san" + }, + { + "type": "build", + "name": "quick-release-san" + }, + { + "type": "build", + "name": "quick-debug-san" + } + ] + }, + { + "name": "quick-release-san", + "displayName": "Configure and Build quick (no testing) with Sanitizers for Release", + "steps": [ + { + "type": "configure", + "name": "quick-san" + }, + { + "type": "build", + "name": "quick-release-san" + } + ] + }, + { + "name": "quick-debug-san", + "displayName": "Configure and Build quick (no testing) with Sanitizers for Debug", + "steps": [ + { + "type": "configure", + "name": "quick-san" + }, + { + "type": "build", + "name": "quick-debug-san" + } + ] + } + ] +} \ No newline at end of file diff --git a/cmake/CommonPresets.json b/cmake/CommonPresets.json new file mode 100644 index 00000000..5bb5a43f --- /dev/null +++ b/cmake/CommonPresets.json @@ -0,0 +1,274 @@ +{ + "version": 8, + "cmakeMinimumRequired": { + "major": 3, + "minor": 28, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "binaryDir": "${sourceDir}/build", + "displayName": "Configure with Defaults (Debug)" + }, + { + "name": "multi", + "inherits": "default", + "binaryDir": "${sourceDir}/build-multi", + "generator": "Ninja Multi-Config", + "displayName": "Configure for Ninja Debug/Release" + }, + { + "name": "multi-san", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-multi-san", + "cacheVariables": { + "UBSAN" : { "type": "BOOL", "value": "ON"}, + "ASAN" : { "type": "BOOL", "value": "ON"}, + "SSP" : { "type": "BOOL", "value": "ON"} + }, + "displayName": "Configure for Ninja Debug/Release with Sanitizers" + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default", + "displayName": "Build for default configuration" + }, + { + "name": "debug", + "configurePreset": "multi", + "configuration": "Debug", + "displayName": "Build for Debug" + }, + { + "name": "debug-san", + "inherits": "debug", + "configurePreset": "multi-san", + "displayName": "Build for Debug with Sanitizers" + }, + { + "name": "release", + "configurePreset": "multi", + "configuration": "Release", + "displayName": "Build for Release" + }, + { + "name": "release-debug", + "configurePreset": "multi", + "configuration": "RelWithDebInfo", + "displayName": "Build for Release with Debug Info" + }, + { + "name": "release-san", + "inherits": "release-debug", + "configurePreset": "multi-san", + "displayName": "Build for Release with Sanitizers and Debug Info" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "default", + "output": { "outputOnFailure": true, "verbosity": "verbose" }, + "execution": { "noTestsAction": "error", "stopOnFailure": true }, + "displayName": "Test with default options" + }, + { + "name": "debug", + "inherits": "default", + "configurePreset": "multi", + "configuration": "Debug", + "displayName": "Test the Debug" + }, + { + "name": "debug-san", + "inherits": "debug", + "configurePreset": "multi-san", + "displayName": "Test the Debug with Sanitizers" + }, + { + "name": "release", + "inherits": "default", + "configurePreset": "multi", + "configuration": "Release", + "displayName": "Test the Release" + }, + { + "name": "release-debug", + "inherits": "default", + "configurePreset": "multi", + "configuration": "RelWithDebInfo", + "displayName": "Test the Release with Debug Info" + }, + { + "name": "release-san", + "inherits": "release-debug", + "configurePreset": "multi-san", + "displayName": "Test the Release with Sanitizers and Debug Info" + } + ], + "workflowPresets": [ + { + "name": "default", + "displayName": "Configure, Build and Test with Defaults (Debug)", + "steps": [ + { + "type": "configure", + "name": "default" + }, + { + "type": "build", + "name": "default" + }, + { + "type": "test", + "name": "default" + } + ] + }, + { + "name": "debug", + "displayName": "Configure, Build and Test with Debug", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "debug" + }, + { + "type": "test", + "name": "debug" + } + ] + }, + { + "name": "release", + "displayName": "Configure, Build and Test with Release", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "release" + }, + { + "type": "test", + "name": "release" + } + ] + }, + { + "name": "release-debug", + "displayName": "Configure, Build and Test Release with Debug Info", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "release-debug" + }, + { + "type": "test", + "name": "release-debug" + } + ] + }, + { + "name": "debug-san", + "displayName": "Configure, Build and Test with Debug and Sanitizers", + "steps": [ + { + "type": "configure", + "name": "multi-san" + }, + { + "type": "build", + "name": "debug-san" + }, + { + "type": "test", + "name": "debug-san" + } + ] + }, + { + "name": "release-san", + "displayName": "Configure, Build and Test with Release and Sanitizers", + "steps": [ + { + "type": "configure", + "name": "multi-san" + }, + { + "type": "build", + "name": "release-san" + }, + { + "type": "test", + "name": "release-san" + } + ] + }, + { + "name": "multi", + "displayName": "Configure, Build and Test with Debug and Release", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "debug" + }, + { + "type": "test", + "name": "debug" + }, + { + "type": "build", + "name": "release-debug" + }, + { + "type": "test", + "name": "release-debug" + } + ] + }, + { + "name": "multi-san", + "displayName": "Configure, Build and Test with Debug and Release and Sanitizers", + "steps": [ + { + "type": "configure", + "name": "multi-san" + }, + { + "type": "build", + "name": "debug-san" + }, + { + "type": "test", + "name": "debug-san" + }, + { + "type": "build", + "name": "release-san" + }, + { + "type": "test", + "name": "release-san" + } + ] + } + ] +} From a60b72c5fb220039a63230a54de3c6f47dfd44c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 13:33:22 +0200 Subject: [PATCH 04/54] Added code coverage CMake workflows and Bash scripts --- .gitignore | 3 +- CMakeLists.txt | 8 +-- CMakePresets.json | 80 +++++++++++++++++++++++- cmake/coverage.cmake | 29 +++++++++ cmake/toolchain/x86_64-linux-gcc16.cmake | 19 ++++++ cmake/warnings.cmake | 17 +++++ coverage-gcovr.sh | 13 ++++ coverage-lcov.sh | 15 +++++ coverage-llvm.sh | 40 ++++++++++++ examples/compile-with-getlibs.sh | 2 +- getlibs.sh | 5 +- src/CMakeLists.txt | 18 +----- 12 files changed, 223 insertions(+), 26 deletions(-) create mode 100644 cmake/coverage.cmake create mode 100644 cmake/toolchain/x86_64-linux-gcc16.cmake create mode 100644 cmake/warnings.cmake create mode 100755 coverage-gcovr.sh create mode 100755 coverage-lcov.sh create mode 100755 coverage-llvm.sh diff --git a/.gitignore b/.gitignore index 42c30551..b80cf36d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /build -/cmake-build-* /build-* +/cmake-build-* /local /examples/build /examples/build-* @@ -10,3 +10,4 @@ /doc/api/html .idea .vscode +.claude diff --git a/CMakeLists.txt b/CMakeLists.txt index 5019dc5a..174ec168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,6 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - add_compile_options(-Wpedantic -Wall -Wextra -Werror=vla -Wno-unused-parameter) - endif() else() message(STATUS "UTAP is NOT the top-level project") endif() @@ -23,14 +20,15 @@ else(WIN32) set(UTAP_STATIC_DEFAULT OFF) endif(WIN32) -option(UTAP_WARNINGS "UTAP Compiler Warnings" ON) +include(cmake/warnings.cmake) +include(cmake/sanitizers.cmake) option(UTAP_TESTS "UTAP Unit Tests" ON) +include(cmake/coverage.cmake) option(UTAP_STATIC "UTAP Static Linking" ${UTAP_STATIC_DEFAULT}) option(UTAP_CLANG_TIDY "Enable clang-tidy linting" ON) option(UTAP_CCACHE "Enables ccache for intermediate object file caching" ON) cmake_policy(SET CMP0048 NEW) # project() command manages VERSION variables -include(cmake/sanitizers.cmake) if (UTAP_CLANG_TIDY) include(cmake/clang-tidy.cmake) endif (UTAP_CLANG_TIDY) diff --git a/CMakePresets.json b/CMakePresets.json index cec8e6c6..7e01cb0c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -26,6 +26,23 @@ "UTAP_CLANG_TIDY": { "type": "bool", "value": "OFF" } }, "displayName": "Configure quick build (no testing) with Sanitizers for Debug and Release" + }, + { + "name": "coverage", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-coverage-gcov", + "cacheVariables": { "UTAP_COVERAGE": "ON" }, + "displayName": "Configure gcov coverage build" + }, + { + "name": "coverage-llvm", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-coverage-llvm", + "cacheVariables": { + "UTAP_COVERAGE": "ON", + "UTAP_COVERAGE_TYPE": "llvm" + }, + "displayName": "Configure llvm coverage build" } ], "buildPresets": [ @@ -52,9 +69,34 @@ "inherits": "debug-san", "configurePreset": "quick-san", "displayName": "Build quick (no testing) with Sanitizers and Debug" + }, + { + "name": "coverage", + "inherits": "debug", + "configurePreset": "coverage", + "displayName": "Build with gcov coverage" + }, + { + "name": "coverage-llvm", + "inherits": "debug", + "configurePreset": "coverage-llvm", + "displayName": "Build with llvm coverage" + } + ], + "testPresets": [ + { + "name": "coverage", + "inherits": "debug", + "configurePreset": "coverage", + "displayName": "Test the Debug with gcov coverage" + }, + { + "name": "coverage-llvm", + "inherits": "debug", + "configurePreset": "coverage-llvm", + "displayName": "Test the Debug with llvm coverage" } ], - "testPresets": [], "workflowPresets": [ { "name": "quick", @@ -147,6 +189,42 @@ "name": "quick-debug-san" } ] + }, + { + "name": "coverage", + "displayName": "Configure, Build and Test with gcov Coverage", + "steps": [ + { + "type": "configure", + "name": "coverage" + }, + { + "type": "build", + "name": "coverage" + }, + { + "type": "test", + "name": "coverage" + } + ] + }, + { + "name": "coverage-llvm", + "displayName": "Configure, Build and Test with llvm Coverage", + "steps": [ + { + "type": "configure", + "name": "coverage-llvm" + }, + { + "type": "build", + "name": "coverage-llvm" + }, + { + "type": "test", + "name": "coverage-llvm" + } + ] } ] } \ No newline at end of file diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 00000000..f1ddafca --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,29 @@ +# Adds code coverage instrumentation to inspect the quality of tests +option(UTAP_COVERAGE "Enable UTAP Code Coverage" OFF) +set(UTAP_COVERAGE_TYPE "gcov" CACHE STRING "Coverage backend: gcov or llvm") +set_property(CACHE UTAP_COVERAGE_TYPE PROPERTY STRINGS gcov llvm) + +add_library(coverage_config INTERFACE) + +if(UTAP_COVERAGE) + if (NOT UTAP_TESTS) + message(WARNING "Code coverage is enabled while UTAP_TESTS is disabled") + endif () + if(UTAP_COVERAGE_TYPE STREQUAL "llvm" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(coverage_config INTERFACE -fcoverage-mcdc -fprofile-instr-generate -fcoverage-mapping -O0 -g) + target_link_options(coverage_config INTERFACE -fcoverage-mcdc -fprofile-instr-generate -fcoverage-mapping) + message(STATUS "Enabled Code Coverage using llvm") + else() + target_compile_options(coverage_config INTERFACE --coverage -O0 -g) + target_link_options(coverage_config INTERFACE --coverage) + message(STATUS "Enabled Code Coverage using gcov") + endif() +else () + message(STATUS "Disabled Code Coverage") +endif() + +function(target_coverage target) + if(UTAP_COVERAGE) + target_link_libraries(${target} PRIVATE coverage_config) + endif() +endfunction() diff --git a/cmake/toolchain/x86_64-linux-gcc16.cmake b/cmake/toolchain/x86_64-linux-gcc16.cmake new file mode 100644 index 00000000..167574ef --- /dev/null +++ b/cmake/toolchain/x86_64-linux-gcc16.cmake @@ -0,0 +1,19 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER gcc-16) +set(CMAKE_CXX_COMPILER g++-16) +set(CMAKE_C_FLAGS -m64) +set(CMAKE_CXX_FLAGS -m64) + +# where is the target environment located +set(CMAKE_FIND_ROOT_PATH "${CMAKE_PREFIX_PATH}") + +# adjust the default behavior of the FIND_XXX() commands: +# search programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) + +# search headers and libraries in the target environment +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake new file mode 100644 index 00000000..c554b798 --- /dev/null +++ b/cmake/warnings.cmake @@ -0,0 +1,17 @@ +# Enables compiler warnings + +option(UTAP_WARNINGS "UTAP Compiler Warnings" ON) +if (UTAP_WARNINGS) + set(UTAP_COMMON_WARN -Wpedantic -Wall -Wextra -Wimplicit-fallthrough -Werror=vla) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(UTAP_WARN ${UTAP_COMMON_WARN} -Wconversion -Wno-sign-conversion) + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(UTAP_WARN ${UTAP_COMMON_WARN} -Wconversion -Wno-sign-conversion) + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(UTAP_WARN ${UTAP_COMMON_WARN}) + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(UTAP_WARN /W4) + else () + message(WARNING "Failed to enable warnings for ${CMAKE_CXX_COMPILER_ID}") + endif () +endif () diff --git a/coverage-gcovr.sh b/coverage-gcovr.sh new file mode 100755 index 00000000..bcc7148c --- /dev/null +++ b/coverage-gcovr.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e + +BUILD_DIR=build-coverage-gcov +COVERAGE_DIR="${BUILD_DIR}/coverage_report" + +mkdir -p "$COVERAGE_DIR" + +cmake --preset coverage +cmake --build --preset coverage +ctest --preset coverage +gcovr --root . "$BUILD_DIR" --html --html-details -o "$COVERAGE_DIR/index.html" +xdg-open "$COVERAGE_DIR/index.html" diff --git a/coverage-lcov.sh b/coverage-lcov.sh new file mode 100755 index 00000000..47548301 --- /dev/null +++ b/coverage-lcov.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e + +BUILD_DIR=build-coverage-gcov +COVERAGE_INFO="${BUILD_DIR}/coverage.info" +COVERAGE_DIR="${BUILD_DIR}/coverage_report" + +cmake --preset coverage +cmake --build --preset coverage +lcov --zerocounters --directory "$BUILD_DIR" +ctest --preset coverage +lcov --capture --directory "$BUILD_DIR" --output-file "$COVERAGE_INFO" +lcov --remove "$COVERAGE_INFO" '/usr/*' --output-file "$COVERAGE_INFO" +genhtml "$COVERAGE_INFO" --output-directory "$COVERAGE_DIR" +xdg-open "$COVERAGE_DIR/index.html" diff --git a/coverage-llvm.sh b/coverage-llvm.sh new file mode 100755 index 00000000..b179443a --- /dev/null +++ b/coverage-llvm.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -e + +BUILD_DIR=build-coverage-llvm +COVERAGE_DIR="${BUILD_DIR}/coverage_report" +PROFDATA="${BUILD_DIR}/coverage.profdata" + +mkdir -p "$COVERAGE_DIR" + +cmake -DCMAKE_TOOLCHAIN_FILE=$PWD/cmake/toolchain/x86_64-linux-clang.cmake --preset coverage-llvm +cmake --build --preset coverage-llvm + +# each test binary gets its own raw profile (keyed by pid), otherwise they clobber +# one another since they'd all default to writing "default.profraw" +rm -f "$BUILD_DIR"/test/coverage-*.profraw +LLVM_PROFILE_FILE="$PWD/$BUILD_DIR/test/coverage-%p.profraw" ctest --preset coverage-llvm + +llvm-profdata merge -sparse "$BUILD_DIR"/test/coverage-*.profraw -o "$PROFDATA" + +# only target_coverage()-linked binaries (e.g. libUTAP.so) carry the __llvm_covmap +# section; the test executables themselves are not instrumented, so discover the +# actual instrumented binaries instead of assuming which ones they are +mapfile -t OBJECTS < <( + find "$BUILD_DIR" -path "$BUILD_DIR/_deps" -prune -o \ + -type f \( -executable -o -name "*.so*" \) -print | + while read -r f; do + readelf -S "$f" 2>/dev/null | grep -q __llvm_covmap && echo "$f" + done +) +OBJECT_ARGS=() +for obj in "${OBJECTS[@]:1}"; do + OBJECT_ARGS+=(-object "$obj") +done + +llvm-cov show "${OBJECTS[0]}" "${OBJECT_ARGS[@]}" \ + -instr-profile="$PROFDATA" \ + -format=html -output-dir="$COVERAGE_DIR" \ + -show-mcdc -show-branches=count \ + -ignore-filename-regex='(_deps|/test/)/' +xdg-open "$COVERAGE_DIR/index.html" diff --git a/examples/compile-with-getlibs.sh b/examples/compile-with-getlibs.sh index 67c9f294..44097a39 100755 --- a/examples/compile-with-getlibs.sh +++ b/examples/compile-with-getlibs.sh @@ -11,7 +11,7 @@ function show_vars() { # Absolute path to the example directory: EXAMPLE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Path to UTAP library source directory: -[ -n "$UTAP_SRC" ] || UTAP_SRC=${EXAMPLE_DIR%/*} +[ -n "$UTAP_SRC" ] || UTAP_SRC=$(dirname "$EXAMPLE_DIR") # Figure out the native host target (architecture and kernel); if [ -z "$TARGET" ] ; then diff --git a/getlibs.sh b/getlibs.sh index 1fe48ffd..62ea907b 100755 --- a/getlibs.sh +++ b/getlibs.sh @@ -13,11 +13,10 @@ LIBXML2_Z="${LIBXML2}.tar.xz" LIBXML2_URL="https://people.cs.aau.dk/~marius/mirrors/libxml2/${LIBXML2_Z}" LIBXML2_SHA256=a2c9ae7b770da34860050c309f903221c67830c86e4a7e760692b803df95143a - -DOCTEST=doctest-2.4.12 +DOCTEST=doctest-2.5.2 DOCTEST_Z="${DOCTEST}.tar.gz" DOCTEST_URL="https://github.com/doctest/doctest/archive/refs/tags/v${DOCTEST#doctest-}.tar.gz" -DOCTEST_SHA256=73381c7aa4dee704bd935609668cf41880ea7f19fa0504a200e13b74999c2d70 +DOCTEST_SHA256=9189960c2bbbc4f3382ce0773b2bb5f13e3afd8fed47f55f193e11e85a4f9854 BISON=bison-3.8.2 BISON_Z="${BISON}.tar.xz" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8017a69..5d145585 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,23 +47,12 @@ if (UTAP_STATIC) target_link_options(UTAP PUBLIC -static) message(STATUS "Enabled LIBXML_STATIC -static") endif (UTAP_STATIC) +target_coverage(UTAP) set_target_properties(UTAP PROPERTIES CXX_STANDARD_REQUIRED ON CXX_STANDARD_EXTENSIONS OFF POSITION_INDEPENDENT_CODE ON) -if (UTAP_WARNINGS) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(UTAP PUBLIC -Wpedantic -Wall -Wextra -Wconversion -Wno-sign-conversion -Wimplicit-fallthrough) - elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(UTAP PUBLIC -Wpedantic -Wall -Wextra -Wconversion -Wno-sign-conversion -Wimplicit-fallthrough) - elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - target_compile_options(UTAP PUBLIC -Wpedantic -Wall -Wextra -Wimplicit-fallthrough) - elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - target_compile_options(UTAP PUBLIC /Wall) - else () - message(WARNING "Failed to enable warnings for ${CMAKE_CXX_COMPILER_ID}") - endif () -endif () +target_compile_options(UTAP PUBLIC ${UTAP_WARN}) target_include_directories(UTAP PRIVATE # where the library itself will look for its internal headers @@ -75,8 +64,7 @@ target_include_directories(UTAP # where external projects will look for the library's public headers $ ) -target_link_libraries(UTAP PRIVATE LibXml2::LibXml2 ${CMAKE_DL_LIBS} - ${LIBXML_WINLIBS}) +target_link_libraries(UTAP PRIVATE LibXml2::LibXml2 ${CMAKE_DL_LIBS} ${LIBXML_WINLIBS}) add_library(UTAP::UTAP ALIAS UTAP) # in case the library is not exported-imported add_executable(pretty pretty.cpp) From 825f9d7af15dbffaa95cb0d4301f813cd97de8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 11:41:42 +0000 Subject: [PATCH 05/54] Add CLAUDE.md with build/test commands and architecture overview Documents the CMake preset workflow, single-test invocation, and the parser/builder/document architecture for future Claude Code sessions. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..62a01d83 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,136 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +`libutap` (UTAP) is the Uppaal Timed Automata Parser — a C++17 library that parses and type-checks +Uppaal model files (`.xml`, `.xta`, `.ta` formats) into an in-memory AST (`Document`). It is a +dependency of the Uppaal model checker toolchain, licensed under LGPL. + +## Build + +Requires GCC 10+ (or compatible clang), Ninja or GNU make, CMake 3.16+ (presets need 3.28+), +`flex` >= 2.6.4, `bison` >= 3.6.0, and `libxml2` >= 2.6.10. + +```shell +sudo apt-get install g++ ninja-build cmake flex bison libxml2-dev doctest-dev +``` + +Preferred workflow uses CMake presets (`CMakePresets.json` + `cmake/CommonPresets.json`): + +```shell +cmake --workflow --preset default # configure + build + test, Debug, single-config +cmake --workflow --preset debug # multi-config Ninja, Debug +cmake --workflow --preset debug-san # Debug with ASan/UBSan/SSP +cmake --workflow --preset quick # build only, no tests, no clang-tidy (fastest iteration) +``` + +Manual configure/build/test (no presets): + +```shell +cmake . -B build -DCMAKE_INSTALL_PREFIX=$MYPATH -G Ninja +cmake --build build +ctest --test-dir build --output-on-failure +cmake --install build +``` + +Cross-compiling / full dependency bootstrap (fetches libxml2 etc. into `local/`, then +configures, builds, tests, and installs): + +```shell +./compile.sh x86_64-linux # see cmake/toolchain/*.cmake for supported target names +``` + +Key CMake options (`CMakeLists.txt`): `UTAP_TESTS` (default ON), `UTAP_STATIC`, `UTAP_CLANG_TIDY` +(default ON — runs clang-tidy during the build), `UTAP_CCACHE` (default ON). + +## Running tests + +Tests use doctest and are registered individually with ctest (see `test/CMakeLists.txt`), so a +single test binary or case can be targeted directly: + +```shell +ctest --test-dir build-multi --output-on-failure # all tests +ctest --test-dir build-multi -R typechecker_test # one test binary by ctest name +build-multi/Debug/typechecker_test --test-case="*scalar*" # one doctest case, run binary directly +``` + +Test binaries: `expression_test`, `statement_test`, `parser_test`, `featurechecker_test`, +`range_test`, `typechecker_test`, `prettyprint_test`, `example_test`. Several depend on +`MODELS_DIR` (compiled in, points at `test/models/`) and `parser_test` additionally depends on the +`external_fn` shared library (built from `test/external_fn.cpp`) to exercise dynamically-loaded +functions. + +## Coverage + +`coverage-gcovr.sh` and `coverage-lcov.sh` configure/build/test the `coverage` preset (gcov) and +open an HTML report. `coverage-llvm.sh` does the same for the LLVM/clang coverage preset. All +regenerate their build dir (`build-coverage-gcov*`) from scratch via the preset. + +## Code style + +- `.clang-format` enforces formatting (Google-based, 4-space indent, 120 col) — run `clang-format` + before committing. +- `.clang-tidy` enables a specific curated check set (readability/modernize/performance subset); + `UTAP_CLANG_TIDY` runs it as part of the normal build. +- C++17, no compiler-specific extensions (`CMAKE_CXX_EXTENSIONS OFF`). + +## Architecture + +Information flow (see README.md section 5 for the original diagram): + +``` +.xml --> libxml2 (SAX) --> xmlreader.cpp --\ + >--> bison parser (parser.y / lexer.l) --> ParserBuilder +.ta/.xta -----------------------------------/ +``` + +- **Grammar**: `src/parser.y` (bison) and `src/lexer.l` (flex) implement the BNF for both old + (3.x) and new (4.x) syntax, and are used both for direct `.ta`/`.xta` parsing and, reused + block-by-block, for text embedded inside `.xml` documents. Generated parser/lexer land in the + build directory (see `src/CMakeLists.txt` custom commands), not in source. +- **`ParserBuilder`** (`include/utap/builder.hpp`) is the abstract callback interface the grammar + drives — one method per grammar production (types, declarations, statements, expressions, + processes/templates, properties, LSC/SMC/priority extensions). The parser has no knowledge of + how the model is stored; it only calls into whatever `ParserBuilder` implementation it's given. +- **Builder inheritance chain** turns those callbacks into an AST: + `AbstractBuilder` (`ParserBuilder` + shared bookkeeping, e.g. type/name stacks) + → `ExpressionBuilder` (expression trees) + → `StatementBuilder` (statements) + → `DocumentBuilder` (full `Document`: templates, processes, declarations). + `PropertyBuilder`/`TigaPropertyBuilder` (`property.hpp`) similarly subclass `StatementBuilder` + for query/property parsing. `PrettyPrinter` subclasses `AbstractBuilder` directly to regenerate + source text from the same callback stream (used by the `pretty` tool and round-trip tests). +- **`Document`** (`include/utap/document.hpp`) is the resulting AST: templates, `Variable`, + `Location`, `Edge`, `Branchpoint`, processes, and the system declaration. Symbols are `Symbol` + objects (name + `Type`) grouped into `Frame`s representing scopes (`include/utap/symbols.hpp`). + All expressions are `Expression` trees (`include/utap/expression.hpp`); all statements are + `Statement` subclasses with a visitor hierarchy (`StatementVisitor` / + `AbstractStatementVisitor` in `include/utap/statement.hpp`). +- **Post-parse passes** walk the built `Document` via `DocumentVisitor`/`AbstractStatementVisitor`: + `TypeChecker` (`TypeChecker.cpp`, by far the largest source file) does the semantic type checking + and rewriting after the whole document is built; `FeatureChecker` inspects which model features + are in use (e.g. to pick symbolic/stochastic/concrete analysis support, see `SupportedMethods`). +- **`xmlreader.cpp`** bridges libxml2 SAX events to the bison parser for embedded expression/ + statement text inside XML elements; `xmlwriter.cpp` does the reverse (serializing a `Document` + back to Uppaal XML). +- Public headers live under `include/utap/`; the top-level umbrella header is + `include/utap/utap.hpp` (parse entry points operating on `Document&`, e.g. `parse_XML_file`, + `parse_XTA`), while `include/utap/builder.hpp` exposes the lower-level entry points that operate + directly on a `ParserBuilder&` instead of a `Document`. +- Command-line utilities built alongside the library (`src/CMakeLists.txt`): `pretty` (pretty- + printer), `syntaxcheck`, `featurecheck` — thin executables over the corresponding builder/checker + classes, useful for manually exercising a code path end-to-end against a real model file. + +## Repo layout notes + +- `examples/` is a self-contained consumer of the installed/built library, with its own CMake + project and two build paths (`compile-with-getlibs.sh` vendors deps into `examples/local/`, + `compile-with-cmake.sh` fetches deps via CMake) — useful as a template for exercising the public + API in isolation from the main build. +- `cmake/toolchain/` holds CMake toolchain files for cross-compilation targets (Linux/macOS/mingw, + multiple GCC/clang versions); `getlibs.sh` fetches and builds dependencies for a given target + into `local/`. +- CI matrix is defined across `.github/workflows/*.yml` (ubuntu-gcc, ubuntu-clang20, ubuntu-mingw, + darwin-appleclang, darwin-brew-gcc15, nix). From cb7312abeb3a560075ed3ea259c82a2eb81b4598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 11:55:09 +0000 Subject: [PATCH 06/54] Add AbstractBuilder unit tests and fix missing MITL defaults AbstractBuilder gives every ParserBuilder method a "not supported" default except expr_MITL_diamond/expr_MITL_box, which stayed pure virtual and happened to be masked by every existing subclass overriding them. Add the same default for consistency, and add abstractbuilder_test to exercise every inherited method, which takes src/AbstractBuilder.cpp from 2% to 100% line coverage. Co-Authored-By: Claude Sonnet 5 --- include/utap/AbstractBuilder.hpp | 2 + src/AbstractBuilder.cpp | 2 + test/AbstractBuilder_test.cpp | 331 +++++++++++++++++++++++++++++++ test/CMakeLists.txt | 5 + 4 files changed, 340 insertions(+) create mode 100644 test/AbstractBuilder_test.cpp diff --git a/include/utap/AbstractBuilder.hpp b/include/utap/AbstractBuilder.hpp index c0132b1c..5c9acf16 100644 --- a/include/utap/AbstractBuilder.hpp +++ b/include/utap/AbstractBuilder.hpp @@ -224,6 +224,8 @@ class AbstractBuilder : public ParserBuilder void expr_MITL_conj() override; void expr_MITL_next() override; void expr_MITL_atom() override; + void expr_MITL_diamond(int, int) override; + void expr_MITL_box(int, int) override; void expr_optimize(int, int, int, int) override; /************************************************************ diff --git a/src/AbstractBuilder.cpp b/src/AbstractBuilder.cpp index fc70ccdb..3edd46c5 100644 --- a/src/AbstractBuilder.cpp +++ b/src/AbstractBuilder.cpp @@ -224,6 +224,8 @@ void AbstractBuilder::expr_MITL_disj() { UNSUPPORTED; } void AbstractBuilder::expr_MITL_conj() { UNSUPPORTED; } void AbstractBuilder::expr_MITL_next() { UNSUPPORTED; } void AbstractBuilder::expr_MITL_atom() { UNSUPPORTED; } +void AbstractBuilder::expr_MITL_diamond(int, int) { UNSUPPORTED; } +void AbstractBuilder::expr_MITL_box(int, int) { UNSUPPORTED; } void AbstractBuilder::expr_optimize(int, int, int, int) { UNSUPPORTED; } void AbstractBuilder::expr_proba_qualitative(Kind, Kind, double) { UNSUPPORTED; } diff --git a/test/AbstractBuilder_test.cpp b/test/AbstractBuilder_test.cpp new file mode 100644 index 00000000..814fa55d --- /dev/null +++ b/test/AbstractBuilder_test.cpp @@ -0,0 +1,331 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +#include "utap/AbstractBuilder.hpp" + +#include + +#include +#include + +using namespace UTAP; + +namespace { +/// Concrete builder implementing only the members that AbstractBuilder itself +/// leaves pure virtual (inherited straight from ParserBuilder), so that every +/// other method exercises AbstractBuilder's own "not supported" default. +struct MinimalBuilder : AbstractBuilder +{ + void add_position(uint32_t, uint32_t, uint32_t, std::shared_ptr) override {} + void handle_error(const TypeException&) override {} + void handle_warning(const TypeException&) override {} +}; +} // namespace + +TEST_SUITE_BEGIN("AbstractBuilder"); + +TEST_CASE("Default query and no-op methods") +{ + auto b = MinimalBuilder{}; + CHECK_NOTHROW(b.set_position(0, 1)); + CHECK_FALSE(b.is_type("int")); + CHECK_FALSE(b.is_type("")); + CHECK_NOTHROW(b.done()); + CHECK_NOTHROW(b.handle_expect("anything")); +} + +TEST_CASE("Exception carries the throwing function's name") +{ + auto b = MinimalBuilder{}; + try { + b.type_duplicate(); + FAIL("expected NotSupportedException"); + } catch (const NotSupportedException& e) { + CHECK(std::string{e.what()}.find("type_duplicate") != std::string::npos); + } +} + +TEST_CASE("Types") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.type_duplicate(), NotSupportedException); + CHECK_THROWS_AS(b.type_pop(), NotSupportedException); + CHECK_THROWS_AS(b.type_bool(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_int(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_string(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_double(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_bounded_int(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_channel(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_clock(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_void(), NotSupportedException); + CHECK_THROWS_AS(b.type_scalar(TypePrefix::NONE), NotSupportedException); + CHECK_THROWS_AS(b.type_name(TypePrefix::NONE, "T"), NotSupportedException); + CHECK_THROWS_AS(b.type_struct(TypePrefix::NONE, 0u), NotSupportedException); + CHECK_THROWS_AS(b.type_array_of_size(0u), NotSupportedException); + CHECK_THROWS_AS(b.type_array_of_type(0u), NotSupportedException); + CHECK_THROWS_AS(b.struct_field("f"), NotSupportedException); + CHECK_THROWS_AS(b.decl_typedef("T"), NotSupportedException); +} + +TEST_CASE("Variable declarations") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.decl_var("x", false), NotSupportedException); + CHECK_THROWS_AS(b.decl_init_list(0u), NotSupportedException); + CHECK_THROWS_AS(b.decl_field_init("f"), NotSupportedException); +} + +TEST_CASE("Gantt chart declarations") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.gantt_decl_begin("g"), NotSupportedException); + CHECK_THROWS_AS(b.gantt_decl_select("id"), NotSupportedException); + CHECK_THROWS_AS(b.gantt_decl_end(), NotSupportedException); + CHECK_THROWS_AS(b.gantt_entry_begin(), NotSupportedException); + CHECK_THROWS_AS(b.gantt_entry_select("id"), NotSupportedException); + CHECK_THROWS_AS(b.gantt_entry_end(), NotSupportedException); +} + +TEST_CASE("Progress measure declarations") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.decl_progress(false), NotSupportedException); +} + +TEST_CASE("Function declarations") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.decl_parameter("p", false), NotSupportedException); + CHECK_THROWS_AS(b.decl_func_begin("f"), NotSupportedException); + CHECK_THROWS_AS(b.decl_func_end(), NotSupportedException); + CHECK_THROWS_AS(b.dynamic_load_lib("lib"), NotSupportedException); + CHECK_THROWS_AS(b.decl_external_func("f", "a"), NotSupportedException); +} + +TEST_CASE("Process declarations") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.proc_begin("P", true, "", ""), NotSupportedException); + CHECK_THROWS_AS(b.proc_end(), NotSupportedException); + CHECK_THROWS_AS(b.proc_location("L", false, false), NotSupportedException); + CHECK_THROWS_AS(b.proc_location_commit("L"), NotSupportedException); + CHECK_THROWS_AS(b.proc_location_urgent("L"), NotSupportedException); + CHECK_THROWS_AS(b.proc_location_init("L"), NotSupportedException); + CHECK_THROWS_AS(b.proc_branchpoint("B"), NotSupportedException); + CHECK_THROWS_AS(b.proc_edge_begin("L1", "L2", true, ""), NotSupportedException); + CHECK_THROWS_AS(b.proc_edge_end("L1", "L2"), NotSupportedException); + CHECK_THROWS_AS(b.proc_select("id"), NotSupportedException); + CHECK_THROWS_AS(b.proc_guard(), NotSupportedException); + CHECK_THROWS_AS(b.proc_sync(Sync::QUE), NotSupportedException); + CHECK_THROWS_AS(b.proc_update(), NotSupportedException); + CHECK_THROWS_AS(b.proc_prob(), NotSupportedException); +} + +TEST_CASE("Process declarations for LSC") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.proc_instance_line(), NotSupportedException); + CHECK_THROWS_AS(b.instance_name("I", true), NotSupportedException); + CHECK_THROWS_AS(b.instance_name_begin("I"), NotSupportedException); + CHECK_THROWS_AS(b.instance_name_end("I", 0u), NotSupportedException); + CHECK_THROWS_AS(b.proc_message("A", "B", 0, false), NotSupportedException); + CHECK_THROWS_AS(b.proc_message(Sync::QUE), NotSupportedException); + CHECK_THROWS_AS(b.proc_condition(std::vector{}, 0, false, false), NotSupportedException); + CHECK_THROWS_AS(b.proc_condition(), NotSupportedException); + CHECK_THROWS_AS(b.proc_LSC_update("a", 0, false), NotSupportedException); + CHECK_THROWS_AS(b.proc_LSC_update(), NotSupportedException); + CHECK_THROWS_AS(b.prechart_set(false), NotSupportedException); +} + +TEST_CASE("Statements") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.block_begin(), NotSupportedException); + CHECK_THROWS_AS(b.block_end(), NotSupportedException); + CHECK_THROWS_AS(b.empty_statement(), NotSupportedException); + CHECK_THROWS_AS(b.for_begin(), NotSupportedException); + CHECK_THROWS_AS(b.for_end(), NotSupportedException); + CHECK_THROWS_AS(b.iteration_begin("i"), NotSupportedException); + CHECK_THROWS_AS(b.iteration_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.while_begin(), NotSupportedException); + CHECK_THROWS_AS(b.while_end(), NotSupportedException); + CHECK_THROWS_AS(b.do_while_begin(), NotSupportedException); + CHECK_THROWS_AS(b.do_while_end(), NotSupportedException); + CHECK_THROWS_AS(b.if_begin(), NotSupportedException); + CHECK_THROWS_AS(b.if_condition(), NotSupportedException); + CHECK_THROWS_AS(b.if_then(), NotSupportedException); + CHECK_THROWS_AS(b.if_end(false), NotSupportedException); + CHECK_THROWS_AS(b.break_statement(), NotSupportedException); + CHECK_THROWS_AS(b.continue_statement(), NotSupportedException); + CHECK_THROWS_AS(b.switch_begin(), NotSupportedException); + CHECK_THROWS_AS(b.switch_end(), NotSupportedException); + CHECK_THROWS_AS(b.case_begin(), NotSupportedException); + CHECK_THROWS_AS(b.case_end(), NotSupportedException); + CHECK_THROWS_AS(b.default_begin(), NotSupportedException); + CHECK_THROWS_AS(b.default_end(), NotSupportedException); + CHECK_THROWS_AS(b.expr_statement(), NotSupportedException); + CHECK_THROWS_AS(b.return_statement(false), NotSupportedException); + CHECK_THROWS_AS(b.assert_statement(), NotSupportedException); +} + +TEST_CASE("Expressions") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.expr_true(), NotSupportedException); + CHECK_THROWS_AS(b.expr_false(), NotSupportedException); + CHECK_THROWS_AS(b.expr_double(0.0), NotSupportedException); + CHECK_THROWS_AS(b.expr_string("s"), NotSupportedException); + CHECK_THROWS_AS(b.expr_location(), NotSupportedException); + CHECK_THROWS_AS(b.expr_identifier("x"), NotSupportedException); + CHECK_THROWS_AS(b.expr_nat(0), NotSupportedException); + CHECK_THROWS_AS(b.expr_call_begin(), NotSupportedException); + CHECK_THROWS_AS(b.expr_call_end(0u), NotSupportedException); + CHECK_THROWS_AS(b.expr_array(), NotSupportedException); + CHECK_THROWS_AS(b.expr_post_increment(), NotSupportedException); + CHECK_THROWS_AS(b.expr_pre_increment(), NotSupportedException); + CHECK_THROWS_AS(b.expr_post_decrement(), NotSupportedException); + CHECK_THROWS_AS(b.expr_pre_decrement(), NotSupportedException); + CHECK_THROWS_AS(b.expr_assignment(Kind::ASSIGN), NotSupportedException); + CHECK_THROWS_AS(b.expr_unary(Kind::NOT), NotSupportedException); + CHECK_THROWS_AS(b.expr_binary(Kind::PLUS), NotSupportedException); + CHECK_THROWS_AS(b.expr_nary(Kind::LIST, 0u), NotSupportedException); + CHECK_THROWS_AS(b.expr_scenario("S"), NotSupportedException); + CHECK_THROWS_AS(b.expr_ternary(Kind::INLINE_IF, false), NotSupportedException); + CHECK_THROWS_AS(b.expr_inline_if(), NotSupportedException); + CHECK_THROWS_AS(b.expr_comma(), NotSupportedException); + CHECK_THROWS_AS(b.expr_dot("f"), NotSupportedException); + CHECK_THROWS_AS(b.expr_deadlock(), NotSupportedException); + CHECK_THROWS_AS(b.expr_forall_begin("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_forall_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_exists_begin("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_exists_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_sum_begin("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_sum_end("i"), NotSupportedException); +} + +TEST_CASE("SMC and learning extensions") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.expr_proba_qualitative(Kind::PROBA_BOX, Kind::LE, 0.5), NotSupportedException); + CHECK_THROWS_AS(b.expr_proba_quantitative(Kind::PROBA_CMP), NotSupportedException); + CHECK_THROWS_AS(b.expr_proba_compare(Kind::PROBA_BOX, Kind::PROBA_DIAMOND), NotSupportedException); + CHECK_THROWS_AS(b.expr_proba_expected("x"), NotSupportedException); + CHECK_THROWS_AS(b.expr_simulate(1, false, 0), NotSupportedException); + CHECK_THROWS_AS(b.expr_builtin_function1(Kind::SQRT_F), NotSupportedException); + CHECK_THROWS_AS(b.expr_builtin_function2(Kind::POW_F), NotSupportedException); + CHECK_THROWS_AS(b.expr_builtin_function3(Kind::FMA_F), NotSupportedException); + CHECK_THROWS_AS(b.expr_optimize_exp(Kind::MIN_EXP, PriceType::TIME, Kind::LE), NotSupportedException); + CHECK_THROWS_AS(b.expr_load_strategy(), NotSupportedException); + CHECK_THROWS_AS(b.expr_save_strategy("strat"), NotSupportedException); +} + +TEST_CASE("MITL extensions") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.expr_MITL_formula(), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_until(0, 1), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_release(0, 1), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_disj(), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_conj(), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_next(), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_atom(), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_diamond(0, 1), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_box(0, 1), NotSupportedException); + CHECK_THROWS_AS(b.expr_optimize(0, 0, 0, 0), NotSupportedException); +} + +TEST_CASE("System declaration") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.instantiation_begin("P", 0u, "T"), NotSupportedException); + CHECK_THROWS_AS(b.instantiation_end("P", 0u, "T", 0u), NotSupportedException); + CHECK_THROWS_AS(b.process("P"), NotSupportedException); + CHECK_THROWS_AS(b.process_list_end(), NotSupportedException); +} + +TEST_CASE("Properties") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.property(), NotSupportedException); + CHECK_THROWS_AS(b.scenario("S"), NotSupportedException); + CHECK_THROWS_AS(b.parse(""), NotSupportedException); + CHECK_THROWS_AS(b.strategy_declaration("strat"), NotSupportedException); + CHECK_THROWS_AS(b.subjection("s"), NotSupportedException); + CHECK_THROWS_AS(b.imitation("s"), NotSupportedException); +} + +TEST_CASE("Guiding") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.before_update(), NotSupportedException); + CHECK_THROWS_AS(b.after_update(), NotSupportedException); +} + +TEST_CASE("Priority") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.chan_priority_begin(), NotSupportedException); + CHECK_THROWS_AS(b.chan_priority_add('<'), NotSupportedException); + CHECK_THROWS_AS(b.chan_priority_default(), NotSupportedException); + CHECK_THROWS_AS(b.proc_priority_inc(), NotSupportedException); + CHECK_THROWS_AS(b.proc_priority("P"), NotSupportedException); +} + +TEST_CASE("Dynamic templates") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.decl_dynamic_template("T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_spawn(0), NotSupportedException); + CHECK_THROWS_AS(b.expr_exit(), NotSupportedException); + CHECK_THROWS_AS(b.expr_numof(), NotSupportedException); + CHECK_THROWS_AS(b.expr_forall_dynamic_begin("i", "T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_forall_dynamic_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_exists_dynamic_begin("i", "T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_exists_dynamic_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_sum_dynamic_begin("i", "T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_sum_dynamic_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_foreach_dynamic_begin("i", "T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_foreach_dynamic_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_dynamic_process_expr("P"), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_forall_dynamic_begin("i", "T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_forall_dynamic_end("i"), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_exists_dynamic_begin("i", "T"), NotSupportedException); + CHECK_THROWS_AS(b.expr_MITL_exists_dynamic_end("i"), NotSupportedException); +} + +TEST_CASE("Verification queries") +{ + auto b = MinimalBuilder{}; + CHECK_THROWS_AS(b.model_option("k", "v"), NotSupportedException); + CHECK_THROWS_AS(b.query_begin(), NotSupportedException); + CHECK_THROWS_AS(b.query_formula("A[] true", ""), NotSupportedException); + CHECK_THROWS_AS(b.query_comment("c"), NotSupportedException); + CHECK_THROWS_AS(b.query_options("o", ""), NotSupportedException); + CHECK_THROWS_AS(b.expectation_begin(), NotSupportedException); + CHECK_THROWS_AS(b.expectation_end(), NotSupportedException); + CHECK_THROWS_AS(b.expectation_value("r", "t", "v"), NotSupportedException); + CHECK_THROWS_AS(b.expect_resource("t", "v", "u"), NotSupportedException); + CHECK_THROWS_AS(b.query_results_begin(), NotSupportedException); + CHECK_THROWS_AS(b.query_results_end(), NotSupportedException); + CHECK_THROWS_AS(b.query_end(), NotSupportedException); +} + +TEST_SUITE_END(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 98400cec..fe1fbfb7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,10 @@ set(MODELS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/models") set(EXAMPLES_PATH "${PROJECT_SOURCE_DIR}/examples") +add_executable(abstractbuilder_test AbstractBuilder_test.cpp) +target_link_libraries(abstractbuilder_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME abstractbuilder_test COMMAND abstractbuilder_test) + add_executable(expression_test expression_test.cpp) target_link_libraries(expression_test PRIVATE UTAP doctest::doctest_with_main) add_test(NAME expression_test COMMAND expression_test) @@ -47,6 +51,7 @@ add_test(NAME example_train_gate "${EXAMPLES_PATH}/train-gate-out.xml") if(UTAP_STATIC) + target_link_options(abstractbuilder_test PRIVATE -static) target_link_options(expression_test PRIVATE -static) target_link_options(statement_test PRIVATE -static) target_link_options(parser_test PRIVATE -static) From 93833c159918e9faa3fd66679fccc9cbba7779c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 12:33:25 +0000 Subject: [PATCH 07/54] Add PrettyPrinter tests and fix two builder-state leak bugs PrettyPrinter had 0% test coverage because prettyprint_test.cpp only exercises Expression::str()/Document printing, not the PrettyPrinter builder itself (used by the `pretty` CLI tool). Add PrettyPrinter_test.cpp driving it through parse_XML_buffer/parse_property against real and synthetic models, covering types, declarations, structs/arrays/strings, all loop forms, expression operators, template printing (rates, invariants, guards, urgent/committed/branchpoint/select/ sync), and the SMC/MITL/CTL callbacks that only run before their enclosing (unimplemented) property()/expr_MITL_formula() throws. While building the fixtures, found two real bugs: - decl_external_func() was a no-op, so the `param` string accumulated by decl_parameter() (and the external function's return type) leaked into the next process's printed parameter list. - do_while_begin()/do_while_end() were no-ops, silently dropping a do-while loop's body and condition from the output entirely. Co-Authored-By: Claude Sonnet 5 --- src/PrettyPrinter.cpp | 26 ++- test/CMakeLists.txt | 7 + test/PrettyPrinter_test.cpp | 342 ++++++++++++++++++++++++++++++++++++ 3 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 test/PrettyPrinter_test.cpp diff --git a/src/PrettyPrinter.cpp b/src/PrettyPrinter.cpp index 0a4c93a0..16777b95 100644 --- a/src/PrettyPrinter.cpp +++ b/src/PrettyPrinter.cpp @@ -251,7 +251,11 @@ void PrettyPrinter::decl_func_end() void PrettyPrinter::dynamic_load_lib(std::string_view name) {} -void PrettyPrinter::decl_external_func(std::string_view name, std::string_view alias) {} +void PrettyPrinter::decl_external_func(std::string_view name, std::string_view alias) +{ + pop_top(type); // discard the return type pushed for this declaration + param.clear(); // discard the parameters accumulated by decl_parameter() +} void PrettyPrinter::block_begin() { @@ -328,9 +332,25 @@ void PrettyPrinter::while_end() // 1 expr, 1 stat delete s; } -void PrettyPrinter::do_while_begin() {} +void PrettyPrinter::do_while_begin() +{ + level++; + o.push(new std::ostringstream{}); +} + +void PrettyPrinter::do_while_end() +{ + auto expr = pop_back(st); + auto* s = dynamic_cast(o.top()); + o.pop(); -void PrettyPrinter::do_while_end() {} + level--; + indent(); + *o.top() << "do\n" << s->str() << '\n'; + indent(); + *o.top() << "while (" << expr << ");\n"; + delete s; +} void PrettyPrinter::if_begin() { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fe1fbfb7..e45770f9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -44,6 +44,12 @@ target_compile_definitions( target_link_libraries(prettyprint_test PRIVATE UTAP doctest::doctest_with_main) add_test(NAME prettyprint_test COMMAND prettyprint_test) +add_executable(prettyprinter_test PrettyPrinter_test.cpp) +target_compile_definitions( + prettyprinter_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(prettyprinter_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME prettyprinter_test COMMAND prettyprinter_test) + add_executable(example_test example_test.cpp) target_link_libraries(example_test PRIVATE UTAP) add_test(NAME example_train_gate @@ -59,6 +65,7 @@ if(UTAP_STATIC) target_link_options(range_test PRIVATE -static) target_link_options(typechecker_test PRIVATE -static) target_link_options(prettyprint_test PRIVATE -static) + target_link_options(prettyprinter_test PRIVATE -static) target_link_options(example_test PRIVATE -static) message(STATUS "Enabled Unit Tests -static") endif(UTAP_STATIC) diff --git a/test/PrettyPrinter_test.cpp b/test/PrettyPrinter_test.cpp new file mode 100644 index 00000000..13558705 --- /dev/null +++ b/test/PrettyPrinter_test.cpp @@ -0,0 +1,342 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +/** + * PrettyPrinter is a ParserBuilder that reconstructs UPPAAL source text + * straight from the parser's callback stream (types, declarations, + * statements, expressions, templates, system declaration and queries), + * as used by the `pretty` command-line tool. This is a different code + * path from Expression::str()/Document printing (see prettyprint_test.cpp), + * so it needs its own model-driven tests. + */ + +#include "document_fixture.h" + +#include "utap/AbstractBuilder.hpp" +#include "utap/PrettyPrinter.hpp" +#include "utap/utap.hpp" + +#include + +#include + +using namespace UTAP; + +TEST_SUITE_BEGIN("PrettyPrinter"); + +TEST_CASE("Template with locations, rates, invariants and guarded edges") +{ + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(read_content("simpleSystem.xml").c_str(), pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"clock c;"}); + CHECK(text == Contains{"process Template()"}); + CHECK(text == Contains{"L3 { ; 1},"}); + CHECK(text == Contains{"First {(c < 2) ; 1};"}); + CHECK(text == Contains{"init First;"}); + CHECK(text == Contains{"First -> L2 {"}); + CHECK(text == Contains{"guard (c > 1);"}); + CHECK(text == Contains{"Process = Template();"}); + CHECK(text == Contains{"Process2 = Template();"}); + CHECK(text == Contains{"system Process, Process2;"}); + CHECK(text == Contains{"/** Query begin: */"}); + CHECK(text == Contains{"/** Query end. */"}); +} + +TEST_CASE("Function declaration with if/else and return statements") +{ + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(read_content("if_statement.xml").c_str(), pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"int some_fun(bool x)"}); + CHECK(text == Contains{"if (x)"}); + CHECK(text == Contains{"return 1;"}); + CHECK(text == Contains{"else"}); +} + +TEST_CASE("Const/typedef declarations, arithmetic and non-empty query text") +{ + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(read_content("powers.xml").c_str(), pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"const int C = (A + B);"}); + CHECK(text == Contains{"const int ISIZE = (IBASE ** IPOWER);"}); + CHECK(text == Contains{"typedef int[0,(ISIZE - 1)] range_t;"}); + CHECK(text == Contains{"range_t ii = (3 ** 3);"}); + CHECK(text == Contains{"/* Formula: A[] not deadlock */"}); + CHECK(text == Contains{"/* Comment: Simple query */"}); +} + +TEST_CASE("Struct, array, string, loops and expression operators in a function body") +{ + static constexpr auto model = R"xml( + + + struct { int x; int y; } point = {1, 2}; +int arr[3] = {1, 2, 3}; +string label = "hello"; + +int compute2(int v) { return v; } + +int compute(int a, int b) { + int total = 0; + int i; + int result; + int x2, y2; + for (i = 0, x2 = 0; i < 3; i++) { + total += arr[i]; + } + for (i : int[0,2]) { + if (i == 1) { + continue; + } + total = total + i; + } + while (total > 1000) { + total--; + break; + } + result = (a > b) ? a : b; + y2 = b; + point.x = a; + if (forall (k : int[0,2]) arr[k] >= 0) { + result = result + compute2(result); + } + ++total; + --x2; + return result + point.y; +} + + system T; + + + + + + + + +)xml"; + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(model, pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"struct {"}); + CHECK(text == Contains{"} point = { 1, 2 };"}); + CHECK(text == Contains{"int arr[3] = { 1, 2, 3 };"}); + CHECK(text == Contains{"string label = \"hello\";"}); + CHECK(text == Contains{"for ( (i = 0), (x2 = 0); (i < 3); i++)"}); + CHECK(text == Contains{"for ( i : int[0,2] )"}); + CHECK(text == Contains{"continue;"}); + CHECK(text == Contains{"while ((total > 1000))"}); + CHECK(text == Contains{"break;"}); + CHECK(text == Contains{"(total += arr[i]);"}); + CHECK(text == Contains{"total--;"}); + CHECK(text == Contains{"(result = (a > b) ? a : b);"}); + CHECK(text == Contains{"(point.x = a);"}); + CHECK(text == Contains{"if (forall (k:int[0,2]) (arr[k] >= 0))"}); + CHECK(text == Contains{"(result = (result + compute2(result)));"}); + CHECK(text == Contains{"++total;"}); + CHECK(text == Contains{"--x2;"}); +} + +TEST_CASE("Urgent/committed locations, branchpoints, select and channel sync") +{ + static constexpr auto model = R"xml( + + + urgent chan a; +broadcast chan b; +clock c; + + system T; + + + + + + + + +)xml"; + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(model, pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"urgent chan a;"}); + CHECK(text == Contains{"broadcast chan b;"}); + CHECK(text == Contains{"branchpoint _id3;"}); + CHECK(text == Contains{"commit Comm;"}); + CHECK(text == Contains{"urgent Urg;"}); + CHECK(text == Contains{"select i:int[0,2];"}); + CHECK(text == Contains{"guard (c >= i);"}); + CHECK(text == Contains{"sync a!;"}); + CHECK(text == Contains{"assign (c = 0);"}); + CHECK(text == Contains{"sync b?;"}); +} + +TEST_CASE("External function import does not leak its parameters into the next process") +{ + // Regression test: decl_external_func() used to be a no-op, leaving the + // `param` string accumulated by decl_parameter() (and the return type + // pushed for the import) to leak into the next proc_begin()/decl_func_begin(). + static constexpr auto model = R"xml( + + + import "libexternal.so" { + int extfun(int v); +}; + + system T; + + + + + + + + +)xml"; + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(model, pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"process T()\n"}); + CHECK_FALSE(text == Contains{"extfun"}); + CHECK_FALSE(text == Contains{"process T(int v)"}); +} + +TEST_CASE("do-while loop prints its body and condition") +{ + // Regression test: do_while_begin()/do_while_end() used to be no-ops, + // silently dropping the loop body and condition from the output. + static constexpr auto model = R"xml( + + + int compute(int total) { + do { + total--; + } while (total > 0); + return total; +} + + system T; + + + + + + + + +)xml"; + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + parse_XML_buffer(model, pretty, true); + const auto text = os.str(); + CHECK(text == Contains{"do\n"}); + CHECK(text == Contains{"total--;"}); + CHECK(text == Contains{"while ((total > 0));"}); +} + +TEST_CASE("SMC probability and simulate query expressions execute before the unsupported property() call") +{ + // PrettyPrinter never overrides property(), so any parsed property + // ultimately throws NotSupportedException there -- but the SMC-specific + // expression callbacks (expr_proba_quantitative, expr_simulate, + // expr_MITL_diamond/box) run to completion first, since the grammar + // reduces them before the enclosing PropertyExpr rule calls property(). + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + CHECK_THROWS_AS(parse_property("Pr[<=10](<> true)", pretty), NotSupportedException); + CHECK_THROWS_AS(parse_property("simulate[<=10;100]{true}", pretty), NotSupportedException); + // expr_MITL_diamond/box run before the outer expr_MITL_formula() call, + // which PrettyPrinter also does not implement. + CHECK_THROWS_AS(parse_property("Pr (<>[0,5] true)", pretty), NotSupportedException); + CHECK_THROWS_AS(parse_property("Pr ([][0,5] true)", pretty), NotSupportedException); +} + +TEST_CASE("Deadlock and process.location expressions execute before an unsupported CTL operator") +{ + // PrettyPrinter's expr_unary() only handles a handful of operators and + // throws TypeException{"Invalid operator"} for CTL quantifiers such as + // EF, but the inner expression is fully evaluated first. + auto os = std::ostringstream{}; + auto pretty = PrettyPrinter{os}; + CHECK_THROWS_AS(parse_property("E<> deadlock", pretty), TypeException); + CHECK_THROWS_AS(parse_property("E<> Process.location", pretty), TypeException); +} + +TEST_SUITE_END(); From 4113232bb89285d5cbf74cf09b6cb7fff551f517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 13:09:46 +0000 Subject: [PATCH 08/54] Add tests for typeexception.cpp and print.hpp, fixing 0% coverage typeexception.cpp's 11 TypeException factory functions had no direct tests. print.hpp (print_infix/infix) is unused elsewhere except one call site in DocumentBuilder.cpp, and being header-only its templates need their own test binary instrumented for coverage to show at all -- add target_coverage(print_test) since only the UTAP library target was instrumented before. Co-Authored-By: Claude Sonnet 5 --- test/CMakeLists.txt | 15 ++++++ test/print_test.cpp | 103 ++++++++++++++++++++++++++++++++++++ test/typeexception_test.cpp | 87 ++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 test/print_test.cpp create mode 100644 test/typeexception_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e45770f9..dc3b8b08 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -50,6 +50,19 @@ target_compile_definitions( target_link_libraries(prettyprinter_test PRIVATE UTAP doctest::doctest_with_main) add_test(NAME prettyprinter_test COMMAND prettyprinter_test) +add_executable(typeexception_test typeexception_test.cpp) +target_link_libraries(typeexception_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME typeexception_test COMMAND typeexception_test) + +add_executable(print_test print_test.cpp) +target_include_directories(print_test PRIVATE "${PROJECT_SOURCE_DIR}/src") +target_link_libraries(print_test PRIVATE UTAP doctest::doctest_with_main) +# print.hpp is header-only, so its templates are instantiated directly in +# this test binary rather than inside the already-instrumented UTAP library; +# it needs its own coverage instrumentation to be measured. +target_coverage(print_test) +add_test(NAME print_test COMMAND print_test) + add_executable(example_test example_test.cpp) target_link_libraries(example_test PRIVATE UTAP) add_test(NAME example_train_gate @@ -66,6 +79,8 @@ if(UTAP_STATIC) target_link_options(typechecker_test PRIVATE -static) target_link_options(prettyprint_test PRIVATE -static) target_link_options(prettyprinter_test PRIVATE -static) + target_link_options(typeexception_test PRIVATE -static) + target_link_options(print_test PRIVATE -static) target_link_options(example_test PRIVATE -static) message(STATUS "Enabled Unit Tests -static") endif(UTAP_STATIC) diff --git a/test/print_test.cpp b/test/print_test.cpp new file mode 100644 index 00000000..9cac2183 --- /dev/null +++ b/test/print_test.cpp @@ -0,0 +1,103 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +#include "print.hpp" + +#include + +#include +#include + +TEST_SUITE_BEGIN("print.hpp"); + +TEST_CASE("print_infix with default delimiter") +{ + auto os = std::ostringstream{}; + auto v = std::vector{1, 2, 3}; + print_infix(os, v); + CHECK(os.str() == "1,2,3"); +} + +TEST_CASE("print_infix with custom delimiter") +{ + auto os = std::ostringstream{}; + auto v = std::vector{1, 2, 3}; + print_infix(os, v, "; "); + CHECK(os.str() == "1; 2; 3"); +} + +TEST_CASE("print_infix on an empty view prints nothing") +{ + auto os = std::ostringstream{}; + auto v = std::vector{}; + print_infix(os, v); + CHECK(os.str().empty()); +} + +TEST_CASE("print_infix on a single-element view prints no delimiter") +{ + auto os = std::ostringstream{}; + auto v = std::vector{42}; + print_infix(os, v); + CHECK(os.str() == "42"); +} + +TEST_CASE("print_infix_p with a custom element printer") +{ + auto os = std::ostringstream{}; + auto v = std::vector{1, 2, 3}; + print_infix_p(os, v, [](std::ostream& out, int x) { out << '<' << x << '>'; }); + CHECK(os.str() == "<1>,<2>,<3>"); +} + +TEST_CASE("print_infix_p with a custom element printer and delimiter") +{ + auto os = std::ostringstream{}; + auto v = std::vector{1, 2, 3}; + print_infix_p( + os, v, [](std::ostream& out, int x) { out << '<' << x << '>'; }, ""); + CHECK(os.str() == "<1><2><3>"); +} + +TEST_CASE("infix helper with default delimiter") +{ + auto os = std::ostringstream{}; + auto v = std::vector{1, 2, 3}; + os << infix(v); + CHECK(os.str() == "1,2,3"); +} + +TEST_CASE("infix helper with custom delimiter") +{ + auto os = std::ostringstream{}; + auto v = std::vector{1, 2, 3}; + os << infix(v, " - "); + CHECK(os.str() == "1 - 2 - 3"); +} + +TEST_CASE("infix helper with an initializer_list") +{ + auto os = std::ostringstream{}; + os << infix{{1, 2, 3}}; + CHECK(os.str() == "1,2,3"); +} + +TEST_SUITE_END(); diff --git a/test/typeexception_test.cpp b/test/typeexception_test.cpp new file mode 100644 index 00000000..1c43d229 --- /dev/null +++ b/test/typeexception_test.cpp @@ -0,0 +1,87 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +#include "utap/builder.hpp" + +#include + +#include + +using namespace UTAP; + +TEST_SUITE_BEGIN("TypeException factory functions"); + +TEST_CASE("unknown_identifier_error") +{ + CHECK(std::string{unknown_identifier_error("foo").what()} == "$Unknown_identifier: foo"); +} + +TEST_CASE("has_no_such_member_error") +{ + CHECK(std::string{has_no_such_member_error("foo").what()} == "$has_no_member_named foo"); +} + +TEST_CASE("is_not_a_struct_error") +{ + CHECK(std::string{is_not_a_struct_error("foo").what()} == "foo $is_not_a_structure"); +} + +TEST_CASE("duplicate_definition_error") +{ + CHECK(std::string{duplicate_definition_error("foo").what()} == "$Duplicate_definition_of foo"); +} + +TEST_CASE("invalid_type_error") +{ + CHECK(std::string{invalid_type_error("foo").what()} == "$Invalid_type foo"); +} + +TEST_CASE("no_such_process_error") +{ + CHECK(std::string{no_such_process_error("foo").what()} == "$No_such_process: foo"); +} + +TEST_CASE("not_a_template_error") +{ + CHECK(std::string{not_a_template_error("foo").what()} == "$Not_a_template: foo"); +} + +TEST_CASE("not_a_process_error") +{ + CHECK(std::string{not_a_process_error("foo").what()} == "foo $is_not_a_process"); +} + +TEST_CASE("strategy_not_declared_error") +{ + CHECK(std::string{strategy_not_declared_error("foo").what()} == "$strategy_not_declared: foo"); +} + +TEST_CASE("unknown_dynamic_template_error") +{ + CHECK(std::string{unknown_dynamic_template_error("foo").what()} == "Unknown dynamic template foo"); +} + +TEST_CASE("shadows_a_variable_warning") +{ + CHECK(std::string{shadows_a_variable_warning("foo").what()} == "foo $shadows_a_variable"); +} + +TEST_SUITE_END(); From 9f9c531a8e0dbe993da537eb8253570b39170e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 13:10:05 +0000 Subject: [PATCH 09/54] Fix flex scan buffer leak when a ParserBuilder throws during parsing parse_XTA/parse_property called scan buffer -> parse -> delete buffer sequentially, so an exception thrown by the ParserBuilder mid-parse (an explicitly documented way to report errors, see builder.hpp) skipped the delete and leaked the flex buffer. Surfaced by AddressSanitizer once PrettyPrinter_test.cpp added the first tests that parse through a builder expected to throw. Fix with a small RAII guard so the buffer is always freed, even on the exception path. Co-Authored-By: Claude Sonnet 5 --- src/parser.y | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/parser.y b/src/parser.y index d8bed8be..f7bf1e86 100644 --- a/src/parser.y +++ b/src/parser.y @@ -2079,6 +2079,14 @@ static int32_t builder_parse_property(ParserBuilder& aParserBuilder, std::string return (utap_parse() != 0) ? -1 : 0; } +/** Deletes the current flex scan buffer on scope exit, even if parsing + * throws (a ParserBuilder is explicitly allowed to report errors by + * throwing, see builder.hpp), so the buffer is never leaked. */ +struct ScanBufferGuard +{ + ~ScanBufferGuard() { utap__delete_buffer(YY_CURRENT_BUFFER); } +}; + namespace UTAP { const char* utap_builtin_declarations() { @@ -2121,9 +2129,8 @@ int32_t parse_XTA(const char *str, ParserBuilder& builder, bool newxta, XTAPart part, std::string_view xpath) { utap__scan_string(str); - int32_t res = builder_parse_XTA(builder, newxta, part, xpath); - utap__delete_buffer(YY_CURRENT_BUFFER); - return res; + auto guard = ScanBufferGuard{}; + return builder_parse_XTA(builder, newxta, part, xpath); } int32_t parse_XTA(const char *str, ParserBuilder& builder, bool newxta) @@ -2138,25 +2145,22 @@ int32_t parse_XTA(FILE *file, ParserBuilder& builder, bool newxta) if (newxta) parse_XTA(utap_builtin_declarations(), builder, newxta, XTAPart::DECLARATION, ""); utap__switch_to_buffer(utap__create_buffer(file, YY_BUF_SIZE)); - int res = builder_parse_XTA(builder, newxta, XTAPart::XTA, ""); - utap__delete_buffer(YY_CURRENT_BUFFER); - return res; + auto guard = ScanBufferGuard{}; + return builder_parse_XTA(builder, newxta, XTAPart::XTA, ""); } int32_t parse_property(const char *str, ParserBuilder& aParserBuilder, const std::string& xpath) { utap__scan_string(str); - int32_t res = builder_parse_property(aParserBuilder, xpath); - utap__delete_buffer(YY_CURRENT_BUFFER); - return res; + auto guard = ScanBufferGuard{}; + return builder_parse_property(aParserBuilder, xpath); } int32_t parse_property(FILE *file, ParserBuilder& aParserBuilder) { utap__switch_to_buffer(utap__create_buffer(file, YY_BUF_SIZE)); - int32_t res = builder_parse_property(aParserBuilder, ""); - utap__delete_buffer(YY_CURRENT_BUFFER); - return res; + auto guard = ScanBufferGuard{}; + return builder_parse_property(aParserBuilder, ""); } } // namespace UTAP \ No newline at end of file From df818562b76340b7107d895da9d21184ecc45cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 13:16:14 +0000 Subject: [PATCH 10/54] Move StatementBuilder's if_begin/if_condition/if_then out of the header These were the only StatementBuilder methods defined inline in the header instead of in StatementBuilder.cpp like the rest of the class. Being trivial but virtual, the compiler emits weak-linkage copies in every including translation unit; gcov's coverage data ends up split across the copies the linker discards, showing 0% even though the methods run on every if-statement. Moving them to the .cpp (matching the class's own convention) fixes the attribution and gets them to 100% via the existing if_statement.xml parsing test. Co-Authored-By: Claude Sonnet 5 --- include/utap/StatementBuilder.hpp | 6 +++--- src/StatementBuilder.cpp | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/utap/StatementBuilder.hpp b/include/utap/StatementBuilder.hpp index c2c8e897..b0de8790 100644 --- a/include/utap/StatementBuilder.hpp +++ b/include/utap/StatementBuilder.hpp @@ -95,9 +95,9 @@ class StatementBuilder : public ExpressionBuilder void while_end() override; void do_while_begin() override; void do_while_end() override; - void if_begin() override {}; - void if_condition() override {}; - void if_then() override {}; + void if_begin() override; + void if_condition() override; + void if_then() override; void if_end(bool) override; void expr_statement() override; void return_statement(bool) override; diff --git a/src/StatementBuilder.cpp b/src/StatementBuilder.cpp index 4f6b8972..4484b5a8 100644 --- a/src/StatementBuilder.cpp +++ b/src/StatementBuilder.cpp @@ -536,7 +536,7 @@ void StatementBuilder::iteration_begin(std::string_view name) get_block().push(std::make_unique(variable->uid, frames.top(), nullptr)); } -void StatementBuilder::iteration_end(std::string_view name) +void StatementBuilder::iteration_end(std::string_view) { // Retrieve the statement that we iterate over. auto statement = get_block().pop(); @@ -565,6 +565,12 @@ void StatementBuilder::do_while_end() get_block().push(std::make_unique(std::move(substat), fragments.pop())); } +void StatementBuilder::if_begin() {} + +void StatementBuilder::if_condition() {} + +void StatementBuilder::if_then() {} + void StatementBuilder::if_end(bool elsePart) { // 1 expr, 1 or 2 statements std::unique_ptr falseCase; From e173adad3ff08d95a8d7a29d34c69c19e9f4ac35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 13:45:16 +0000 Subject: [PATCH 11/54] Fix parse_expression: declared with a never-defined type, never implemented utap.hpp declared `expression_t parse_expression(const char*, Document&, bool)`, but expression_t was only ever forward-declared (symbols.hpp) and never defined, and no .cpp anywhere implemented parse_expression -- any caller would hit a linker error. Meanwhile TypeChecker.cpp had a fully working parseExpression() (camelCase) doing the real work, just never declared in any public header. This looks like a half-finished rename. Fix by renaming the working implementation to parse_expression and declaring it with its actual return type (Expression), and drop the now-unused expression_t forward declaration. Co-Authored-By: Claude Sonnet 5 --- include/utap/symbols.hpp | 1 - include/utap/utap.hpp | 2 +- src/TypeChecker.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/utap/symbols.hpp b/include/utap/symbols.hpp index 13a12d39..4ab89612 100644 --- a/include/utap/symbols.hpp +++ b/include/utap/symbols.hpp @@ -33,7 +33,6 @@ namespace UTAP { class Frame; -class expression_t; class NoParentException : public std::exception {}; diff --git a/include/utap/utap.hpp b/include/utap/utap.hpp index c7a0e4bb..022abcad 100644 --- a/include/utap/utap.hpp +++ b/include/utap/utap.hpp @@ -41,7 +41,7 @@ int32_t parse_XML_buffer(const char* buffer, Document&, bool newxta, int32_t parse_XML_file(const std::filesystem::path&, Document&, bool newxta, const std::vector& libpaths = {}); int32_t parse_XML_fd(int fd, Document&, bool newxta, const std::vector& libpaths = {}); -expression_t parse_expression(const char* buffer, Document&, bool); +Expression parse_expression(const char* buffer, Document&, bool); int32_t write_XML_file(const char* filename, Document& doc); /** returns a string representation of built-in types and constants (see parser.y) */ diff --git a/src/TypeChecker.cpp b/src/TypeChecker.cpp index ffed12ab..c7763edc 100644 --- a/src/TypeChecker.cpp +++ b/src/TypeChecker.cpp @@ -2705,7 +2705,7 @@ int32_t parse_XML_fd(int fd, Document& doc, bool newxta, const std::vector Date: Wed, 8 Jul 2026 13:45:30 +0000 Subject: [PATCH 12/54] Add TypeChecker.cpp coverage tests for statement forms, type-prefix errors, expression type errors, and Document-based parse entry points Adds 20 test cases found by working empirically: TypeChecker only runs when the document has no earlier (parser/builder-level) errors, so many error-message factory functions need a document that is otherwise clean but semantically wrong in one specific way. Covers: - assert/empty/for/range-iteration/do-while statement visitors, none of which were exercised by any existing test - 5 type-prefix errors (meta/const/urgent/broadcast misuse), reachable only through a typedef'd clock since the grammar has no direct `TypePrefix T_CLOCK` production - 10 expression-level type errors (invalid assignment, wrong argument count, unknown struct field, etc.) - parse_XTA(const char*), parse_XTA(FILE*), parse_XML_fd, and the newly-fixed parse_expression, none of which any existing test called Co-Authored-By: Claude Sonnet 5 --- test/typechecker_test.cpp | 281 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) diff --git a/test/typechecker_test.cpp b/test/typechecker_test.cpp index c05f08da..4b14500d 100644 --- a/test/typechecker_test.cpp +++ b/test/typechecker_test.cpp @@ -23,6 +23,10 @@ #include +#include +#include +#include + TEST_SUITE("Quantifier sum") { TEST_CASE("sum expression") @@ -528,3 +532,280 @@ TEST_CASE("Nested structs") const auto& warns = doc.get_warnings(); CHECK_MESSAGE(warns.empty(), warns.front().msg); } + +TEST_SUITE("Statement forms") +{ + TEST_CASE("assert, empty, for, iteration and do-while statements") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() {" + " int i;" + " int total = 0;" + " ;" // empty statement + " assert(true);" + " for (i = 0; i < 3; i++) { total += i; }" + " for (i : int[0,2]) { total += i; }" + " do { total--; } while (total > 0);" + "}") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto& warns = doc.get_warnings(); + CHECK_MESSAGE(warns.empty(), warns.front().msg); + } +} + +TEST_SUITE("Type prefix errors") +{ + TEST_CASE("meta not allowed for clocks") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("typedef clock ClockT; meta ClockT c;") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Prefix_meta_not_allowed_for_clocks"); + } + + TEST_CASE("const not allowed for clocks") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("typedef clock ClockT; const ClockT c = 0;") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Prefix_const_not_allowed_for_clocks"); + } + + TEST_CASE("urgent only allowed for locations and channels") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("typedef clock ClockT; urgent ClockT c;") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Prefix_urgent_only_allowed_for_locations_and_channels"); + } + + TEST_CASE("broadcast only allowed for channels") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("typedef clock ClockT; broadcast ClockT c;") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Prefix_broadcast_only_allowed_for_channels"); + } + + TEST_CASE("type cannot be declared const or meta") + { + auto doc = document_fixture{}.add_default_process().add_global_decl("meta chan c;").parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Type_cannot_be_declared_const_or_meta"); + } +} + +TEST_SUITE("Expression type errors") +{ + TEST_CASE("invalid assignment expression") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("chan c; void f() { c; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Invalid_assignment_expression"); + } + + TEST_CASE("boolean expected in if-condition") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("clock c; void f() { if (c) {} }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Boolean_expected"); + } + + TEST_CASE("invalid return type") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("clock clock_returning_function() { clock c; return c; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Invalid_return_type"); + } + + TEST_CASE("increment only integers and cost") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() { double d; d += 1; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Increment_can_only_be_used_for_integers_and_cost_variables"); + } + + TEST_CASE("non-integer types must use regular assignment") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() { double d; d -= 1; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Non-integer_types_must_use_regular_assignment_operator"); + } + + TEST_CASE("integer expected for increment/decrement") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() { double d; d++; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Integer_expected"); + } + + TEST_CASE("array expected") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() { int x; x[0] = 1; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Array_expected"); + } + + TEST_CASE("wrong number of arguments, too many") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("int f(int a) { return a; } void g() { f(1,2); }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Wrong_number_of_arguments"); + } + + TEST_CASE("wrong number of arguments, too few") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("int f(int a, int b) { return a; } void g() { int x = f(1); }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Wrong_number_of_arguments"); + } + + TEST_CASE("unknown field name and incomplete initialiser") + { + auto doc = + document_fixture{}.add_default_process().add_global_decl("struct { int x; } s = {y: 1};").parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 2); + CHECK(errs[0].msg == "$Unknown_field_name"); + CHECK(errs[1].msg == "$Incomplete_initialiser"); + } + + TEST_CASE("first argument of inline if must be an integer") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("int x = (1.5) ? 1 : 2;") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$First_argument_of_inline_if_must_be_an_integer"); + } +} + +TEST_SUITE("Document-based parse entry points") +{ + TEST_CASE("parse_XTA from a buffer") + { + static constexpr auto xta = R"XTA( +clock c; +process Template() +{ + state L0; + init L0; +} +Process = Template(); +system Process; +)XTA"; + auto doc = UTAP::Document{}; + CHECK(parse_XTA(xta, doc, true)); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } + + TEST_CASE("parse_XTA from a FILE*") + { + static constexpr auto xta = R"XTA( +clock c; +process Template() +{ + state L0; + init L0; +} +Process = Template(); +system Process; +)XTA"; + auto path = std::filesystem::temp_directory_path() / "utap_typechecker_test.xta"; + { + auto ofs = std::ofstream{path}; + ofs << xta; + } + auto* file = std::fopen(path.string().c_str(), "r"); + REQUIRE(file != nullptr); + auto doc = UTAP::Document{}; + auto ok = parse_XTA(file, doc, true); + std::fclose(file); + std::filesystem::remove(path); + CHECK(ok); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } + + TEST_CASE("parse_XML_fd from a file descriptor") + { + auto path = std::filesystem::temp_directory_path() / "utap_typechecker_test.xml"; + { + auto ofs = std::ofstream{path}; + ofs << document_fixture{}.add_default_process().str(); + } + auto* file = std::fopen(path.string().c_str(), "r"); + REQUIRE(file != nullptr); + auto doc = UTAP::Document{}; + auto res = parse_XML_fd(fileno(file), doc, true); + std::fclose(file); + std::filesystem::remove(path); + CHECK(res == 0); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } + + TEST_CASE("parse_expression parses and type-checks a bare expression") + { + auto doc = UTAP::Document{}; + auto expr = parse_expression("1+1", doc, true); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + CHECK(expr.get_kind() == UTAP::Kind::PLUS); + } +} From 75c4b7dd5c22ca8d13b92adba9c651c0a71bfff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 13:54:24 +0000 Subject: [PATCH 13/54] Add 4 more TypeChecker.cpp coverage tests Struct initializer errors (multiple initialisers for a field, too many elements) and the dynamic-template spawn checks (spawning a declared- but-undefined template, and doing so outside an edge update, both of which fire together from a single spawn-in-a-function-body call). Co-Authored-By: Claude Sonnet 5 --- test/typechecker_test.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/typechecker_test.cpp b/test/typechecker_test.cpp index 4b14500d..d159d333 100644 --- a/test/typechecker_test.cpp +++ b/test/typechecker_test.cpp @@ -732,6 +732,41 @@ TEST_SUITE("Expression type errors") REQUIRE(errs.size() == 1); CHECK(errs[0].msg == "$First_argument_of_inline_if_must_be_an_integer"); } + + TEST_CASE("multiple initialisers for the same field") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("struct { int x; } s = {x: 1, x: 2};") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Multiple_initialisers_for_field"); + } + + TEST_CASE("too many elements in initialiser") + { + auto doc = + document_fixture{}.add_default_process().add_global_decl("struct { int x; } s = {1, 2};").parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Too_many_elements_in_initialiser"); + } + + TEST_CASE("spawn outside an edge, on a declared-but-undefined dynamic template") + { + // Exercises both template_only_declared_and_undefined (the + // template has no body) and dynamic_constructs_supported_only_on_edges + // (spawn is used in a function body, not on an edge update). + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("dynamic Child(); void f() { int x = spawn Child(); }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 2); + CHECK(errs[0].msg == "$Template_is_only_declared_and_not_defined"); + CHECK(errs[1].msg == "$Dynamic_constructs_supported_only_on_edges"); + } } TEST_SUITE("Document-based parse entry points") From 1d2e4d8f974b3a1dcccbc700b4a45fc219a44e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 14:12:49 +0000 Subject: [PATCH 14/54] Add SMC/probability query type-check tests to TypeChecker.cpp coverage E[...](min:...), Pr[...](<> ...), probability comparison, quantitative threshold comparison and simulate[...] queries, parsed via parse_property and type-checked through the QueryBuilder pattern (document_fixture.h). These exercise checkNrOfRuns, checkBoundTypeOrBoundedExpr, checkBound, checkAggregationOp, checkMonitoredExpr, checkPredicate, checkUntilCond, checkPathQuant and checkProbBound, none of which were reachable from the document_fixture- only tests added so far since they only run inside a property/query, not a plain document. Co-Authored-By: Claude Sonnet 5 --- test/typechecker_test.cpp | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/test/typechecker_test.cpp b/test/typechecker_test.cpp index d159d333..f80ac295 100644 --- a/test/typechecker_test.cpp +++ b/test/typechecker_test.cpp @@ -844,3 +844,72 @@ system Process; CHECK(expr.get_kind() == UTAP::Kind::PLUS); } } + +TEST_SUITE("SMC/probability property checks") +{ + // These SMC query forms exercise TypeChecker's PROBA_EXP/PROBA_BOX/ + // PROBA_DIAMOND/PROBA_CMP handling (checkNrOfRuns, checkBoundTypeOrBoundedExpr, + // checkBound, checkAggregationOp, checkMonitoredExpr, checkPredicate, + // checkUntilCond, checkPathQuant, checkProbBound), none of which any + // existing test reached. + static UTAP::Document make_doc() + { + return document_fixture{}.add_default_process().add_global_decl("clock c; bool b;").parse(); + } + + TEST_CASE("expected value query (min aggregation)") + { + auto doc = make_doc(); + auto qb = QueryBuilder{doc}; + auto res = parse_property("E[<=10;100] (min: c)", qb); + REQUIRE(res == 0); + qb.typecheck(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } + + TEST_CASE("probability of eventually reaching a predicate") + { + auto doc = make_doc(); + auto qb = QueryBuilder{doc}; + auto res = parse_property("Pr[<=10;100](<> b)", qb); + REQUIRE(res == 0); + qb.typecheck(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } + + TEST_CASE("probability comparison between two runs") + { + auto doc = make_doc(); + auto qb = QueryBuilder{doc}; + auto res = parse_property("Pr[<=10](<> b) >= Pr[<=10](<> b)", qb); + REQUIRE(res == 0); + qb.typecheck(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } + + TEST_CASE("probability quantitative comparison against a threshold") + { + auto doc = make_doc(); + auto qb = QueryBuilder{doc}; + auto res = parse_property("Pr[<=10;100](<> b) <= 0.5", qb); + REQUIRE(res == 0); + qb.typecheck(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Explicit_number_of_runs_is_not_supported_here"); + } + + TEST_CASE("simulate query") + { + auto doc = make_doc(); + auto qb = QueryBuilder{doc}; + auto res = parse_property("simulate[<=10;100] {b}", qb); + REQUIRE(res == 0); + qb.typecheck(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } +} From de05b1d6dd5ef27c4ba116ced3e461f1e856caad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 14:20:30 +0000 Subject: [PATCH 15/54] Add 8 more TypeChecker.cpp coverage tests Edge-level checks (guard must be side-effect free, sync must be a channel, both needing a custom template with a real transition since document_fixture's default template has none), iteration-variable type checks, array-initialiser field-name misuse, sum-expression body type checks, and progress-measure guard/measure type checks (built via a raw XML document since document_fixture::add_system_decl() inserts text before the `system X;` line, but progress must follow it per the grammar: System: SysDecl Progress GanttDecl). Co-Authored-By: Claude Sonnet 5 --- test/typechecker_test.cpp | 121 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/test/typechecker_test.cpp b/test/typechecker_test.cpp index f80ac295..f7a03da2 100644 --- a/test/typechecker_test.cpp +++ b/test/typechecker_test.cpp @@ -913,3 +913,124 @@ TEST_SUITE("SMC/probability property checks") CHECK_MESSAGE(errs.empty(), errs.front().msg); } } + +TEST_SUITE("More expression and edge errors") +{ + TEST_CASE("edge guard must be side-effect free") + { + auto doc = document_fixture{} + .add_global_decl("int i;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Expression_must_be_side-effect_free"); + } + + TEST_CASE("channel expected for edge synchronisation") + { + auto doc = document_fixture{} + .add_global_decl("chan c[2];") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Channel_expected"); + } + + TEST_CASE("scalar set or integer expected for iteration variable") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() { for (i : bool) {} }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Scalar_set_or_integer_expected"); + } + + TEST_CASE("field name not allowed in array initialiser") + { + auto doc = + document_fixture{}.add_default_process().add_global_decl("int a[2] = {x: 1, 2};").parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Field_name_not_allowed_in_array_initialiser"); + } + + TEST_CASE("number expected and unknown type in sum body") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("chan carr[3]; void f() { int x = sum(i:int[0,2]) carr[i]; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 2); + CHECK(errs[0].msg == "$Number_expected"); + CHECK(errs[1].msg == "$Unknown_type_of_the_expression"); + } + + static UTAP::Document parse_with_progress(const std::string& progress_decl) + { + // The `progress { ... }` block must follow the `system ...;` line + // (grammar: System: SysDecl Progress GanttDecl), which + // document_fixture::add_system_decl() cannot express since it + // inserts text before that line -- build the document manually. + auto doc = UTAP::Document{}; + auto xml = std::string{R"XML( + + + clock c; + + +Process = Template(); +system Process; +)XML"} + progress_decl + "\n \n\n"; + parse_XML_buffer(xml.c_str(), doc, true); + return doc; + } + + TEST_CASE("progress guard must evaluate to a boolean value") + { + auto doc = parse_with_progress("progress { c : 1; }"); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Progress_guard_must_evaluate_to_a_boolean_value"); + } + + TEST_CASE("progress measure must evaluate to an integer value") + { + auto doc = parse_with_progress("progress { true : c; }"); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Progress_measure_must_evaluate_to_a_integer_value"); + } +} From bd6787e9df926dcb1715516ce275b6e2500106fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 14:26:50 +0000 Subject: [PATCH 16/54] Add 4 more TypeChecker.cpp coverage tests Array-index/comma-expression type mismatches, a reference-parameter template instantiation given a non-unique-reference argument (exercises isUniqueReference), and a fully-defined dynamic template spawned on an edge with matching arguments (exercises checkSpawnParameterCompatible's success path). Co-Authored-By: Claude Sonnet 5 --- test/typechecker_test.cpp | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/typechecker_test.cpp b/test/typechecker_test.cpp index f7a03da2..e8fc2880 100644 --- a/test/typechecker_test.cpp +++ b/test/typechecker_test.cpp @@ -1033,4 +1033,69 @@ system Process; REQUIRE(errs.size() == 1); CHECK(errs[0].msg == "$Progress_measure_must_evaluate_to_a_integer_value"); } + + TEST_CASE("incompatible type for array index") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("int a[3]; void f() { clock c; int x = a[c]; }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Incompatible_type"); + } + + TEST_CASE("incompatible type for comma expression") + { + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("chan c; void f() { int i; for (i = 0, c; i < 1; i++) {} }") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Incompatible_type_for_comma_expression"); + } + + TEST_CASE("incompatible argument: reference parameter given a non-unique-reference argument") + { + // Also exercises isUniqueReference(), which returns false for a + // literal passed where a non-const reference parameter is expected. + auto doc = document_fixture{} + .add_template(template_fixture{"T"}.add_parameter("int& x").str()) + .add_system_decl("Process = T(5);") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Incompatible_argument"); + } + + TEST_CASE("spawning a fully-defined dynamic template with matching arguments on an edge") + { + // Exercises checkSpawnParameterCompatible() on the success path. + auto doc = document_fixture{} + .add_global_decl("dynamic Child(int p);") + .add_template(R"XML()XML") + .add_template(R"XML()XML") + .add_system_decl("Process = Parent();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + } } From 62bff2189f8e8f5728e2e58e2fe9aeb9acd38e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 14:37:24 +0000 Subject: [PATCH 17/54] Add CSP/refinement and urgent/deterministic edge checks to TypeChecker.cpp Covers: urgent edges with clock guards / strict bounds, broadcast input edges into branchpoints (must be deterministic) and into locations with a non-true invariant, mixing CSP-style and IO-style channel synchronisation on the same template, and the three refinement-only warnings (uncontrollable output, controllable input, CSP sync incompatible with refinement) -- these last three only fire when TypeChecker is constructed with refinement=true, which static_analysis() (used by every parse_XTA/parse_XML_* overload) never does, so those tests re-visit an already-parsed Document with a refinement-enabled checker directly. Co-Authored-By: Claude Sonnet 5 --- test/typechecker_test.cpp | 195 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/test/typechecker_test.cpp b/test/typechecker_test.cpp index e8fc2880..b9f81a01 100644 --- a/test/typechecker_test.cpp +++ b/test/typechecker_test.cpp @@ -1099,3 +1099,198 @@ system Process; CHECK_MESSAGE(errs.empty(), errs.front().msg); } } + +TEST_SUITE("CSP/refinement and urgent/deterministic edge checks") +{ + TEST_CASE("urgent edge with a clock guard and a strict bound") + { + // Exercises both clock_guards_not_allowed_on_urgent_edges and + // strict_bounds_on_urgent_edges, since an urgent channel with a + // strict clock upper bound guard hits both independent checks. + auto doc = document_fixture{} + .add_global_decl("urgent chan c; clock x;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto& warns = doc.get_warnings(); + REQUIRE(warns.size() == 2); + CHECK(warns[0].msg == "$Clock_guards_are_not_allowed_on_urgent_edges"); + CHECK(warns[1].msg == "$Strict_bounds_on_urgent_edges_may_not_make_sense"); + } + + TEST_CASE("broadcast input edge into a branchpoint must be deterministic") + { + auto doc = document_fixture{} + .add_global_decl("broadcast chan c;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto& warns = doc.get_warnings(); + REQUIRE(warns.size() == 1); + CHECK(warns[0].msg == "SMC requires input edges to be deterministic"); + } + + TEST_CASE("broadcast input edge into a location with a non-true invariant") + { + auto doc = document_fixture{} + .add_global_decl("broadcast chan c; clock x;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto& warns = doc.get_warnings(); + bool found = false; + for (const auto& w : warns) + found |= (w.msg == "$It_may_be_needed_to_add_a_guard_involving_the_target_invariant"); + CHECK(found); + } + + TEST_CASE("mixing CSP-style and IO-style synchronisation is not allowed") + { + auto doc = document_fixture{} + .add_global_decl("chan c;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$CSP_and_IO_synchronisations_cannot_be_mixed"); + } + + // The remaining refinement-only checks (outputs_should_be_uncontrollable, + // inputs_should_be_controllable, csp_sync_is_incompatible_with_refinement_checking) + // only run when TypeChecker is constructed with refinement=true, which + // static_analysis() (used by every parse_XTA/parse_XML_* overload) never + // does -- so a document must be re-visited manually with a + // refinement-enabled checker to reach them. + TEST_CASE("refinement warnings: uncontrollable output, controllable input, CSP incompatibility") + { + auto doc = document_fixture{} + .add_global_decl("chan c;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$CSP_and_IO_synchronisations_cannot_be_mixed"); + + auto checker = UTAP::TypeChecker{doc, true}; + doc.accept(checker); + + const auto& warns = doc.get_warnings(); + bool has_output_warning = false; + bool has_csp_refinement_warning = false; + for (const auto& w : warns) { + has_output_warning |= (w.msg == "$Outputs_should_be_uncontrollable_for_refinement_checking"); + has_csp_refinement_warning |= (w.msg == "$CSP_synchronisations_are_incompatible_with_refinement_checking"); + } + CHECK(has_output_warning); + CHECK(has_csp_refinement_warning); + } + + TEST_CASE("refinement warning: input edge should be controllable") + { + auto doc = document_fixture{} + .add_global_decl("chan c;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + + auto checker = UTAP::TypeChecker{doc, true}; + doc.accept(checker); + + const auto& warns = doc.get_warnings(); + bool found = false; + for (const auto& w : warns) + found |= (w.msg == "$Inputs_should_be_controllable_for_refinement_checking"); + CHECK(found); + } +} From 2db8a1192c1947fb1bdf3548f991a1193b76ad63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 16:40:36 +0200 Subject: [PATCH 18/54] Fixed linter warnings about unused parameters --- include/utap/document.hpp | 2 +- src/AbstractBuilder.cpp | 125 +++++++++++++++++++------------------- src/DocumentBuilder.cpp | 6 +- src/ExpressionBuilder.cpp | 6 +- src/PrettyPrinter.cpp | 30 ++++----- src/document.cpp | 4 +- src/property.cpp | 4 +- test/document_fixture.h | 9 +-- 8 files changed, 91 insertions(+), 95 deletions(-) diff --git a/include/utap/document.hpp b/include/utap/document.hpp index 90174656..13183163 100644 --- a/include/utap/document.hpp +++ b/include/utap/document.hpp @@ -460,7 +460,7 @@ struct Resource std::string value; std::optional unit; Resource(std::string name, std::string value, std::optional unit): - name{std::move(name)}, value{std::move(name)}, unit{std::move(unit)} + name{std::move(name)}, value{std::move(value)}, unit{std::move(unit)} {} }; diff --git a/src/AbstractBuilder.cpp b/src/AbstractBuilder.cpp index 3edd46c5..d548627f 100644 --- a/src/AbstractBuilder.cpp +++ b/src/AbstractBuilder.cpp @@ -72,75 +72,74 @@ void AbstractBuilder::type_channel(TypePrefix) { UNSUPPORTED; } void AbstractBuilder::type_clock(TypePrefix) { UNSUPPORTED; } void AbstractBuilder::type_void() { UNSUPPORTED; } void AbstractBuilder::type_scalar(TypePrefix) { UNSUPPORTED; } -void AbstractBuilder::type_name(TypePrefix, std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::type_struct(TypePrefix, uint32_t fields) { UNSUPPORTED; } +void AbstractBuilder::type_name(TypePrefix, std::string_view) { UNSUPPORTED; } +void AbstractBuilder::type_struct(TypePrefix, uint32_t) { UNSUPPORTED; } void AbstractBuilder::type_array_of_size(uint32_t) { UNSUPPORTED; } void AbstractBuilder::type_array_of_type(uint32_t) { UNSUPPORTED; } -void AbstractBuilder::struct_field(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::struct_field(std::string_view) { UNSUPPORTED; } -void AbstractBuilder::decl_typedef(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::decl_var(std::string_view name, bool init) { UNSUPPORTED; } -void AbstractBuilder::decl_init_list(uint32_t num) { UNSUPPORTED; } -void AbstractBuilder::decl_field_init(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::decl_typedef(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::decl_var(std::string_view, bool) { UNSUPPORTED; } +void AbstractBuilder::decl_init_list(uint32_t) { UNSUPPORTED; } +void AbstractBuilder::decl_field_init(std::string_view) { UNSUPPORTED; } -void AbstractBuilder::gantt_decl_begin(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::gantt_decl_select(std::string_view id) { UNSUPPORTED; } +void AbstractBuilder::gantt_decl_begin(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::gantt_decl_select(std::string_view) { UNSUPPORTED; } void AbstractBuilder::gantt_decl_end() { UNSUPPORTED; } void AbstractBuilder::gantt_entry_begin() { UNSUPPORTED; } -void AbstractBuilder::gantt_entry_select(std::string_view id) { UNSUPPORTED; } +void AbstractBuilder::gantt_entry_select(std::string_view) { UNSUPPORTED; } void AbstractBuilder::gantt_entry_end() { UNSUPPORTED; } void AbstractBuilder::decl_progress(bool) { UNSUPPORTED; } -void AbstractBuilder::decl_parameter(std::string_view name, bool) { UNSUPPORTED; } -void AbstractBuilder::decl_func_begin(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::decl_parameter(std::string_view, bool) { UNSUPPORTED; } +void AbstractBuilder::decl_func_begin(std::string_view) { UNSUPPORTED; } void AbstractBuilder::decl_func_end() { UNSUPPORTED; } -void AbstractBuilder::dynamic_load_lib(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::decl_external_func(std::string_view name, std::string_view alias) { UNSUPPORTED; } +void AbstractBuilder::dynamic_load_lib(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::decl_external_func(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::proc_begin(std::string_view name, const bool isTA, std::string_view type, std::string_view mode) +void AbstractBuilder::proc_begin(std::string_view, const bool, std::string_view, std::string_view) { UNSUPPORTED; } void AbstractBuilder::proc_end() { UNSUPPORTED; } -void AbstractBuilder::proc_location(std::string_view name, bool hasInvariant, bool hasER) { UNSUPPORTED; } -void AbstractBuilder::proc_location_commit(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::proc_location_urgent(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::proc_location_init(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::proc_branchpoint(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::proc_edge_begin(std::string_view from, std::string_view to, const bool control, - std::string_view actname) +void AbstractBuilder::proc_location(std::string_view, bool, bool) { UNSUPPORTED; } +void AbstractBuilder::proc_location_commit(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::proc_location_urgent(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::proc_location_init(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::proc_branchpoint(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::proc_edge_begin(std::string_view, std::string_view, const bool, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::proc_edge_end(std::string_view from, std::string_view to) { UNSUPPORTED; } -void AbstractBuilder::proc_select(std::string_view id) { UNSUPPORTED; } +void AbstractBuilder::proc_edge_end(std::string_view, std::string_view) { UNSUPPORTED; } +void AbstractBuilder::proc_select(std::string_view) { UNSUPPORTED; } void AbstractBuilder::proc_guard() { UNSUPPORTED; } -void AbstractBuilder::proc_sync(Sync type) { UNSUPPORTED; } +void AbstractBuilder::proc_sync(Sync) { UNSUPPORTED; } void AbstractBuilder::proc_update() { UNSUPPORTED; } void AbstractBuilder::proc_prob() { UNSUPPORTED; } // LSC -void AbstractBuilder::proc_message(Sync type) { UNSUPPORTED; } +void AbstractBuilder::proc_message(Sync) { UNSUPPORTED; } void AbstractBuilder::proc_instance_line() { UNSUPPORTED; } -void AbstractBuilder::instance_name_begin(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::instance_name_end(std::string_view name, uint32_t arguments) { UNSUPPORTED; } -void AbstractBuilder::instance_name(std::string_view name, bool templ) { UNSUPPORTED; } +void AbstractBuilder::instance_name_begin(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::instance_name_end(std::string_view, uint32_t) { UNSUPPORTED; } +void AbstractBuilder::instance_name(std::string_view, bool) { UNSUPPORTED; } -void AbstractBuilder::proc_message(std::string_view from, std::string_view to, const int loc, const bool pch) +void AbstractBuilder::proc_message(std::string_view, std::string_view, const int, const bool) { UNSUPPORTED; } -void AbstractBuilder::proc_condition(const std::vector& anchors, const int loc, const bool pch, - const bool hot) +void AbstractBuilder::proc_condition(const std::vector&, const int, const bool, + const bool) { UNSUPPORTED; } void AbstractBuilder::proc_condition() { UNSUPPORTED; } -void AbstractBuilder::proc_LSC_update(std::string_view anchor, const int loc, const bool pch) { UNSUPPORTED; } +void AbstractBuilder::proc_LSC_update(std::string_view, const int, const bool) { UNSUPPORTED; } void AbstractBuilder::proc_LSC_update() { UNSUPPORTED; } -void AbstractBuilder::prechart_set(const bool pch) { UNSUPPORTED; } +void AbstractBuilder::prechart_set(const bool) { UNSUPPORTED; } // end LSC void AbstractBuilder::block_begin() { UNSUPPORTED; } @@ -148,8 +147,8 @@ void AbstractBuilder::block_end() { UNSUPPORTED; } void AbstractBuilder::empty_statement() { UNSUPPORTED; } void AbstractBuilder::for_begin() { UNSUPPORTED; } void AbstractBuilder::for_end() { UNSUPPORTED; } -void AbstractBuilder::iteration_begin(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::iteration_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::iteration_begin(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::iteration_end(std::string_view) { UNSUPPORTED; } void AbstractBuilder::while_begin() { UNSUPPORTED; } void AbstractBuilder::while_end() { UNSUPPORTED; } @@ -178,45 +177,45 @@ void AbstractBuilder::assert_statement() { UNSUPPORTED; } void AbstractBuilder::expr_true() { UNSUPPORTED; } void AbstractBuilder::expr_false() { UNSUPPORTED; } void AbstractBuilder::expr_double(double) { UNSUPPORTED; } -void AbstractBuilder::expr_string(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::expr_identifier(std::string_view varName) { UNSUPPORTED; } +void AbstractBuilder::expr_string(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::expr_identifier(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_nat(int32_t) { UNSUPPORTED; } void AbstractBuilder::expr_call_begin() { UNSUPPORTED; } -void AbstractBuilder::expr_call_end(uint32_t n) { UNSUPPORTED; } +void AbstractBuilder::expr_call_end(uint32_t) { UNSUPPORTED; } void AbstractBuilder::expr_array() { UNSUPPORTED; } void AbstractBuilder::expr_post_increment() { UNSUPPORTED; } void AbstractBuilder::expr_pre_increment() { UNSUPPORTED; } void AbstractBuilder::expr_post_decrement() { UNSUPPORTED; } void AbstractBuilder::expr_pre_decrement() { UNSUPPORTED; } -void AbstractBuilder::expr_assignment(Kind op) { UNSUPPORTED; } -void AbstractBuilder::expr_unary(Kind unaryop) { UNSUPPORTED; } -void AbstractBuilder::expr_binary(Kind binaryop) { UNSUPPORTED; } -void AbstractBuilder::expr_nary(Kind kind, uint32_t num) { UNSUPPORTED; } +void AbstractBuilder::expr_assignment(Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_unary(Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_binary(Kind) { UNSUPPORTED; } +void AbstractBuilder::expr_nary(Kind, uint32_t) { UNSUPPORTED; } // LSC -void AbstractBuilder::expr_scenario(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_scenario(std::string_view) { UNSUPPORTED; } // end LSC -void AbstractBuilder::expr_ternary(Kind ternaryop, bool firstMissing) { UNSUPPORTED; } +void AbstractBuilder::expr_ternary(Kind, bool) { UNSUPPORTED; } void AbstractBuilder::expr_inline_if() { UNSUPPORTED; } void AbstractBuilder::expr_comma() { UNSUPPORTED; } void AbstractBuilder::expr_location() { UNSUPPORTED; } void AbstractBuilder::expr_dot(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_deadlock() { UNSUPPORTED; } -void AbstractBuilder::expr_forall_begin(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::expr_forall_end(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::expr_sum_begin(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::expr_sum_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_forall_begin(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::expr_forall_end(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::expr_sum_begin(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::expr_sum_end(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_builtin_function1(Kind) { UNSUPPORTED; } void AbstractBuilder::expr_builtin_function2(Kind) { UNSUPPORTED; } void AbstractBuilder::expr_builtin_function3(Kind) { UNSUPPORTED; } -void AbstractBuilder::expr_simulate(int no_of_exprs, bool, int) { UNSUPPORTED; } +void AbstractBuilder::expr_simulate(int, bool, int) { UNSUPPORTED; } void AbstractBuilder::expr_optimize_exp(Kind, PriceType, Kind) { UNSUPPORTED; } void AbstractBuilder::expr_load_strategy() { UNSUPPORTED; } -void AbstractBuilder::expr_save_strategy(std::string_view strategy_name) { UNSUPPORTED; } +void AbstractBuilder::expr_save_strategy(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_MITL_formula() { UNSUPPORTED; } void AbstractBuilder::expr_MITL_until(int, int) { UNSUPPORTED; } void AbstractBuilder::expr_MITL_release(int, int) { UNSUPPORTED; } @@ -232,8 +231,8 @@ void AbstractBuilder::expr_proba_qualitative(Kind, Kind, double) { UNSUPPORTED; void AbstractBuilder::expr_proba_quantitative(Kind) { UNSUPPORTED; } void AbstractBuilder::expr_proba_compare(Kind, Kind) { UNSUPPORTED; } void AbstractBuilder::expr_proba_expected(std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_exists_begin(std::string_view name) { UNSUPPORTED; } -void AbstractBuilder::expr_exists_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_exists_begin(std::string_view) { UNSUPPORTED; } +void AbstractBuilder::expr_exists_end(std::string_view) { UNSUPPORTED; } void AbstractBuilder::instantiation_begin(std::string_view, uint32_t, std::string_view) { UNSUPPORTED; } void AbstractBuilder::instantiation_end(std::string_view, uint32_t, std::string_view, uint32_t) { UNSUPPORTED; } @@ -247,7 +246,7 @@ void AbstractBuilder::parse(const char*) { UNSUPPORTED; } // end LSC void AbstractBuilder::done() {} -void AbstractBuilder::handle_expect(std::string_view text) {} +void AbstractBuilder::handle_expect(std::string_view) {} void AbstractBuilder::property() { UNSUPPORTED; } void AbstractBuilder::strategy_declaration(std::string_view) { UNSUPPORTED; } @@ -257,29 +256,29 @@ void AbstractBuilder::imitation(std::string_view) { UNSUPPORTED; } void AbstractBuilder::before_update() { UNSUPPORTED; } void AbstractBuilder::after_update() { UNSUPPORTED; } void AbstractBuilder::chan_priority_begin() { UNSUPPORTED; } -void AbstractBuilder::chan_priority_add(char separator) { UNSUPPORTED; } +void AbstractBuilder::chan_priority_add(char) { UNSUPPORTED; } void AbstractBuilder::chan_priority_default() { UNSUPPORTED; } void AbstractBuilder::proc_priority_inc() { UNSUPPORTED; } void AbstractBuilder::proc_priority(std::string_view) { UNSUPPORTED; } -void AbstractBuilder::decl_dynamic_template(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::decl_dynamic_template(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_spawn(int) { UNSUPPORTED; } void AbstractBuilder::expr_exit() { UNSUPPORTED; } void AbstractBuilder::expr_numof() { UNSUPPORTED; } void AbstractBuilder::expr_forall_dynamic_begin(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_forall_dynamic_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_forall_dynamic_end(std::string_view ) { UNSUPPORTED; } void AbstractBuilder::expr_exists_dynamic_begin(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_exists_dynamic_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_exists_dynamic_end(std::string_view ) { UNSUPPORTED; } void AbstractBuilder::expr_sum_dynamic_begin(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_sum_dynamic_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_sum_dynamic_end(std::string_view ) { UNSUPPORTED; } void AbstractBuilder::expr_foreach_dynamic_begin(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_foreach_dynamic_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_foreach_dynamic_end(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_dynamic_process_expr(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_MITL_forall_dynamic_begin(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_MITL_forall_dynamic_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_MITL_forall_dynamic_end(std::string_view) { UNSUPPORTED; } void AbstractBuilder::expr_MITL_exists_dynamic_begin(std::string_view, std::string_view) { UNSUPPORTED; } -void AbstractBuilder::expr_MITL_exists_dynamic_end(std::string_view name) { UNSUPPORTED; } +void AbstractBuilder::expr_MITL_exists_dynamic_end(std::string_view) { UNSUPPORTED; } void AbstractBuilder::query_begin() { UNSUPPORTED; } void AbstractBuilder::query_end() { UNSUPPORTED; } @@ -288,7 +287,7 @@ void AbstractBuilder::query_comment(std::string_view) { UNSUPPORTED; } void AbstractBuilder::query_options(std::string_view, std::string_view) { UNSUPPORTED; } void AbstractBuilder::expectation_begin() { UNSUPPORTED; } void AbstractBuilder::expectation_end() { UNSUPPORTED; } -void AbstractBuilder::expectation_value(std::string_view type, std::string_view res, std::string_view value) +void AbstractBuilder::expectation_value(std::string_view, std::string_view, std::string_view) { UNSUPPORTED; } diff --git a/src/DocumentBuilder.cpp b/src/DocumentBuilder.cpp index aba80c4c..a0f4e126 100644 --- a/src/DocumentBuilder.cpp +++ b/src/DocumentBuilder.cpp @@ -228,7 +228,7 @@ void DocumentBuilder::proc_edge_begin(std::string_view from, std::string_view to } } -void DocumentBuilder::proc_edge_end(std::string_view from, std::string_view to) { frames.pop(); } +void DocumentBuilder::proc_edge_end(std::string_view, std::string_view) { frames.pop(); } void DocumentBuilder::proc_select(std::string_view id) { addSelectSymbolToFrame(id, currentEdge->select, position); } @@ -272,7 +272,7 @@ void DocumentBuilder::proc_prob() * System declaration */ -void DocumentBuilder::instantiation_begin(std::string_view name, uint32_t parameters, std::string_view templ_name) +void DocumentBuilder::instantiation_begin(std::string_view name, uint32_t, std::string_view templ_name) { // Make sure this identifier is new. if (frames.top().contains(name)) @@ -422,7 +422,7 @@ void DocumentBuilder::instance_name(std::string_view name, bool templ) currentTemplate->frame.add_symbol(name, Type::create_primitive(Kind::INSTANCE_LINE), position, currentInstanceLine); } -void DocumentBuilder::instance_name_begin(std::string_view name) +void DocumentBuilder::instance_name_begin(std::string_view) { // Push parameters to frame stack. auto frame = frames.top().make_sub(); diff --git a/src/ExpressionBuilder.cpp b/src/ExpressionBuilder.cpp index 6ec4b744..16270778 100644 --- a/src/ExpressionBuilder.cpp +++ b/src/ExpressionBuilder.cpp @@ -615,7 +615,7 @@ void ExpressionBuilder::expr_forall_begin(std::string_view name) } } -void ExpressionBuilder::expr_forall_end(std::string_view name) +void ExpressionBuilder::expr_forall_end(std::string_view) { /* Create the forall expression. The symbol is added as an identifier * expression as the first child. Notice that the frame is discarded @@ -629,7 +629,7 @@ void ExpressionBuilder::expr_forall_end(std::string_view name) void ExpressionBuilder::expr_exists_begin(std::string_view name) { expr_forall_begin(name); } -void ExpressionBuilder::expr_exists_end(std::string_view name) +void ExpressionBuilder::expr_exists_end(std::string_view) { /* Create the exists expression. The symbol is added as an identifier * expression as the first child. Notice that the frame is discarded @@ -643,7 +643,7 @@ void ExpressionBuilder::expr_exists_end(std::string_view name) void ExpressionBuilder::expr_sum_begin(std::string_view name) { expr_forall_begin(name); } -void ExpressionBuilder::expr_sum_end(std::string_view name) +void ExpressionBuilder::expr_sum_end(std::string_view) { /* Create the sum expression. The symbol is added as an identifier * expression as the first child. Notice that the frame is discarded diff --git a/src/PrettyPrinter.cpp b/src/PrettyPrinter.cpp index 16777b95..c6ccfcdd 100644 --- a/src/PrettyPrinter.cpp +++ b/src/PrettyPrinter.cpp @@ -79,7 +79,7 @@ PrettyPrinter::PrettyPrinter(std::ostream& stream) select = guard = sync = update = probability = -1; } -void PrettyPrinter::add_position(uint32_t position, uint32_t offset, uint32_t line, std::shared_ptr path) +void PrettyPrinter::add_position(uint32_t, uint32_t, uint32_t, std::shared_ptr) {} void PrettyPrinter::handle_error(const TypeException& msg) { throw msg; } @@ -125,9 +125,9 @@ void PrettyPrinter::type_name(TypePrefix prefix, std::string_view name) type.push(label(prefix) + std::string{name}); } -void PrettyPrinter::type_array_of_size(uint32_t n) { array.push(pop_back(st)); } +void PrettyPrinter::type_array_of_size(uint32_t) { array.push(pop_back(st)); } -void PrettyPrinter::type_array_of_type(uint32_t n) +void PrettyPrinter::type_array_of_type(uint32_t) { array.push(type.top()); type.pop(); @@ -249,9 +249,9 @@ void PrettyPrinter::decl_func_end() *o.top() << "}\n"; } -void PrettyPrinter::dynamic_load_lib(std::string_view name) {} +void PrettyPrinter::dynamic_load_lib(std::string_view) {} -void PrettyPrinter::decl_external_func(std::string_view name, std::string_view alias) +void PrettyPrinter::decl_external_func(std::string_view, std::string_view) { pop_top(type); // discard the return type pushed for this declaration param.clear(); // discard the parameters accumulated by decl_parameter() @@ -287,7 +287,7 @@ void PrettyPrinter::iteration_begin(std::string_view id) type.pop(); } -void PrettyPrinter::iteration_end(std::string_view id) +void PrettyPrinter::iteration_end(std::string_view) { *o.top() << '\n'; level--; @@ -413,7 +413,7 @@ void PrettyPrinter::return_statement(bool hasValue) } } -void PrettyPrinter::proc_begin(std::string_view id, const bool isTA, std::string_view type, std::string_view mode) +void PrettyPrinter::proc_begin(std::string_view id, const bool, std::string_view, std::string_view) { *o.top() << "process " << id << templateset << '(' << param << ")\n{\n"; param.clear(); @@ -558,7 +558,7 @@ void PrettyPrinter::proc_edge_begin(std::string_view source, std::string_view ta } } -void PrettyPrinter::proc_edge_end(std::string_view source, std::string_view target) +void PrettyPrinter::proc_edge_end(std::string_view, std::string_view) { level++; @@ -880,7 +880,7 @@ void PrettyPrinter::expr_forall_begin(std::string_view name) st.push_back("forall (" + std::string{name} + ":" + pop_top(type) + ") "); } -void PrettyPrinter::expr_forall_end(std::string_view name) +void PrettyPrinter::expr_forall_end(std::string_view) { auto expr = pop_back(st); st.back() += expr; @@ -891,7 +891,7 @@ void PrettyPrinter::expr_exists_begin(std::string_view name) st.push_back("exists (" + std::string{name} + ":" + pop_top(type) + ") "); } -void PrettyPrinter::expr_exists_end(std::string_view name) +void PrettyPrinter::expr_exists_end(std::string_view) { auto expr = pop_back(st); st.back() += expr; @@ -902,7 +902,7 @@ void PrettyPrinter::expr_sum_begin(std::string_view name) st.push_back("sum (" + std::string{name} + ":" + pop_top(type) + ") "); } -void PrettyPrinter::expr_sum_end(std::string_view name) +void PrettyPrinter::expr_sum_end(std::string_view) { auto expr = pop_back(st); st.back() += expr; @@ -928,12 +928,12 @@ void PrettyPrinter::after_update() *o.top() << "}\n"; } -void PrettyPrinter::instantiation_begin(std::string_view id, uint32_t, std::string_view templ) +void PrettyPrinter::instantiation_begin(std::string_view, uint32_t, std::string_view) { // Ignore } -void PrettyPrinter::instantiation_end(std::string_view id, uint32_t parameters, std::string_view templ, +void PrettyPrinter::instantiation_end(std::string_view id, uint32_t, std::string_view templ, uint32_t arguments) { auto s = std::stack{}; @@ -986,7 +986,7 @@ void PrettyPrinter::exprProba2(bool isTimedBound, int type) st.push_back(ss.str()); } */ -void PrettyPrinter::expr_proba_quantitative(Kind type) +void PrettyPrinter::expr_proba_quantitative(Kind) { const auto pred2 = pop_back(st); const auto pred1 = pop_back(st); @@ -1056,7 +1056,7 @@ void PrettyPrinter::expr_simulate(int nbExpr, bool hasReach, int nbOfAcceptingRu /** Built-in verification queries if any */ void PrettyPrinter::query_begin() { *o.top() << "\n/** Query begin: */\n"; } -void PrettyPrinter::query_formula(std::string_view formula, std::string_view location) +void PrettyPrinter::query_formula(std::string_view formula, std::string_view) { if (not formula.empty()) *o.top() << "/* Formula: " << formula << " */\n"; diff --git a/src/document.cpp b/src/document.cpp index cd83fb7d..a8eb07c9 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -172,7 +172,7 @@ std::ostream& Declarations::print_typedefs(std::ostream& os) const return os; } -std::ostream& Declarations::print_variables(std::ostream& os, bool global) const +std::ostream& Declarations::print_variables(std::ostream& os, bool) const { if (!variables.empty()) { os << "// variables\n"; @@ -929,7 +929,7 @@ void Document::copy_variables_from_to(const Template* from, Template* to) const } } -void Document::copy_functions_from_to(const Template* from, Template* to) const +void Document::copy_functions_from_to(const Template* /*from*/, Template* /*to*/) const { // TODO to be implemented and to be used in Translator::lscProcBegin (see Translator.cpp) } diff --git a/src/property.cpp b/src/property.cpp index b980b32c..cfe056c5 100644 --- a/src/property.cpp +++ b/src/property.cpp @@ -276,12 +276,12 @@ void PropertyBuilder::parse(const char* buf, const std::string& xpath, const Opt properties.back().options = options; } -Variable* PropertyBuilder::add_variable(Type type, std::string_view name, Expression init, position_t pos) +Variable* PropertyBuilder::add_variable(Type, std::string_view, Expression, position_t) { throw NotSupportedException("add_variable is not supported"); } -bool PropertyBuilder::add_function(Type type, std::string_view name, position_t pos) +bool PropertyBuilder::add_function(Type, std::string_view, position_t) { throw NotSupportedException("add_function is not supported"); } diff --git a/test/document_fixture.h b/test/document_fixture.h index 175bdf2a..6f540fc1 100644 --- a/test/document_fixture.h +++ b/test/document_fixture.h @@ -131,8 +131,6 @@ class template_fixture ")XML"; return string_format(simple_template, name.c_str(), parameters.c_str(), declarations.c_str()); } - -private: }; class QueryBuilder : public UTAP::StatementBuilder @@ -150,15 +148,14 @@ class QueryBuilder : public UTAP::StatementBuilder query = fragments[0]; fragments.pop(); } - void strategy_declaration(std::string_view strategy_name) override {} + void strategy_declaration(std::string_view /* strategy_name */) override {} void typecheck() { checker.checkExpression(query); } [[nodiscard]] UTAP::Expression getQuery() const { return query; } - UTAP::Variable* add_variable(UTAP::Type type, std::string_view name, UTAP::Expression init, - UTAP::position_t pos) override + UTAP::Variable* add_variable(UTAP::Type, std::string_view, UTAP::Expression,UTAP::position_t) override { throw UTAP::NotSupportedException(__FUNCTION__); } - bool add_function(UTAP::Type type, std::string_view name, UTAP::position_t pos) override + bool add_function(UTAP::Type, std::string_view, UTAP::position_t) override { throw UTAP::NotSupportedException(__FUNCTION__); } From 0e6e50ae7808af2f042beef702c019b68fb8fbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Wed, 8 Jul 2026 15:18:36 +0000 Subject: [PATCH 19/54] Add property_test.cpp, exercising PropertyBuilder/TigaPropertyBuilder property.cpp/property.hpp were at 27%/5% coverage because the only existing consumers of PropertyBuilder's real pipeline (via document_fixture's QueryFixture/TigaPropertyBuilder) were a handful of query forms in prettyprint_test.cpp (E<>, Pr[...] comparisons, simulate, saveStrategy); every other query form used the local lightweight QueryBuilder class instead, which overrides property() to just stash the expression and skip typeProperty() entirely. Adds tests for the quant_t classification of A[]/E[]/A<>/-->/ sup{}/inf{}/bounds{}, the SMC probability-threshold and MITL forms, the TIGA control-synthesis forms (control:, E<> control:, minE/maxE), the deadlock-predicate and dynamic-template restrictions in property(), handle_expect()/parseExpect()'s status/time/memory token parsing (a fully public but otherwise uncalled-from-anywhere API), and the duplicate-strategy-declaration/undeclared-strategy-subjection checks in TigaPropertyBuilder. Also found along the way: parser.y's CALL() macro catches any TypeException thrown from a builder callback and converts it into a recorded document error via handle_error(), so PropertyBuilder's `throw TypeException{...}` calls never propagate as C++ exceptions during normal parsing -- only document_fixture's QueryFixture re-throws as std::logic_error, and only because it explicitly checks doc.get_errors() itself after the parse call returns. Co-Authored-By: Claude Sonnet 5 --- test/CMakeLists.txt | 6 + test/property_test.cpp | 260 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 test/property_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dc3b8b08..784d8ecb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -63,6 +63,11 @@ target_link_libraries(print_test PRIVATE UTAP doctest::doctest_with_main) target_coverage(print_test) add_test(NAME print_test COMMAND print_test) +add_executable(property_test property_test.cpp) +target_compile_definitions(property_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(property_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME property_test COMMAND property_test) + add_executable(example_test example_test.cpp) target_link_libraries(example_test PRIVATE UTAP) add_test(NAME example_train_gate @@ -81,6 +86,7 @@ if(UTAP_STATIC) target_link_options(prettyprinter_test PRIVATE -static) target_link_options(typeexception_test PRIVATE -static) target_link_options(print_test PRIVATE -static) + target_link_options(property_test PRIVATE -static) target_link_options(example_test PRIVATE -static) message(STATUS "Enabled Unit Tests -static") endif(UTAP_STATIC) diff --git a/test/property_test.cpp b/test/property_test.cpp new file mode 100644 index 00000000..9e726ae8 --- /dev/null +++ b/test/property_test.cpp @@ -0,0 +1,260 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +/** + * PropertyBuilder/TigaPropertyBuilder::typeProperty() classifies a parsed + * property expression into a quant_t and populates a PropInfo. Existing + * tests that use document_fixture's local (lightweight) QueryBuilder class + * never reach this code -- only the QueryFixture/TigaPropertyBuilder path + * (parse_query) does, and only a handful of query forms (E<>, Pr[...] + * comparisons, simulate, saveStrategy) were exercised that way before. + */ + +#include "document_fixture.h" + +#include "utap/property.hpp" + +#include + +TEST_SUITE_BEGIN("PropertyBuilder/TigaPropertyBuilder"); + +using namespace UTAP; + +namespace { +/// A document with a template with three named, linearly-connected +/// locations (L1 -> L2 -> L3), for tests needing leads-to/location +/// references, plus a clock and bool for guards/predicates. +document_fixture named_locations_fixture() +{ + return document_fixture{} + .add_global_decl("clock c; bool b;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process"); +} +} // namespace + +TEST_CASE("A[] classifies as quant_t::AG") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("A[] true"); + CHECK(info.type == quant_t::AG); +} + +TEST_CASE("E[] classifies as quant_t::EG") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("E[] true"); + CHECK(info.type == quant_t::EG); +} + +TEST_CASE("A<> classifies as quant_t::AE") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("A<> true"); + CHECK(info.type == quant_t::AE); +} + +TEST_CASE("--> classifies as quant_t::leads_to") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("Process.L2 --> Process.L3"); + CHECK(info.type == quant_t::leads_to); +} + +TEST_CASE("sup{}: classifies as quant_t::supremum") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("sup{true}: c"); + CHECK(info.type == quant_t::supremum); +} + +TEST_CASE("inf{}: classifies as quant_t::infimum") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("inf{true}: c"); + CHECK(info.type == quant_t::infimum); +} + +TEST_CASE("bounds{}: classifies as quant_t::bounds") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("bounds{true}: c"); + CHECK(info.type == quant_t::bounds); +} + +TEST_CASE("Pr[...](<> ...) >= p classifies as quant_t::probaMinDiamond") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("Pr[<=10](<> b) >= 0.5"); + CHECK(info.type == quant_t::probaMinDiamond); +} + +TEST_CASE("Pr[...]([] ...) >= p classifies as quant_t::probaMinBox") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("Pr[<=10]([] b) >= 0.5"); + CHECK(info.type == quant_t::probaMinBox); +} + +TEST_CASE("Pr[...]([] ...) classifies as quant_t::probaBox") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("Pr[<=10]([] b)"); + CHECK(info.type == quant_t::probaBox); +} + +TEST_CASE("Pr (MITL) classifies as quant_t::Mitl") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("Pr (<>[0,5] b)"); + CHECK(info.type == quant_t::Mitl); +} + +TEST_CASE("control: A<> classifies as quant_t::control_AF with a ZoneStrategy result") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("control: A<> true"); + CHECK(info.type == quant_t::control_AF); + CHECK(info.result_type == ZoneStrategy); +} + +TEST_CASE("control: A[] classifies as quant_t::control_AG") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("control: A[] true"); + CHECK(info.type == quant_t::control_AG); +} + +TEST_CASE("control: A[] (p && A<> q) classifies as quant_t::control_ABuchi") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("control: A[] (true && A<> true)"); + CHECK(info.type == quant_t::control_ABuchi); +} + +TEST_CASE("E<> control: A<> classifies as quant_t::EF_control_AF") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("E<> control: A<> true"); + CHECK(info.type == quant_t::EF_control_AF); +} + +TEST_CASE("E<> control: A[] classifies as quant_t::EF_control_AG") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("E<> control: A[] true"); + CHECK(info.type == quant_t::EF_control_AG); +} + +TEST_CASE("minE(...)[...] classifies as quant_t::control_MinExp with a NonZoneStrategy result") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("minE(c)[<=20]: <> b"); + CHECK(info.type == quant_t::control_MinExp); + CHECK(info.result_type == NonZoneStrategy); +} + +TEST_CASE("maxE(...)[...] classifies as quant_t::control_MaxExp") +{ + auto f = named_locations_fixture().build_query_fixture(); + const auto& info = f.parse_query("maxE(c)[<=20]: <> b"); + CHECK(info.type == quant_t::control_MaxExp); +} + +TEST_CASE("A deadlock predicate is only supported for E<>/A[]") +{ + // property()'s `throw TypeException` is caught by the bison-generated + // CALL() wrapper and turned into a recorded document error (see + // parser.y: "catch (TypeException &te) { ch->handle_error(te); }"), + // not a propagating C++ exception -- so check doc.get_errors() + // directly instead of expecting a throw. + auto doc = named_locations_fixture().parse(); + REQUIRE(doc.get_errors().empty()); + auto pb = TigaPropertyBuilder{doc}; + parse_property("E[] deadlock", pb); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$Cannot_handle_this_deadlock_predicate"); +} + +TEST_CASE("A dynamic template restricts non-SMC queries") +{ + auto doc = document_fixture{} + .add_global_decl("dynamic Child();") + .add_template(R"XML()XML") + .add_default_process() + .parse(); + REQUIRE(doc.get_errors().empty()); + auto pb = TigaPropertyBuilder{doc}; + parse_property("A[] true", pb); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "Dynamic templates are only supported for SMC queries"); +} + +TEST_CASE("handle_expect parses status/time/memory tokens") +{ + auto doc = document_fixture{}.add_default_process().parse(); + auto pb = TigaPropertyBuilder{doc}; + REQUIRE(parse_property("A[] true", pb) == 0); + pb.handle_expect("T, 5S, 100KB"); + const auto& info = pb.getProperties().back(); + REQUIRE(info.expect != nullptr); + CHECK(std::get(info.expect->result) == status_t::DONE_TRUE); + CHECK(info.expect->time_ms == 5000); + CHECK(info.expect->mem_kib == 100); +} + +TEST_CASE("declaring the same strategy name twice warns about a duplicate definition") +{ + auto doc = document_fixture{}.add_default_process().parse(); + auto pb = TigaPropertyBuilder{doc}; + REQUIRE(parse_property("strategy S = control: A[] true", pb) == 0); + REQUIRE(parse_property("strategy S = control: A<> true", pb) == 0); + const auto& warns = doc.get_warnings(); + REQUIRE(warns.size() == 1); + CHECK(warns[0].msg == "$Duplicate_definition_of S"); +} + +TEST_CASE("subjection referring to an undeclared strategy is an error") +{ + auto doc = document_fixture{}.add_default_process().parse(); + auto pb = TigaPropertyBuilder{doc}; + parse_property("Pr[<=10](<> true) under NoSuchStrategy", pb); + const auto& errs = doc.get_errors(); + REQUIRE(errs.size() == 1); + CHECK(errs[0].msg == "$strategy_not_declared: NoSuchStrategy"); +} + +TEST_SUITE_END(); From e134d8d576805f628c7d86c8dacec2d0937e97fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Thu, 9 Jul 2026 06:52:42 +0000 Subject: [PATCH 20/54] Add ExpressionBuilder_test.cpp, exercising 15 previously-uncovered functions Covers pre-decrement, the three built-in-math-function arities, global vs. template-local scalar[] declarations (the latter exercising collect_dependencies(), which only runs when a scalar set's size is declared inside a template), the control_t* time-optimal synthesis form (expr_ternary, also fixing property.cpp's CONTROL_TOPT gap), numOf()/foreach() (both throw a later type/lookup error but the builder callback itself already ran), forall/exists/sum over a properly-defined dynamic template (also covers push/pop_dynamic_frame_of transitively), and the MITL until/release/box/next forms. Also adds one property.cpp test: PropertyBuilder::scenario() throws a raw std::runtime_error (not TypeException), so unlike other PropertyBuilder checks it is NOT caught by parser.y's CALL() macro and propagates directly out of parse_property() -- this also means expr_scenario(), the next callback in the same grammar rule, likely never runs when scenario() rejects its argument, so it's still uncovered pending a full LSC template fixture. Co-Authored-By: Claude Sonnet 5 --- test/CMakeLists.txt | 6 ++ test/ExpressionBuilder_test.cpp | 171 ++++++++++++++++++++++++++++++++ test/property_test.cpp | 11 ++ 3 files changed, 188 insertions(+) create mode 100644 test/ExpressionBuilder_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 784d8ecb..7a794e8d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -68,6 +68,11 @@ target_compile_definitions(property_test PRIVATE MODELS_DIR="${MODELS_PATH}") target_link_libraries(property_test PRIVATE UTAP doctest::doctest_with_main) add_test(NAME property_test COMMAND property_test) +add_executable(expressionbuilder_test ExpressionBuilder_test.cpp) +target_compile_definitions(expressionbuilder_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(expressionbuilder_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME expressionbuilder_test COMMAND expressionbuilder_test) + add_executable(example_test example_test.cpp) target_link_libraries(example_test PRIVATE UTAP) add_test(NAME example_train_gate @@ -87,6 +92,7 @@ if(UTAP_STATIC) target_link_options(typeexception_test PRIVATE -static) target_link_options(print_test PRIVATE -static) target_link_options(property_test PRIVATE -static) + target_link_options(expressionbuilder_test PRIVATE -static) target_link_options(example_test PRIVATE -static) message(STATUS "Enabled Unit Tests -static") endif(UTAP_STATIC) diff --git a/test/ExpressionBuilder_test.cpp b/test/ExpressionBuilder_test.cpp new file mode 100644 index 00000000..106355c0 --- /dev/null +++ b/test/ExpressionBuilder_test.cpp @@ -0,0 +1,171 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +#include "document_fixture.h" + +#include + +TEST_SUITE_BEGIN("ExpressionBuilder"); + +using namespace UTAP; + +TEST_CASE("Pre-decrement and built-in math functions") +{ + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("void f() {" + " int i = 5;" + " double d1;" + " double d2;" + " double d3;" + " --i;" // expr_pre_decrement + " d1 = sqrt(2.0);" // expr_builtin_function1 + " d2 = pow(2.0, 3.0);" // expr_builtin_function2 + " d3 = fma(1.0, 2.0, 3.0);" // expr_builtin_function3 + "}") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); +} + +TEST_CASE("Global scalar type declaration") +{ + // type_scalar() outside a template (currentTemplate == nullptr): the + // dependency-tracking branch is skipped. + auto doc = document_fixture{}.add_default_process().add_global_decl("scalar[3] s;").parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); +} + +TEST_CASE("Template-local scalar type declaration tracks free-parameter dependencies") +{ + // type_scalar() inside a template's local declarations exercises the + // collect_dependencies() call that marks symbols used in the scalar + // set's size as restricted (they may not depend on free parameters). + auto doc = document_fixture{} + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); +} + +TEST_CASE("control_t* time-optimal synthesis query exercises expr_ternary") +{ + auto f = document_fixture{}.add_global_decl("clock c;").add_default_process().build_query_fixture(); + const auto& info = f.parse_query("control_t*(1, c): A<> true"); + CHECK(info.type == quant_t::control_opt_AF); +} + +TEST_CASE("numOf() exercises expr_numof before the later not-a-dynamic-template check") +{ + auto f = document_fixture{}.add_default_process().build_query_fixture(); + CHECK_THROWS_WITH_AS(f.parse_query("E<> numOf(Process) >= 0"), "$Not_a_dynamic_template", std::logic_error); +} + +TEST_CASE("foreach() over a non-dynamic-template exercises expr_foreach_dynamic_begin/end") +{ + auto f = document_fixture{}.add_default_process().build_query_fixture(); + CHECK_THROWS_WITH_AS(f.parse_query("foreach (p : Process) (true)"), "Unknown dynamic template Process", + std::logic_error); +} + +TEST_CASE("forall/exists/sum over a dynamic template") +{ + // Exercises expr_forall_dynamic_begin/end, expr_exists_dynamic_begin/end, + // expr_sum_dynamic_begin/end, and (transitively) push_dynamic_frame_of/ + // pop_dynamic_frame_of. The dynamic template must already be fully + // defined (Template::is_defined) by the time it's referenced, so Child + // is declared and defined before Parent uses it on an edge. + auto doc = document_fixture{} + .add_global_decl("dynamic Child(int p); int total;") + .add_template(R"XML()XML") + .add_template(R"XML()XML") + .add_system_decl("Process = Parent();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); +} + +TEST_CASE("MITL until/release/box/next forms") +{ + auto f = document_fixture{} + .add_global_decl("bool b;") + .add_default_process() + .build_query_fixture(); + + SUBCASE("until") + { + const auto& info = f.parse_query("Pr (b U[0,5] b)"); + CHECK(info.type == quant_t::Mitl); + } + SUBCASE("release") + { + const auto& info = f.parse_query("Pr (b R[0,5] b)"); + CHECK(info.type == quant_t::Mitl); + } + SUBCASE("box") + { + const auto& info = f.parse_query("Pr ([][0,5] b)"); + CHECK(info.type == quant_t::Mitl); + } + SUBCASE("next") + { + const auto& info = f.parse_query("Pr (X b)"); + CHECK(info.type == quant_t::Mitl); + } +} + +TEST_SUITE_END(); diff --git a/test/property_test.cpp b/test/property_test.cpp index 9e726ae8..65d4595c 100644 --- a/test/property_test.cpp +++ b/test/property_test.cpp @@ -257,4 +257,15 @@ TEST_CASE("subjection referring to an undeclared strategy is an error") CHECK(errs[0].msg == "$strategy_not_declared: NoSuchStrategy"); } +TEST_CASE("sat: over a non-LSC-template is rejected by PropertyBuilder::scenario()") +{ + // Unlike most PropertyBuilder checks, scenario() throws a raw + // std::runtime_error rather than TypeException, so it is *not* caught + // by the CALL() macro in parser.y (which only catches TypeException) -- + // it propagates straight out of parse_property(), bypassing + // QueryFixture's own doc.get_errors() check entirely. + auto f = document_fixture{}.add_default_process().build_query_fixture(); + CHECK_THROWS_WITH_AS(f.parse_query("sat: Process"), "$Not_a_LSC_template: Process", std::runtime_error); +} + TEST_SUITE_END(); From 123dde30df8d4939185de74d35ae28c6a8e42e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Thu, 9 Jul 2026 07:22:25 +0000 Subject: [PATCH 21/54] Fix crash in Location::print()/Edge::print() on empty Expression fields Both unconditionally called .print() on their invariant/exp_rate (resp. guard/sync/assign) Expression members, but Expression::print() dereferences a null internal pointer for an empty Expression -- crashing for the extremely common case of a location with no invariant/rate, or an edge missing any of guard/sync/assign. Neither method had ever been exercised by any test, which is why this went unnoticed. Guard each call with .empty(), matching the style already used in Variable::print()'s "if (!init.empty())". Co-Authored-By: Claude Sonnet 5 --- src/document.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/document.cpp b/src/document.cpp index a8eb07c9..89859edf 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -67,9 +67,12 @@ template struct Stringify; std::ostream& Location::print(std::ostream& os) const { os << "LOCATION (" << uid.get_name() << ", "; - invariant.print(os) << ", "; - exp_rate.print(os) << ')'; - return os; + if (!invariant.empty()) + invariant.print(os); + os << ", "; + if (!exp_rate.empty()) + exp_rate.print(os); + return os << ')'; } std::ostream& Edge::print(std::ostream& os) const @@ -78,9 +81,14 @@ std::ostream& Edge::print(std::ostream& os) const src->print(os) << ' '; dst->print(os) << ")\n"; os << "\t"; - guard.print(os) << ", "; - sync.print(os) << ", "; - assign.print(os); + if (!guard.empty()) + guard.print(os); + os << ", "; + if (!sync.empty()) + sync.print(os); + os << ", "; + if (!assign.empty()) + assign.print(os); return os; } From 324be8fc9ce9f68ab218ce3abaf4e92d44b1978c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Thu, 9 Jul 2026 07:22:42 +0000 Subject: [PATCH 22/54] Add document_test.cpp, exercising 17 previously-uncovered Document/ Template/Instance/ChanPriority members Covers find_template, get_dynamic_templates, queries_empty, get_options/set_options, get_proc_priority (via a system-level "P1 < P2" priority ordering), remove_process, copy_variables_from_to (plus a bare call to copy_functions_from_to, currently an intentional no-op), add_gantt (built via a raw XML document since document_fixture's add_system_decl() inserts text before the `system X;` line, but gantt must follow it per the grammar), add_io_decl, ChanPriority::print/str, Location::print/str and Edge::print/str (the two crash fixes from the previous commit), Variable::str/Function::str/Declarations::str, Template::is_invariant (via a minimal LSC template), and Instance::arguments_str/mapping_str/print_arguments/print_mapping (via a parameterized template instantiation). Co-Authored-By: Claude Sonnet 5 --- test/CMakeLists.txt | 6 + test/document_test.cpp | 302 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 test/document_test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7a794e8d..354dcad5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -73,6 +73,11 @@ target_compile_definitions(expressionbuilder_test PRIVATE MODELS_DIR="${MODELS_P target_link_libraries(expressionbuilder_test PRIVATE UTAP doctest::doctest_with_main) add_test(NAME expressionbuilder_test COMMAND expressionbuilder_test) +add_executable(document_test document_test.cpp) +target_compile_definitions(document_test PRIVATE MODELS_DIR="${MODELS_PATH}") +target_link_libraries(document_test PRIVATE UTAP doctest::doctest_with_main) +add_test(NAME document_test COMMAND document_test) + add_executable(example_test example_test.cpp) target_link_libraries(example_test PRIVATE UTAP) add_test(NAME example_train_gate @@ -93,6 +98,7 @@ if(UTAP_STATIC) target_link_options(print_test PRIVATE -static) target_link_options(property_test PRIVATE -static) target_link_options(expressionbuilder_test PRIVATE -static) + target_link_options(document_test PRIVATE -static) target_link_options(example_test PRIVATE -static) message(STATUS "Enabled Unit Tests -static") endif(UTAP_STATIC) diff --git a/test/document_test.cpp b/test/document_test.cpp new file mode 100644 index 00000000..d42ce8bc --- /dev/null +++ b/test/document_test.cpp @@ -0,0 +1,302 @@ +// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*- + +/* libutap - Uppaal Timed Automata Parser. + Copyright (C) 2026 Aalborg University. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +#include "document_fixture.h" + +#include + +#include + +TEST_SUITE_BEGIN("Document"); + +using namespace UTAP; + +TEST_CASE("find_template finds an existing template and returns nullptr otherwise") +{ + auto doc = document_fixture{}.add_default_process().parse(); + CHECK(doc.find_template("Template") != nullptr); + CHECK(doc.find_template("NoSuchTemplate") == nullptr); +} + +TEST_CASE("get_dynamic_templates lists declared-and-defined dynamic templates") +{ + auto doc = document_fixture{} + .add_global_decl("dynamic Child();") + .add_template(R"XML()XML") + .add_default_process() + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + REQUIRE(doc.get_dynamic_templates().size() == 1); + CHECK(doc.get_dynamic_templates().front()->uid.get_name() == "Child"); +} + +TEST_CASE("queries_empty reflects whether any query has been parsed") +{ + auto doc = document_fixture{}.add_default_process().parse(); + CHECK(doc.queries_empty()); +} + +TEST_CASE("get_options/set_options round-trip") +{ + auto doc = document_fixture{}.add_default_process().parse(); + CHECK(doc.get_options().empty()); + doc.get_options().emplace_back("Key", "Value"); + REQUIRE(doc.get_options().size() == 1); + auto opts = doc.get_options(); + opts.emplace_back("Other", "2"); + doc.set_options(opts); + CHECK(doc.get_options().size() == 2); +} + +TEST_CASE("get_proc_priority reflects a system-level priority ordering") +{ + static constexpr auto xta = R"XTA( +process T() { state L0; init L0; } +P1 = T(); +P2 = T(); +system P1 < P2; +)XTA"; + auto doc = Document{}; + REQUIRE(parse_XTA(xta, doc, true)); + CHECK(doc.get_proc_priority("P1") < doc.get_proc_priority("P2")); +} + +TEST_CASE("remove_process removes an instance from the process list") +{ + auto doc = document_fixture{}.add_default_process().parse(); + auto& processes = doc.get_processes(); + const auto before = processes.size(); + REQUIRE(before > 0); + doc.remove_process(processes.front()); + CHECK(doc.get_processes().size() == before - 1); +} + +TEST_CASE("copy_variables_from_to copies a template's local variables") +{ + auto doc = document_fixture{} + .add_template(R"XML()XML") + .add_template(R"XML()XML") + .add_system_decl("Process = T1();") + .add_process("Process") + .parse(); + auto& templates = doc.get_templates(); + auto t1 = std::find_if(templates.begin(), templates.end(), [](auto& t) { return t.uid.get_name() == "T1"; }); + auto t2 = std::find_if(templates.begin(), templates.end(), [](auto& t) { return t.uid.get_name() == "T2"; }); + REQUIRE(t1 != templates.end()); + REQUIRE(t2 != templates.end()); + CHECK(t2->variables.empty()); + doc.copy_variables_from_to(&*t1, &*t2); + REQUIRE(t2->variables.size() == 1); + CHECK(t2->variables.front().uid.get_name() == "x"); + // copy_functions_from_to is currently an intentional no-op (see its + // comment: "to be implemented"); call it so it's still exercised. + doc.copy_functions_from_to(&*t1, &*t2); +} + +TEST_CASE("add_gantt registers a gantt chart entry, parsed after the system declaration") +{ + // The gantt block follows `system ...;` per the grammar + // (System: SysDecl Progress GanttDecl), which document_fixture's + // add_system_decl() cannot express since it inserts text before that + // line -- build the document manually. + static constexpr auto xml = R"XML( + + + bool b; + + +Process = Template(); +system Process; +gantt { G : b -> b; } + + +)XML"; + auto doc = UTAP::Document{}; + parse_XML_buffer(xml, doc, true); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + CHECK(doc.get_globals().ganttChart.size() == 1); +} + +TEST_CASE("add_io_decl appends a new IODecl to the global declarations") +{ + auto doc = document_fixture{}.add_default_process().parse(); + const auto before = doc.get_globals().iodecl.size(); + auto* decl = doc.add_io_decl(); + REQUIRE(decl != nullptr); + CHECK(doc.get_globals().iodecl.size() == before + 1); +} + +TEST_CASE("ChanPriority::print/str renders a channel priority declaration") +{ + auto doc = document_fixture{} + .add_global_decl("chan c1; chan c2; chan priority c1 < c2;") + .add_default_process() + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + REQUIRE(doc.get_chan_priorities().size() == 1); + CHECK(doc.get_chan_priorities().front().str() == "chan priority c1 < c2"); +} + +TEST_CASE("Location::print/str handles locations with and without an invariant") +{ + // Regression test: Location::print() used to call .print() on its + // (possibly empty) invariant/exp_rate Expression members + // unconditionally, crashing with a null-pointer dereference for the + // very common case of a location with no invariant. + auto doc = document_fixture{} + .add_global_decl("clock c;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto& t = doc.get_templates().front(); + REQUIRE(t.locations.size() == 2); + CHECK(t.locations[0].str() == "LOCATION (_id0, , )"); + // TypeChecker's RateDecomposer folds invariants into an "1 && ..." + // conjunction (it seeds the accumulator with a constant true), hence + // the "1 &&" prefix below rather than a bare "(c < 5)". + CHECK(t.locations[1].str() == "LOCATION (_id1, 1 && c < 5, )"); +} + +TEST_CASE("Edge::print/str handles edges with and without a guard/sync/assignment") +{ + // Regression test, same root cause as the Location one above: Edge::print() + // unconditionally called .print() on its (possibly empty) guard/sync/assign. + auto doc = document_fixture{} + .add_global_decl("bool b;") + .add_template(R"XML()XML") + .add_system_decl("Process = T();") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto& t = doc.get_templates().front(); + REQUIRE(t.edges.size() == 2); + // TypeChecker defaults a missing guard/assignment to the constant 1 + // (true / no-op), but leaves a missing synchronisation genuinely empty + // -- which is what Edge::print() used to crash on. + CHECK(t.edges[0].str() == "EDGE (LOCATION (_id0, , ) LOCATION (_id1, , ))\n\t1, , 1"); + CHECK(t.edges[1].str() == "EDGE (LOCATION (_id1, , ) LOCATION (_id2, , ))\n\tb, , 1"); +} + +TEST_CASE("Variable::str, Function::str and Declarations::str") +{ + auto doc = document_fixture{} + .add_default_process() + .add_global_decl("int g = 1; int f(int x) { return x; }") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + auto& globals = doc.get_globals(); + auto g = std::find_if(globals.variables.begin(), globals.variables.end(), + [](auto& v) { return v.uid.get_name() == "g"; }); + REQUIRE(g != globals.variables.end()); + CHECK(g->str() == "int g = 1"); + REQUIRE(!globals.functions.empty()); + CHECK(globals.functions.front().str() == Contains{"f(int x)"}); + // Declarations::str(bool) hides the Stringify::str() base + // overload for ordinary lookup; call it explicitly. + CHECK(static_cast&>(globals).str() == Contains{"int g = 1;"}); +} + +TEST_CASE("Template::is_invariant reflects an LSC template's declared mode") +{ + static constexpr auto xml = R"XML( + + + + + + LscTemplate + Universal + invariant + A + + +Process = A(); +system Process; + + +)XML"; + auto doc = UTAP::Document{}; + parse_XML_buffer(xml, doc, true); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + const auto* t = doc.find_template("LscTemplate"); + REQUIRE(t != nullptr); + CHECK(t->is_invariant()); +} + +TEST_CASE("Instance::arguments_str/mapping_str/print_arguments/print_mapping") +{ + auto doc = document_fixture{} + .add_template(template_fixture{"T"}.add_parameter("int p").str()) + .add_system_decl("Process = T(5);") + .add_process("Process") + .parse(); + const auto& errs = doc.get_errors(); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + REQUIRE(!doc.get_processes().empty()); + const auto& process = doc.get_processes().front(); + CHECK(process.arguments_str() == "5"); + CHECK(process.mapping_str() == "p = 5\n"); +} + +TEST_SUITE_END(); From 6c2f209df1cb33ad790a38506e1c5d44027bc973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Thu, 9 Jul 2026 07:48:49 +0000 Subject: [PATCH 23/54] Fix XMLReader::model_options() and expectation() to parse nested elements model_options() used begin(Tag::OPTION) with the default skipEmpty=true, silently skipping self-closing