diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2026-05-01 18:45:23 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2026-05-02 10:54:02 +0200 |
| commit | 19005581a0d35233f862e57308734d3486569bb9 (patch) | |
| tree | 36b925df49b2f7ca8e4283c0682a3f3087abbce3 /src/gen_syntax.cc | |
| parent | 957b8404b8f902fee6a8de144e6274f05b55d342 (diff) | |
Diffstat (limited to 'src/gen_syntax.cc')
| -rw-r--r-- | src/gen_syntax.cc | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/gen_syntax.cc b/src/gen_syntax.cc new file mode 100644 index 0000000..f455eb7 --- /dev/null +++ b/src/gen_syntax.cc @@ -0,0 +1,136 @@ +#include "args.hh" +#include "errors.hh" +#include "grammar.hh" +#include "io.hh" +#include "prefix_tree.hh" + +#include <algorithm> +#include <cassert> +#include <charconv> +#include <cstddef> +#include <cstdint> +#include <fstream> +#include <iostream> +#include <set> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +#include <iostream> + +namespace { + +enum class CharacterClass : uint8_t { + kIdentifier = 0, + kLiteral = 1, +}; + +std::vector<std::string> const kCharacterClassNames( + {"Identifier", "Literal"}); + +std::string make_define(std::string_view filename) { + std::string ret; + ret.reserve(filename.size()); + for (char c : filename) { + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') { + ret.push_back(c); + } else if (c >= 'a' && c <= 'z') { + ret.push_back(static_cast<char>(c & ~0x20)); + } else { + ret.push_back('_'); + } + } + return ret; +} + +class Generator { + public: + bool generate(std::string_view header_name, std::string_view source_name, + std::string const& ns, grammar::Grammar& grammar); +}; + +bool Generator::generate(std::string_view header_name, + std::string_view source_name, std::string const& ns, + grammar::Grammar& grammar) { + std::fstream header{std::string(header_name), + std::fstream::trunc | std::fstream::out}; + std::fstream source{std::string(source_name), + std::fstream::trunc | std::fstream::out}; + + auto header_guard = make_define(header_name); + + header << "#ifndef " << header_guard << "\n" + << "#define " << header_guard << "\n" + << "\n" + << "namespace " << ns << " {\n" + << "\n"; + + + header << "\n" + << "} // namespace " << ns << "\n" + << "\n" + << "#endif // " << header_guard << "\n"; + + source << "#include \"" << header_name << "\"\n" + << "\n" + << "namespace " << ns << " {\n" + << "\n"; + + + source << "\n" + << "} // namespace " << ns << "\n"; + + return true; +} + +} // namespace + +int main(int argc, char** argv) { + auto args = Args::create(); + auto opt_help = args->option('h', "help", "display this text and exit"); + auto opt_ns = args->option_argument('\0', "namespace", "ARG", + "Namespace for syntax reader"); + std::vector<std::string_view> arguments; + if (!args->run(argc, argv, &arguments)) { + args->print_error(std::cerr); + std::cerr << "Try `gen_syntax --help` for usage\n"; + return 1; + } + if (opt_help->is_set()) { + std::cout << "Usage: `gen_syntax [OPTIONS...] syntax.grammar" + << " OUTPUT.hh OUTPUT.cc`\n" + << "Generates a syntax reader for grammar.\n" + << "\n"; + args->print_help(std::cout); + return 0; + } + if (!opt_ns->is_set()) { + std::cerr << "No namespace given.\n" + << "Try `gen_syntax --help` for usage\n"; + return 1; + } + auto ns = opt_ns->argument(); + if (arguments.size() != 3) { + std::cerr << "Expecting three arguments. No more, no less.\n" + << "Try `gen_syntax --help` for usage\n"; + return 1; + } + + auto filename = std::string(arguments[0]); + auto reader = io::open(filename); + if (!reader.has_value()) { + std::cerr << "Unable to open " << filename << '\n'; + return 1; + } + auto errors = src::file_errors(std::move(filename)); + auto grammar = + grammar::load(std::move(reader.value()), kCharacterClassNames, *errors); + if (!grammar || errors->errors() > 0) + return 1; + + Generator generator; + if (!generator.generate(arguments[1], arguments[2], ns, *grammar)) + return 1; + return 0; +} |
