diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/s2wasm-main.cpp | 50 | ||||
-rw-r--r-- | src/support/command-line.cpp | 148 | ||||
-rw-r--r-- | src/support/command-line.h | 50 |
3 files changed, 183 insertions, 65 deletions
diff --git a/src/s2wasm-main.cpp b/src/s2wasm-main.cpp index 00b71ab16..a7060dd6e 100644 --- a/src/s2wasm-main.cpp +++ b/src/s2wasm-main.cpp @@ -25,24 +25,33 @@ using namespace cashew; using namespace wasm; int main(int argc, const char *argv[]) { - Options options; - processCommandLine(argc, argv, &options, - "s2wasm INFILE\n\n" - "Link .s file into .wast\n\n" - "Optional arguments:\n" - " -n, --help Show this help message and exit\n" - " -d, --debug Print debug information to stderr\n" - " -o, --output Output file (stdout if not specified)\n" - " --global-base=N Where to start to place globals\n"); + Options options("s2wasm", "Link .s file into .wast"); + options.add("--output", "-o", "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options *o, const std::string &argument) { + o->extra["output"] = argument; + }) + .add("--global-base", "-g", "Where to start to place globals", + Options::Arguments::One, + [](Options *o, const std::string &argument) { + o->extra["global-base"] = argument; + }) + .add_positional("INFILE", Options::Arguments::One, + [](Options *o, const std::string &argument) { + o->extra["infile"] = argument; + }); + options.parse(argc, argv); std::string input; { if (options.debug) { - std::cerr << "Loading '" << options.infile << "'..." << std::endl; + std::cerr << "Loading '" << options.extra["infile"] << "'..." + << std::endl; } - std::ifstream infile(options.infile); + std::ifstream infile(options.extra["infile"]); if (!infile.is_open()) { - std::cerr << "Failed opening '" << options.infile << "'" << std::endl; + std::cerr << "Failed opening '" << options.extra["infile"] << "'" + << std::endl; exit(EXIT_FAILURE); } infile.seekg(0, std::ios::end); @@ -54,11 +63,15 @@ int main(int argc, const char *argv[]) { std::streambuf *buffer; std::ofstream outfile; - if (options.outfile.size()) { - if (options.debug) std::cerr << "Opening '" << options.outfile << std::endl; - outfile.open(options.outfile, std::ofstream::out | std::ofstream::trunc); + if (options.extra["output"].size()) { + if (options.debug) { + std::cerr << "Opening '" << options.extra["output"] << std::endl; + } + outfile.open(options.extra["output"], + std::ofstream::out | std::ofstream::trunc); if (!outfile.is_open()) { - std::cerr << "Failed opening '" << options.outfile << "'" << std::endl; + std::cerr << "Failed opening '" << options.extra["output"] << "'" + << std::endl; exit(EXIT_FAILURE); } buffer = outfile.rdbuf(); @@ -69,7 +82,10 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "Parsing and wasming..." << std::endl; AllocatingModule wasm; - size_t globalBase = options.extra["global-base"] ? atoi(options.extra["global-base"]) : 1; + size_t globalBase = options.extra.find("global-base") != options.extra.end() + ? std::stoull(options.extra["global-base"]) + : 1; + if (options.debug) std::cerr << "Global base " << globalBase << '\n'; S2WasmBuilder s2wasm(wasm, input.c_str(), options.debug, globalBase); if (options.debug) std::cerr << "Emscripten gluing..." << std::endl; diff --git a/src/support/command-line.cpp b/src/support/command-line.cpp index 3c03d48f4..40e11f60a 100644 --- a/src/support/command-line.cpp +++ b/src/support/command-line.cpp @@ -16,50 +16,122 @@ #include "support/command-line.h" -namespace { -bool optionIs(const char *arg, const char *LongOpt, const char *ShortOpt) { - return strcmp(arg, LongOpt) == 0 || strcmp(arg, ShortOpt) == 0; +using namespace wasm; + +Options::Options(const std::string &command, const std::string &description) + : debug(false), positional(Arguments::Zero) { + add("--help", "-h", "Show this help message and exit", Arguments::Zero, + [this, command, description](Options *o, const std::string &) { + std::cerr << command; + if (positional != Arguments::Zero) std::cerr << ' ' << positionalName; + std::cerr << "\n\n" << description << "\n\nOptions:\n"; + size_t optionWidth = 0; + for (const auto &o : options) { + optionWidth = + std::max(optionWidth, o.longName.size() + o.shortName.size()); + } + // TODO: line-wrap description. + for (const auto &o : options) { + size_t pad = 1 + optionWidth - o.longName.size() - o.shortName.size(); + std::cerr << " " << o.longName << ',' << o.shortName + << std::string(pad, ' ') << o.description << '\n'; + } + std::cerr << '\n'; + exit(EXIT_SUCCESS); + }); + add("--debug", "-d", "Print debug information to stderr", Arguments::Zero, + [](Options *o, const std::string &) { o->debug = true; }); } -bool startsWith(const char *str, const char *prefix) { - return strncmp(str, prefix, strlen(prefix)) == 0; + +Options::~Options() {} + +Options &Options::add(const std::string &longName, const std::string &shortName, + const std::string &description, Arguments arguments, + const Action &action) { + options.push_back({longName, shortName, description, arguments, action, 0}); + return *this; } -} // anonymous namespace -void wasm::processCommandLine(int argc, const char *argv[], Options *options, - const char *help) { +Options &Options::add_positional(const std::string &name, Arguments arguments, + const Action &action) { + positional = arguments; + positionalName = name; + positionalAction = action; + return *this; +} + +void Options::parse(int argc, const char *argv[]) { assert(argc > 0 && "expect at least program name as an argument"); + size_t positionalsSeen = 0; + auto dashes = [](const std::string &s) { + for (size_t i = 0;; ++i) { + if (s[i] != '-') return i; + } + }; for (size_t i = 1, e = argc; i != e; ++i) { - if (optionIs(argv[i], "--help", "-h")) { - std::cerr << help; - exit(EXIT_SUCCESS); - } else if (optionIs(argv[i], "--debug", "-d")) { - options->debug = true; - } else if (optionIs(argv[i], "--output", "-o")) { - if (i + 1 == e) { - std::cerr << "No output file" << std::endl; - exit(EXIT_FAILURE); - } - if (options->outfile.size()) { - std::cerr << "Expected only one output file, got '" << options->outfile - << "' and '" << argv[i] << "'" << std::endl; - exit(EXIT_FAILURE); - } - options->outfile = argv[++i]; - } else if (startsWith(argv[i], "--")) { - size_t j = 2; - std::string name; - while (argv[i][j] && argv[i][j] != '=') { - name += argv[i][j]; - j++; - } - options->extra[name] = argv[i][j] == '=' ? &argv[i][j + 1] : "(no value)"; - } else { - if (options->infile.size()) { - std::cerr << "Expected only one input file, got '" << options->infile - << "' and '" << argv[i] << "'" << std::endl; - exit(EXIT_FAILURE); + std::string currentOption = argv[i]; + + if (dashes(currentOption) == 0) { + // Positional. + switch (positional) { + case Arguments::Zero: + std::cerr << "Unexpected positional argument '" << currentOption + << "'\n"; + exit(EXIT_FAILURE); + case Arguments::One: + if (positionalsSeen) { + std::cerr << "Unexpected second positional argument '" + << currentOption << "' for " << positionalName << '\n'; + exit(EXIT_FAILURE); + } + // Fallthrough. + case Arguments::N: + positionalAction(this, currentOption); + ++positionalsSeen; } - options->infile = argv[i]; + continue; + } + + // Non-positional. + std::string argument; + auto equal = currentOption.find_first_of('='); + if (equal != std::string::npos) { + argument = currentOption.substr(equal + 1); + currentOption = currentOption.substr(0, equal - 1); + } + Option *option = nullptr; + for (auto &o : options) + if (o.longName == currentOption || o.shortName == currentOption) + option = &o; + if (!option) { + std::cerr << "Unknown option '" << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + switch (option->arguments) { + case Arguments::Zero: + if (argument.size()) { + std::cerr << "Unexpected argument '" << argument + << "' for option '" << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + break; + case Arguments::One: + if (option->seen) { + std::cerr << "Unexpected second argument '" << argument << "' for '" + << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + // Fallthrough. + case Arguments::N: + if (!argument.size()) { + if (i + 1 == e) { + std::cerr << "Couldn't find expected argument for '" << currentOption << "'\n"; + exit(EXIT_FAILURE); + } + argument = argv[++i]; + } } + option->action(this, argument); + ++option->seen; } } diff --git a/src/support/command-line.h b/src/support/command-line.h index f57929b6b..14dbce57f 100644 --- a/src/support/command-line.h +++ b/src/support/command-line.h @@ -21,22 +21,52 @@ #ifndef wasm_support_command_line_h #define wasm_support_command_line_h +#include <functional> +#include <map> +#include <string> +#include <utility> +#include <vector> + #include "wasm.h" + namespace wasm { -struct Options { - // standard options +class Options { + public: + typedef std::function<void(Options *, const std::string &)> Action; + enum class Arguments { Zero, One, N }; + bool debug; - std::string infile; - std::string outfile; - // extra options - std::map<std::string, const char *> extra; - Options() : debug(false) {} -}; + std::map<std::string, std::string> extra; -void processCommandLine(int argc, const char *argv[], Options *options, - const char *help); + Options(const std::string &command, const std::string &description); + ~Options(); + Options &add(const std::string &longName, const std::string &shortName, + const std::string &description, Arguments arguments, + const Action &action); + Options &add_positional(const std::string &name, Arguments arguments, + const Action &action); + void parse(int argc, const char *argv[]); + + private: + Options() = delete; + Options(const Options &) = delete; + Options &operator=(const Options &) = delete; + + struct Option { + std::string longName; + std::string shortName; + std::string description; + Arguments arguments; + Action action; + size_t seen; + }; + std::vector<Option> options; + Arguments positional; + std::string positionalName; + Action positionalAction; +}; } // namespace wasm |