diff --git a/.clang-format b/.clang-format index 1be1c078..9f9a7610 100644 --- a/.clang-format +++ b/.clang-format @@ -7,4 +7,5 @@ AllowShortIfStatementsOnASingleLine: Never SpaceBeforeParens: ControlStatements AllowShortFunctionsOnASingleLine: Empty AlignAfterOpenBracket: Align -ColumnLimit: 100 \ No newline at end of file +ColumnLimit: 100 +BinPackArguments: false \ No newline at end of file diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index d40811c0..256ff8dc 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -33,13 +33,5 @@ jobs: - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build - - - name: Test - #working-directory: ${{github.workspace}}/build/apps/TestsApp - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - # ну почему не из корня ... - #run: ctest -C ${{env.BUILD_TYPE}} - run: ./build/apps/TestsApp/TestsApp diff --git a/.github/workflows/integrationTests.yml b/.github/workflows/integrationTests.yml new file mode 100644 index 00000000..fca36c90 --- /dev/null +++ b/.github/workflows/integrationTests.yml @@ -0,0 +1,35 @@ +name: IntegrationTests + +on: [ push ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build + + - name: Test + #working-directory: ${{github.workspace}}/build/apps/TestsApp + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # ну почему не из корня ... + #run: ctest -C ${{env.BUILD_TYPE}} + run: ./build/apps/IntegrationTestsApp/IntegrationTestsApp + + \ No newline at end of file diff --git a/.github/workflows/metamorphicTests.yml b/.github/workflows/metamorphicTests.yml new file mode 100644 index 00000000..a270ce96 --- /dev/null +++ b/.github/workflows/metamorphicTests.yml @@ -0,0 +1,35 @@ +name: MetamorphicTests + +on: [ push ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build + + - name: Test + #working-directory: ${{github.workspace}}/build/apps/TestsApp + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # ну почему не из корня ... + #run: ctest -C ${{env.BUILD_TYPE}} + run: ./build/apps/MetamorphicTestsApp/MetamorphicTestsApp + + \ No newline at end of file diff --git a/.github/workflows/unitTests.yml b/.github/workflows/unitTests.yml new file mode 100644 index 00000000..70ba8992 --- /dev/null +++ b/.github/workflows/unitTests.yml @@ -0,0 +1,35 @@ +name: UnitTests + +on: [ push ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build + + - name: Test + #working-directory: ${{github.workspace}}/build/apps/TestsApp + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # ну почему не из корня ... + #run: ctest -C ${{env.BUILD_TYPE}} + run: ./build/apps/UnitTestsApp/UnitTestsApp + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d5540c4..1d1a16e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,4 +14,6 @@ add_subdirectory(libs/Interpreter) add_subdirectory(libs/InputGenerator) add_subdirectory(apps/InterpreterApp) add_subdirectory(apps/InputGeneratorApp) -add_subdirectory(apps/TestsApp) \ No newline at end of file +add_subdirectory(apps/UnitTestsApp) +add_subdirectory(apps/IntegrationTestsApp) +add_subdirectory(apps/MetamorphicTestsApp) \ No newline at end of file diff --git a/apps/IntegrationTestsApp/CMakeLists.txt b/apps/IntegrationTestsApp/CMakeLists.txt new file mode 100644 index 00000000..2047bf6e --- /dev/null +++ b/apps/IntegrationTestsApp/CMakeLists.txt @@ -0,0 +1,37 @@ +# Set the project name +project(IntegrationTestsApp) + +# Create a sources variable with a link to all cpp files to compile + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) + +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +set(SOURCES + src/main.cpp + src/IntegrationTests.cpp) + + +# Add a library with the above sources +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include + ) + +target_link_libraries(${PROJECT_NAME} + InputGenerator + Interpreter + Tester + Objects + gtest + ) + +enable_testing() +add_test(NAME IntegrationTests + COMMAND ${PROJECT_NAME}) \ No newline at end of file diff --git a/apps/IntegrationTestsApp/src/IntegrationTests.cpp b/apps/IntegrationTestsApp/src/IntegrationTests.cpp new file mode 100644 index 00000000..3497014c --- /dev/null +++ b/apps/IntegrationTestsApp/src/IntegrationTests.cpp @@ -0,0 +1 @@ +#include "gtest/gtest.h" \ No newline at end of file diff --git a/apps/IntegrationTestsApp/src/main.cpp b/apps/IntegrationTestsApp/src/main.cpp new file mode 100644 index 00000000..63fb136b --- /dev/null +++ b/apps/IntegrationTestsApp/src/main.cpp @@ -0,0 +1,11 @@ +#include "gtest/gtest.h" +#include +#include +using namespace std; + +int main(int argc, char** argv) { + cout << "Integration Tests\n"; + // Тестирование + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/apps/MetamorphicTestsApp/CMakeLists.txt b/apps/MetamorphicTestsApp/CMakeLists.txt new file mode 100644 index 00000000..917f40c5 --- /dev/null +++ b/apps/MetamorphicTestsApp/CMakeLists.txt @@ -0,0 +1,37 @@ +# Set the project name +project(MetamorphicTestsApp) + +# Create a sources variable with a link to all cpp files to compile + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) + +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +set(SOURCES + src/main.cpp + src/MetamorphicTests.cpp) + + +# Add a library with the above sources +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include + ) + +target_link_libraries(${PROJECT_NAME} + InputGenerator + Interpreter + Tester + Objects + gtest + ) + +enable_testing() +add_test(NAME IntegrationTests + COMMAND ${PROJECT_NAME}) \ No newline at end of file diff --git a/apps/MetamorphicTestsApp/src/MetamorphicTests.cpp b/apps/MetamorphicTestsApp/src/MetamorphicTests.cpp new file mode 100644 index 00000000..3497014c --- /dev/null +++ b/apps/MetamorphicTestsApp/src/MetamorphicTests.cpp @@ -0,0 +1 @@ +#include "gtest/gtest.h" \ No newline at end of file diff --git a/apps/MetamorphicTestsApp/src/main.cpp b/apps/MetamorphicTestsApp/src/main.cpp new file mode 100644 index 00000000..80bb5ed4 --- /dev/null +++ b/apps/MetamorphicTestsApp/src/main.cpp @@ -0,0 +1,11 @@ +#include "gtest/gtest.h" +#include +#include +using namespace std; + +int main(int argc, char** argv) { + cout << "Metamorphic Tests\n"; + // Тестирование + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/apps/TestsApp/src/main.cpp b/apps/TestsApp/src/main.cpp deleted file mode 100644 index a4d50078..00000000 --- a/apps/TestsApp/src/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "TestsApp/Example.h" -#include -using namespace std; - -int main() { - cout << "Test\n"; - // Тестирование - Example::test_all(); - Example::all_examples(); - Example::logger_test(); -} \ No newline at end of file diff --git a/apps/TestsApp/CMakeLists.txt b/apps/UnitTestsApp/CMakeLists.txt similarity index 60% rename from apps/TestsApp/CMakeLists.txt rename to apps/UnitTestsApp/CMakeLists.txt index a74c4337..64811668 100644 --- a/apps/TestsApp/CMakeLists.txt +++ b/apps/UnitTestsApp/CMakeLists.txt @@ -1,10 +1,22 @@ # Set the project name -project(TestsApp) +project(UnitTestsApp) # Create a sources variable with a link to all cpp files to compile + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) + +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + set(SOURCES src/Example.cpp - src/main.cpp) + src/main.cpp + src/UnitTests.cpp) + # Add a library with the above sources add_executable(${PROJECT_NAME} ${SOURCES}) @@ -18,9 +30,9 @@ target_link_libraries(${PROJECT_NAME} Interpreter Tester Objects + gtest ) enable_testing() - add_test(NAME UnitTests COMMAND ${PROJECT_NAME}) \ No newline at end of file diff --git a/apps/TestsApp/include/TestsApp/Example.h b/apps/UnitTestsApp/include/UnitTestsApp/Example.h similarity index 73% rename from apps/TestsApp/include/TestsApp/Example.h rename to apps/UnitTestsApp/include/UnitTestsApp/Example.h index f0a7ce26..7948173f 100644 --- a/apps/TestsApp/include/TestsApp/Example.h +++ b/apps/UnitTestsApp/include/UnitTestsApp/Example.h @@ -27,7 +27,6 @@ class Example { static void intersection(); static void regex_parsing(); static void regex_generating(); - static void random_regex_parsing(); static void tasks_generating(); static void parsing_regex(string); static void transformation_monoid_example(); @@ -48,22 +47,4 @@ class Example { static void arden_lemma_testing(); static void fa_to_pgrammar(); static void logger_test(); - - // запуск всех тестов - static void test_all(); - static void test_fa_equal(); - static void test_fa_equiv(); - static void test_bisimilar(); - static void test_regex_subset(); - static void test_merge_bisimilar(); - static void test_regex_equal(); - static void test_ambiguity(); - static void test_arden(); - static void test_pump_length(); - static void test_is_one_unambiguous(); - static void test_get_one_unambiguous_regex(); - static void test_interpreter(); - static void test_TransformationMonoid(); - static void test_GlaisterShallit(); - static void test_fa_to_pgrammar(); }; \ No newline at end of file diff --git a/apps/UnitTestsApp/include/UnitTestsApp/UnitTests.h b/apps/UnitTestsApp/include/UnitTestsApp/UnitTests.h new file mode 100644 index 00000000..d20abd79 --- /dev/null +++ b/apps/UnitTestsApp/include/UnitTestsApp/UnitTests.h @@ -0,0 +1,29 @@ +#pragma once + +#include "AutomatonToImage/AutomatonToImage.h" +#include "InputGenerator/RegexGenerator.h" +#include "Interpreter/Interpreter.h" +#include "Objects/AlgExpression.h" +#include "Objects/FiniteAutomaton.h" +#include "Objects/Grammar.h" +#include "Objects/Language.h" +#include "Objects/Regex.h" +#include "Objects/TransformationMonoid.h" +#include "Tester/Tester.h" +#include "gtest/gtest.h" +#include + +class UnitTests +{ +public: + UnitTests(){}; + + static int RunTests(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); + }; + + using Lexeme = AlgExpression::Lexeme; + using LexemeType = AlgExpression::Lexeme::Type; + static std::vector parse_string(std::string str) {return AlgExpression::parse_string(str);}; +}; diff --git a/apps/TestsApp/src/Example.cpp b/apps/UnitTestsApp/src/Example.cpp similarity index 50% rename from apps/TestsApp/src/Example.cpp rename to apps/UnitTestsApp/src/Example.cpp index 132141e5..2e9db86e 100644 --- a/apps/TestsApp/src/Example.cpp +++ b/apps/UnitTestsApp/src/Example.cpp @@ -1,4 +1,4 @@ -#include "TestsApp/Example.h" +#include "UnitTestsApp/Example.h" void Example::determinize() { vector states; @@ -187,19 +187,6 @@ void Example::tasks_generating() { cout << "\n" << TG.generate_task(5, 6, false, true); // для динам. тайпчека } -void Example::random_regex_parsing() { - RegexGenerator rg(15, 10, 5, 3); - for (int i = 0; i < 30; i++) { - string str = rg.generate_regex(); - cout << "\n" << str << "\n"; - Regex r1(str); - string r1_str = r1.to_txt(); - Regex r2(r1_str); - cout << r1_str << endl; - assert(Regex::equivalent(r1, r2)); - } -} - void Example::parsing_regex(string str) { cout << str << endl; Regex r(str); @@ -533,295 +520,6 @@ void Example::arden_lemma_testing() { }); } -// TEST - -void Example::test_all() { - test_fa_equal(); - test_fa_equiv(); - test_bisimilar(); - test_regex_subset(); - test_merge_bisimilar(); - test_regex_equal(); - test_ambiguity(); - test_arden(); - test_pump_length(); - test_is_one_unambiguous(); - test_get_one_unambiguous_regex(); - test_interpreter(); - test_TransformationMonoid(); - test_GlaisterShallit(); - test_fa_to_pgrammar(); - cout << "all tests passed\n\n"; -} - -void Example::test_fa_equal() { - vector states1; - for (int i = 0; i < 6; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states1.push_back(s); - } - states1[0].set_transition(1, "b"); - states1[0].set_transition(2, "b"); - states1[0].set_transition(5, "c"); - states1[1].set_transition(3, "a"); - states1[1].set_transition(4, "c"); - states1[2].set_transition(4, "a"); - states1[5].set_transition(4, "a"); - states1[3].is_terminal = true; - states1[4].is_terminal = true; - FiniteAutomaton fa1(0, states1, {"a", "b", "c"}); - - vector states2; - for (int i = 0; i < 6; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states2.push_back(s); - } - states2[0].set_transition(1, "b"); - states2[0].set_transition(2, "b"); - states2[0].set_transition(5, "c"); - states2[1].set_transition(3, "a"); - states2[1].set_transition(3, "c"); - states2[2].set_transition(4, "a"); - states2[5].set_transition(4, "a"); - states2[3].is_terminal = true; - states2[4].is_terminal = true; - FiniteAutomaton fa2(0, states2, {"a", "b", "c"}); - - vector states3; - for (int i = 0; i < 6; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states3.push_back(s); - } - states3[5].set_transition(4, "b"); - states3[5].set_transition(3, "b"); - states3[5].set_transition(0, "c"); - states3[4].set_transition(2, "a"); - states3[4].set_transition(1, "c"); - states3[3].set_transition(1, "a"); - states3[0].set_transition(1, "a"); - states3[2].is_terminal = true; - states3[1].is_terminal = true; - FiniteAutomaton fa3(5, states3, {"a", "b", "c"}); - - assert(FiniteAutomaton::equal(fa1, fa1)); - assert(!FiniteAutomaton::equal(fa1, fa2)); - assert(FiniteAutomaton::equal(fa1, fa3)); - assert(FiniteAutomaton::equal(Regex("(aab|aab)*").to_thompson().remove_eps(), - Regex("(aab|aab)*").to_glushkov())); - assert(FiniteAutomaton::equal(Regex("a(a)*ab(bb)*baa").to_thompson().remove_eps(), - Regex("a(a)*ab(bb)*baa").to_glushkov())); - assert(FiniteAutomaton::equal( - Regex("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|bbb*(aaa*bbb*)*") - .to_thompson() - .remove_eps(), - Regex("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|bbb*(aaa*bbb*)*").to_glushkov())); -} - -void Example::test_fa_equiv() { - vector states1; - for (int i = 0; i < 3; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states1.push_back(s); - } - states1[0].set_transition(0, "c"); - states1[0].set_transition(1, "d"); - states1[1].set_transition(2, "c"); - states1[1].set_transition(0, "d"); - states1[2].set_transition(1, "c"); - states1[2].set_transition(2, "d"); - states1[0].is_terminal = true; - FiniteAutomaton fa1(0, states1, {"c", "d"}); - - vector states2; - for (int i = 0; i < 4; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states2.push_back(s); - } - states2[0].set_transition(0, "c"); - states2[0].set_transition(1, "d"); - states2[1].set_transition(2, "c"); - states2[1].set_transition(0, "d"); - states2[2].set_transition(3, "c"); - states2[2].set_transition(2, "d"); - states2[3].set_transition(2, "c"); - states2[3].set_transition(0, "d"); - states2[0].is_terminal = true; - FiniteAutomaton fa2(0, states2, {"c", "d"}); - - assert(FiniteAutomaton::equivalent(fa1, fa2)); -} - -void Example::test_bisimilar() { - vector states1; - for (int i = 0; i < 3; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states1.push_back(s); - } - states1[0].set_transition(1, "a"); - states1[0].set_transition(1, "eps"); - states1[0].set_transition(2, "b"); - states1[1].set_transition(2, "a"); - states1[1].set_transition(1, "b"); - states1[2].set_transition(1, "a"); - states1[2].set_transition(1, "eps"); - states1[2].set_transition(0, "b"); - states1[0].is_terminal = true; - states1[2].is_terminal = true; - FiniteAutomaton fa1(1, states1, {"a", "b"}); - - vector states2; - for (int i = 0; i < 2; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states2.push_back(s); - } - states2[0].set_transition(1, "a"); - states2[0].set_transition(1, "eps"); - states2[0].set_transition(0, "b"); - states2[1].set_transition(0, "a"); - states2[1].set_transition(1, "b"); - states2[0].is_terminal = true; - FiniteAutomaton fa2(1, states2, {"a", "b"}); - - assert(FiniteAutomaton::bisimilar(fa1, fa2)); -} - -void Example::test_merge_bisimilar() { - FiniteAutomaton fa = Regex("(a|b)*b").to_glushkov(); - FiniteAutomaton fa1 = fa.merge_bisimilar(); - - vector states2; - for (int i = 0; i < 3; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states2.push_back(s); - } - states2[0].set_transition(1, "a"); - states2[0].set_transition(1, "eps"); - states2[0].set_transition(2, "b"); - states2[1].set_transition(2, "a"); - states2[1].set_transition(1, "b"); - states2[2].set_transition(1, "a"); - states2[2].set_transition(1, "eps"); - states2[2].set_transition(0, "b"); - states2[0].is_terminal = true; - states2[2].is_terminal = true; - FiniteAutomaton fa2(1, states2, {"a", "b"}); - - vector states3; - for (int i = 0; i < 2; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states3.push_back(s); - } - states3[0].set_transition(0, "b"); - states3[0].set_transition(1, "a"); - states3[1].set_transition(0, "eps"); - states3[1].set_transition(0, "a"); - states3[1].set_transition(1, "b"); - states3[1].is_terminal = true; - FiniteAutomaton fa3(0, states3, {"a", "b"}); - - assert(FiniteAutomaton::equal(Regex("(a|b)*b").to_ilieyu(), fa1)); - assert(FiniteAutomaton::equal(fa2.merge_bisimilar(), fa3)); -} - -void Example::test_regex_subset() { - Regex r1("a*baa"); - Regex r2("abaa"); - - assert(r1.subset(r2)); - assert(!r2.subset(r1)); - assert(!Regex("ab*").subset(Regex("a*b*"))); -} - -void Example::test_regex_equal() { - Regex r1("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*"); - Regex r2("aaa*(bbb*aaa*)*|a(bbb*aaa*)*bb*"); - Regex r3("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)"); - - assert(Regex::equal(r1, r2)); - assert(!Regex::equal(r1.linearize(), r1)); - assert(Regex::equal(r1.linearize(), r1.linearize())); - assert(!Regex::equal(r1, r3)); -} - -void Example::test_ambiguity() { - enum AutomatonType { - thompson, - glushkov, - ilieyu - }; - using Test = tuple; - vector tests = { - //{0, "(a*)*", thompson, FiniteAutomaton::exponentially_ambiguous}, - {1, "a*a*", glushkov, FiniteAutomaton::polynomially_ambigious}, - {2, "abc", thompson, FiniteAutomaton::unambigious}, - //{3, "b|a", thompson, FiniteAutomaton::almost_unambigious}, - {4, "(aa|aa)*", glushkov, FiniteAutomaton::exponentially_ambiguous}, - {5, "(aab|aab)*", glushkov, FiniteAutomaton::exponentially_ambiguous}, - {6, "a*a*((a)*)*", glushkov, FiniteAutomaton::polynomially_ambigious}, - //{7, "a*a*((a)*)*", thompson, - // FiniteAutomaton::exponentially_ambiguous}, - //{8, "a*(b*)*", thompson, FiniteAutomaton::exponentially_ambiguous}, - //{9, "a*((ab)*)*", thompson, FiniteAutomaton::exponentially_ambiguous}, - {10, "(aa|aa)(aa|bb)*|a(ba)*", glushkov, FiniteAutomaton::almost_unambigious}, - {11, "(aaa)*(a|)(a|)", ilieyu, FiniteAutomaton::almost_unambigious}, - {12, "(a|)(ab|aaa|baa)*(a|)", glushkov, FiniteAutomaton::almost_unambigious}, - {13, "(a|b|c)*(d|d)*(a|b|c|d)*", glushkov, FiniteAutomaton::almost_unambigious}, - {14, "(ac*|ad*)*", glushkov, FiniteAutomaton::exponentially_ambiguous}, - {15, "(a|b|c)*(a|b|c|d)(a|b|c)*|(a|b)*ca*", glushkov, FiniteAutomaton::almost_unambigious}, - {16, "(a|b|c)*(a|b|c|d)(a|b|c)*|(ac*|ad*)*", glushkov, FiniteAutomaton::almost_unambigious}, - {17, - "(ab)*ab(ab)*|(ac)*(ac)*|(d|c)*", // (abab)*abab(abab)*|(aac)*(aac)*|(b|d|c)* - glushkov, FiniteAutomaton::almost_unambigious}, - {18, "(abab)*abab(abab)*|(aac)*(aac)*", glushkov, FiniteAutomaton::polynomially_ambigious}, - {19, "(ab)*ab(ab)*", // (abab)*abab(abab)* - glushkov, FiniteAutomaton::polynomially_ambigious}, - {20, "(ab)*ab(ab)*|(ac)*(ac)*", glushkov, FiniteAutomaton::polynomially_ambigious}, - // {21, "(a|b)*(f*)*q", thompson, - // FiniteAutomaton::exponentially_ambiguous}, - {22, "((bb*c|c)c*b|bb*b|b)(b|(c|bb*c)c*b|bb*b)*", glushkov, - FiniteAutomaton::exponentially_ambiguous}, - }; - - for_each(tests.begin(), tests.end(), [](const Test& test) { - auto [test_number, reg_string, type, expected_res] = test; - cout << test_number << endl; - switch (type) { - case thompson: - assert(Regex(reg_string).to_thompson().ambiguity() == expected_res); - break; - case glushkov: - assert(Regex(reg_string).to_glushkov().ambiguity() == expected_res); - break; - case ilieyu: - assert(Regex(reg_string).to_ilieyu().ambiguity() == expected_res); - break; - } - }); -} -void Example::test_arden() { - auto test_equivalence = [](string rgx_str) { - Regex reg(rgx_str); - assert(Regex::equivalent(reg, reg.to_thompson().to_regex())); - assert(Regex::equivalent(reg, reg.to_glushkov().to_regex())); - assert(Regex::equivalent(reg, reg.to_ilieyu().to_regex())); - assert(Regex::equivalent(reg, reg.to_antimirov().to_regex())); - }; - - test_equivalence("a"); - test_equivalence("a*"); - test_equivalence("(ab)*a"); - test_equivalence("a(a)*ab(bb)*baa"); - test_equivalence("(b)*(b)"); - test_equivalence("a*|"); - test_equivalence("|b((b((a)*)(a(|(a))))*)"); - test_equivalence("(((a*)))(((a(b|)|a)*||b))"); - test_equivalence("((b(((ba|b)|||(b))*)))"); - test_equivalence("(((((a*)((a*)|bb)(((|||((b)))))))))"); -} -void Example::test_pump_length() { - assert(Regex("abaa").pump_length() == 5); -} - void Example::fa_to_pgrammar() { FiniteAutomaton a1 = Regex("(c,1(ab*a|b*)*d,1)|(c,2(ba*b|a*)*d,25)") .to_glushkov() @@ -866,162 +564,4 @@ void Example::fa_to_pgrammar() { cout << g.pg_to_txt(); cout << "+++++++++++++++++++++++++++++" << endl; cout << g.prefix_grammar_to_automaton().to_txt(); -} - -void Example::test_fa_to_pgrammar() { - // cout << "fa to grammar\n"; - vector states1; - for (int i = 0; i < 5; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states1.push_back(s); - } - - states1[4].set_transition(1, "a"); - states1[1].set_transition(2, "b"); - states1[1].set_transition(4, "c"); - states1[2].set_transition(2, "b"); - states1[2].set_transition(2, "c"); - states1[2].is_terminal = true; - states1[0].set_transition(4, "c"); - states1[3].set_transition(0, "a"); - states1[3].set_transition(0, "b"); - states1[4].is_terminal = true; - FiniteAutomaton dfa1 = FiniteAutomaton(3, states1, {"a", "b", "c"}); - - Grammar g; - - // cout << "1\n"; - g.fa_to_prefix_grammar(dfa1); - // cout << "2\n"; - assert(FiniteAutomaton::equivalent(dfa1, g.prefix_grammar_to_automaton())); - // cout << "3\n"; - g.fa_to_prefix_grammar_TM(dfa1); - // cout << "4\n"; - assert(FiniteAutomaton::equivalent(dfa1, g.prefix_grammar_to_automaton())); -} - -void Example::test_is_one_unambiguous() { - Regex r1("(a|b)*a"); - Regex r2("(a|b)*(ac|bd)"); - Regex r3("(a|b)*a(a|b)"); - Regex r4("(c(a|b)*c)*"); - Regex r5("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|"); - - // ok - assert(r1.to_glushkov().is_one_unambiguous()); - // doesn't fulfills the orbit property - assert(!r2.to_glushkov().is_one_unambiguous()); - // consists of a single orbit, but neither a nor b is consistent - assert(!r3.to_glushkov().is_one_unambiguous()); - // ok - assert(r4.to_glushkov().is_one_unambiguous()); - // doesn't fulfills the orbit property - assert(!r5.to_glushkov().is_one_unambiguous()); -}; - -void Example::test_get_one_unambiguous_regex() { - auto check_one_unambiguous = [](string rgx_str, bool expected_res) { - assert(Regex(rgx_str).get_one_unambiguous_regex().is_one_unambiguous() == expected_res); - }; - // ok - check_one_unambiguous("(a|b)*a", true); - // doesn't fulfills the orbit property - check_one_unambiguous("(a|b)*(ac|bd)", false); - // consists of a single orbit, but neither a nor b is consistent - check_one_unambiguous("(a|b)*a(a|b)", false); - // ok - check_one_unambiguous("(c(a|b)*c)*", true); - // doesn't fulfills the orbit property - check_one_unambiguous("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|", false); -} - -void Example::test_interpreter() { - Interpreter interpreter; - interpreter.set_log_mode(Interpreter::LogMode::nothing); - assert(!interpreter.run_line("A = Annote (Glushkova {a})")); - assert(interpreter.run_line(" N1 = ( ( Glushkov ({ab|a}) )) ")); - assert(interpreter.run_line(" N2 = (Annote N1)")); - assert(!interpreter.run_line("N2 = (Glushkov N1)")); - assert(!interpreter.run_line("Equiv N1 N3")); - assert(interpreter.run_line(" Equiv (( N1)) ( (Reverse .Reverse (N2) !! ))")); - assert(interpreter.run_line("Test (Glushkov {a*}) {a*} 1")); - - assert(interpreter.run_line("A = Annote.Glushkov.DeAnnote {a}")); - assert(interpreter.run_line("B = Annote (Glushkov.DeAnnote {a})")); - assert(interpreter.run_line("B = Annote (Glushkov(DeAnnote {a}))")); - assert(interpreter.run_line("A = Annote .Glushkov. DeAnnote {a} !! ")); - assert(interpreter.run_line("B = Annote (Glushkov.DeAnnote {a}) !! ")); - assert(interpreter.run_line("B = Annote ( Glushkov(DeAnnote {a})) !! ")); - assert(interpreter.run_line("B = Annote (Glushkov {a} !!)")); - assert(interpreter.run_line("B = Annote (Glushkov(DeAnnote {a} !!) !!) !!")); - - // Arrays - assert(interpreter.run_line("A = []")); - assert(interpreter.run_line("A = [[] []]")); - assert(interpreter.run_line("A = [{a} {b}]")); - assert(interpreter.run_line("A = [[(([{a}]))] [{a} []]]")); - assert(!interpreter.run_line("A = [[(([{a}])] [{a} []]]")); - assert(!interpreter.run_line("A = [[([{a}]))] [{a} []]]")); - assert(!interpreter.run_line("A = [[(([{a}]))] [{a} []]")); - assert(!interpreter.run_line("A = [[(([a}]))] [{a} (Glushkov(DeAnnote {a} !!) !!) []]]")); - - // Normalize - assert(interpreter.run_line("A = Normalize {abc} [[{a} {b}]]")); - assert(!interpreter.run_line("A = Normalize {abc} [[{a} []]]")); -} - -void Example::test_TransformationMonoid() { - FiniteAutomaton fa1 = Regex("a*b*c*").to_thompson().minimize(); - TransformationMonoid tm1(fa1); - assert(tm1.class_card() == 7); - assert(tm1.class_length() == 2); - assert(tm1.is_minimal()); - assert(tm1.get_classes_number_MyhillNerode() == 3); - - vector states; - for (int i = 0; i < 5; i++) { - State s = {i, {i}, to_string(i), false, map>()}; - states.push_back(s); - } - states[0].set_transition(1, "a"); - states[1].set_transition(2, "c"); - states[2].set_transition(3, "a"); - states[3].set_transition(2, "c"); - states[3].set_transition(4, "b"); - states[4].set_transition(4, "b"); - states[4].set_transition(4, "c"); - states[4].is_terminal = true; - FiniteAutomaton fa2(0, states, {"a", "b", "c"}); - TransformationMonoid tm2(fa2); - assert(tm2.class_card() == 12); - assert(tm2.class_length() == 4); - assert(tm2.is_minimal() == 1); - assert(tm2.get_classes_number_MyhillNerode() == 5); - - FiniteAutomaton fa3 = Regex("ab|b").to_glushkov().minimize(); - TransformationMonoid tm3(fa3); - assert(tm3.is_minimal()); - - FiniteAutomaton fa4 = Regex("a").to_glushkov().minimize(); - TransformationMonoid tm4(fa4); - assert(tm4.is_minimal()); - - FiniteAutomaton fa5 = Regex("b*a*").to_thompson().minimize(); - TransformationMonoid tm5(fa5); - assert(tm5.is_minimal()); -} - -void Example::test_GlaisterShallit() { - auto check_classes_number = [](string rgx_str, int num) { - assert(Regex(rgx_str).to_glushkov().get_classes_number_GlaisterShallit() == num); - }; - check_classes_number("abc", 4); - check_classes_number("a*b*c*", 3); - check_classes_number("aa*bb*cc*", 4); - check_classes_number("ab|abc", 4); - check_classes_number("a(a|b)*(a|b)", 3); - check_classes_number("a((a|b)*)*(b|c)", 3); - check_classes_number("a(b|c)(a|b)(b|c)", 5); - check_classes_number("abc|bca", 6); - check_classes_number("abc|bbc", 4); } \ No newline at end of file diff --git a/apps/UnitTestsApp/src/UnitTests.cpp b/apps/UnitTestsApp/src/UnitTests.cpp new file mode 100644 index 00000000..e0d99604 --- /dev/null +++ b/apps/UnitTestsApp/src/UnitTests.cpp @@ -0,0 +1,491 @@ +#include "UnitTestsApp/UnitTests.h" + +TEST(ParseStringTest, Test_regex_lexer) { + struct Test { + string regex_str; + bool want_err; + int lexemes_len = 0; + }; + + vector tests = { + {"[]", true}, + {"[]:", true}, + {"[a]", true}, + {"[a]:", true}, + {"[[a]:1", true}, + {"a]:1", true}, + {"[a]:1", false, 3}, + {"&", true}, + {"&1", false, 1}, + {"[b[a]:1&1]:2&2", false, 11}, + // тесты на отрицание + {"^a", false, 2}, + {"a^|b", true}, + {"d^*^b", true}, + {"d|^|b", true}, + {"a^", false, 4}, // a . ^ eps + {"a|(c^)", false, 8}, // a | ( c . ^ eps ) + }; + + for (const auto& t : tests) { + stringstream message; + message << "Case: " << t.regex_str << ", WantErr: " << t.want_err; + SCOPED_TRACE(message.str()); + + vector l = UnitTests::parse_string(t.regex_str); + ASSERT_FALSE(l.empty()); + + if (t.want_err) { + ASSERT_EQ(UnitTests::LexemeType::error, l[0].type); + } else { + ASSERT_NE(UnitTests::LexemeType::error, l[0].type); + ASSERT_EQ(t.lexemes_len, l.size()); + // TODO: добавить проверку содержимого l + } + } +} + +TEST(TestCaseName, Test_random_regex_parsing) { + RegexGenerator rg(15, 10, 5, 3); + for (int i = 0; i < 30; i++) { + string str = rg.generate_regex(); + Regex r1(str); + string r1_str = r1.to_txt(); + Regex r2(r1_str); + ASSERT_EQ(true, Regex::equivalent(r1, r2)); + } +} + +TEST(TestCaseName, Test_fa_equal) { + vector states1; + for (int i = 0; i < 6; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states1.push_back(s); + } + states1[0].set_transition(1, "b"); + states1[0].set_transition(2, "b"); + states1[0].set_transition(5, "c"); + states1[1].set_transition(3, "a"); + states1[1].set_transition(4, "c"); + states1[2].set_transition(4, "a"); + states1[5].set_transition(4, "a"); + states1[3].is_terminal = true; + states1[4].is_terminal = true; + FiniteAutomaton fa1(0, states1, {"a", "b", "c"}); + + vector states2; + for (int i = 0; i < 6; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states2.push_back(s); + } + states2[0].set_transition(1, "b"); + states2[0].set_transition(2, "b"); + states2[0].set_transition(5, "c"); + states2[1].set_transition(3, "a"); + states2[1].set_transition(3, "c"); + states2[2].set_transition(4, "a"); + states2[5].set_transition(4, "a"); + states2[3].is_terminal = true; + states2[4].is_terminal = true; + FiniteAutomaton fa2(0, states2, {"a", "b", "c"}); + + vector states3; + for (int i = 0; i < 6; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states3.push_back(s); + } + states3[5].set_transition(4, "b"); + states3[5].set_transition(3, "b"); + states3[5].set_transition(0, "c"); + states3[4].set_transition(2, "a"); + states3[4].set_transition(1, "c"); + states3[3].set_transition(1, "a"); + states3[0].set_transition(1, "a"); + states3[2].is_terminal = true; + states3[1].is_terminal = true; + FiniteAutomaton fa3(5, states3, {"a", "b", "c"}); + + ASSERT_TRUE(FiniteAutomaton::equal(fa1, fa1)); + ASSERT_TRUE(!FiniteAutomaton::equal(fa1, fa2)); + ASSERT_TRUE(FiniteAutomaton::equal(fa1, fa3)); + ASSERT_TRUE(FiniteAutomaton::equal(Regex("(aab|aab)*").to_thompson().remove_eps(), + Regex("(aab|aab)*").to_glushkov())); + ASSERT_TRUE(FiniteAutomaton::equal(Regex("a(a)*ab(bb)*baa").to_thompson().remove_eps(), + Regex("a(a)*ab(bb)*baa").to_glushkov())); + ASSERT_TRUE(FiniteAutomaton::equal( + Regex("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|bbb*(aaa*bbb*)*") + .to_thompson() + .remove_eps(), + Regex("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|bbb*(aaa*bbb*)*").to_glushkov())); +} + +TEST(TestCaseName, Test_fa_equiv) { + vector states1; + for (int i = 0; i < 3; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states1.push_back(s); + } + states1[0].set_transition(0, "c"); + states1[0].set_transition(1, "d"); + states1[1].set_transition(2, "c"); + states1[1].set_transition(0, "d"); + states1[2].set_transition(1, "c"); + states1[2].set_transition(2, "d"); + states1[0].is_terminal = true; + FiniteAutomaton fa1(0, states1, {"c", "d"}); + + vector states2; + for (int i = 0; i < 4; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states2.push_back(s); + } + states2[0].set_transition(0, "c"); + states2[0].set_transition(1, "d"); + states2[1].set_transition(2, "c"); + states2[1].set_transition(0, "d"); + states2[2].set_transition(3, "c"); + states2[2].set_transition(2, "d"); + states2[3].set_transition(2, "c"); + states2[3].set_transition(0, "d"); + states2[0].is_terminal = true; + FiniteAutomaton fa2(0, states2, {"c", "d"}); + + ASSERT_TRUE(FiniteAutomaton::equivalent(fa1, fa2)); +} + +TEST(TestCaseName, Test_bisimilar) { + vector states1; + for (int i = 0; i < 3; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states1.push_back(s); + } + states1[0].set_transition(1, "a"); + states1[0].set_transition(1, "eps"); + states1[0].set_transition(2, "b"); + states1[1].set_transition(2, "a"); + states1[1].set_transition(1, "b"); + states1[2].set_transition(1, "a"); + states1[2].set_transition(1, "eps"); + states1[2].set_transition(0, "b"); + states1[0].is_terminal = true; + states1[2].is_terminal = true; + FiniteAutomaton fa1(1, states1, {"a", "b"}); + + vector states2; + for (int i = 0; i < 2; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states2.push_back(s); + } + states2[0].set_transition(1, "a"); + states2[0].set_transition(1, "eps"); + states2[0].set_transition(0, "b"); + states2[1].set_transition(0, "a"); + states2[1].set_transition(1, "b"); + states2[0].is_terminal = true; + FiniteAutomaton fa2(1, states2, {"a", "b"}); + + ASSERT_TRUE(FiniteAutomaton::bisimilar(fa1, fa2)); +} + +TEST(TestCaseName, Test_merge_bisimilar) { + FiniteAutomaton fa = Regex("(a|b)*b").to_glushkov(); + FiniteAutomaton fa1 = fa.merge_bisimilar(); + + vector states2; + for (int i = 0; i < 3; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states2.push_back(s); + } + states2[0].set_transition(1, "a"); + states2[0].set_transition(1, "eps"); + states2[0].set_transition(2, "b"); + states2[1].set_transition(2, "a"); + states2[1].set_transition(1, "b"); + states2[2].set_transition(1, "a"); + states2[2].set_transition(1, "eps"); + states2[2].set_transition(0, "b"); + states2[0].is_terminal = true; + states2[2].is_terminal = true; + FiniteAutomaton fa2(1, states2, {"a", "b"}); + + vector states3; + for (int i = 0; i < 2; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states3.push_back(s); + } + states3[0].set_transition(0, "b"); + states3[0].set_transition(1, "a"); + states3[1].set_transition(0, "eps"); + states3[1].set_transition(0, "a"); + states3[1].set_transition(1, "b"); + states3[1].is_terminal = true; + FiniteAutomaton fa3(0, states3, {"a", "b"}); + + ASSERT_TRUE(FiniteAutomaton::equal(Regex("(a|b)*b").to_ilieyu(), fa1)); + ASSERT_TRUE(FiniteAutomaton::equal(fa2.merge_bisimilar(), fa3)); +} + +TEST(TestCaseName, Test_regex_subset) { + Regex r1("a*baa"); + Regex r2("abaa"); + + ASSERT_TRUE(r1.subset(r2)); + ASSERT_TRUE(!r2.subset(r1)); + ASSERT_TRUE(!Regex("ab*").subset(Regex("a*b*"))); +} + +TEST(TestCaseName, Test_regex_equal) { + Regex r1("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*"); + Regex r2("aaa*(bbb*aaa*)*|a(bbb*aaa*)*bb*"); + Regex r3("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)"); + + ASSERT_TRUE(Regex::equal(r1, r2)); + ASSERT_TRUE(!Regex::equal(r1.linearize(), r1)); + ASSERT_TRUE(Regex::equal(r1.linearize(), r1.linearize())); + ASSERT_TRUE(!Regex::equal(r1, r3)); +} + +TEST(TestCaseName, Test_ambiguity) { + enum AutomatonType { + thompson, + glushkov, + ilieyu + }; + using Test = tuple; + vector tests = { + //{0, "(a*)*", thompson, FiniteAutomaton::exponentially_ambiguous}, + {1, "a*a*", glushkov, FiniteAutomaton::polynomially_ambigious}, + {2, "abc", thompson, FiniteAutomaton::unambigious}, + //{3, "b|a", thompson, FiniteAutomaton::almost_unambigious}, + {4, "(aa|aa)*", glushkov, FiniteAutomaton::exponentially_ambiguous}, + {5, "(aab|aab)*", glushkov, FiniteAutomaton::exponentially_ambiguous}, + {6, "a*a*((a)*)*", glushkov, FiniteAutomaton::polynomially_ambigious}, + //{7, "a*a*((a)*)*", thompson, + // FiniteAutomaton::exponentially_ambiguous}, + //{8, "a*(b*)*", thompson, FiniteAutomaton::exponentially_ambiguous}, + //{9, "a*((ab)*)*", thompson, FiniteAutomaton::exponentially_ambiguous}, + {10, "(aa|aa)(aa|bb)*|a(ba)*", glushkov, FiniteAutomaton::almost_unambigious}, + {11, "(aaa)*(a|)(a|)", ilieyu, FiniteAutomaton::almost_unambigious}, + {12, "(a|)(ab|aaa|baa)*(a|)", glushkov, FiniteAutomaton::almost_unambigious}, + {13, "(a|b|c)*(d|d)*(a|b|c|d)*", glushkov, FiniteAutomaton::almost_unambigious}, + {14, "(ac*|ad*)*", glushkov, FiniteAutomaton::exponentially_ambiguous}, + {15, "(a|b|c)*(a|b|c|d)(a|b|c)*|(a|b)*ca*", glushkov, FiniteAutomaton::almost_unambigious}, + {16, "(a|b|c)*(a|b|c|d)(a|b|c)*|(ac*|ad*)*", glushkov, FiniteAutomaton::almost_unambigious}, + {17, + "(ab)*ab(ab)*|(ac)*(ac)*|(d|c)*", // (abab)*abab(abab)*|(aac)*(aac)*|(b|d|c)* + glushkov, + FiniteAutomaton::almost_unambigious}, + {18, "(abab)*abab(abab)*|(aac)*(aac)*", glushkov, FiniteAutomaton::polynomially_ambigious}, + {19, + "(ab)*ab(ab)*", // (abab)*abab(abab)* + glushkov, + FiniteAutomaton::polynomially_ambigious}, + {20, "(ab)*ab(ab)*|(ac)*(ac)*", glushkov, FiniteAutomaton::polynomially_ambigious}, + // {21, "(a|b)*(f*)*q", thompson, + // FiniteAutomaton::exponentially_ambiguous}, + {22, + "((bb*c|c)c*b|bb*b|b)(b|(c|bb*c)c*b|bb*b)*", + glushkov, + FiniteAutomaton::exponentially_ambiguous}, + }; + + for_each(tests.begin(), tests.end(), [](const Test& test) { + auto [test_number, reg_string, type, expected_res] = test; + // cout << test_number << endl; + switch (type) { + case thompson: + ASSERT_TRUE(Regex(reg_string).to_thompson().ambiguity() == expected_res); + break; + case glushkov: + ASSERT_TRUE(Regex(reg_string).to_glushkov().ambiguity() == expected_res); + break; + case ilieyu: + ASSERT_TRUE(Regex(reg_string).to_ilieyu().ambiguity() == expected_res); + break; + } + }); +} + +TEST(TestCaseName, Test_arden) { + auto test_equivalence = [](const string& rgx_str) { + Regex reg(rgx_str); + ASSERT_TRUE(Regex::equivalent(reg, reg.to_thompson().to_regex())); + ASSERT_TRUE(Regex::equivalent(reg, reg.to_glushkov().to_regex())); + ASSERT_TRUE(Regex::equivalent(reg, reg.to_ilieyu().to_regex())); + ASSERT_TRUE(Regex::equivalent(reg, reg.to_antimirov().to_regex())); + }; + + test_equivalence("a"); + test_equivalence("a*"); + test_equivalence("(ab)*a"); + test_equivalence("a(a)*ab(bb)*baa"); + test_equivalence("(b)*(b)"); + test_equivalence("a*|"); + test_equivalence("|b((b((a)*)(a(|(a))))*)"); + test_equivalence("(((a*)))(((a(b|)|a)*||b))"); + test_equivalence("((b(((ba|b)|||(b))*)))"); + test_equivalence("(((((a*)((a*)|bb)(((|||((b)))))))))"); +} + +TEST(TestCaseName, Test_pump_length) { + ASSERT_TRUE(Regex("abaa").pump_length() == 5); +} + +TEST(TestCaseName, Test_fa_to_pgrammar) { + // cout << "fa to grammar\n"; + vector states1; + for (int i = 0; i < 5; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states1.push_back(s); + } + + states1[4].set_transition(1, "a"); + states1[1].set_transition(2, "b"); + states1[1].set_transition(4, "c"); + states1[2].set_transition(2, "b"); + states1[2].set_transition(2, "c"); + states1[2].is_terminal = true; + states1[0].set_transition(4, "c"); + states1[3].set_transition(0, "a"); + states1[3].set_transition(0, "b"); + states1[4].is_terminal = true; + FiniteAutomaton dfa1 = FiniteAutomaton(3, states1, {"a", "b", "c"}); + + Grammar g; + + // cout << "1\n"; + g.fa_to_prefix_grammar(dfa1); + // cout << "2\n"; + ASSERT_TRUE(FiniteAutomaton::equivalent(dfa1, g.prefix_grammar_to_automaton())); + // cout << "3\n"; + g.fa_to_prefix_grammar_TM(dfa1); + // cout << "4\n"; + ASSERT_TRUE(FiniteAutomaton::equivalent(dfa1, g.prefix_grammar_to_automaton())); +} + +TEST(TestCaseName, Test_is_one_unambiguous) { + Regex r1("(a|b)*a"); + Regex r2("(a|b)*(ac|bd)"); + Regex r3("(a|b)*a(a|b)"); + Regex r4("(c(a|b)*c)*"); + Regex r5("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|"); + + // ok + ASSERT_TRUE(r1.to_glushkov().is_one_unambiguous()); + // doesn't fulfill the orbit property + ASSERT_TRUE(!r2.to_glushkov().is_one_unambiguous()); + // consists of a single orbit, but neither a nor b is consistent + ASSERT_TRUE(!r3.to_glushkov().is_one_unambiguous()); + // ok + ASSERT_TRUE(r4.to_glushkov().is_one_unambiguous()); + // doesn't fulfill the orbit property + ASSERT_TRUE(!r5.to_glushkov().is_one_unambiguous()); +} + +TEST(TestCaseName, Test_get_one_unambiguous_regex) { + auto check_one_unambiguous = [](const string& rgx_str, bool expected_res) { + ASSERT_TRUE(Regex(rgx_str).get_one_unambiguous_regex().is_one_unambiguous() == + expected_res); + }; + // ok + check_one_unambiguous("(a|b)*a", true); + // doesn't fulfill the orbit property + check_one_unambiguous("(a|b)*(ac|bd)", false); + // consists of a single orbit, but neither a nor b is consistent + check_one_unambiguous("(a|b)*a(a|b)", false); + // ok + check_one_unambiguous("(c(a|b)*c)*", true); + // doesn't fulfill the orbit property + check_one_unambiguous("a(bbb*aaa*)*bb*|aaa*(bbb*aaa*)*|b(aaa*bbb*)*aa*|", false); +} + +TEST(TestCaseName, test_interpreter) { + Interpreter interpreter; + interpreter.set_log_mode(Interpreter::LogMode::nothing); + ASSERT_TRUE(!interpreter.run_line("A = Annote (Glushkova {a})")); + ASSERT_TRUE(interpreter.run_line(" N1 = ( ( Glushkov ({ab|a}) )) ")); + ASSERT_TRUE(interpreter.run_line(" N2 = (Annote N1)")); + ASSERT_TRUE(!interpreter.run_line("N2 = (Glushkov N1)")); + ASSERT_TRUE(!interpreter.run_line("Equiv N1 N3")); + ASSERT_TRUE(interpreter.run_line(" Equiv (( N1)) ( (Reverse .Reverse (N2) !! ))")); + ASSERT_TRUE(interpreter.run_line("Test (Glushkov {a*}) {a*} 1")); + + ASSERT_TRUE(interpreter.run_line("A = Annote.Glushkov.DeAnnote {a}")); + ASSERT_TRUE(interpreter.run_line("B = Annote (Glushkov.DeAnnote {a})")); + ASSERT_TRUE(interpreter.run_line("B = Annote (Glushkov(DeAnnote {a}))")); + ASSERT_TRUE(interpreter.run_line("A = Annote .Glushkov. DeAnnote {a} !! ")); + ASSERT_TRUE(interpreter.run_line("B = Annote (Glushkov.DeAnnote {a}) !! ")); + ASSERT_TRUE(interpreter.run_line("B = Annote ( Glushkov(DeAnnote {a})) !! ")); + ASSERT_TRUE(interpreter.run_line("B = Annote (Glushkov {a} !!)")); + ASSERT_TRUE(interpreter.run_line("B = Annote (Glushkov(DeAnnote {a} !!) !!) !!")); + + // Arrays + ASSERT_TRUE(interpreter.run_line("A = []")); + ASSERT_TRUE(interpreter.run_line("A = [[] []]")); + ASSERT_TRUE(interpreter.run_line("A = [{a} {b}]")); + ASSERT_TRUE(interpreter.run_line("A = [[(([{a}]))] [{a} []]]")); + ASSERT_TRUE(!interpreter.run_line("A = [[(([{a}])] [{a} []]]")); + ASSERT_TRUE(!interpreter.run_line("A = [[([{a}]))] [{a} []]]")); + ASSERT_TRUE(!interpreter.run_line("A = [[(([{a}]))] [{a} []]")); + ASSERT_TRUE(!interpreter.run_line("A = [[(([a}]))] [{a} (Glushkov(DeAnnote {a} !!) !!) []]]")); + + // Normalize + ASSERT_TRUE(interpreter.run_line("A = Normalize {abc} [[{a} {b}]]")); + ASSERT_TRUE(!interpreter.run_line("A = Normalize {abc} [[{a} []]]")); +} + +TEST(TestCaseName, Test_TransformationMonoid) { + FiniteAutomaton fa1 = Regex("a*b*c*").to_thompson().minimize(); + TransformationMonoid tm1(fa1); + ASSERT_TRUE(tm1.class_card() == 7); + ASSERT_TRUE(tm1.class_length() == 2); + ASSERT_TRUE(tm1.is_minimal()); + ASSERT_TRUE(tm1.get_classes_number_MyhillNerode() == 3); + + vector states; + for (int i = 0; i < 5; i++) { + State s = {i, {i}, to_string(i), false, map>()}; + states.push_back(s); + } + states[0].set_transition(1, "a"); + states[1].set_transition(2, "c"); + states[2].set_transition(3, "a"); + states[3].set_transition(2, "c"); + states[3].set_transition(4, "b"); + states[4].set_transition(4, "b"); + states[4].set_transition(4, "c"); + states[4].is_terminal = true; + FiniteAutomaton fa2(0, states, {"a", "b", "c"}); + TransformationMonoid tm2(fa2); + ASSERT_TRUE(tm2.class_card() == 12); + ASSERT_TRUE(tm2.class_length() == 4); + ASSERT_TRUE(tm2.is_minimal() == 1); + ASSERT_TRUE(tm2.get_classes_number_MyhillNerode() == 5); + + FiniteAutomaton fa3 = Regex("ab|b").to_glushkov().minimize(); + TransformationMonoid tm3(fa3); + ASSERT_TRUE(tm3.is_minimal()); + + FiniteAutomaton fa4 = Regex("a").to_glushkov().minimize(); + TransformationMonoid tm4(fa4); + ASSERT_TRUE(tm4.is_minimal()); + + FiniteAutomaton fa5 = Regex("b*a*").to_thompson().minimize(); + TransformationMonoid tm5(fa5); + ASSERT_TRUE(tm5.is_minimal()); +} + +TEST(TestCaseName, Test_GlaisterShallit) { + auto check_classes_number = [](const string& rgx_str, int num) { + ASSERT_TRUE(Regex(rgx_str).to_glushkov().get_classes_number_GlaisterShallit() == num); + }; + check_classes_number("abc", 4); + check_classes_number("a*b*c*", 3); + check_classes_number("aa*bb*cc*", 4); + check_classes_number("ab|abc", 4); + check_classes_number("a(a|b)*(a|b)", 3); + check_classes_number("a((a|b)*)*(b|c)", 3); + check_classes_number("a(b|c)(a|b)(b|c)", 5); + check_classes_number("abc|bca", 6); + check_classes_number("abc|bbc", 4); +} diff --git a/apps/UnitTestsApp/src/main.cpp b/apps/UnitTestsApp/src/main.cpp new file mode 100644 index 00000000..a18639eb --- /dev/null +++ b/apps/UnitTestsApp/src/main.cpp @@ -0,0 +1,12 @@ +#include "UnitTestsApp/Example.h" +#include "UnitTestsApp/UnitTests.h" +#include "gtest/gtest.h" +#include +#include +using namespace std; + +int main(int argc, char** argv) { + cout << "Unit Tests\n"; + // Тестирование + return UnitTests::RunTests(argc, argv); +} diff --git a/libs/Objects/include/Objects/AlgExpression.h b/libs/Objects/include/Objects/AlgExpression.h index 6a2d9942..518951e5 100644 --- a/libs/Objects/include/Objects/AlgExpression.h +++ b/libs/Objects/include/Objects/AlgExpression.h @@ -6,6 +6,7 @@ #include class AlgExpression : public BaseObject { + protected: struct Lexeme { enum Type { @@ -15,6 +16,7 @@ class AlgExpression : public BaseObject { alt, // | conc, // . star, // * + negative, // ^ symb, // alphabet symbol eps, // Epsilon squareBrL, // [ @@ -38,6 +40,7 @@ class AlgExpression : public BaseObject { conc, // Unary: star, + negative, // Terminal: symb, // [i] @@ -77,16 +80,19 @@ class AlgExpression : public BaseObject { // Turns string into lexeme vector static vector parse_string(string); bool from_string(const string&); + // возвращаемый тип нижеперечисленных методов зависит от типа объекта (Regex/BackRefRegex) // внутреннее состояние не имеет значения - virtual AlgExpression* expr(const vector&, int, int); + // Построение из вектора лексем дерева регулярного выражения + // 2 и 3 аргумент - это начальный и конечный индекс рассматриваемых лексем в векторе + virtual AlgExpression* expr(const vector&, int, int) = 0; AlgExpression* scan_conc(const vector&, int, int); AlgExpression* scan_star(const vector&, int, int); AlgExpression* scan_alt(const vector&, int, int); AlgExpression* scan_symb(const vector&, int, int); AlgExpression* scan_eps(const vector&, int, int); AlgExpression* scan_par(const vector&, int, int); - static bool update_balance(const AlgExpression::Lexeme&, int&); + static void update_balance(const AlgExpression::Lexeme&, int&); // список листьев дерева regex vector pre_order_travers(); @@ -129,4 +135,5 @@ class AlgExpression : public BaseObject { friend class FiniteAutomaton; friend class Tester; + friend class UnitTests; }; \ No newline at end of file diff --git a/libs/Objects/include/Objects/BackRefRegex.h b/libs/Objects/include/Objects/BackRefRegex.h index 18316787..df06be8a 100644 --- a/libs/Objects/include/Objects/BackRefRegex.h +++ b/libs/Objects/include/Objects/BackRefRegex.h @@ -24,8 +24,8 @@ class BackRefRegex : public AlgExpression { BackRefRegex(const BackRefRegex&); // dynamic_cast к типу BackRefRegex* - template static BackRefRegex* cast(T* ptr); - template static const BackRefRegex* cast(const T* ptr); + template static BackRefRegex* cast(T* ptr, bool NotNullPtr = true); + template static const BackRefRegex* cast(const T* ptr, bool NotNullPtr = true); string to_txt() const override; }; \ No newline at end of file diff --git a/libs/Objects/include/Objects/Regex.h b/libs/Objects/include/Objects/Regex.h index 10e6e609..fac596f5 100644 --- a/libs/Objects/include/Objects/Regex.h +++ b/libs/Objects/include/Objects/Regex.h @@ -36,6 +36,9 @@ class Regex : public AlgExpression { void normalize_this_regex(const vector>&); // переписывание regex по // пользовательским правилам + Regex* expr(const vector&, int, int) override; + Regex* scan_minus(const vector&, int, int); + public: Regex() = default; Regex(const string&); @@ -45,8 +48,8 @@ class Regex : public AlgExpression { Regex(const Regex&) = default; // dynamic_cast к типу Regex* - template static Regex* cast(T* ptr); - template static const Regex* cast(const T* ptr); + template static Regex* cast(T* ptr, bool NotNullPtr = true); + template static const Regex* cast(const T* ptr, bool NotNullPtr = true); // dynamic_cast каждого элемента вектора к типу Regex* template static vector cast(vector ptr); diff --git a/libs/Objects/src/AlgExpression.cpp b/libs/Objects/src/AlgExpression.cpp index 53c255cf..8ea7d5bb 100644 --- a/libs/Objects/src/AlgExpression.cpp +++ b/libs/Objects/src/AlgExpression.cpp @@ -92,7 +92,7 @@ string AlgExpression::to_txt() const { if (term_l && term_l->type == Type::alt) { str1 = "(" + str1 + ")"; } - if (term_r && term_l->type == Type::alt) { + if (term_r && term_r->type == Type::alt) { str2 = "(" + str2 + ")"; } break; @@ -111,6 +111,9 @@ string AlgExpression::to_txt() const { // ставим скобки при итерации, если символов > 1 str1 = "(" + str1 + ")"; break; + case Type::negative: + symb = '^'; + return symb + str1; default: break; } @@ -161,6 +164,8 @@ string AlgExpression::type_to_str() const { return "*"; case Type::symb: return "symb"; + case Type::negative: + return "^"; default: break; } @@ -235,6 +240,9 @@ vector AlgExpression::parse_string(string str) { char c = str[index]; Lexeme lexeme; switch (c) { + case '^': + lexeme.type = Lexeme::Type::negative; + break; case '(': lexeme.type = Lexeme::Type::parL; brackets_checker.push('('); @@ -276,11 +284,14 @@ vector AlgExpression::parse_string(string str) { brackets_are_empty = false; break; case '|': + if (index != 0 && lexemes.back().type == Lexeme::Type::negative) + return {Lexeme::Type::error}; lexeme.type = Lexeme::Type::alt; break; case '*': if (index == 0 || (index != 0 && (lexemes.back().type == Lexeme::Type::star || - lexemes.back().type == Lexeme::Type::alt))) + lexemes.back().type == Lexeme::Type::alt || + lexemes.back().type == Lexeme::Type::negative))) return {Lexeme::Type::error}; lexeme.type = Lexeme::Type::star; break; @@ -330,7 +341,8 @@ vector AlgExpression::parse_string(string str) { ( // AlgExpression::Lexeme right lexeme.type == Lexeme::Type::symb || lexeme.type == Lexeme::Type::parL || - lexeme.type == Lexeme::Type::squareBrL || lexeme.type == Lexeme::Type::ref)) { + lexeme.type == Lexeme::Type::squareBrL || lexeme.type == Lexeme::Type::ref || + lexeme.type == Lexeme::Type::negative)) { // We place . between lexemes.emplace_back(Lexeme::Type::conc); } @@ -339,7 +351,7 @@ vector AlgExpression::parse_string(string str) { ((lexeme.type == Lexeme::Type::alt && (lexemes.back().type == Lexeme::Type::parL || lexemes.back().type == Lexeme::Type::squareBrL)) || - (lexemes.back().type == Lexeme::Type::alt && + ((lexemes.back().type == Lexeme::Type::alt || lexemes.back().type == Lexeme::Type::negative) && (lexeme.type == Lexeme::Type::parR || lexeme.type == Lexeme::Type::squareBrR || lexeme.type == Lexeme::Type::alt)))) { // We place eps between @@ -377,7 +389,7 @@ vector AlgExpression::parse_string(string str) { lexemes.insert(lexemes.begin(), {Lexeme::Type::eps}); } - if (lexemes.back().type == Lexeme::Type::alt) { + if (lexemes.back().type == Lexeme::Type::alt || lexemes.back().type == Lexeme::Type::negative) { lexemes.emplace_back(Lexeme::Type::eps); } @@ -393,7 +405,7 @@ bool AlgExpression::from_string(const string& str) { return true; } - vector l = parse_string(str); + vector l = parse_string(str); AlgExpression* root = expr(l, 0, l.size()); if (root == nullptr || root->type == eps) { @@ -408,36 +420,13 @@ bool AlgExpression::from_string(const string& str) { return true; } -AlgExpression* AlgExpression::expr(const vector& lexemes, int index_start, - int index_end) { - AlgExpression* p; - p = scan_alt(lexemes, index_start, index_end); - if (!p) { - p = scan_conc(lexemes, index_start, index_end); - } - if (!p) { - p = scan_star(lexemes, index_start, index_end); - } - if (!p) { - p = scan_symb(lexemes, index_start, index_end); - } - if (!p) { - p = scan_eps(lexemes, index_start, index_end); - } - if (!p) { - p = scan_par(lexemes, index_start, index_end); - } - return p; -} - -bool AlgExpression::update_balance(const AlgExpression::Lexeme& l, int& balance) { +void AlgExpression::update_balance(const AlgExpression::Lexeme& l, int& balance) { if (l.type == Lexeme::Type::parL || l.type == Lexeme::Type::squareBrL) { balance++; } if (l.type == Lexeme::Type::parR || l.type == Lexeme::Type::squareBrR) { balance--; } - return balance >= 0; } AlgExpression* AlgExpression::scan_conc(const vector& lexemes, @@ -445,8 +434,7 @@ AlgExpression* AlgExpression::scan_conc(const vector& lex AlgExpression* p = nullptr; int balance = 0; for (int i = index_start; i < index_end; i++) { - if (!update_balance(lexemes[i], balance)) - return nullptr; + update_balance(lexemes[i], balance); if (lexemes[i].type == Lexeme::Type::conc && balance == 0) { AlgExpression* l = expr(lexemes, index_start, i); AlgExpression* r = expr(lexemes, i + 1, index_end); @@ -477,8 +465,7 @@ AlgExpression* AlgExpression::scan_star(const vector& lex AlgExpression* p = nullptr; int balance = 0; for (int i = index_start; i < index_end; i++) { - if (!update_balance(lexemes[i], balance)) - return nullptr; + update_balance(lexemes[i], balance); if (lexemes[i].type == Lexeme::Type::star && balance == 0) { AlgExpression* l = expr(lexemes, index_start, i); if (l == nullptr || l->type == AlgExpression::eps) { @@ -503,8 +490,7 @@ AlgExpression* AlgExpression::scan_alt(const vector& lexe AlgExpression* p = nullptr; int balance = 0; for (int i = index_start; i < index_end; i++) { - if (!update_balance(lexemes[i], balance)) - return nullptr; + update_balance(lexemes[i], balance); if (lexemes[i].type == Lexeme::Type::alt && balance == 0) { AlgExpression* l = expr(lexemes, index_start, i); AlgExpression* r = expr(lexemes, i + 1, index_end); @@ -605,15 +591,19 @@ bool AlgExpression::contains_eps() const { } bool AlgExpression::equality_checker(const AlgExpression* expr1, const AlgExpression* expr2) { - if (expr1 == nullptr && expr2 == nullptr) return true; - if (expr1 == nullptr || expr2 == nullptr) return false; - if (expr1->value.type != expr2->value.type) return false; + if (expr1 == nullptr && expr2 == nullptr) + return true; + if (expr1 == nullptr || expr2 == nullptr) + return false; + if (expr1->value.type != expr2->value.type) + return false; if (expr1->value.type == Lexeme::Type::symb) { alphabet_symbol r1_symb, r2_symb; r1_symb = expr1->value.symbol; r2_symb = expr2->value.symbol; - if (r1_symb != r2_symb) return false; + if (r1_symb != r2_symb) + return false; } if (equality_checker(expr1->term_l, expr2->term_l) && diff --git a/libs/Objects/src/BackRefRegex.cpp b/libs/Objects/src/BackRefRegex.cpp index 2799b80b..b9c6feaa 100644 --- a/libs/Objects/src/BackRefRegex.cpp +++ b/libs/Objects/src/BackRefRegex.cpp @@ -39,18 +39,18 @@ BackRefRegex* BackRefRegex::make() const { return new BackRefRegex; } -template BackRefRegex* BackRefRegex::cast(T* ptr) { +template BackRefRegex* BackRefRegex::cast(T* ptr, bool NotNullPtr) { auto* r = dynamic_cast(ptr); - if (!r) { + if (!r && NotNullPtr) { throw std::runtime_error("Failed to cast to BackRefRegex"); } return r; } -template const BackRefRegex* BackRefRegex::cast(const T* ptr) { +template const BackRefRegex* BackRefRegex::cast(const T* ptr, bool NotNullPtr) { auto* r = dynamic_cast(ptr); - if (!r) { + if (!r && NotNullPtr) { throw std::runtime_error("Failed to cast to BackRefRegex"); } @@ -58,7 +58,7 @@ template const BackRefRegex* BackRefRegex::cast(const T* ptr) { } string BackRefRegex::to_txt() const { - BackRefRegex* br_term_l; + BackRefRegex* br_term_l, *br_term_r; string str1, str2; if (term_l) { str1 = term_l->to_txt(); @@ -66,6 +66,7 @@ string BackRefRegex::to_txt() const { } if (term_r) { str2 = term_r->to_txt(); + br_term_r = cast(term_r); } string symb; switch (type) { @@ -73,7 +74,7 @@ string BackRefRegex::to_txt() const { if (term_l && br_term_l->type == Type::alt) { str1 = "(" + str1 + ")"; } - if (term_r && br_term_l->type == Type::alt) { + if (term_r && br_term_r->type == Type::alt) { str2 = "(" + str2 + ")"; } break; @@ -152,10 +153,7 @@ BackRefRegex* BackRefRegex::expr(const vector& lexemes, i if (!p) { p = scan_square_br(lexemes, index_start, index_end); } - if (!p) { - return nullptr; - } - return cast(p); + return cast(p, false); } BackRefRegex* BackRefRegex::scan_ref(const vector& lexemes, int index_start, diff --git a/libs/Objects/src/Regex.cpp b/libs/Objects/src/Regex.cpp index b59ab520..d471a2f0 100644 --- a/libs/Objects/src/Regex.cpp +++ b/libs/Objects/src/Regex.cpp @@ -40,18 +40,18 @@ Regex* Regex::make() const { return new Regex; } -template Regex* Regex::cast(T* ptr) { +template Regex* Regex::cast(T* ptr, bool NotNullPtr) { auto* r = dynamic_cast(ptr); - if (!r) { + if (!r && NotNullPtr) { throw runtime_error("Failed to cast to Regex"); } return r; } -template const Regex* Regex::cast(const T* ptr) { +template const Regex* Regex::cast(const T* ptr, bool NotNullPtr) { auto* r = dynamic_cast(ptr); - if (!r) { + if (!r && NotNullPtr) { throw runtime_error("Failed to cast to Regex"); } @@ -72,6 +72,55 @@ template vector Regex::cast(vector ptrs) { return regexPointers; } +Regex* Regex::expr(const vector& lexemes, int index_start, + int index_end) { + AlgExpression* p; + p = scan_symb(lexemes, index_start, index_end); + if (!p) { + p = scan_eps(lexemes, index_start, index_end); + } + + if (!p) { + p = scan_alt(lexemes, index_start, index_end); + } + if (!p) { + p = scan_conc(lexemes, index_start, index_end); + } + if (!p) { + p = scan_minus(lexemes, index_start, index_end); + } + if (!p) { + p = scan_star(lexemes, index_start, index_end); + } + if (!p) { + p = scan_par(lexemes, index_start, index_end); + } + + return cast(p, false); +} + +Regex* Regex::scan_minus(const vector& lexemes, + int index_start, int index_end) { + Regex* p = nullptr; + + if (lexemes[index_start].type != Lexeme::Type::negative) { + return nullptr; + } + + Regex* l = expr(lexemes, index_start + 1, index_end); + if (l == nullptr) { + delete l; + return nullptr; + } + p = make(); + p->term_l = l; + p->value = lexemes[index_start]; + p->type = negative; + + p->alphabet = l->alphabet; + return p; +} + // возвращает пару <вектор сотсояний, max_index> pair, int> Regex::get_thompson(int max_index) const { string str; // идентификатор состояния