diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml new file mode 100644 index 0000000..55da6b6 --- /dev/null +++ b/.github/workflows/Test.yml @@ -0,0 +1,24 @@ +name: BuildTest + +on: + push: + branches: [ master, CYK ] + pull_request: + branches: [ master, CYK ] +jobs: + Build-ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: "Build and run tests" + run : | + git clone https://github.com/google/googletest + cd googletest + cd googletest + cmake .. + make + sudo make install + cd ../../ + cmake tests + make + ./Test diff --git a/CYK.cpp b/CYK.cpp new file mode 100644 index 0000000..7cdfaed --- /dev/null +++ b/CYK.cpp @@ -0,0 +1,111 @@ +#include "CYK.hpp" + +bool CFG::isCNF() { + bool ok = true; + for (auto rule : P) { + if (!ok) { + break; + } + if (!N.contains(rule.first)) { + ok = false; + break; + } + for (auto rpart : rule.second) { + if (rpart.size() > 2) { + ok = false; + break; + } + if (rpart.size() == 1 && (!alp.contains(rpart[0]) || (rpart == "1" && rule.first != S))) { + ok = false; + break; + } + if (rpart.size() == 2 && (!N.contains(rpart[0]) || !N.contains(rpart[1]))) { + ok = false; + break; + } + } + } + return ok; +} + +void CFG::Scan() { + cout << "Enter size of sigma: "; + size_t sz = 0; + char c = 0; + string s; + cin >> sz; + cout << endl << "Enter symbols:" << endl; + for (size_t i = 0; i < sz; ++i) { + cin >> c; + alp.insert(c); + } + cout << endl << "Enter size of N: "; + cin >> sz; + cout << endl << "Enter symbols:" << endl; + for (size_t i = 0; i < sz; ++i) { + cin >> c; + N.insert(c); + } + cout << endl << "Enter start symbol: "; + cin >> S; + if (!N.contains(S)) { + throw runtime_error("N does not contain start symbol!!!"); + } + cout << endl << "Enter size of set of prod rules: "; + cin >> sz; + for (size_t i = 0; i < sz; ++i) { + cin >> c; + if (!N.contains(c)) { + throw runtime_error("Context free grammar can not contain terminal in left part of the rule!!!"); + } + cout << "right part: "; + cin >> s; + cout << endl; + P[c].insert(s); + } +} + +bool CFG::Recognize(const string &w) { + if (!isCNF()) { + throw runtime_error("Not in CNF!!!"); + } + if (P[S].contains("1") && w.size() == 0) { + return true; + } else if (w.size() == 0 && !P[S].contains("1")) { + return false; + } + return CYK(w); +} + +bool CFG::CYK(const string &w) { + map>> tb; + for (char A : N) { + tb[A] = vector>(w.size() + 1, vector(w.size() + 1, false)); + for (size_t i = 0; i <= w.size(); ++i) { + for (size_t j = 0; j <= w.size(); ++j) { + if (j == i + 1) { + for (auto rule_rp : P[A]) { + if (rule_rp.size() == 1 && rule_rp[0] == w[i]) { + tb[A][i][j] = true; + } + } + } + } + } + } + for (size_t l = 2; l <= w.size(); ++l) { + for (size_t i = 0; i <= w.size() - l; ++i) { + size_t j = i + l; + for (size_t k = i + 1; k <= j - 1; ++k) { + for (auto A : N) { + for (auto rule_rp : P[A]) { + if (rule_rp.size() == 2 && tb[rule_rp[0]][i][k] && tb[rule_rp[1]][k][j]) { + tb[A][i][j] = true; + } + } + } + } + } + } + return tb[S][0][w.size()]; +} diff --git a/CYK.hpp b/CYK.hpp new file mode 100644 index 0000000..b50ddd7 --- /dev/null +++ b/CYK.hpp @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include + +using namespace std; + +class CFG { +public: + set alp; + set N; + char S; + map> P; + + CFG() = default; + + void Scan(); + + bool isCNF(); + + bool Recognize(const string& w); + + bool CYK(const string& w); +}; diff --git a/README.md b/README.md index e69de29..7b4b4c3 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +In: Context-free grammar in Chomsky normal form. Word + +Out: Indicator of the belonging of the word to the language diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..39f38c8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.5) + +project(Test) +set(CMAKE_CXX_STANDARD 20) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} "main.cpp" "RecognizeTestCase.hpp" "RecognizeTestCase.cpp" "../CYK.cpp") + +target_include_directories(${PROJECT_NAME} + PRIVATE + ${GTEST_INCLUDE_DIR}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + GTest::GTest + Threads::Threads) diff --git a/tests/RecognizeTestCase.cpp b/tests/RecognizeTestCase.cpp new file mode 100644 index 0000000..70dc5c7 --- /dev/null +++ b/tests/RecognizeTestCase.cpp @@ -0,0 +1,277 @@ +#include "RecognizeTestCase.hpp" + +TEST_F(RecognizeTestCase, EmptyWordRec) { + string w = "1"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('a'); + gr.N.insert('S'); + gr.S = 'S'; + gr.P['S'].insert(w); + ASSERT_EQ(gr.Recognize(w), true); +} + +TEST_F(RecognizeTestCase, EmptyWordNotRec) { + string w = "1"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('a'); + gr.N.insert('S'); + gr.S = 'S'; + gr.P['S'].insert("a"); + ASSERT_EQ(gr.Recognize(w), false); +} + +TEST_F(RecognizeTestCase, CBS_true_01) { + string w = "()()(()())"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), true); +} + +TEST_F(RecognizeTestCase, CBS_true_02) { + string w = "(())()(()())"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), true); +} + + +TEST_F(RecognizeTestCase, CBS_true_03) { + string w = "(()()(()())(((())))())"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), true); +} +TEST_F(RecognizeTestCase, CBS_true_04_stresstest) { + string w = "(()()(()())(((())))())(((((((((((((((((((()())))))))))))()))))))))()()()((((((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))))))(((((((((())))))))))()()()"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), true); +} +TEST_F(RecognizeTestCase, CBS_true_05_stresstest) { + string w = "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), true); +} +TEST_F(RecognizeTestCase, CBS_false_01) { + string w = "(()()(()())(((())))(())"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), false); +} + + +TEST_F(RecognizeTestCase, CBS_false_02) { + string w = "(()"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), false); +} + +TEST_F(RecognizeTestCase, CBS_false_03) { + string w = "((())())()()())))("; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), false); +} + +TEST_F(RecognizeTestCase, CBS_false_04_stresstest) { + string w = "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((()()()((()))))))))))))))))))(((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))())))))))))))()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"; + CFG gr; + gr.alp.insert('1'); + gr.alp.insert('('); + gr.alp.insert(')'); + gr.S = 'A'; + gr.N.insert('A'); + gr.N.insert('B'); + gr.N.insert('C'); + gr.N.insert('E'); + gr.N.insert('D'); + gr.P['A'].insert("1"); + gr.P['A'].insert("BB"); + gr.P['A'].insert("CD"); + gr.P['B'].insert("BB"); + gr.P['B'].insert("CD"); + gr.P['C'].insert("("); + gr.P['D'].insert("BE"); + gr.P['D'].insert(")"); + gr.P['E'].insert(")"); + ASSERT_EQ(gr.Recognize(w), false); +} + +TEST_F(RecognizeTestCase, NotCNF_01) { + CFG gr; + gr.alp.insert('b'); + gr.alp.insert('a'); + gr.N.insert('S'); + gr.N.insert('P'); + gr.S = 'S'; + gr.P['S'].insert("ab"); + ASSERT_THROW(gr.Recognize("ab"), std::runtime_error); +} + +TEST_F(RecognizeTestCase, NotCNF_02) { + CFG gr; + gr.alp.insert('a'); + gr.N.insert('S'); + gr.S = 'S'; + gr.P['S'].insert("Sa"); + ASSERT_EQ(gr.isCNF(), 0); +} + +TEST_F(RecognizeTestCase, NotCNF_03) { + CFG gr; + gr.alp.insert('a'); + gr.N.insert('S'); + gr.S = 'S'; + gr.P['S'].insert("SSS"); + ASSERT_EQ(gr.isCNF(), 0); +} + +TEST_F(RecognizeTestCase, NotCNF_04) { + CFG gr; + gr.alp.insert('a'); + gr.N.insert('S'); + gr.N.insert('N'); + gr.S = 'S'; + gr.P['N'].insert("#"); + ASSERT_EQ(gr.isCNF(), 0); +} diff --git a/tests/RecognizeTestCase.hpp b/tests/RecognizeTestCase.hpp new file mode 100644 index 0000000..4f23ece --- /dev/null +++ b/tests/RecognizeTestCase.hpp @@ -0,0 +1,4 @@ +#include +#include "../CYK.hpp" + +class RecognizeTestCase : public ::testing::Test {}; diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..5ebbc76 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}