diff --git a/alib2algo/src/grammar/simplify/ToCNF.cpp b/alib2algo/src/grammar/simplify/ToCNF.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c3e60d286d5212fe44922b439c9239bccef652b --- /dev/null +++ b/alib2algo/src/grammar/simplify/ToCNF.cpp @@ -0,0 +1,247 @@ +/* + * ToCNF.cpp + * + * Created on: 24. 11. 2014 + * Author: Jan Travnicek + */ + +#include "ToCNF.h" + +#include "EpsilonRemover.h" +#include "SimpleRulesRemover.h" +#include "alphabet/LabeledSymbol.h" +#include "alphabet/SymbolPairSymbol.h" + +namespace grammar { + +namespace simplify { + +template<class T> +std::pair<alphabet::Symbol, alphabet::Symbol> splitToPairs(T& grammar, const std::vector<alphabet::Symbol>& rhs, unsigned from, unsigned size, std::map<alphabet::Symbol, alphabet::Symbol>& createdSymbols) { + if(size == 2) { + return std::make_pair(rhs[from], rhs[from + 1]); + } else if(size == 3) { + alphabet::Symbol firstLhs {rhs[from]}; + + auto second = splitToPairs(grammar, rhs, from + 1, 2, createdSymbols); + alphabet::Symbol secondProposal{alphabet::SymbolPairSymbol(second)}; + if(!createdSymbols.count(secondProposal)) { + createdSymbols.insert(std::make_pair(secondProposal, alphabet::createUniqueSymbol(secondProposal, grammar.getTerminalAlphabet(), grammar.getNonterminalAlphabet()))); + } + grammar.addNonterminalSymbol(createdSymbols.find(secondProposal)->second); + grammar.addRawRule(createdSymbols.find(secondProposal)->second, {second.first, second.second}); + + return std::make_pair(firstLhs, createdSymbols.find(secondProposal)->second); + } else { + auto first = splitToPairs(grammar, rhs, from, size / 2, createdSymbols); + alphabet::Symbol firstProposal{alphabet::SymbolPairSymbol(first)}; + if(!createdSymbols.count(firstProposal)) { + createdSymbols.insert(std::make_pair(firstProposal, alphabet::createUniqueSymbol(firstProposal, grammar.getTerminalAlphabet(), grammar.getNonterminalAlphabet()))); + } + grammar.addNonterminalSymbol(createdSymbols.find(firstProposal)->second); + grammar.addRawRule(createdSymbols.find(firstProposal)->second, {first.first, first.second}); + + auto second = splitToPairs(grammar, rhs, from + size / 2, size - size / 2, createdSymbols); + alphabet::Symbol secondProposal{alphabet::SymbolPairSymbol(second)}; + if(!createdSymbols.count(secondProposal)) { + createdSymbols.insert(std::make_pair(secondProposal, alphabet::createUniqueSymbol(secondProposal, grammar.getTerminalAlphabet(), grammar.getNonterminalAlphabet()))); + } + grammar.addNonterminalSymbol(createdSymbols.find(secondProposal)->second); + grammar.addRawRule(createdSymbols.find(secondProposal)->second, {second.first, second.second}); + + return std::make_pair(createdSymbols.find(firstProposal)->second, createdSymbols.find(secondProposal)->second); + } +} + +template<class T> +grammar::CNF convertInternal( const T & origGrammar ) { + T grammarTmp(origGrammar.getInitialSymbol()); + + for( const auto & symbol : origGrammar.getNonterminalAlphabet() ) + grammarTmp.addNonterminalSymbol( symbol ); + + for( const auto & symbol : origGrammar.getTerminalAlphabet() ) + grammarTmp.addTerminalSymbol( symbol ); + + std::map<alphabet::Symbol, alphabet::Symbol> createdSymbols; + auto origRules = origGrammar.getRawRules(); + for( const auto & origRule : origRules ) { + for( const auto& origRhs : origRule.second ) { + if(origRhs.size() == 1 || origRhs.size() == 2) + grammarTmp.addRawRule(origRule.first, origRhs); + else if(origRhs.size() > 2) { + auto second = splitToPairs(grammarTmp, origRhs, 0, origRhs.size(), createdSymbols); + grammarTmp.addRawRule(origRule.first, {second.first, second.second}); + } else + throw exception::AlibException("Invalid rule in grammar"); + } + } + + grammarTmp.setGeneratesEpsilon(origGrammar.getGeneratesEpsilon()); + + grammar::CNF grammar(grammarTmp.getInitialSymbol()); + + for( const auto & symbol : grammarTmp.getNonterminalAlphabet() ) + grammar.addNonterminalSymbol( symbol ); + + for( const auto & symbol : grammarTmp.getTerminalAlphabet() ) + grammar.addTerminalSymbol( symbol ); + + std::map<alphabet::Symbol, alphabet::Symbol> terminalToShadowNonterminal; + for( const auto & symbol : grammarTmp.getTerminalAlphabet() ) { + alphabet::Symbol shadowSymbol = alphabet::createUniqueSymbol(symbol, grammar.getTerminalAlphabet(), grammar.getNonterminalAlphabet()); + terminalToShadowNonterminal.insert( std::make_pair( symbol, shadowSymbol )); + grammar.addNonterminalSymbol( shadowSymbol ); + grammar.addRule(shadowSymbol, symbol); + } + + auto tmpRules = grammarTmp.getRawRules(); + for( const auto & tmpRule : tmpRules ) { + for( const auto& tmpRhs : tmpRule.second ) { + if(tmpRhs.size() == 2) { + if(grammarTmp.getNonterminalAlphabet().count(tmpRhs[0])) { + if(grammarTmp.getNonterminalAlphabet().count(tmpRhs[1])) { + grammar.addRawRule(tmpRule.first, tmpRhs); + } else { + grammar.addRawRule(tmpRule.first, {tmpRhs[0], terminalToShadowNonterminal.find(tmpRhs[1])->second}); + } + } else { + if(grammarTmp.getNonterminalAlphabet().count(tmpRhs[1])) { + grammar.addRawRule(tmpRule.first, {terminalToShadowNonterminal.find(tmpRhs[0])->second, tmpRhs[1]}); + } else { + grammar.addRawRule(tmpRule.first, {terminalToShadowNonterminal.find(tmpRhs[0])->second, terminalToShadowNonterminal.find(tmpRhs[1])->second }); + } + } + } else // tmpRhs.size() == 1 + grammar.addRawRule(tmpRule.first, tmpRhs); + } + } + + grammar.setGeneratesEpsilon(grammarTmp.getGeneratesEpsilon()); + + return grammar; +} + + + +grammar::CNF ToCNF::convert(const grammar::CFG& origGrammar) +{ + return convertInternal(grammar::simplify::SimpleRulesRemover::remove(grammar::simplify::EpsilonRemover::remove(origGrammar))); +} + +grammar::CNF ToCNF::convert(const grammar::EpsilonFreeCFG& origGrammar) +{ + return convertInternal(grammar::simplify::SimpleRulesRemover::remove(origGrammar)); +} + +grammar::CNF ToCNF::convert(const grammar::CNF& origGrammar) +{ + return origGrammar; +} + +grammar::CNF ToCNF::convert(const grammar::GNF& origGrammar) +{ + return convertInternal(origGrammar); +} + +grammar::CNF ToCNF::convert(const grammar::LG& origGrammar) +{ + return convertInternal(grammar::simplify::SimpleRulesRemover::remove(grammar::simplify::EpsilonRemover::remove(origGrammar))); +} + +grammar::CNF ToCNF::convert(const grammar::LeftLG& origGrammar) +{ + return convertInternal(grammar::simplify::SimpleRulesRemover::remove(grammar::simplify::EpsilonRemover::remove(origGrammar))); +} + +grammar::CNF ToCNF::convert(const grammar::LeftRG& origGrammar) +{ + return convertInternal(origGrammar); +} + +grammar::CNF ToCNF::convert(const grammar::RightLG& origGrammar) +{ + return convertInternal(grammar::simplify::SimpleRulesRemover::remove(grammar::simplify::EpsilonRemover::remove(origGrammar))); +} + +grammar::CNF ToCNF::convert(const grammar::RightRG& origGrammar) +{ + return convertInternal(origGrammar); +} + + + +grammar::Grammar ToCNF::convert(const grammar::Grammar& grammar) { + grammar::Grammar* out = NULL; + grammar.getData().Accept((void*) &out, ToCNF::TO_CNF); + grammar::Grammar res = std::move(*out); + delete out; + return res; +} + +void ToCNF::Visit(void* data, const grammar::LeftLG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::LeftRG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::RightLG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::RightRG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::LG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::CFG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::EpsilonFreeCFG& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::CNF& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void* data, const grammar::GNF& grammar) const { + grammar::Grammar* & out = *((grammar::Grammar**) data); + out = new Grammar(std::move(this->convert(grammar))); +} + +void ToCNF::Visit(void*, const grammar::CSG&) const { + throw exception::AlibException("Unsupported grammar type CSG"); +} + +void ToCNF::Visit(void*, const grammar::NonContractingGrammar&) const { + throw exception::AlibException("Unsupported grammar type NonConctractingGrammar"); +} + +void ToCNF::Visit(void*, const grammar::ContextPreservingUnrestrictedGrammar&) const { + throw exception::AlibException("Unsupported grammar type ContextPreservingUnrestrictedGrammar"); +} + +void ToCNF::Visit(void*, const grammar::UnrestrictedGrammar&) const { + throw exception::AlibException("Unsupported grammar type UnrestrictedGrammar"); +} + +const ToCNF ToCNF::TO_CNF; + +} /* namespace simplify */ + +} /* namespace grammar */ diff --git a/alib2algo/src/grammar/simplify/ToCNF.h b/alib2algo/src/grammar/simplify/ToCNF.h new file mode 100644 index 0000000000000000000000000000000000000000..485a076e164b0648479cfa894be810e5834692d4 --- /dev/null +++ b/alib2algo/src/grammar/simplify/ToCNF.h @@ -0,0 +1,67 @@ +/* + * ToCNF.h + * + * Created on: 24. 11. 2014 + * Author: Jan Travnicek + */ + +#ifndef TO_CNF_H_ +#define TO_CNF_H_ + +#include <map> +#include <algorithm> + +#include <grammar/Grammar.h> + +#include <grammar/ContextFree/CFG.h> +#include <grammar/ContextFree/EpsilonFreeCFG.h> +#include <grammar/ContextFree/CNF.h> +#include <grammar/ContextFree/GNF.h> +#include <grammar/ContextFree/LG.h> +#include <grammar/Regular/LeftLG.h> +#include <grammar/Regular/LeftRG.h> +#include <grammar/Regular/RightLG.h> +#include <grammar/Regular/RightRG.h> +#include <exception/AlibException.h> + +namespace grammar { + +namespace simplify { + +class ToCNF : public grammar::VisitableGrammarBase::const_visitor_type { +public: + static grammar::Grammar convert( const grammar::Grammar & grammar ); + + static grammar::CNF convert( const grammar::CFG & grammar ); + static grammar::CNF convert( const grammar::EpsilonFreeCFG & grammar ); + static grammar::CNF convert( const grammar::GNF & grammar ); + static grammar::CNF convert( const grammar::CNF & grammar ); + static grammar::CNF convert( const grammar::LG & grammar ); + static grammar::CNF convert( const grammar::LeftLG & grammar ); + static grammar::CNF convert( const grammar::LeftRG & grammar ); + static grammar::CNF convert( const grammar::RightLG & grammar ); + static grammar::CNF convert( const grammar::RightRG & grammar ); + +private: + void Visit(void*, const grammar::LeftLG& grammar) const; + void Visit(void*, const grammar::LeftRG& grammar) const; + void Visit(void*, const grammar::RightLG& grammar) const; + void Visit(void*, const grammar::RightRG& grammar) const; + void Visit(void*, const grammar::LG& grammar) const; + void Visit(void*, const grammar::CFG& grammar) const; + void Visit(void*, const grammar::EpsilonFreeCFG& grammar) const; + void Visit(void*, const grammar::CNF& grammar) const; + void Visit(void*, const grammar::GNF& grammar) const; + void Visit(void*, const grammar::CSG& grammar) const; + void Visit(void*, const grammar::NonContractingGrammar& grammar) const; + void Visit(void*, const grammar::ContextPreservingUnrestrictedGrammar& grammar) const; + void Visit(void*, const grammar::UnrestrictedGrammar& grammar) const; + + static const ToCNF TO_CNF; +}; + +} /* namespace simplify */ + +} /* namespace grammar */ + +#endif /* TO_CNF_H_ */ diff --git a/alib2algo/test-src/grammar/simplify/GrammarToCNFTest.cpp b/alib2algo/test-src/grammar/simplify/GrammarToCNFTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5619eb15b7a32a1cffa8e9ae0c9a34c7cadbc20a --- /dev/null +++ b/alib2algo/test-src/grammar/simplify/GrammarToCNFTest.cpp @@ -0,0 +1,113 @@ +#include "GrammarToCNFTest.h" + +#include "grammar/simplify/ToCNF.h" + +#include "grammar/ContextFree/CFG.h" +#include "grammar/ContextFree/CNF.h" + +#include "grammar/GrammarToStringComposer.h" +#include "alphabet/SymbolPairSymbol.h" +#include "alphabet/UniqueSymbol.h" +#include "primitive/Integer.h" + +#include "std/pair.hpp" + +#define CPPUNIT_IMPLY(x, y) CPPUNIT_ASSERT(!(x) || (y)) + +CPPUNIT_TEST_SUITE_REGISTRATION( GrammarToCNFTest ); + +void GrammarToCNFTest::setUp() { +} + +void GrammarToCNFTest::tearDown() { +} + +void GrammarToCNFTest::testToCNFRules1() { + alphabet::Symbol S = alphabet::symbolFrom("S"); + alphabet::Symbol A = alphabet::symbolFrom("A"); + alphabet::Symbol B = alphabet::symbolFrom("B"); + alphabet::Symbol C = alphabet::symbolFrom("C"); + alphabet::Symbol D = alphabet::symbolFrom("D"); + + alphabet::Symbol a = alphabet::symbolFrom("a"); + alphabet::Symbol b = alphabet::symbolFrom("b"); + + alphabet::Symbol aP = alphabet::symbolFrom("a'"); + alphabet::Symbol bP = alphabet::symbolFrom("b'"); + + grammar::CFG grammar1(S); + grammar1.setNonterminalAlphabet({S, A, B, C, D}); + grammar1.setTerminalAlphabet({a, b}); + + grammar::CNF grammar2 = grammar::simplify::ToCNF::convert(grammar1); + + grammar::CNF grammar3(S); + grammar3.setNonterminalAlphabet({S, A, B, C, D, aP, bP}); + grammar3.setTerminalAlphabet({a, b}); + grammar3.addRule(aP, a); + grammar3.addRule(bP, b); + + std::cout << grammar2 << std::endl; + std::cout << grammar3 << std::endl; + + CPPUNIT_ASSERT(grammar2 == grammar3); +} + +void GrammarToCNFTest::testToCNFRules2() { + alphabet::Symbol S = alphabet::symbolFrom("S"); + alphabet::Symbol X = alphabet::symbolFrom("X"); + alphabet::Symbol Y = alphabet::symbolFrom("Y"); + + alphabet::Symbol a = alphabet::symbolFrom("a"); + alphabet::Symbol b = alphabet::symbolFrom("b"); + alphabet::Symbol c = alphabet::symbolFrom("c"); + + grammar::CFG grammar1(S); + grammar1.setNonterminalAlphabet({S, X, Y}); + grammar1.setTerminalAlphabet({a, b, c}); + grammar1.addRule({S}, {a, X, b, X}); + grammar1.addRule({X}, {a, Y}); + grammar1.addRule({X}, {b, Y}); + grammar1.addRule({X}, {}); + grammar1.addRule({Y}, {X}); + grammar1.addRule({Y}, {c}); + + grammar::CNF grammar2 = grammar::simplify::ToCNF::convert(grammar1); + alphabet::Symbol aP = alphabet::symbolFrom("a'"); + alphabet::Symbol bP = alphabet::symbolFrom("b'"); + alphabet::Symbol cP = alphabet::symbolFrom("c'"); + alphabet::Symbol Xb = alphabet::Symbol(alphabet::UniqueSymbol(alphabet::Symbol(alphabet::SymbolPairSymbol(std::make_pair(X, b))), primitive::Integer(0))); + alphabet::Symbol aX = alphabet::Symbol(alphabet::UniqueSymbol(alphabet::Symbol(alphabet::SymbolPairSymbol(std::make_pair(a, X))), primitive::Integer(0))); + alphabet::Symbol bX = alphabet::Symbol(alphabet::UniqueSymbol(alphabet::Symbol(alphabet::SymbolPairSymbol(std::make_pair(b, X))), primitive::Integer(0))); + + grammar::CNF grammar3(S); + grammar3.setNonterminalAlphabet({S, X, Y, aP, bP, cP, Xb, aX, bX}); + grammar3.setTerminalAlphabet({a, b, c}); + grammar3.addRule(S, std::make_pair(aX, bX)); + grammar3.addRule(S, std::make_pair(aP, bX)); + grammar3.addRule(S, std::make_pair(aP, Xb)); + grammar3.addRule(S, std::make_pair(aP, bP)); + grammar3.addRule(aX, std::make_pair(aP, X)); + grammar3.addRule(bX, std::make_pair(bP, X)); + grammar3.addRule(Xb, std::make_pair(X, bP)); + grammar3.addRule(X, std::make_pair(aP, Y)); + grammar3.addRule(X, std::make_pair(bP, Y)); + grammar3.addRule(X, a); + grammar3.addRule(X, b); + grammar3.addRule(Y, std::make_pair(aP, Y)); + grammar3.addRule(Y, std::make_pair(bP, Y)); + grammar3.addRule(Y, a); + grammar3.addRule(Y, b); + grammar3.addRule(Y, c); + grammar3.addRule(aP, a); + grammar3.addRule(bP, b); + grammar3.addRule(cP, c); + + grammar::GrammarToStringComposer gscomp; + + std::cout << gscomp.compose(grammar2) << std::endl; + std::cout << gscomp.compose(grammar3) << std::endl; + + CPPUNIT_ASSERT(grammar2 == grammar3); +} + diff --git a/alib2algo/test-src/grammar/simplify/GrammarToCNFTest.h b/alib2algo/test-src/grammar/simplify/GrammarToCNFTest.h new file mode 100644 index 0000000000000000000000000000000000000000..85081c5b4db2efb8d2d54018e64beddedd814f5a --- /dev/null +++ b/alib2algo/test-src/grammar/simplify/GrammarToCNFTest.h @@ -0,0 +1,21 @@ +#ifndef GRAMMAR_TO_CNF_TEST_H_ +#define GRAMMAR_TO_CNF_TEST_H_ + +#include <cppunit/extensions/HelperMacros.h> + +class GrammarToCNFTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE( GrammarToCNFTest ); + CPPUNIT_TEST( testToCNFRules1 ); + CPPUNIT_TEST( testToCNFRules2 ); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void testToCNFRules1(); + void testToCNFRules2(); +}; + +#endif /* GRAMMAR_TO_CNF_TEST_H_ */