#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 #include #include #include namespace pds { namespace backends { /** * A backend that emits c++ code */ template class CppLRBackend : public Backend { public: CppLRBackend(util::ParserType parserType): Backend(), 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(util::ParserType parserType) { return parserType == m_parserType; } void generateParser(std::function(std::string)> getOstreamForFileName, std::string parserName, const Config& config) { assert(parserName.length()); Generator gen(config.grammar); lr::LRTable table(gen.generate()); std::map 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(config.grammar.rules.size())); topLevel["num_symbols"] = templ::make_string(std::to_string(config.grammar.terminals.size() + 1 + config.grammar.variables.size())); // + 1 for EOF std::vector symbols; symbols.push_back(templ::make_map({{"symbol", templ::make_string("T_EOF")}})); for (auto& s : config.grammar.terminals) symbols.push_back(templ::make_map({{"symbol", templ::make_string("T_" + s)}})); for (auto& s : config.grammar.variables) symbols.push_back(templ::make_map({{"symbol", templ::make_string("V_" + s)}})); topLevel["symbols"] = std::move(symbols); std::vector rules; for (std::size_t i = 0; i < config.grammar.rules.size(); i++) { std::map r; r["index"] = templ::make_string(std::to_string(i)); r["rhs_length"] = templ::make_string(std::to_string(config.grammar.rules[i]->tail.size())); if (false /* the rule has a name */) r["rname"] = templ::make_string(""); //The name rules.push_back(templ::make_map(std::move(r))); } topLevel["rules"] = templ::make_array(std::move(rules)); std::set terminals = config.grammar.terminals; terminals.insert(util::EOF_PLACEHOLDER); std::vector states(table.act.size()); for (std::size_t i = 0; i < table.act.size(); i++) { std::map st; std::vector 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(" + parserName + "_Symbol::V_" + config.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 gotos; for (const auto& nonterm : config.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 headerOut = getOstreamForFileName(parserName + ".h"); std::unique_ptr implOut = getOstreamForFileName(parserName + ".cpp"); this->doTemplate(*headerOut, "c++/lr.h", topLevel); this->doTemplate(*implOut, "c++/lr.cpp", topLevel); } private: util::ParserType m_parserType; }; } } #endif //PARSODUS_BACKENDS_CPP_H