From 5fe060d582d4a82444243a28b18085c971a85628 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 27 Jul 2018 17:07:52 -0700 Subject: 4447 --- html/003trace.cc.html | 677 ++++++++++++++++++++++++++------------------------ 1 file changed, 348 insertions(+), 329 deletions(-) (limited to 'html/003trace.cc.html') diff --git a/html/003trace.cc.html b/html/003trace.cc.html index caf54354..5fd1ef5d 100644 --- a/html/003trace.cc.html +++ b/html/003trace.cc.html @@ -22,7 +22,6 @@ a:hover { text-decoration: underline; } .LineNr { color: #444444; } .Identifier { color: #c0a020; } .Normal { color: #aaaaaa; background-color: #080808; padding-bottom: 1px; } -.PreProc { color: #800080; } .cSpecial { color: #008000; } --> @@ -105,7 +104,7 @@ if ('onhashchange' in window) { 45 //: 46 //: Between layers and domain-driven testing, programming starts to look like a 47 //: fundamentally different activity. Instead of a) superficial, b) local rules - 48 //: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet + 48 //: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet], 49 //: we allow programmers to engage with the a) deep, b) global structure of the 50 //: c) domain. If you can systematically track discontinuities in the domain, 51 //: you don't care if the code used gotos as long as it passed the tests. If @@ -137,341 +136,361 @@ if ('onhashchange' in window) { 77 }; 78 79 :(before "End Globals") - 80 bool Hide_errors = false; - 81 bool Dump_trace = false; - 82 string Dump_label = ""; + 80 bool Hide_errors = false; // if set, don't print even error trace lines to screen + 81 bool Dump_trace = false; // if set, print trace lines to screen + 82 string Dump_label = ""; // if set, print trace lines matching a single label to screen 83 :(before "End Reset") - 84 Hide_errors = false; - 85 Dump_trace = false; + 84 Hide_errors = false; + 85 Dump_trace = false; 86 Dump_label = ""; 87 - 88 :(before "End Types") - 89 // Pre-define some global constants that trace_stream needs to know about. - 90 // Since they're in the Types section, they'll be included in any cleaved - 91 // compilation units. So no extern linkage. - 92 const int Max_depth = 9999; - 93 const int Error_depth = 0; // definitely always print errors - 94 const int App_depth = 2; // temporarily where all Mu code will trace to - 95 - 96 struct trace_stream { - 97 vector<trace_line> past_lines; - 98 // accumulator for current line - 99 ostringstream* curr_stream; -100 string curr_label; -101 int curr_depth; -102 int callstack_depth; -103 int collect_depth; -104 ofstream null_stream; // never opens a file, so writes silently fail -105 trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {} -106 ~trace_stream() { if (curr_stream) delete curr_stream; } -107 -108 ostream& stream(string label) { -109 return stream(Max_depth, label); -110 } -111 -112 ostream& stream(int depth, string label) { -113 if (depth > collect_depth) return null_stream; -114 curr_stream = new ostringstream; -115 curr_label = label; -116 curr_depth = depth; -117 return *curr_stream; -118 } -119 -120 void dump() { -121 ofstream fout("last_run"); -122 fout << readable_contents(""); -123 fout.close(); -124 } -125 -126 // be sure to call this before messing with curr_stream or curr_label -127 void newline(); -128 // useful for debugging -129 string readable_contents(string label); // empty label = show everything -130 }; -131 -132 :(code) -133 void trace_stream::newline() { -134 if (!curr_stream) return; -135 string curr_contents = curr_stream->str(); -136 if (!curr_contents.empty()) { -137 past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents)); // preserve indent in contents -138 if ((!Hide_errors && curr_label == "error") -139 || Dump_trace -140 || (!Dump_label.empty() && curr_label == Dump_label)) -141 cerr << curr_label << ": " << curr_contents << '\n'; -142 } -143 delete curr_stream; -144 curr_stream = NULL; -145 curr_label.clear(); -146 curr_depth = Max_depth; -147 } + 88 //: Support for tracing an entire run. + 89 //: Traces can have a lot of overhead, so only turn them on when asked. + 90 :(before "End Commandline Options(*arg)") + 91 else if (is_equal(*arg, "--trace")) { + 92 Save_trace = true; + 93 } + 94 :(before "End Commandline Parsing") + 95 if (Save_trace) { + 96 cerr << "initializing trace\n"; + 97 Trace_stream = new trace_stream; + 98 } + 99 :(code) +100 void cleanup_main() { +101 if (!Trace_stream) return; +102 if (Save_trace) +103 Trace_stream->save(); +104 delete Trace_stream; +105 Trace_stream = NULL; +106 } +107 :(before "End One-time Setup") +108 atexit(cleanup_main); +109 +110 :(before "End Types") +111 // Pre-define some global constants that trace_stream needs to know about. +112 // Since they're in the Types section, they'll be included in any cleaved +113 // compilation units. So no extern linkage. +114 const int Max_depth = 9999; +115 const int Error_depth = 0; // definitely always print errors +116 const int App_depth = 2; // temporarily where all Mu code will trace to +117 +118 struct trace_stream { +119 vector<trace_line> past_lines; +120 // accumulator for current line +121 ostringstream* curr_stream; +122 string curr_label; +123 int curr_depth; +124 int callstack_depth; +125 int collect_depth; +126 ofstream null_stream; // never opens a file, so writes silently fail +127 trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {} +128 ~trace_stream() { if (curr_stream) delete curr_stream; } +129 +130 ostream& stream(string label) { +131 return stream(Max_depth, label); +132 } +133 +134 ostream& stream(int depth, string label) { +135 if (depth > collect_depth) return null_stream; +136 curr_stream = new ostringstream; +137 curr_label = label; +138 curr_depth = depth; +139 return *curr_stream; +140 } +141 +142 void save() { +143 cerr << "saving trace to 'last_run'\n"; +144 ofstream fout("last_run"); +145 fout << readable_contents(""); +146 fout.close(); +147 } 148 -149 string trace_stream::readable_contents(string label) { -150 ostringstream output; -151 label = trim(label); -152 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p) -153 if (label.empty() || label == p->label) { -154 output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n'; -155 } -156 return output.str(); -157 } -158 -159 :(before "End Globals") -160 trace_stream* Trace_stream = NULL; -161 int Trace_errors = 0; // used only when Trace_stream is NULL -162 -163 :(before "End Includes") -164 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream; -165 -166 // Top-level helper. IMPORTANT: can't nest -167 #define trace(...) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__) -168 -169 // Just for debugging; 'git log' should never show any calls to 'dbg'. -170 #define dbg trace(0, "a") -171 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label); -172 -173 // Errors are a special layer. -174 #define raise (!Trace_stream ? (scroll_to_bottom_and_close_console(),++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error")) -175 // If we aren't yet sure how to deal with some corner case, use assert_for_now -176 // to indicate that it isn't an inviolable invariant. -177 #define assert_for_now assert -178 -179 //: Automatically close the console in some situations. -180 :(before "End One-time Setup") -181 atexit(scroll_to_bottom_and_close_console); -182 :(code) -183 void scroll_to_bottom_and_close_console() { -184 if (!tb_is_active()) return; -185 // leave the screen in a relatively clean state -186 tb_set_cursor(tb_width()-1, tb_height()-1); -187 cout << "\r\n"; -188 tb_shutdown(); -189 } -190 -191 // Inside tests, fail any tests that displayed (unexpected) errors. -192 // Expected errors in tests should always be hidden and silently checked for. -193 :(before "End Test Teardown") -194 if (Passed && !Hide_errors && trace_contains_errors()) { -195 Passed = false; -196 } -197 :(code) -198 bool trace_contains_errors() { -199 return Trace_errors > 0 || trace_count("error") > 0; -200 } +149 // be sure to call this before messing with curr_stream or curr_label +150 void newline(); +151 // useful for debugging +152 string readable_contents(string label); // empty label = show everything +153 }; +154 +155 :(code) +156 void trace_stream::newline() { +157 if (!curr_stream) return; +158 string curr_contents = curr_stream->str(); +159 if (!curr_contents.empty()) { +160 past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents)); // preserve indent in contents +161 if ((!Hide_errors && curr_label == "error") +162 || Dump_trace +163 || (!Dump_label.empty() && curr_label == Dump_label)) +164 cerr << curr_label << ": " << curr_contents << '\n'; +165 } +166 delete curr_stream; +167 curr_stream = NULL; +168 curr_label.clear(); +169 curr_depth = Max_depth; +170 } +171 +172 string trace_stream::readable_contents(string label) { +173 ostringstream output; +174 label = trim(label); +175 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p) +176 if (label.empty() || label == p->label) { +177 output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n'; +178 } +179 return output.str(); +180 } +181 +182 :(before "End Globals") +183 trace_stream* Trace_stream = NULL; +184 int Trace_errors = 0; // used only when Trace_stream is NULL +185 +186 :(before "End Includes") +187 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream; +188 +189 // Top-level helper. IMPORTANT: can't nest +190 #define trace(...) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__) +191 +192 // Just for debugging; 'git log' should never show any calls to 'dbg'. +193 #define dbg trace(0, "a") +194 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label); +195 +196 // Errors are a special layer. +197 #define raise (!Trace_stream ? (scroll_to_bottom_and_close_console(),++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error")) +198 // If we aren't yet sure how to deal with some corner case, use assert_for_now +199 // to indicate that it isn't an inviolable invariant. +200 #define assert_for_now assert 201 -202 :(before "End Types") -203 struct end {}; -204 :(code) -205 ostream& operator<<(ostream& os, vestigial end) { -206 if (Trace_stream) Trace_stream->newline(); -207 return os; -208 } -209 -210 :(before "End Globals") -211 bool Save_trace = false; -212 -213 // Trace_stream is a resource, lease_tracer uses RAII to manage it. -214 :(before "End Types") -215 struct lease_tracer { -216 lease_tracer(); -217 ~lease_tracer(); -218 }; -219 :(code) -220 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; } -221 lease_tracer::~lease_tracer() { -222 if (!Trace_stream) return; // in case tests close Trace_stream -223 if (Save_trace) Trace_stream->dump(); -224 delete Trace_stream, Trace_stream = NULL; -225 } -226 :(before "End Includes") -227 #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer; -228 :(before "End Test Setup") -229 START_TRACING_UNTIL_END_OF_SCOPE -230 -231 :(before "End Includes") -232 #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) -233 -234 #define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors()) -235 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \ -236 if (Passed && trace_contains_errors()) { \ -237 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \ -238 DUMP("error"); \ -239 Passed = false; \ -240 return; \ -241 } -242 -243 #define CHECK_TRACE_COUNT(label, count) \ -244 if (Passed && trace_count(label) != (count)) { \ -245 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \ -246 cerr << " got " << trace_count(label) << '\n'; /* multiple eval */ \ -247 DUMP(label); \ -248 Passed = false; \ -249 return; /* Currently we stop at the very first failure. */ \ -250 } -251 -252 #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__)) -253 -254 :(code) -255 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) { -256 if (!Passed) return false; -257 if (!Trace_stream) return false; -258 vector<string> expected_lines = split(expected, "^D"); -259 int curr_expected_line = 0; -260 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) -261 ++curr_expected_line; -262 if (curr_expected_line == SIZE(expected_lines)) return true; -263 string label, contents; -264 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); -265 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -266 if (label != p->label) continue; -267 if (contents != trim(p->contents)) continue; -268 ++curr_expected_line; -269 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) -270 ++curr_expected_line; -271 if (curr_expected_line == SIZE(expected_lines)) return true; -272 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); -273 } -274 -275 if (line_exists_anywhere(label, contents)) { -276 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n"; -277 DUMP(""); -278 } -279 else { -280 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n"; -281 DUMP(label); -282 } -283 Passed = false; -284 return false; -285 } -286 -287 void split_label_contents(const string& s, string* label, string* contents) { -288 static const string delim(": "); -289 size_t pos = s.find(delim); -290 if (pos == string::npos) { -291 *label = ""; -292 *contents = trim(s); -293 } -294 else { -295 *label = trim(s.substr(0, pos)); -296 *contents = trim(s.substr(pos+SIZE(delim))); -297 } -298 } -299 -300 bool line_exists_anywhere(const string& label, const string& contents) { -301 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -302 if (label != p->label) continue; -303 if (contents == trim(p->contents)) return true; +202 //: Automatically close the console in some situations. +203 :(before "End One-time Setup") +204 atexit(scroll_to_bottom_and_close_console); +205 :(code) +206 void scroll_to_bottom_and_close_console() { +207 if (!tb_is_active()) return; +208 // leave the screen in a relatively clean state +209 tb_set_cursor(tb_width()-1, tb_height()-1); +210 cout << "\r\n"; +211 tb_shutdown(); +212 } +213 +214 // Inside tests, fail any tests that displayed (unexpected) errors. +215 // Expected errors in tests should always be hidden and silently checked for. +216 :(before "End Test Teardown") +217 if (Passed && !Hide_errors && trace_contains_errors()) { +218 Passed = false; +219 } +220 :(code) +221 bool trace_contains_errors() { +222 return Trace_errors > 0 || trace_count("error") > 0; +223 } +224 +225 :(before "End Types") +226 struct end {}; +227 :(code) +228 ostream& operator<<(ostream& os, end /*unused*/) { +229 if (Trace_stream) Trace_stream->newline(); +230 return os; +231 } +232 +233 :(before "End Globals") +234 bool Save_trace = false; // if set, write out trace to disk +235 +236 // Trace_stream is a resource, lease_tracer uses RAII to manage it. +237 :(before "End Types") +238 struct lease_tracer { +239 lease_tracer(); +240 ~lease_tracer(); +241 }; +242 :(code) +243 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; } +244 lease_tracer::~lease_tracer() { +245 if (Save_trace) Trace_stream->save(); +246 delete Trace_stream, Trace_stream = NULL; +247 } +248 :(before "End Includes") +249 #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer; +250 :(before "End Test Setup") +251 START_TRACING_UNTIL_END_OF_SCOPE +252 +253 :(before "End Includes") +254 #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) +255 +256 #define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors()) +257 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \ +258 if (Passed && trace_contains_errors()) { \ +259 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \ +260 DUMP("error"); \ +261 Passed = false; \ +262 return; \ +263 } +264 +265 #define CHECK_TRACE_COUNT(label, count) \ +266 if (Passed && trace_count(label) != (count)) { \ +267 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \ +268 cerr << " got " << trace_count(label) << '\n'; /* multiple eval */ \ +269 DUMP(label); \ +270 Passed = false; \ +271 return; /* Currently we stop at the very first failure. */ \ +272 } +273 +274 #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__)) +275 +276 :(code) +277 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) { +278 if (!Passed) return false; +279 if (!Trace_stream) return false; +280 vector<string> expected_lines = split(expected, "^D"); +281 int curr_expected_line = 0; +282 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) +283 ++curr_expected_line; +284 if (curr_expected_line == SIZE(expected_lines)) return true; +285 string label, contents; +286 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); +287 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { +288 if (label != p->label) continue; +289 if (contents != trim(p->contents)) continue; +290 ++curr_expected_line; +291 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) +292 ++curr_expected_line; +293 if (curr_expected_line == SIZE(expected_lines)) return true; +294 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); +295 } +296 +297 if (line_exists_anywhere(label, contents)) { +298 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n"; +299 DUMP(""); +300 } +301 else { +302 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n"; +303 DUMP(label); 304 } -305 return false; -306 } -307 -308 int trace_count(string label) { -309 return trace_count(label, ""); -310 } -311 -312 int trace_count(string label, string line) { -313 if (!Trace_stream) return 0; -314 long result = 0; -315 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -316 if (label == p->label) { -317 if (line == "" || trim(line) == trim(p->contents)) -318 ++result; -319 } -320 } -321 return result; -322 } -323 -324 int trace_count_prefix(string label, string prefix) { -325 if (!Trace_stream) return 0; -326 long result = 0; -327 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -328 if (label == p->label) { -329 if (starts_with(trim(p->contents), trim(prefix))) -330 ++result; -331 } -332 } -333 return result; -334 } -335 -336 bool trace_doesnt_contain(string label, string line) { -337 return trace_count(label, line) == 0; -338 } -339 -340 bool trace_doesnt_contain(string expected) { -341 vector<string> tmp = split_first(expected, ": "); -342 return trace_doesnt_contain(tmp.at(0), tmp.at(1)); -343 } -344 -345 vector<string> split(string s, string delim) { -346 vector<string> result; -347 size_t begin=0, end=s.find(delim); -348 while (true) { -349 if (end == string::npos) { -350 result.push_back(string(s, begin, string::npos)); -351 break; -352 } -353 result.push_back(string(s, begin, end-begin)); -354 begin = end+SIZE(delim); -355 end = s.find(delim, begin); -356 } -357 return result; -358 } -359 -360 vector<string> split_first(string s, string delim) { -361 vector<string> result; -362 size_t end=s.find(delim); -363 result.push_back(string(s, 0, end)); -364 if (end != string::npos) -365 result.push_back(string(s, end+SIZE(delim), string::npos)); -366 return result; -367 } -368 -369 string trim(const string& s) { -370 string::const_iterator first = s.begin(); -371 while (first != s.end() && isspace(*first)) -372 ++first; -373 if (first == s.end()) return ""; -374 -375 string::const_iterator last = --s.end(); -376 while (last != s.begin() && isspace(*last)) -377 --last; -378 ++last; -379 return string(first, last); +305 Passed = false; +306 return false; +307 } +308 +309 void split_label_contents(const string& s, string* label, string* contents) { +310 static const string delim(": "); +311 size_t pos = s.find(delim); +312 if (pos == string::npos) { +313 *label = ""; +314 *contents = trim(s); +315 } +316 else { +317 *label = trim(s.substr(0, pos)); +318 *contents = trim(s.substr(pos+SIZE(delim))); +319 } +320 } +321 +322 bool line_exists_anywhere(const string& label, const string& contents) { +323 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { +324 if (label != p->label) continue; +325 if (contents == trim(p->contents)) return true; +326 } +327 return false; +328 } +329 +330 int trace_count(string label) { +331 return trace_count(label, ""); +332 } +333 +334 int trace_count(string label, string line) { +335 if (!Trace_stream) return 0; +336 long result = 0; +337 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { +338 if (label == p->label) { +339 if (line == "" || trim(line) == trim(p->contents)) +340 ++result; +341 } +342 } +343 return result; +344 } +345 +346 int trace_count_prefix(string label, string prefix) { +347 if (!Trace_stream) return 0; +348 long result = 0; +349 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { +350 if (label == p->label) { +351 if (starts_with(trim(p->contents), trim(prefix))) +352 ++result; +353 } +354 } +355 return result; +356 } +357 +358 bool trace_doesnt_contain(string label, string line) { +359 return trace_count(label, line) == 0; +360 } +361 +362 bool trace_doesnt_contain(string expected) { +363 vector<string> tmp = split_first(expected, ": "); +364 return trace_doesnt_contain(tmp.at(0), tmp.at(1)); +365 } +366 +367 vector<string> split(string s, string delim) { +368 vector<string> result; +369 size_t begin=0, end=s.find(delim); +370 while (true) { +371 if (end == string::npos) { +372 result.push_back(string(s, begin, string::npos)); +373 break; +374 } +375 result.push_back(string(s, begin, end-begin)); +376 begin = end+SIZE(delim); +377 end = s.find(delim, begin); +378 } +379 return result; 380 } 381 -382 :(before "End Includes") -383 #include <vector> -384 using std::vector; -385 #include <list> -386 using std::list; -387 #include <map> -388 using std::map; -389 #include <set> -390 using std::set; -391 -392 #include <sstream> -393 using std::istringstream; -394 using std::ostringstream; -395 -396 #include <fstream> -397 using std::ifstream; -398 using std::ofstream; -399 -400 #include "termbox/termbox.h" -401 -402 :(before "End Globals") -403 //: In future layers we'll use the depth field as follows: -404 //: -405 //: Errors will be depth 0. -406 //: Mu 'applications' will be able to use depths 1-100 as they like. -407 //: Primitive statements will occupy 101-9989 -408 extern const int Initial_callstack_depth = 101; -409 extern const int Max_callstack_depth = 9989; -410 //: Finally, details of primitive Mu statements will occupy depth 9990-9999 -411 //: (more on that later as well) -412 //: -413 //: This framework should help us hide some details at each level, mixing -414 //: static ideas like layers with the dynamic notion of call-stack depth. +382 vector<string> split_first(string s, string delim) { +383 vector<string> result; +384 size_t end=s.find(delim); +385 result.push_back(string(s, 0, end)); +386 if (end != string::npos) +387 result.push_back(string(s, end+SIZE(delim), string::npos)); +388 return result; +389 } +390 +391 string trim(const string& s) { +392 string::const_iterator first = s.begin(); +393 while (first != s.end() && isspace(*first)) +394 ++first; +395 if (first == s.end()) return ""; +396 +397 string::const_iterator last = --s.end(); +398 while (last != s.begin() && isspace(*last)) +399 --last; +400 ++last; +401 return string(first, last); +402 } +403 +404 :(before "End Includes") +405 #include <vector> +406 using std::vector; +407 #include <list> +408 using std::list; +409 #include <set> +410 using std::set; +411 +412 #include <sstream> +413 using std::istringstream; +414 using std::ostringstream; +415 +416 #include <fstream> +417 using std::ifstream; +418 using std::ofstream; +419 +420 #include "termbox/termbox.h" +421 +422 :(before "End Globals") +423 //: In future layers we'll use the depth field as follows: +424 //: +425 //: Errors will be depth 0. +426 //: Mu 'applications' will be able to use depths 1-100 as they like. +427 //: Primitive statements will occupy 101-9989 +428 extern const int Initial_callstack_depth = 101; +429 extern const int Max_callstack_depth = 9989; +430 //: Finally, details of primitive Mu statements will occupy depth 9990-9999 +431 //: (more on that later as well) +432 //: +433 //: This framework should help us hide some details at each level, mixing +434 //: static ideas like layers with the dynamic notion of call-stack depth. -- cgit 1.4.1-2-gfad0