From b883f0029cda1f19c5f86d6e5a98d07e5c8b5f03 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Thu, 8 Feb 2024 09:54:24 +0300 Subject: [PATCH] CFG compiler ini --- Language/private/Grammar.cpp | 18 --- Language/public/ContextFreeCompiler.hpp | 140 ++++++++++++++++++++++-- Language/public/Grammar.hpp | 22 ++-- Language/public/Parser.hpp | 16 ++- Language/public/SimpleParser.hpp | 4 +- 5 files changed, 154 insertions(+), 46 deletions(-) diff --git a/Language/private/Grammar.cpp b/Language/private/Grammar.cpp index c89f60c..d6acecf 100644 --- a/Language/private/Grammar.cpp +++ b/Language/private/Grammar.cpp @@ -29,24 +29,6 @@ bool ContextFreeGrammar::Rule::isProductive() const { return true; } -bool ContextFreeGrammar::NonTerminal::isProductive() const { - for (auto rule : rules) { - if (rule->isProductive()) return true; - } - return false; -} - -bool ContextFreeGrammar::NonTerminal::isLooped(Map& processed, const String& id) const { - for (auto ref : referencing) { - if (processed.presents(ref->key)) return true; - } - processed.put(id, {}); - for (auto ref : referencing) { - if (ref->val->isLooped(processed, ref->key)) return true; - } - return false; -} - void ContextFreeGrammar::addRule(const Rule& rule) { mRules.append(rule); } void ContextFreeGrammar::addRule(const String& id, const InitialierList& args) { addRule(Rule(id, args)); } diff --git a/Language/public/ContextFreeCompiler.hpp b/Language/public/ContextFreeCompiler.hpp index 2b8b0a1..2ffcf5f 100644 --- a/Language/public/ContextFreeCompiler.hpp +++ b/Language/public/ContextFreeCompiler.hpp @@ -7,27 +7,149 @@ namespace tp { class ContextFreeCompiler { - typedef ContextFreeGrammar Grammar; - public: - // Terminals or non terminals - struct Alphabet { + typedef ualni SymbolID; + + struct Symbol { String mId; bool mIsTerminal = false; }; struct Item { - Grammar::Rule mRule; + const ContextFreeGrammar::Rule* mRule = nullptr; ualni mAdvanceIdx = 0; }; - typedef FiniteStateAutomation NFA; - typedef typename NFA::State State; + struct NonTerminal { + Buffer rules; + Map references; + Map referencing; + + public: + [[nodiscard]] bool isProductive() const { + for (auto rule : rules) { + if (rule->isProductive()) return true; + } + return false; + } + + [[nodiscard]] bool isLooped(Map& processed, const String& id) const { + for (auto ref : referencing) { + if (processed.presents(ref->key)) return true; + } + processed.put(id, {}); + for (auto ref : referencing) { + if (ref->val->isLooped(processed, ref->key)) return true; + } + return false; + } + }; public: - void compile(const Grammar& in, NFA& out) { nfa = &out; } + bool compile(const ContextFreeGrammar& grammar, FiniteStateAutomation& automata) { + if (!init(grammar)) return false; + return true; + } private: - NFA* nfa = nullptr; + bool init(const ContextFreeGrammar& grammar) { + + if (!grammar.getRules()->size()) { + return false; + } + + for (auto rule : *grammar.getRules()) { + if (!rule->getArgs()->size()) { + return false; + } + } + + findNonTerminals(grammar); + + for (auto nonTerminal : mNonTerminals) { + for (auto rule : nonTerminal->val.rules) { + for (auto arg : *rule->getArgs()) { + if (arg->isTerminal() || arg->isEpsilon()) continue; + + if (!mNonTerminals.presents(arg->getId())) { + printf("Referenced non-terminal '%s' is not defined\n", arg->getId().read()); + return false; + } + } + } + } + + findAllReferences(grammar); + + for (auto nonTerminal : mNonTerminals) { + if (!nonTerminal->val.references.size() && nonTerminal->key != grammar.getStartTerminal()) { + printf("Non-terminal '%s' is defined but not used\n", nonTerminal->key.read()); + return false; + } + } + + for (auto nonTerminal : mNonTerminals) { + if (!nonTerminal->val.isProductive()) { + printf("Non-terminal '%s' is not productive\n", nonTerminal->val.rules.first()->data->id.read()); + return false; + } + } + + Map processed; + if (mNonTerminals.get(grammar.getStartTerminal()).isLooped(processed, grammar.getStartTerminal())) { + printf("Note that grammar is looped.\n"); + return false; + } + + initSymbols(grammar); + } + + void findNonTerminals(const ContextFreeGrammar& grammar) { + for (auto rule : *grammar.getRules()) { + if (!mNonTerminals.presents(rule->getId())) { + mNonTerminals.put(rule->getId(), {}); + } + auto nonTerminal = &mNonTerminals.get(rule->getId()); + nonTerminal->rules.append(&rule.data()); + } + } + + void findAllReferences(const ContextFreeGrammar& grammar) { + for (auto nonTerminal : mNonTerminals) { + for (auto rule : nonTerminal->val.rules) { + for (auto arg : *rule->getArgs()) { + if (arg->isTerminal() || arg->isEpsilon()) continue; + + NonTerminal* reference = &mNonTerminals.get(arg->getId()); + nonTerminal->val.referencing.put(arg->getId(), reference); + reference->references.put(nonTerminal->key, &nonTerminal->val); + } + } + } + } + + void initSymbols(const ContextFreeGrammar& grammar) { + for (auto nonTerminal : mNonTerminals) { + mSymbols.append({ nonTerminal->key, false }); + mSymbolLookup.put(nonTerminal->key, SymbolID(mSymbols.size() - 1)); + + for (auto rule : nonTerminal->val.rules) { + for (auto arg : *rule->getArgs()) { + if (arg->isEpsilon() || arg->isTerminal()) continue; + if (mTerminals.presents(arg->getId())) continue; + mTerminals.put(arg->getId(), {}); + + mSymbols.append({ nonTerminal->key, true }); + mSymbolLookup.put(nonTerminal->key, SymbolID(mSymbols.size() - 1)); + } + } + } + } + + private: + Map mNonTerminals; + Map mTerminals; + Buffer mSymbols; + Map mSymbolLookup; }; } diff --git a/Language/public/Grammar.hpp b/Language/public/Grammar.hpp index 8c65287..d5dfc5f 100644 --- a/Language/public/Grammar.hpp +++ b/Language/public/Grammar.hpp @@ -20,6 +20,8 @@ namespace tp { public: bool operator==(const Arg& in) const; [[nodiscard]] const String& getId() const; + [[nodiscard]] bool isTerminal() const { return mIsTerminal; } + [[nodiscard]] bool isEpsilon() const { return mIsEpsilon; } private: String mId; @@ -35,24 +37,14 @@ namespace tp { public: bool operator==(const Rule& in) const; [[nodiscard]] bool isProductive() const; + [[nodiscard]] const String& getId() const { return mId; } + [[nodiscard]] const Buffer* getArgs() const { return &mArgs; } private: String mId; Buffer mArgs; }; - private: - // cache data - struct NonTerminal { - Buffer rules; - Map references; - Map referencing; - - public: - [[nodiscard]] bool isProductive() const; - [[nodiscard]] bool isLooped(Map& processed, const String& id) const; - }; - public: ContextFreeGrammar() = default; @@ -60,9 +52,11 @@ namespace tp { void addRule(const Rule& rule); void addRule(const String& id, const InitialierList& args); void setStart(const String& startRule); + [[nodiscard]] const Buffer* getRules() const { return &mRules; } + [[nodiscard]] bool isValid() const { return false; } + [[nodiscard]] const String& getStartTerminal() const { return mStartTerminal; } - private: - Map mNonTerminals; + public: Buffer mRules; String mStartTerminal; bool mIsLooped = false; diff --git a/Language/public/Parser.hpp b/Language/public/Parser.hpp index 5196bc1..1c380e2 100644 --- a/Language/public/Parser.hpp +++ b/Language/public/Parser.hpp @@ -21,16 +21,20 @@ namespace tp { // ContextFreeGrammar; // ContextFreeCompiler; - typedef FiniteStateAutomation ContextFreeGraph; - typedef ContextFreeAutomata ContextFreeAutomata; + typedef FiniteStateAutomation ContextFreeGraph; + typedef ContextFreeAutomata ContextFreeAutomata; public: Parser() = default; public: - void compileTables(const ContextFreeGrammar& cfGrammar, const RegularGrammar& reGrammar) { + bool compileTables(const ContextFreeGrammar& cfGrammar, const RegularGrammar& reGrammar) { // Compile Regular Grammar { + if (!reGrammar.isValid()) { + return false; + } + RegularGraph graph; RegularCompiler compiler; compiler.compile(graph, reGrammar); @@ -40,12 +44,18 @@ namespace tp { // compile context free grammar { + if (!cfGrammar.isValid()) { + return false; + } + ContextFreeGraph graph; ContextFreeCompiler compiler; compiler.compile(cfGrammar, graph); graph.makeDeterministic(); mContextFreeAutomata.construct(graph); } + + return true; } void parse(const tAlphabetType* sentence, ualni sentenceLength, AST& out) {} diff --git a/Language/public/SimpleParser.hpp b/Language/public/SimpleParser.hpp index 071f761..c860c36 100644 --- a/Language/public/SimpleParser.hpp +++ b/Language/public/SimpleParser.hpp @@ -55,7 +55,7 @@ namespace tp { } private: - Parser mUnifiedGrammarParser; - Parser mUserParser; + Parser mUnifiedGrammarParser; + Parser mUserParser; }; }