From 6e1eeeebfb453fa7c871869c19375ce60fbd7413 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 27 Jul 2019 16:01:55 -0700 Subject: 5485 - promote SubX to top-level --- 001help.cc | 144 +++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 87 insertions(+), 57 deletions(-) (limited to '001help.cc') diff --git a/001help.cc b/001help.cc index 78877561..7236ca50 100644 --- a/001help.cc +++ b/001help.cc @@ -5,54 +5,13 @@ if (argc <= 1 || is_equal(argv[1], "--help")) { //: this is the functionality later layers will provide // currently no automated tests for commandline arg parsing - if (argc <= 1) { - cerr << "Please provide a Mu program to run.\n" - << "\n"; - } - cerr << "Usage:\n" - << " mu [options] [test] [files]\n" - << "or:\n" - << " mu [options] [test] [files] -- [ingredients for function/recipe 'main']\n" - << "Square brackets surround optional arguments.\n" - << "\n" - << "Examples:\n" - << " To load files and run 'main':\n" - << " mu file1.mu file2.mu ...\n" - << " To run 'main' and dump a trace of all operations at the end:\n" - << " mu --trace file1.mu file2.mu ...\n" - << " To run all tests:\n" - << " mu test\n" - << " To load files and then run all tests:\n" - << " mu test file1.mu file2.mu ...\n" - << " To run a single Mu scenario:\n" - << " mu test file1.mu file2.mu ... scenario\n" - << " To run a single Mu scenario and dump a trace at the end:\n" - << " mu --trace test file1.mu file2.mu ... scenario\n" - << " To load files and run only the tests in explicitly loaded files (for apps):\n" - << " mu --test-only-app test file1.mu file2.mu ...\n" - << " To load all files with a numeric prefix in a directory:\n" - << " mu directory1 directory2 ...\n" - << " You can test directories just like files.\n" - << " mu test directory1 directory2 ...\n" - << " To pass ingredients to a mu program, provide them after '--':\n" - << " mu file_or_dir1 file_or_dir2 ... -- ingredient1 ingredient2 ...\n" - << " To see where a mu program is spending its time:\n" - << " mu --profile file_or_dir1 file_or_dir2 ...\n" - << " this slices and dices time spent in various profile.* output files\n" - << " To print out the trace to stderr:\n" - << " mu --dump file1.mu file2.mu ...\n" - << " this is handy when you want to see sandboxed traces alongside the main one\n" - << "\n" - << " To browse a trace generated by a previous run:\n" - << " mu browse-trace file\n" - ; + cerr << get(Help, "usage"); return 0; } //: Support for option parsing. //: Options always begin with '--' and are always the first arguments. An //: option will never follow a non-option. -:(before "End Commandline Parsing") char** arg = &argv[1]; while (argc > 1 && starts_with(*arg, "--")) { if (false) @@ -63,15 +22,76 @@ while (argc > 1 && starts_with(*arg, "--")) { --argc; ++argv; ++arg; } -//:: Helper function used by the above fragment of code (and later layers too, -//:: who knows?). -//: The :(code) directive appends function definitions to the end of the -//: project. Regardless of where functions are defined, we can call them -//: anywhere we like as long as we format the function header in a specific -//: way: put it all on a single line without indent, end the line with ') {' -//: and no trailing whitespace. As long as functions uniformly start this -//: way, our 'build*' scripts contain a little command to automatically -//: generate declarations for them. +if (is_equal(argv[1], "help")) { + if (argc == 2) { + cerr << "help on what?\n"; + help_contents(); + return 0; + } + string key(argv[2]); + // End Help Special-cases(key) + if (contains_key(Help, key)) { + cerr << get(Help, key); + return 0; + } + else { + cerr << "No help found for '" << key << "'\n"; + help_contents(); + cerr << "Please check your command for typos.\n"; + return 1; + } +} + +:(code) +void help_contents() { + cerr << "Available top-level topics:\n"; + cerr << " usage\n"; + // End Help Contents +} + +:(before "End Globals") +map Help; +:(before "End Includes") +#include +using std::map; +:(before "End One-time Setup") +init_help(); +:(code) +void init_help() { + put(Help, "usage", + "Welcome to SubX, a better way to program in machine code.\n" + "SubX uses a subset of the x86 instruction set. SubX programs will run\n" + "without modification on Linux computers.\n" + "It provides a better experience and better error messages than\n" + "programming directly in machine code, but you have to stick to the\n" + "instructions it supports.\n" + "\n" + "== Ways to invoke subx\n" + "- Run tests:\n" + " subx test\n" + "- See this message:\n" + " subx --help\n" + "- Convert a textual SubX program into a standard ELF binary that you can\n" + " run on your computer:\n" + " subx translate input1.subx input2.subx ... -o \n" + "- Run a SubX binary using SubX itself (for better error messages):\n" + " subx run \n" + "\n" + "== Debugging aids\n" + "- Add '--trace' to any of these commands to save a trace.\n" + "- Add '--debug' to add information to traces. 'subx --debug translate' will\n" + " save metadata to disk that 'subx --debug --trace run' uses to make traces\n" + " more informative.\n" + "\n" + "Options starting with '--' must always come before any other arguments.\n" + "\n" + "To start learning how to write SubX programs, see Readme.md (particularly\n" + "the section on the x86 instruction set) and then run:\n" + " subx help\n" + ); + // End Help Texts +} + :(code) bool is_equal(char* s, const char* lit) { return strncmp(s, lit, strlen(lit)) == 0; @@ -164,7 +184,7 @@ void dump_and_exit(int sig, siginfo_t* /*unused*/, void* /*unused*/) { switch (sig) { case SIGABRT: #ifndef __APPLE__ - cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure or exception\n"; + cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n"; _Exit(1); #endif break; @@ -213,18 +233,23 @@ int feenableexcept(unsigned int excepts) { // from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map template typename T::mapped_type& get(T& map, typename T::key_type const& key) { typename T::iterator iter(map.find(key)); - assert(iter != map.end()); + if (iter == map.end()) { + cerr << "get couldn't find key '" << key << "'\n"; + assert(iter != map.end()); + } return iter->second; } template typename T::mapped_type const& get(const T& map, typename T::key_type const& key) { typename T::const_iterator iter(map.find(key)); - assert(iter != map.end()); + if (iter == map.end()) { + cerr << "get couldn't find key '" << key << "'\n"; + assert(iter != map.end()); + } return iter->second; } template typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { - // map[key] requires mapped_type to have a zero-arg (default) constructor - map.insert(std::make_pair(key, value)).first->second = value; - return value; + map[key] = value; + return map[key]; } template bool contains_key(T& map, typename T::key_type const& key) { return map.find(key) != map.end(); @@ -232,6 +257,11 @@ template bool contains_key(T& map, typename T::key_type const& key) template typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) { return map[key]; } +template typename T::mapped_type const& put_new(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { + assert(map.find(key) == map.end()); + map[key] = value; + return map[key]; +} //: The contract: any container that relies on get_or_insert should never call //: contains_key. -- cgit 1.4.1-2-gfad0