#include "CfGrammar.hpp" #include "Timing.hpp" using namespace tp; void CfGrammar::generateSentences(List& out) { constexpr ualni maxTime = 1000; constexpr ualni maxSentences = 200; constexpr ualni maxQueue = 1000000; List queue; Timer timer(maxTime); // add start production Sentence start; start.terms.pushBack({ startTerminal, false }); queue.pushBack(start); PASS: auto const sentential = &queue.first()->data; bool isSentence = true; ualni termIdx = 0; for (auto term : sentential->terms) { if (term->terminal) { termIdx++; continue; } auto nonTerminal = &mNonTerminals.get(term->id); for (auto production : nonTerminal->rules) { queue.pushBack(*sentential); auto copy = &queue.last()->data; auto appendTerm = copy->terms.findIdx(termIdx); auto deleteTerm = appendTerm; for (auto arg : production->args) { if (arg->isEpsilon) continue; auto newTerm = copy->terms.newNode({ arg->id, arg->isTerminal }); copy->terms.attach(newTerm, appendTerm); appendTerm = newTerm; } copy->terms.removeNode(deleteTerm); } isSentence = false; termIdx++; } if (isSentence) { auto exists = false; for (auto iter : out) { if (iter->terms == sentential->terms) { exists = true; break; } } if (!exists) out.pushBack(*sentential); } queue.popFront(); bool stop = false; stop |= !queue.length(); stop |= timer.isTimeout(); stop |= queue.length() > maxQueue; stop |= out.length() > maxSentences; if (!stop) goto PASS; } void CfGrammar::printSentence(Sentence& in) { printf("["); for (auto term : in.terms) { printf(" %s", term->id.read()); } printf(" ]\n"); } bool CfGrammar::compile() { if (!rules.length()) { printf("Grammar must have at leas one rule\n"); return false; } if (startTerminal == String()) { printf("Using first rule's non-terminal as grammar's root. Define explicitly - 'Start : id'\n"); startTerminal = rules.first()->data.id; } // find all rules for (auto rule : rules) { if (!rule->args.length()) { printf("Rule must have at leas one terminal or non-terminal. See rule '%s'\n", rule->id.read()); return false; } if (!mNonTerminals.presents(rule->id)) { mNonTerminals.put(rule->id, {}); } auto nonTerminal = &mNonTerminals.get(rule->id); nonTerminal->rules.pushBack(&rule.data()); } for (auto nonTerminal : mNonTerminals) { for (auto rule : nonTerminal->val.rules) { for (auto arg : rule->args) { if (arg->isTerminal) { mTerminals.put(rule->id, {}); } else if (!arg->isEpsilon) { auto idx = mNonTerminals.presents(arg->id); if (!idx) { printf("Referenced non-terminal '%s' is not defined\n", arg->id.read()); return false; } auto reference = &mNonTerminals.getSlotVal(idx); reference->references.put(nonTerminal->key, &nonTerminal->val); nonTerminal->val.referencing.put(arg->id, reference); } } } } List remove; for (auto nonTerminal : mNonTerminals) { if (!nonTerminal->val.references.size() && nonTerminal->key != startTerminal) { printf("Non-terminal '%s' is defined but not used\n", nonTerminal->key.read()); remove.pushBack(nonTerminal->key); } } for (auto rem : remove) { auto nonTerminal = &mNonTerminals.get(rem.data()); for (auto referencing : nonTerminal->referencing) { referencing->val->references.remove(nonTerminal->rules.first()->data->id); } for (auto rule : nonTerminal->rules) { rules.removeNode(rules.find(*rule.data())); } } 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(startTerminal).isLooped(processed, startTerminal)) { printf("Note that grammar is looped.\n"); } return true; }