Parsodus/include/Parsodus/backends/cppLR.h

149 lines
6.5 KiB
C++

#pragma once
#ifndef PARSODUS_BACKENDS_CPP_H
#define PARSODUS_BACKENDS_CPP_H
#include "Parsodus/backend.h"
#include "Parsodus/lrtables/table.h"
#include "Parsodus/template.h"
#include "Parsodus/util/symbols.h"
#include <cassert>
#include <map>
#include <string>
#include <vector>
namespace pds {
namespace backends {
/**
* A backend that emits c++ code
*/
template<typename Generator>
class CppLRBackend : public Backend {
public:
CppLRBackend(std::string parserType): Backend() {
for (char& c : parserType)
c = std::tolower(c);
m_parserType = parserType;
}
~CppLRBackend() {}
std::string getName() {
return "c++";
}
bool canProcessLang(std::string lang) {
for (char& c : lang)
c = std::tolower(c);
return lang == "c++" || lang == "cpp" || lang == "cxx";
}
bool canGenerateParser(std::string parserType) {
for (char& c : parserType)
c = std::tolower(c);
return parserType == m_parserType;
}
void generateParser(std::function<std::unique_ptr<std::ostream>(std::string)> getOstreamForFileName, std::string parserName, const Config& config) {
assert(parserName.length());
Grammar grammar(config.grammar);
grammar.variables.emplace("error");
Generator gen(grammar);
lr::LRTable table(gen.generate());
std::map<const std::string, templ::TemplateContext> topLevel;
topLevel["name"] = parserName;
topLevel["num_states"] = templ::make_string(std::to_string(table.act.size()));
topLevel["num_rules"] = templ::make_string(std::to_string(grammar.rules.size()));
topLevel["num_symbols"] = templ::make_string(std::to_string(grammar.terminals.size() + 1 + grammar.variables.size())); // + 1 for EOF
std::vector<templ::TemplateContext> symbols;
for (auto& s : grammar.terminals)
symbols.push_back(templ::make_map({{"symbol", templ::make_string("T_" + s)}}));
symbols.push_back(templ::make_map({{"symbol", templ::make_string("T_EOF")}}));
for (auto& s : grammar.variables)
symbols.push_back(templ::make_map({{"symbol", templ::make_string("V_" + s)}}));
topLevel["symbols"] = std::move(symbols);
std::vector<templ::TemplateContext> rules;
std::set<std::string> rulenames;
for (std::size_t i = 0; i < grammar.rules.size(); i++) {
std::map<const std::string, templ::TemplateContext> r;
r["index"] = templ::make_string(std::to_string(i));
r["rhs_length"] = templ::make_string(std::to_string(grammar.rules[i]->tail.size()));
if (false /* the rule has a name */) {
r["rname"] = templ::make_string(""); //The name
rulenames.insert(""); //The name
} else {
rulenames.insert(std::to_string(i));
}
rules.push_back(templ::make_map(std::move(r)));
}
topLevel["rules"] = templ::make_array(std::move(rules));
std::vector<templ::TemplateContext> rulenamesT;
for (auto& s : rulenames) {
rulenamesT.emplace_back(templ::make_map({{"rname", templ::make_string(s)}}));
}
topLevel["rulenames"] = templ::make_array(std::move(rulenamesT));
std::set<std::string> terminals = grammar.terminals;
terminals.insert(util::EOF_PLACEHOLDER);
std::vector<templ::TemplateContext> states(table.act.size());
for (std::size_t i = 0; i < table.act.size(); i++) {
std::map<const std::string, templ::TemplateContext> st;
std::vector<templ::TemplateContext> actions;
for (std::string term : terminals) {
std::string a = "ERROR";
std::string data = "0";
if (table.act[i].find(term) != table.act[i].end()) {
const auto& tmp = table.act[i][term];
switch (tmp.first) {
case lr::Action::SHIFT:
a = "SHIFT";
data = std::to_string(tmp.second);
break;
case lr::Action::REDUCE:
a = "REDUCE";
data = "static_cast<std::uint64_t>(" + parserName + "_Symbol::V_" + grammar.rules[tmp.second]->head + ") << 31 | " + std::to_string(tmp.second);
break;
case lr::Action::ACCEPT:
a = "ACCEPT";
break;
case lr::Action::ERROR:
a = "ERROR";
break;
}
}
actions.push_back(templ::make_map({{"action", templ::make_string(a)},
{"data", templ::make_string(data)}}));
}
std::vector<templ::TemplateContext> gotos;
for (const auto& nonterm : grammar.variables) {
gotos.push_back(templ::make_string(std::to_string(table.goto_[i][nonterm])));
}
st["actions"] = templ::make_array(std::move(actions));
st["gotos"] = templ::make_array(std::move(gotos));
states[i] = templ::make_map(std::move(st));
}
topLevel["states"] = templ::make_array(std::move(states));
std::unique_ptr<std::ostream> headerOut = getOstreamForFileName(parserName + ".h");
std::unique_ptr<std::ostream> implOut = getOstreamForFileName(parserName + ".cpp");
this->doTemplate(*headerOut, "c++/lr.h", topLevel);
this->doTemplate(*implOut, "c++/lr.cpp", topLevel);
}
private:
std::string m_parserType;
};
}
}
#endif //PARSODUS_BACKENDS_CPP_H