Modules/Language/old/Parser/private/CfGrammar.cpp
2024-11-24 22:41:13 +03:00

161 lines
3.8 KiB
C++

#include "CfGrammar.hpp"
#include "Timing.hpp"
using namespace tp;
void CfGrammar::generateSentences(List<Sentence>& out) {
constexpr ualni maxTime = 1000;
constexpr ualni maxSentences = 200;
constexpr ualni maxQueue = 1000000;
List<Sentence> 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<String> 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<String, ualni> processed;
if (mNonTerminals.get(startTerminal).isLooped(processed, startTerminal)) {
printf("Note that grammar is looped.\n");
}
return true;
}