CFG compiler ini
This commit is contained in:
parent
4f88c45c9e
commit
a9a810c197
5 changed files with 154 additions and 46 deletions
|
|
@ -29,24 +29,6 @@ bool ContextFreeGrammar::Rule::isProductive() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContextFreeGrammar::NonTerminal::isProductive() const {
|
|
||||||
for (auto rule : rules) {
|
|
||||||
if (rule->isProductive()) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ContextFreeGrammar::NonTerminal::isLooped(Map<String, ualni>& 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 Rule& rule) { mRules.append(rule); }
|
||||||
|
|
||||||
void ContextFreeGrammar::addRule(const String& id, const InitialierList<Arg>& args) { addRule(Rule(id, args)); }
|
void ContextFreeGrammar::addRule(const String& id, const InitialierList<Arg>& args) { addRule(Rule(id, args)); }
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,149 @@
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
class ContextFreeCompiler {
|
class ContextFreeCompiler {
|
||||||
typedef ContextFreeGrammar Grammar;
|
|
||||||
|
|
||||||
public:
|
typedef ualni SymbolID;
|
||||||
// Terminals or non terminals
|
|
||||||
struct Alphabet {
|
struct Symbol {
|
||||||
String mId;
|
String mId;
|
||||||
bool mIsTerminal = false;
|
bool mIsTerminal = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Item {
|
struct Item {
|
||||||
Grammar::Rule mRule;
|
const ContextFreeGrammar::Rule* mRule = nullptr;
|
||||||
ualni mAdvanceIdx = 0;
|
ualni mAdvanceIdx = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef FiniteStateAutomation<Alphabet, Item> NFA;
|
struct NonTerminal {
|
||||||
typedef typename NFA::State State;
|
Buffer<ContextFreeGrammar::Rule*> rules;
|
||||||
|
Map<String, NonTerminal*> references;
|
||||||
|
Map<String, NonTerminal*> referencing;
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool isProductive() const {
|
||||||
|
for (auto rule : rules) {
|
||||||
|
if (rule->isProductive()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isLooped(Map<String, ualni>& 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:
|
public:
|
||||||
void compile(const Grammar& in, NFA& out) { nfa = &out; }
|
bool compile(const ContextFreeGrammar& grammar, FiniteStateAutomation<SymbolID, Item>& automata) {
|
||||||
|
if (!init(grammar)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
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<String, ualni> 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<String, NonTerminal> mNonTerminals;
|
||||||
|
Map<String, bool> mTerminals;
|
||||||
|
Buffer<Symbol> mSymbols;
|
||||||
|
Map<String, SymbolID> mSymbolLookup;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ namespace tp {
|
||||||
public:
|
public:
|
||||||
bool operator==(const Arg& in) const;
|
bool operator==(const Arg& in) const;
|
||||||
[[nodiscard]] const String& getId() const;
|
[[nodiscard]] const String& getId() const;
|
||||||
|
[[nodiscard]] bool isTerminal() const { return mIsTerminal; }
|
||||||
|
[[nodiscard]] bool isEpsilon() const { return mIsEpsilon; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String mId;
|
String mId;
|
||||||
|
|
@ -35,24 +37,14 @@ namespace tp {
|
||||||
public:
|
public:
|
||||||
bool operator==(const Rule& in) const;
|
bool operator==(const Rule& in) const;
|
||||||
[[nodiscard]] bool isProductive() const;
|
[[nodiscard]] bool isProductive() const;
|
||||||
|
[[nodiscard]] const String& getId() const { return mId; }
|
||||||
|
[[nodiscard]] const Buffer<Arg>* getArgs() const { return &mArgs; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String mId;
|
String mId;
|
||||||
Buffer<Arg> mArgs;
|
Buffer<Arg> mArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
// cache data
|
|
||||||
struct NonTerminal {
|
|
||||||
Buffer<Rule*> rules;
|
|
||||||
Map<String, NonTerminal*> references;
|
|
||||||
Map<String, NonTerminal*> referencing;
|
|
||||||
|
|
||||||
public:
|
|
||||||
[[nodiscard]] bool isProductive() const;
|
|
||||||
[[nodiscard]] bool isLooped(Map<String, ualni>& processed, const String& id) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ContextFreeGrammar() = default;
|
ContextFreeGrammar() = default;
|
||||||
|
|
||||||
|
|
@ -60,9 +52,11 @@ namespace tp {
|
||||||
void addRule(const Rule& rule);
|
void addRule(const Rule& rule);
|
||||||
void addRule(const String& id, const InitialierList<Arg>& args);
|
void addRule(const String& id, const InitialierList<Arg>& args);
|
||||||
void setStart(const String& startRule);
|
void setStart(const String& startRule);
|
||||||
|
[[nodiscard]] const Buffer<Rule>* getRules() const { return &mRules; }
|
||||||
|
[[nodiscard]] bool isValid() const { return false; }
|
||||||
|
[[nodiscard]] const String& getStartTerminal() const { return mStartTerminal; }
|
||||||
|
|
||||||
private:
|
public:
|
||||||
Map<String, NonTerminal> mNonTerminals;
|
|
||||||
Buffer<Rule> mRules;
|
Buffer<Rule> mRules;
|
||||||
String mStartTerminal;
|
String mStartTerminal;
|
||||||
bool mIsLooped = false;
|
bool mIsLooped = false;
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,20 @@ namespace tp {
|
||||||
|
|
||||||
// ContextFreeGrammar;
|
// ContextFreeGrammar;
|
||||||
// ContextFreeCompiler;
|
// ContextFreeCompiler;
|
||||||
typedef FiniteStateAutomation<ContextFreeCompiler::Alphabet, ContextFreeCompiler::Item> ContextFreeGraph;
|
typedef FiniteStateAutomation<ContextFreeCompiler::SymbolID, ContextFreeCompiler::Item> ContextFreeGraph;
|
||||||
typedef ContextFreeAutomata<ContextFreeCompiler::Alphabet, ContextFreeCompiler::Item> ContextFreeAutomata;
|
typedef ContextFreeAutomata<ContextFreeCompiler::SymbolID, ContextFreeCompiler::Item> ContextFreeAutomata;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Parser() = default;
|
Parser() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void compileTables(const ContextFreeGrammar& cfGrammar, const RegularGrammar& reGrammar) {
|
bool compileTables(const ContextFreeGrammar& cfGrammar, const RegularGrammar& reGrammar) {
|
||||||
// Compile Regular Grammar
|
// Compile Regular Grammar
|
||||||
{
|
{
|
||||||
|
if (!reGrammar.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
RegularGraph graph;
|
RegularGraph graph;
|
||||||
RegularCompiler compiler;
|
RegularCompiler compiler;
|
||||||
compiler.compile(graph, reGrammar);
|
compiler.compile(graph, reGrammar);
|
||||||
|
|
@ -40,12 +44,18 @@ namespace tp {
|
||||||
|
|
||||||
// compile context free grammar
|
// compile context free grammar
|
||||||
{
|
{
|
||||||
|
if (!cfGrammar.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ContextFreeGraph graph;
|
ContextFreeGraph graph;
|
||||||
ContextFreeCompiler compiler;
|
ContextFreeCompiler compiler;
|
||||||
compiler.compile(cfGrammar, graph);
|
compiler.compile(cfGrammar, graph);
|
||||||
graph.makeDeterministic();
|
graph.makeDeterministic();
|
||||||
mContextFreeAutomata.construct(graph);
|
mContextFreeAutomata.construct(graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse(const tAlphabetType* sentence, ualni sentenceLength, AST& out) {}
|
void parse(const tAlphabetType* sentence, ualni sentenceLength, AST& out) {}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace tp {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser<tAlphabetType, UGTokens> mUnifiedGrammarParser;
|
Parser<tAlphabetType, UGTokens, 0, 127> mUnifiedGrammarParser;
|
||||||
Parser<tAlphabetType, UGTokens> mUserParser;
|
Parser<tAlphabetType, UGTokens, 0, 127> mUserParser;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue