#include "ActionMap.h"
#include "Cfa.h"
#include "Output.h"
#include "ParseFile.h"
#include "Parser.h"
#include <chrono>
#include <print>
enum
{
MAIN_SUCCESS = 0,
MAIN_ARG_ERROR,
MAIN_NOT_ACCEPTED,
MAIN_PARSE_ERROR,
MAIN_MAKECFA_ERROR,
MAIN_OUTPUT_ERROR,
MAIN_CAUGHT
};
int main(int argc, const char* argv[])
{
"LR(1)/GLR-Parser Generator command line tool v{}.{}.{}\n"
"\n"
" Where:\n"
" 1. <Grammar> is a grammar definition file.\n"
" 2. Generated C++ source files are named as:\n"
" <Filename>IdDef.h - Lexical token enumerations\n"
" <Filename>.h - Header of parser class\n"
" <Filename>.cpp - Implementation of parser class\n"
" 3. Generated token definitions are written to <TokensOutput> to feed scannergen\n"
, VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE)};
C_Paths inc_dirs;
int flags = 0;
ezargs.
position_args(std::array{
"Grammar",
"Filename",
"TokensOutput"})
.add_flag("include-dir", 'I', "Search path of #include directive within <Grammar>",
[&](auto s){
for (std::string line; std::getline(in, line, ':'); inc_dirs.emplace_back(line));
})
.add_flag("yes-to-all", 'a', "Quietly overwrite all existing output files",
[&]{
flags |= ALWAYS_OVERWRITE;
})
.add_flag("with-bom", "Prefix BOM to all output files (utf-8 encoding already)",
[&]{
flags |= WITH_BOM;
});
auto ret = ezargs.
parse(argc, argv);
if (!ret)
{
std::print("{}\n", ret.message());
return MAIN_ARG_ERROR;
}
if (!inc_dirs.empty())
{
std::print("INCLUDE_PATHS: {{");
bool first = true;
for (auto &i: inc_dirs)
{
if (first)
first = false;
else
std::print(", ");
std::print("{}", i.string());
}
std::print("}}\n");
}
try
{
std::ios_base::sync_with_stdio(true);
const auto startTime = std::chrono::system_clock::now();
C_BNFContext c{inc_dirs};
Main::C_BNFParser parser{c};
parseFile(argv[1], parser, TID_EOF);
if (!parser.accepted())
{
std::print("Not accepted on EOF\n");
return MAIN_NOT_ACCEPTED;
}
if (c.m_ErrorTotal[LL_ERROR] || c.m_ErrorTotal[LL_FATAL])
return MAIN_PARSE_ERROR;
C_States states;
C_GotoMap stateMap(c);
if (const auto n = makeCfa(c, states, stateMap))
{
std::print("\n"
"There are {} errors in call to makeCfa()", n);
return MAIN_MAKECFA_ERROR;
}
C_ActionShifts loserShits;
auto actionMap = makeActionMap(c, states, loserShits);
std::print("\n"
"Spent {}\"\n", std::chrono::duration<double>(std::chrono::system_clock::now() - startTime).count());
if (!loserShits.empty())
{
const size_t orgSize = stateMap.size();
size_t count{};
for (auto i: loserShits)
{
C_State fake;
fake.m_id = i.first;
auto found = stateMap.find({&fake, i.second});
if (found != stateMap.end())
{
++count;
stateMap.erase(found);
}
}
std::print("{} out of {} goto keys erased for redundancy.\n", count, orgSize);
if (count < loserShits.size())
std::print("Fail to find {} goto keys.\n", loserShits.size() - count);
}
FC_Output output{c, stateMap, actionMap, argv[1]};
if (!output(argv[2], argv[3], flags))
return MAIN_OUTPUT_ERROR;
if (c.checkUnusedOptions() ||
c.checkSemanticlessProductions())
{
std::print("Press a key to continue ...");
std::print("\n");
}
std::print("Parser created\n");
return MAIN_SUCCESS;
}
catch (const std::exception &t)
{
std::print(
"{}: {} ... \n",
HRTN(t), t.what());
return MAIN_CAUGHT;
}
catch (...)
{
std::print("Unknown exception\n");
return MAIN_CAUGHT;
}
}
C_ErrorOrIndex parse(std::integral auto argc, const char *const argv[]) const
C_EZArgs & position_args(const std::ranges::forward_range auto &arg_names, const std::ranges::forward_range auto &count_optionals, bool unlimited=false)
THE common namespace of bux library.