about summary refs log tree commit diff stats
path: root/001help.cc
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-07-27 16:01:55 -0700
committerKartik Agaram <vc@akkartik.com>2019-07-27 17:47:59 -0700
commit6e1eeeebfb453fa7c871869c19375ce60fbd7413 (patch)
tree539c4a3fdf1756ae79770d5c4aaf6366f1d1525e /001help.cc
parent8846a7f85cc04b77b2fe8a67b6d317723437b00c (diff)
downloadmu-6e1eeeebfb453fa7c871869c19375ce60fbd7413.tar.gz
5485 - promote SubX to top-level
Diffstat (limited to '001help.cc')
-rw-r--r--001help.cc144
1 files changed, 87 insertions, 57 deletions
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<string, string> Help;
+:(before "End Includes")
+#include <map>
+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 <output ELF binary>\n"
+    "- Run a SubX binary using SubX itself (for better error messages):\n"
+    "    subx run <ELF binary>\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> 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> 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> 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<typename T> bool contains_key(T& map, typename T::key_type const& key) {
   return map.find(key) != map.end();
@@ -232,6 +257,11 @@ template<typename T> bool contains_key(T& map, typename T::key_type const& key)
 template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) {
   return map[key];
 }
+template<typename T> 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.