diff --git a/tools/parse_emit.cpp b/tools/parse_emit.cpp index 23bfec35..26aeeae9 100644 --- a/tools/parse_emit.cpp +++ b/tools/parse_emit.cpp @@ -4,12 +4,22 @@ #include #include #include +#include #endif #include +#include #include #include +#ifdef C4_EXCEPTIONS +#include +#else +#include +std::jmp_buf jmp_env = {}; +c4::csubstr jmp_msg = {}; +#endif + using namespace c4; @@ -18,6 +28,13 @@ C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast") C4_SUPPRESS_WARNING_GCC("-Wuseless-cast") +bool quiet = false; +bool debug_tree = false; +bool emit_as_json = false; +bool timed_sections = false; +bool emit_to_string = false; + + //----------------------------------------------------------------------------- struct timed_section @@ -32,12 +49,142 @@ struct timed_section timed_section(csubstr n) : name(n), start(myclock::now()) {} ~timed_section() { - fprintf(stderr, "%.6fms: %.*s\n", since().count(), (int)name.len, name.str); - fflush(stderr); + if(timed_sections) + { + fprintf(stderr, "%.6fms: %.*s\n", since().count(), (int)name.len, name.str); + fflush(stderr); + } } }; -#define TS(name) timed_section name##__##__LINE__(#name) +#define TS(name) timed_section C4_XCAT(__, C4_XCAT(name, __LINE__))(#name) + +// LCOV_EXCL_START + +constexpr const char *usage = R"(usage: + + %s + +Parse yaml from file (or stdin when file is `-`) and emit to stdout. + +Options: + + -q,--quiet do not emit (default: emit yaml) + -d,--debug print rapidyaml tree + -j,--json emit json instead of yaml (default: emit yaml) + -s,--string emit to string before dumping to stdout. + otherwise, emit directly to stdout + -t,--timed time sections (print timings to stderr) + +)"; + +csubstr parse_args(int argc, const char *argv[]) +{ + if(argc < 2) + { + printf(usage, argv[0]); + yml::error("unknown argument"); + } + csubstr file = to_csubstr(argv[argc - 1]); + for(int i = 1; i+1 < argc; ++i) + { + csubstr arg = to_csubstr(argv[i]); + if(arg == "-q" || arg == "--quiet") + quiet = true; + else if(arg == "-t" || arg == "--timed") + timed_sections = true; + else if(arg == "-s" || arg == "--string") + emit_to_string = true; + else if(arg == "-d" || arg == "--debug") + debug_tree = true; + else if(arg == "-j" || arg == "--json") + emit_as_json = true; + else + { + printf(usage, argv[0]); + yml::error("unknown argument"); + } + } + return file; +} + +void load_file(csubstr filename, std::string *buf) +{ + buf->clear(); + if(filename == "-") // read from stdin + { + for(int c = std::getchar(); c != EOF; c = std::getchar()) + { + buf->push_back((char)c); + if(buf->size() == buf->capacity()) + buf->reserve(2u * (buf->capacity() >= 128u ? buf->capacity() : 128u)); + } + } + else + { + if(!fs::path_exists(filename.str)) + { + std::fprintf(stderr, "cannot find file: %s (cwd=%s)\n", filename.str, fs::cwd().c_str()); + yml::error("file not found"); + } + fs::file_get_contents(filename.str, buf); + } +} + +void emit_json_docs(yml::Tree const& tree, std::string *dst=nullptr) +{ + auto emitnode = [&](yml::ConstNodeRef node){ + if(dst) + { + emitrs_json(node, dst, /*append*/true); + *dst += '\n'; + } + else + { + emit_json(node, stdout); + } + }; + yml::ConstNodeRef root = tree.rootref(); + if(!root.is_stream()) + emitnode(root); + else + for(yml::ConstNodeRef doc : root.children()) + emitnode(doc); +} + +void report_error(const char* msg, size_t length, yml::Location loc, FILE *f) +{ + if(!loc.name.empty()) + { + fwrite(loc.name.str, 1, loc.name.len, f); + fputc(':', f); + } + fprintf(f, "%zu:", loc.line); + if(loc.col) + fprintf(f, "%zu:", loc.col); + if(loc.offset) + fprintf(f, " (%zuB):", loc.offset); + fputc(' ', f); + fprintf(f, "%.*s\n", static_cast(length), msg); + fflush(f); +} + +yml::Callbacks create_custom_callbacks() +{ + yml::Callbacks callbacks = {}; + callbacks.m_error = [](const char *msg, size_t msg_len, yml::Location location, void *) + { + report_error(msg, msg_len, location, stderr); + C4_IF_EXCEPTIONS( + throw std::runtime_error({msg, msg_len}); + , + jmp_msg.assign(msg, msg_len); + std::longjmp(jmp_env, 1); + ); + }; + return callbacks; +} +// LCOV_EXCL_STOP //----------------------------------------------------------------------------- @@ -60,8 +207,7 @@ void process_file(csubstr file) TS(estimate_capacity); cap = yml::estimate_tree_capacity(to_csubstr(contents)); } - if(debug_tree) - fprintf(stderr, "reserving capacity=%zu\n", (size_t)cap); + fprintf(stderr, "reserving capacity=%zu\n", (size_t)cap); tree.reserve(cap); } { @@ -114,76 +260,17 @@ void process_file(csubstr file) int main(int argc, const char *argv[]) { - bool print_emitted_to_stdout = true; - csubstr file; - // LCOV_EXCL_START - auto show_usage = [argv]{ - printf("usage: %s [-s] \n", argv[0]); - }; - if(argc == 2) - { - file = to_csubstr(argv[1]); - } - else if(argc > 2) + TS(TOTAL); + set_callbacks(create_custom_callbacks()); + C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0)) { - file = to_csubstr(argv[2]); - csubstr arg = to_csubstr(argv[1]); - if(arg == "-s") - { - print_emitted_to_stdout = false; - } - else - { - show_usage(); - return 1; - } + csubstr file = parse_args(argc, argv); + process_file(file); } - else + C4_IF_EXCEPTIONS_(catch(std::exception const&), else) { - show_usage(); return 1; } - // LCOV_EXCL_STOP - - TS(TOTAL); - - C4_CHECK_MSG(fs::path_exists(file.str), "cannot find file: %s (cwd=%s)", file.str, fs::cwd().c_str()); - - { - TS(objects); - std::string contents, output; - yml::Tree tree; - { - TS(read_file); - fs::file_get_contents(file.str, &contents); - } - { - TS(tree_reserve); - yml::id_type cap; - { - TS(estimate_capacity); - cap = yml::estimate_tree_capacity(to_csubstr(contents)); - } - fprintf(stderr, "reserving capacity=%zu\n", (size_t)cap); - tree.reserve(cap); - } - { - TS(parse_yml); - yml::parse_in_place(file, to_substr(contents), &tree); - } - { - TS(emit_to_buffer); - output.resize(contents.size()); // resize, not just reserve - yml::emitrs_yaml(tree, &output); - } - if(print_emitted_to_stdout) - { - TS(print_stdout); - fwrite(output.data(), 1, output.size(), stdout); - putchar('\n'); - } - } - return 0; }