From 5fe060d582d4a82444243a28b18085c971a85628 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 27 Jul 2018 17:07:52 -0700 Subject: 4447 --- html/subx/001help.cc.html | 477 ++++++++++++++++++++++++++-------------------- 1 file changed, 275 insertions(+), 202 deletions(-) (limited to 'html/subx/001help.cc.html') diff --git a/html/subx/001help.cc.html b/html/subx/001help.cc.html index da5d00c5..03a2160c 100644 --- a/html/subx/001help.cc.html +++ b/html/subx/001help.cc.html @@ -16,7 +16,6 @@ a { color:#eeeeee; text-decoration: none; } a:hover { text-decoration: underline; } * { font-size: 12pt; font-size: 1em; } .Constant { color: #00a0a0; } -.SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Comment a { color:#0000ee; text-decoration:underline; } .Delimiter { color: #800080; } @@ -64,210 +63,284 @@ if ('onhashchange' in window) { 2 //: This should give you a sense for what to look forward to in later layers. 3 4 :(before "End Commandline Parsing") - 5 if (argc <= 1 || is_equal(argv[1], "--help")) { + 5 if (argc <= 1 || is_equal(argv[1], "--help")) { 6 //: this is the functionality later layers will provide 7 // currently no automated tests for commandline arg parsing - 8 cerr << "Usage:\n" - 9 << " subx test\n"; - 10 return 0; - 11 } - 12 - 13 //:: Helper function used by the above fragment of code (and later layers too, - 14 //:: who knows?). - 15 //: The :(code) directive appends function definitions to the end of the - 16 //: project. Regardless of where functions are defined, we can call them - 17 //: anywhere we like as long as we format the function header in a specific - 18 //: way: put it all on a single line without indent, end the line with ') {' - 19 //: and no trailing whitespace. As long as functions uniformly start this - 20 //: way, our 'build' script contains a little command to automatically - 21 //: generate declarations for them. - 22 :(code) - 23 bool is_equal(char* s, const char* lit) { - 24 return strncmp(s, lit, strlen(lit)) == 0; - 25 } - 26 - 27 bool starts_with(const string& s, const string& pat) { - 28 string::const_iterator a=s.begin(), b=pat.begin(); - 29 for (/*nada*/; a!=s.end() && b!=pat.end(); ++a, ++b) - 30 if (*a != *b) return false; - 31 return b == pat.end(); - 32 } - 33 - 34 //: I'll throw some style conventions here for want of a better place for them. - 35 //: As a rule I hate style guides. Do what you want, that's my motto. But since - 36 //: we're dealing with C/C++, the one big thing we want to avoid is undefined - 37 //: behavior. If a compiler ever encounters undefined behavior it can make - 38 //: your program do anything it wants. - 39 //: - 40 //: For reference, my checklist of undefined behaviors to watch out for: - 41 //: out-of-bounds access - 42 //: uninitialized variables - 43 //: use after free - 44 //: dereferencing invalid pointers: null, a new of size 0, others - 45 //: - 46 //: casting a large number to a type too small to hold it - 47 //: - 48 //: integer overflow - 49 //: division by zero and other undefined expressions - 50 //: left-shift by negative count - 51 //: shifting values by more than or equal to the number of bits they contain - 52 //: bitwise operations on signed numbers - 53 //: - 54 //: Converting pointers to types of different alignment requirements - 55 //: T* -> void* -> T*: defined - 56 //: T* -> U* -> T*: defined if non-function pointers and alignment requirements are same - 57 //: function pointers may be cast to other function pointers - 58 //: - 59 //: Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast) - 60 //: - 61 //: To guard against these, some conventions: - 62 //: - 63 //: 0. Initialize all primitive variables in functions and constructors. - 64 //: - 65 //: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and - 66 //: 'delete' as far as possible. Rely on STL to perform memory management to - 67 //: avoid use-after-free issues (and memory leaks). - 68 //: - 69 //: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[] - 70 //: except with map. Use at() with STL vectors and so on. - 71 //: - 72 //: 3. Valgrind all the things. - 73 //: - 74 //: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but - 75 //: the extra range doesn't matter, and it's one less confusing category of - 76 //: interaction gotchas to worry about. - 77 //: - 78 //: Corollary: don't use the size() method on containers, since it returns an - 79 //: unsigned and that'll cause warnings about mixing signed and unsigned, - 80 //: yadda-yadda. Instead use this macro below to perform an unsafe cast to - 81 //: signed. We'll just give up immediately if a container's ever too large. - 82 //: Basically, Mu is not concerned about this being a little slower than it - 83 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7) - 84 //: - 85 //: Addendum to corollary: We're going to uniformly use int everywhere, to - 86 //: indicate that we're oblivious to number size, and since Clang on 32-bit - 87 //: platforms doesn't yet support multiplication over 64-bit integers, and - 88 //: since multiplying two integers seems like a more common situation to end - 89 //: up in than integer overflow. - 90 :(before "End Includes") - 91 #define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size())) + 8 cerr << get(Help, "usage"); + 9 return 0; + 10 } + 11 + 12 //: Support for option parsing. + 13 //: Options always begin with '--' and are always the first arguments. An + 14 //: option will never follow a non-option. + 15 char** arg = &argv[1]; + 16 while (argc > 1 && starts_with(*arg, "--")) { + 17 if (false) + 18 ; // no-op branch just so any further additions can consistently always start with 'else' + 19 // End Commandline Options(*arg) + 20 else + 21 cerr << "skipping unknown option " << *arg << '\n'; + 22 --argc; ++argv; ++arg; + 23 } + 24 + 25 if (is_equal(argv[1], "help")) { + 26 if (argc == 2) { + 27 cerr << "help on what?\n"; + 28 help_contents(); + 29 return 0; + 30 } + 31 string key(argv[2]); + 32 // End Help Special-cases(key) + 33 if (contains_key(Help, key)) { + 34 cerr << get(Help, key); + 35 return 0; + 36 } + 37 else { + 38 cerr << "No help found for '" << key << "'\n"; + 39 help_contents(); + 40 cerr << "Please check your command for typos.\n"; + 41 return 1; + 42 } + 43 } + 44 + 45 :(code) + 46 void help_contents() { + 47 cerr << "Available top-level topics:\n"; + 48 cerr << " usage\n"; + 49 // End Help Contents + 50 } + 51 + 52 :(before "End Globals") + 53 map<string, string> Help; + 54 :(before "End Includes") + 55 #include <map> + 56 using std::map; + 57 :(before "End One-time Setup") + 58 init_help(); + 59 :(code) + 60 void init_help() { + 61 put(Help, "usage", + 62 "Welcome to SubX, a better way to program in machine code.\n" + 63 "SubX uses a subset of the x86 instruction set. SubX programs will run\n" + 64 "without modification on Linux computers.\n" + 65 "It provides a better experience and better error messages than\n" + 66 "programming directly in machine code, but you have to stick to the\n" + 67 "instructions it supports.\n" + 68 "\n" + 69 "== Ways to invoke subx\n" + 70 "- Run tests:\n" + 71 " subx test\n" + 72 "- See this message:\n" + 73 " subx --help\n" + 74 "- Convert a textual SubX program into a standard ELF binary that you can\n" + 75 " run on your computer:\n" + 76 " subx translate <input 'source' file> <output ELF binary>\n" + 77 "- Run a SubX binary using SubX itself (for better error messages):\n" + 78 " subx run <ELF binary>\n" + 79 "Add '--trace' to any of these commands to also emit a trace, for debugging purposes.\n" + 80 "However, options starting with '--' must always come before any other arguments.\n" + 81 "\n" + 82 "To start learning how to write SubX programs, run:\n" + 83 " subx help\n" + 84 ); + 85 // End Help Texts + 86 } + 87 + 88 :(code) + 89 bool is_equal(char* s, const char* lit) { + 90 return strncmp(s, lit, strlen(lit)) == 0; + 91 } 92 - 93 //: 5. Integer overflow is guarded against at runtime using the -ftrapv flag - 94 //: to the compiler, supported by Clang (GCC version only works sometimes: - 95 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work). - 96 :(before "atexit(reset)") - 97 initialize_signal_handlers(); // not always necessary, but doesn't hurt - 98 //? cerr << INT_MAX+1 << '\n'; // test overflow - 99 //? assert(false); // test SIGABRT -100 :(code) -101 // based on https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c -102 void initialize_signal_handlers() { -103 struct sigaction action; -104 bzero(&action, sizeof(action)); -105 action.sa_sigaction = dump_and_exit; -106 sigemptyset(&action.sa_mask); -107 sigaction(SIGABRT, &action, NULL); // assert() failure or integer overflow on linux (with -ftrapv) -108 sigaction(SIGILL, &action, NULL); // integer overflow on OS X (with -ftrapv) -109 } -110 void dump_and_exit(int sig, unused siginfo_t* dummy1, unused void* dummy2) { -111 switch (sig) { -112 case SIGABRT: -113 #ifndef __APPLE__ -114 cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n"; -115 _Exit(1); -116 #endif -117 break; -118 case SIGILL: -119 #ifdef __APPLE__ -120 cerr << "SIGILL: most likely caused by integer overflow\n"; -121 _Exit(1); -122 #endif -123 break; -124 default: -125 break; -126 } -127 } -128 :(before "End Includes") -129 #include <signal.h> -130 -131 //: For good measure we'll also enable SIGFPE. -132 :(before "atexit(reset)") -133 feenableexcept(FE_OVERFLOW | FE_UNDERFLOW); -134 //? assert(sizeof(int) == 4 && sizeof(float) == 4); -135 //? // | exp | mantissa -136 //? int smallest_subnormal = 0b00000000000000000000000000000001; -137 //? float smallest_subnormal_f = *reinterpret_cast<float*>(&smallest_subnormal); -138 //? cerr << "ε: " << smallest_subnormal_f << '\n'; -139 //? cerr << "ε/2: " << smallest_subnormal_f/2 << " (underflow)\n"; // test SIGFPE -140 :(before "End Includes") -141 #include <fenv.h> -142 :(code) -143 #ifdef __APPLE__ -144 // Public domain polyfill for feenableexcept on OS X -145 // http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c -146 int feenableexcept(unsigned int excepts) { -147 static fenv_t fenv; -148 unsigned int new_excepts = excepts & FE_ALL_EXCEPT; -149 unsigned int old_excepts; -150 if (fegetenv(&fenv)) return -1; -151 old_excepts = fenv.__control & FE_ALL_EXCEPT; -152 fenv.__control &= ~new_excepts; -153 fenv.__mxcsr &= ~(new_excepts << 7); -154 return fesetenv(&fenv) ? -1 : old_excepts; -155 } -156 #endif -157 -158 //: 6. Map's operator[] being non-const is fucking evil. -159 :(before "Globals") // can't generate prototypes for these -160 // from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map -161 template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) { -162 typename T::iterator iter(map.find(key)); -163 assert(iter != map.end()); -164 return iter->second; -165 } -166 template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) { -167 typename T::const_iterator iter(map.find(key)); -168 assert(iter != map.end()); -169 return iter->second; -170 } -171 template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { -172 map[key] = value; -173 return map[key]; -174 } -175 template<typename T> bool contains_key(T& map, typename T::key_type const& key) { -176 return map.find(key) != map.end(); -177 } -178 template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) { -179 return map[key]; -180 } -181 //: The contract: any container that relies on get_or_insert should never call -182 //: contains_key. -183 -184 //: 7. istreams are a royal pain in the arse. You have to be careful about -185 //: what subclass you try to putback into. You have to watch out for the pesky -186 //: failbit and badbit. Just avoid eof() and use this helper instead. -187 :(code) -188 bool has_data(istream& in) { -189 return in && !in.eof(); -190 } -191 -192 :(before "End Includes") -193 #include <assert.h> -194 -195 #include <iostream> -196 using std::istream; -197 using std::ostream; -198 using std::iostream; -199 using std::cin; -200 using std::cout; -201 using std::cerr; -202 #include <iomanip> -203 -204 #include <string.h> -205 #include <string> -206 using std::string; -207 -208 #define unused __attribute__((unused)) + 93 bool starts_with(const string& s, const string& pat) { + 94 string::const_iterator a=s.begin(), b=pat.begin(); + 95 for (/*nada*/; a!=s.end() && b!=pat.end(); ++a, ++b) + 96 if (*a != *b) return false; + 97 return b == pat.end(); + 98 } + 99 +100 //: I'll throw some style conventions here for want of a better place for them. +101 //: As a rule I hate style guides. Do what you want, that's my motto. But since +102 //: we're dealing with C/C++, the one big thing we want to avoid is undefined +103 //: behavior. If a compiler ever encounters undefined behavior it can make +104 //: your program do anything it wants. +105 //: +106 //: For reference, my checklist of undefined behaviors to watch out for: +107 //: out-of-bounds access +108 //: uninitialized variables +109 //: use after free +110 //: dereferencing invalid pointers: null, a new of size 0, others +111 //: +112 //: casting a large number to a type too small to hold it +113 //: +114 //: integer overflow +115 //: division by zero and other undefined expressions +116 //: left-shift by negative count +117 //: shifting values by more than or equal to the number of bits they contain +118 //: bitwise operations on signed numbers +119 //: +120 //: Converting pointers to types of different alignment requirements +121 //: T* -> void* -> T*: defined +122 //: T* -> U* -> T*: defined if non-function pointers and alignment requirements are same +123 //: function pointers may be cast to other function pointers +124 //: +125 //: Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast) +126 //: +127 //: To guard against these, some conventions: +128 //: +129 //: 0. Initialize all primitive variables in functions and constructors. +130 //: +131 //: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and +132 //: 'delete' as far as possible. Rely on STL to perform memory management to +133 //: avoid use-after-free issues (and memory leaks). +134 //: +135 //: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[] +136 //: except with map. Use at() with STL vectors and so on. +137 //: +138 //: 3. Valgrind all the things. +139 //: +140 //: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but +141 //: the extra range doesn't matter, and it's one less confusing category of +142 //: interaction gotchas to worry about. +143 //: +144 //: Corollary: don't use the size() method on containers, since it returns an +145 //: unsigned and that'll cause warnings about mixing signed and unsigned, +146 //: yadda-yadda. Instead use this macro below to perform an unsafe cast to +147 //: signed. We'll just give up immediately if a container's ever too large. +148 //: Basically, Mu is not concerned about this being a little slower than it +149 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7) +150 //: +151 //: Addendum to corollary: We're going to uniformly use int everywhere, to +152 //: indicate that we're oblivious to number size, and since Clang on 32-bit +153 //: platforms doesn't yet support multiplication over 64-bit integers, and +154 //: since multiplying two integers seems like a more common situation to end +155 //: up in than integer overflow. +156 :(before "End Includes") +157 #define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size())) +158 +159 //: 5. Integer overflow is guarded against at runtime using the -ftrapv flag +160 //: to the compiler, supported by Clang (GCC version only works sometimes: +161 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work). +162 :(before "atexit(reset)") +163 initialize_signal_handlers(); // not always necessary, but doesn't hurt +164 //? cerr << INT_MAX+1 << '\n'; // test overflow +165 //? assert(false); // test SIGABRT +166 :(code) +167 // based on https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c +168 void initialize_signal_handlers() { +169 struct sigaction action; +170 bzero(&action, sizeof(action)); +171 action.sa_sigaction = dump_and_exit; +172 sigemptyset(&action.sa_mask); +173 sigaction(SIGABRT, &action, NULL); // assert() failure or integer overflow on linux (with -ftrapv) +174 sigaction(SIGILL, &action, NULL); // integer overflow on OS X (with -ftrapv) +175 } +176 void dump_and_exit(int sig, siginfo_t* /*unused*/, void* /*unused*/) { +177 switch (sig) { +178 case SIGABRT: +179 #ifndef __APPLE__ +180 cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n"; +181 _Exit(1); +182 #endif +183 break; +184 case SIGILL: +185 #ifdef __APPLE__ +186 cerr << "SIGILL: most likely caused by integer overflow\n"; +187 _Exit(1); +188 #endif +189 break; +190 default: +191 break; +192 } +193 } +194 :(before "End Includes") +195 #include <signal.h> +196 +197 //: For good measure we'll also enable SIGFPE. +198 :(before "atexit(reset)") +199 feenableexcept(FE_OVERFLOW | FE_UNDERFLOW); +200 //? assert(sizeof(int) == 4 && sizeof(float) == 4); +201 //? // | exp | mantissa +202 //? int smallest_subnormal = 0b00000000000000000000000000000001; +203 //? float smallest_subnormal_f = *reinterpret_cast<float*>(&smallest_subnormal); +204 //? cerr << "ε: " << smallest_subnormal_f << '\n'; +205 //? cerr << "ε/2: " << smallest_subnormal_f/2 << " (underflow)\n"; // test SIGFPE +206 :(before "End Includes") +207 #include <fenv.h> +208 :(code) +209 #ifdef __APPLE__ +210 // Public domain polyfill for feenableexcept on OS X +211 // http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c +212 int feenableexcept(unsigned int excepts) { +213 static fenv_t fenv; +214 unsigned int new_excepts = excepts & FE_ALL_EXCEPT; +215 unsigned int old_excepts; +216 if (fegetenv(&fenv)) return -1; +217 old_excepts = fenv.__control & FE_ALL_EXCEPT; +218 fenv.__control &= ~new_excepts; +219 fenv.__mxcsr &= ~(new_excepts << 7); +220 return fesetenv(&fenv) ? -1 : old_excepts; +221 } +222 #endif +223 +224 //: 6. Map's operator[] being non-const is fucking evil. +225 :(before "Globals") // can't generate prototypes for these +226 // from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map +227 template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) { +228 typename T::iterator iter(map.find(key)); +229 if (iter == map.end()) { +230 cerr << "get couldn't find key '" << key << "'\n"; +231 assert(iter != map.end()); +232 } +233 return iter->second; +234 } +235 template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) { +236 typename T::const_iterator iter(map.find(key)); +237 if (iter == map.end()) { +238 cerr << "get couldn't find key '" << key << "'\n"; +239 assert(iter != map.end()); +240 } +241 return iter->second; +242 } +243 template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { +244 map[key] = value; +245 return map[key]; +246 } +247 template<typename T> bool contains_key(T& map, typename T::key_type const& key) { +248 return map.find(key) != map.end(); +249 } +250 template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) { +251 return map[key]; +252 } +253 //: The contract: any container that relies on get_or_insert should never call +254 //: contains_key. +255 +256 //: 7. istreams are a royal pain in the arse. You have to be careful about +257 //: what subclass you try to putback into. You have to watch out for the pesky +258 //: failbit and badbit. Just avoid eof() and use this helper instead. +259 :(code) +260 bool has_data(istream& in) { +261 return in && !in.eof(); +262 } +263 +264 :(before "End Includes") +265 #include <assert.h> +266 +267 #include <iostream> +268 using std::istream; +269 using std::ostream; +270 using std::iostream; +271 using std::cin; +272 using std::cout; +273 using std::cerr; +274 #include <iomanip> +275 +276 #include <string.h> +277 #include <string> +278 using std::string; +279 +280 #include <algorithm> +281 using std::min; +282 using std::max; -- cgit 1.4.1-2-gfad0