1 //: Everything this project/binary supports. 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")) { 6 //: this is the functionality later layers will provide 7 // currently no automated tests for commandline arg parsing 8 if (argc <= 1) { 9 cerr << "Please provide a Mu program to run.\n" 10 << "\n"; 11 } 12 cerr << "Usage:\n" 13 << " mu [options] [test] [files]\n" 14 << "or:\n" 15 << " mu [options] [test] [files] -- [ingredients for function/recipe 'main']\n" 16 << "Square brackets surround optional arguments.\n" 17 << "\n" 18 << "Examples:\n" 19 << " To load files and run 'main':\n" 20 << " mu file1.mu file2.mu ...\n" 21 << " To run 'main' and dump a trace of all operations at the end:\n" 22 << " mu --trace file1.mu file2.mu ...\n" 23 << " To run all tests:\n" 24 << " mu test\n" 25 << " To load files and then run all tests:\n" 26 << " mu test file1.mu file2.mu ...\n" 27 << " To run a single Mu scenario:\n" 28 << " mu test file1.mu file2.mu ... scenario\n" 29 << " To run a single Mu scenario and dump a trace at the end:\n" 30 << " mu --trace test file1.mu file2.mu ... scenario\n" 31 << " To load files and run only the tests in explicitly loaded files (for apps):\n" 32 << " mu --test-only-app test file1.mu file2.mu ...\n" 33 << " To load all files with a numeric prefix in a directory:\n" 34 << " mu directory1 directory2 ...\n" 35 << " You can test directories just like files.\n" 36 << " mu test directory1 directory2 ...\n" 37 << " To pass ingredients to a mu program, provide them after '--':\n" 38 << " mu file_or_dir1 file_or_dir2 ... -- ingredient1 ingredient2 ...\n" 39 << "\n" 40 << " To browse a trace generated by a previous run:\n" 41 << " mu browse-trace file\n" 42 ; 43 return 0; 44 } 45 46 //: Support for option parsing. 47 //: Options always begin with '--' and are always the first arguments. An 48 //: option will never follow a non-option. 49 :(before "End Commandline Parsing") 50 char** arg = &argv[1]; 51 while (argc > 1 && starts_with(*arg, "--")) { 52 if (false) 53 ; // no-op branch just so any further additions can consistently always start with 'else' 54 // End Commandline Options(*arg) 55 else 56 cerr << "skipping unknown option " << *arg << '\n'; 57 --argc; ++argv; ++arg; 58 } 59 60 //:: Helper function used by the above fragment of code (and later layers too, 61 //:: who knows?). 62 //: The :(code) directive appends function definitions to the end of the 63 //: project. Regardless of where functions are defined, we can call them 64 //: anywhere we like as long as we format the function header in a specific 65 //: way: put it all on a single line without indent, end the line with ') {' 66 //: and no trailing whitespace. As long as functions uniformly start this 67 //: way, our 'build' script contains a little command to automatically 68 //: generate declarations for them. 69 :(code) 70 bool is_equal(char* s, const char* lit) { 71 return strncmp(s, lit, strlen(lit)) == 0; 72 } 73 74 bool starts_with(const string& s, const string& pat) { 75 string::const_iterator a=s.begin(), b=pat.begin(); 76 for (/*nada*/; a!=s.end() && b!=pat.end(); ++a, ++b) 77 if (*a != *b) return false; 78 return b == pat.end(); 79 } 80 81 //: I'll throw some style conventions here for want of a better place for them. 82 //: As a rule I hate style guides. Do what you want, that's my motto. But since 83 //: we're dealing with C/C++, the one big thing we want to avoid is undefined 84 //: behavior. If a compiler ever encounters undefined behavior it can make 85 //: your program do anything it wants. 86 //: 87 //: For reference, my checklist of undefined behaviors to watch out for: 88 //: out-of-bounds access 89 //: uninitialized variables 90 //: use after free 91 //: dereferencing invalid pointers: null, a new of size 0, others 92 //: 93 //: casting a large number to a type too small to hold it 94 //: 95 //: integer overflow 96 //: division by zero and other undefined expressions 97 //: left-shift by negative count 98 //: shifting values by more than or equal to the number of bits they contain 99 //: bitwise operations on signed numbers 100 //: 101 //: Converting pointers to types of different alignment requirements 102 //: T* -> void* -> T*: defined 103 //: T* -> U* -> T*: defined if non-function pointers and alignment requirements are same 104 //: function pointers may be cast to other function pointers 105 //: 106 //: Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast) 107 //: 108 //: To guard against these, some conventions: 109 //: 110 //: 0. Initialize all primitive variables in functions and constructors. 111 //: 112 //: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and 113 //: 'delete' as far as possible. Rely on STL to perform memory management to 114 //: avoid use-after-free issues (and memory leaks). 115 //: 116 //: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[] 117 //: except with map. Use at() with STL vectors and so on. 118 //: 119 //: 3. Valgrind all the things. 120 //: 121 //: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but 122 //: the extra range doesn't matter, and it's one less confusing category of 123 //: interaction gotchas to worry about. 124 //: 125 //: Corollary: don't use the size() method on containers, since it returns an 126 //: unsigned and that'll cause warnings about mixing signed and unsigned, 127 //: yadda-yadda. Instead use this macro below to perform an unsafe cast to 128 //: signed. We'll just give up immediately if a container's ever too large. 129 //: Basically, Mu is not concerned about this being a little slower than it 130 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7) 131 //: 132 //: Addendum to corollary: We're going to uniformly use int everywhere, to 133 //: indicate that we're oblivious to number size, and since Clang on 32-bit 134 //: platforms doesn't yet support multiplication over 64-bit integers, and 135 //: since multiplying two integers seems like a more common situation to end 136 //: up in than integer overflow. 137 :(before "End Includes") 138 #define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size())) 139 140 //: 5. Integer overflow is guarded against at runtime using the -ftrapv flag 141 //: to the compiler, supported by Clang (GCC version only works sometimes: 142 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work). 143 :(before "atexit(teardown)") 144 initialize_signal_handlers(); // not always necessary, but doesn't hurt 145 //? cerr << INT_MAX+1 << '\n'; // test overflow 146 //? assert(false); // test SIGABRT 147 :(code) 148 // based on https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c 149 void initialize_signal_handlers() { 150 struct sigaction action; 151 bzero(&action, sizeof(action)); 152 action.sa_sigaction = dump_and_exit; 153 sigemptyset(&action.sa_mask); 154 sigaction(SIGABRT, &action, NULL); // assert() failure or integer overflow on linux (with -ftrapv) 155 sigaction(SIGILL, &action, NULL); // integer overflow on OS X (with -ftrapv) 156 } 157 void dump_and_exit(int sig, unused siginfo_t* dummy1, unused void* dummy2) { 158 switch (sig) { 159 case SIGABRT: 160 #ifndef __APPLE__ 161 cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n"; 162 _Exit(1); 163 #endif 164 break; 165 case SIGILL: 166 #ifdef __APPLE__ 167 cerr << "SIGILL: most likely caused by integer overflow\n"; 168 _Exit(1); 169 #endif 170 break; 171 default: 172 break; 173 } 174 } 175 :(before "End Includes") 176 #include <signal.h> 177 178 //: For good measure we'll also enable SIGFPE. 179 :(before "atexit(teardown)") 180 feenableexcept(FE_OVERFLOW | FE_UNDERFLOW); 181 //? assert(sizeof(int) == 4 && sizeof(float) == 4); 182 //? // | exp | mantissa 183 //? int smallest_subnormal = 0b00000000000000000000000000000001; 184 //? float smallest_subnormal_f = *reinterpret_cast<float*>(&smallest_subnormal); 185 //? cerr << "ε: " << smallest_subnormal_f << '\n'; 186 //? cerr << "ε/2: " << smallest_subnormal_f/2 << " (underflow)\n"; // test SIGFPE 187 :(before "End Includes") 188 #include <fenv.h> 189 :(code) 190 #ifdef __APPLE__ 191 // Public domain polyfill for feenableexcept on OS X 192 // http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c 193 int feenableexcept (unsigned int excepts) { 194 static fenv_t fenv; 195 unsigned int new_excepts = excepts & FE_ALL_EXCEPT; 196 unsigned int old_excepts; 197 if (fegetenv(&fenv)) return -1; 198 old_excepts = fenv.__control & FE_ALL_EXCEPT; 199 fenv.__control &= ~new_excepts; 200 fenv.__mxcsr &= ~(new_excepts << 7); 201 return fesetenv(&fenv) ? -1 : old_excepts; 202 } 203 #endif 204 205 //: 6. Map's operator[] being non-const is fucking evil. 206 :(before "Globals") // can't generate prototypes for these 207 // from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map 208 template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) { 209 typename T::iterator iter(map.find(key)); 210 assert(iter != map.end()); 211 return iter->second; 212 } 213 template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) { 214 typename T::const_iterator iter(map.find(key)); 215 assert(iter != map.end()); 216 return iter->second; 217 } 218 template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { 219 map[key] = value; 220 return map[key]; 221 } 222 template<typename T> bool contains_key(T& map, typename T::key_type const& key) { 223 return map.find(key) != map.end(); 224 } 225 template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) { 226 return map[key]; 227 } 228 //: The contract: any container that relies on get_or_insert should never call 229 //: contains_key. 230 231 //: 7. istreams are a royal pain in the arse. You have to be careful about 232 //: what subclass you try to putback into. You have to watch out for the pesky 233 //: failbit and badbit. Just avoid eof() and use this helper instead. 234 :(code) 235 bool has_data(istream& in) { 236 return in && !in.eof(); 237 } 238 239 :(before "End Includes") 240 #include <assert.h> 241 242 #include <iostream> 243 using std::istream; 244 using std::ostream; 245 using std::iostream; 246 using std::cin; 247 using std::cout; 248 using std::cerr; 249 #include <iomanip> 250 251 #include <string.h> 252 #include <string> 253 using std::string; 254 255 #define unused __attribute__((unused))