diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2015-04-21 15:11:09 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2015-04-21 16:26:57 -0700 |
commit | c08e91ff5f7f55cda630ad000fdeadd8ba302cb0 (patch) | |
tree | 7afc69cccb8f4d5919d6040a0f740a47ad1b9a01 | |
parent | d9cd13ad40db3f7cdf5dbb8c55af7b5aafc9c32f (diff) | |
download | mu-c08e91ff5f7f55cda630ad000fdeadd8ba302cb0.tar.gz |
1117 - redo entire tangler
Instead of adding a third-level hack for the new bug (failing test) with multiple directives, I'm giving up on deducing #line directives directly. Instead I'm going to maintain the file and line for every single line as I read it, and then emit directives on their basis as a post-processing step. This way tangling itself can remain oblivious to line numbers, even if we're passing objects around rather than naked strings.
-rw-r--r-- | cpp/tangle/001trace.cc | 4 | ||||
-rw-r--r-- | cpp/tangle/030tangle.cc | 326 | ||||
-rw-r--r-- | cpp/tangle/030tangle.test.cc | 212 | ||||
-rw-r--r-- | cpp/tangle/makefile | 2 |
4 files changed, 284 insertions, 260 deletions
diff --git a/cpp/tangle/001trace.cc b/cpp/tangle/001trace.cc index 558b4922..a56fc200 100644 --- a/cpp/tangle/001trace.cc +++ b/cpp/tangle/001trace.cc @@ -20,7 +20,9 @@ struct trace_stream { // be sure to call this before messing with curr_stream or curr_layer or frame void newline() { if (!curr_stream) return; - past_lines.push_back(pair<string, pair<int, string> >(curr_layer, pair<int, string>(frame[curr_layer], curr_stream->str()))); + string curr_contents = curr_stream->str(); + curr_contents.erase(curr_contents.find_last_not_of("\r\n")+1); + past_lines.push_back(pair<string, pair<int, string> >(curr_layer, pair<int, string>(frame[curr_layer], curr_contents))); if (curr_layer == "dump") cerr << with_newline(curr_stream->str()); else if ((!dump_layer.empty() && prefix_match(dump_layer, curr_layer)) diff --git a/cpp/tangle/030tangle.cc b/cpp/tangle/030tangle.cc index 1e30cc3c..b1316b88 100644 --- a/cpp/tangle/030tangle.cc +++ b/cpp/tangle/030tangle.cc @@ -2,46 +2,91 @@ // Insert #line directives to preserve line numbers in the original. // Clear lines starting with '//:' (tangle comments). -size_t Line_number = 0; -string Filename; +//// Preliminaries regarding line number management + +struct Line { + string filename; + size_t line_number; + string contents; + Line() :line_number(0) {} +}; + +// Emit a list of line contents, inserting directives just at discontinuities. +// Needs to be a macro because 'out' can have the side effect of creating a +// new trace in Trace_stream. +#define EMIT(lines, out) if (!lines.empty()) { \ + string last_file = lines.begin()->filename; \ + size_t last_line = lines.begin()->line_number-1; \ + out << line_directive(lines.begin()->line_number, lines.begin()->filename) << '\n'; \ + for (list<Line>::const_iterator p = lines.begin(); p != lines.end(); ++p) { \ + if (last_file != p->filename || last_line != p->line_number-1) \ + out << line_directive(p->line_number, p->filename) << '\n'; \ + out << p->contents << '\n'; \ + last_file = p->filename; \ + last_line = p->line_number; \ + } \ +} + +string line_directive(size_t line_number, string filename) { + ostringstream result; + if (filename.empty()) + result << "#line " << line_number; + else + result << "#line " << line_number << " \"" << filename << '"'; + return result.str(); +} + +//// Tangle + string Toplevel = "run"; int tangle(int argc, const char* argv[]) { - list<string> result; + list<Line> result; for (int i = 1; i < argc; ++i) { //? cerr << "new file " << argv[i] << '\n'; //? 1 - ifstream in(argv[i]); - Filename = argv[i]; Toplevel = "run"; - tangle(in, result); + ifstream in(argv[i]); + tangle(in, argv[i], result); } - for (list<string>::iterator p = result.begin(); p != result.end(); ++p) - cout << *p << '\n'; + + EMIT(result, cout); return 0; } -void tangle(istream& in, list<string>& out) { - Line_number = 1; - out.push_back(line_directive(Line_number, Filename)); +void tangle(istream& in, const string& filename, list<Line>& out) { string curr_line; + size_t line_number = 1; while (!in.eof()) { getline(in, curr_line); - Line_number++; - if (starts_with(curr_line, ":(")) - process_next_hunk(in, trim(curr_line), out); - else - out.push_back(curr_line); - } - for (list<string>::iterator p = out.begin(); p != out.end(); ++p) { - if (starts_with(*p, "//:")) - p->clear(); // leave the empty lines around so as to not mess up #line numbers + if (starts_with(curr_line, ":(")) { + ++line_number; + process_next_hunk(in, trim(curr_line), filename, line_number, out); + continue; + } + if (starts_with(curr_line, "//:")) { + ++line_number; + continue; + } + Line curr; + curr.filename = filename; + curr.line_number = line_number; + curr.contents = curr_line; + out.push_back(curr); + ++line_number; } - trace_all("tangle", out); + + // Trace all line contents, inserting directives just at discontinuities. + if (!Trace_stream) return; + EMIT(out, Trace_stream->stream("tangle")); +} + +// just for tests +void tangle(istream& in, list<Line>& out) { + tangle(in, "", out); } -void process_next_hunk(istream& in, const string& directive, list<string>& out) { - list<string> hunk; - hunk.push_back(line_directive(Line_number, Filename)); +void process_next_hunk(istream& in, const string& directive, const string& filename, size_t& line_number, list<Line>& out) { + list<Line> hunk; string curr_line; while (!in.eof()) { std::streampos old = in.tellg(); @@ -51,8 +96,12 @@ void process_next_hunk(istream& in, const string& directive, list<string>& out) break; } else { - ++Line_number; - hunk.push_back(curr_line); + Line curr; + curr.line_number = line_number; + curr.filename = filename; + curr.contents = curr_line; + hunk.push_back(curr); + ++line_number; } } @@ -70,56 +119,47 @@ void process_next_hunk(istream& in, const string& directive, list<string>& out) } if (cmd == "scenario") { - list<string> result; + list<Line> result; string name = next_tangle_token(directive_stream); - result.push_back(hunk.front()); // line number directive - hunk.pop_front(); emit_test(name, hunk, result); +//? cerr << out.size() << " " << result.size() << '\n'; //? 1 out.insert(out.end(), result.begin(), result.end()); +//? cerr << out.size() << " " << result.size() << '\n'; //? 1 return; } if (cmd == "before" || cmd == "after" || cmd == "replace" || cmd == "replace{}" || cmd == "delete" || cmd == "delete{}") { - list<string>::iterator target = locate_target(out, directive_stream); + list<Line>::iterator target = locate_target(out, directive_stream); if (target == out.end()) { raise << "Couldn't find target " << directive << '\n' << die(); return; } - size_t end_line_number = 0; - string end_filename; - scan_up_to_last_line_directive(target, out.begin(), end_line_number, end_filename); - indent_all(hunk, target); if (cmd == "before") { - hunk.push_back(line_directive(end_line_number, end_filename)); out.splice(target, hunk); } else if (cmd == "after") { - ++end_line_number; - hunk.push_back(line_directive(end_line_number, end_filename)); ++target; out.splice(target, hunk); } else if (cmd == "replace" || cmd == "delete") { - hunk.push_back(line_directive(end_line_number, end_filename)); out.splice(target, hunk); out.erase(target); } else if (cmd == "replace{}" || cmd == "delete{}") { - hunk.push_back(line_directive(end_line_number, end_filename)); if (find_trim(hunk, ":OLD_CONTENTS") == hunk.end()) { out.splice(target, hunk); out.erase(target, balancing_curly(target)); } else { - list<string>::iterator next = balancing_curly(target); - list<string> old_version; + list<Line>::iterator next = balancing_curly(target); + list<Line> old_version; old_version.splice(old_version.begin(), out, target, next); old_version.pop_back(); old_version.pop_front(); // contents only please, not surrounding curlies - list<string>::iterator new_pos = find_trim(hunk, ":OLD_CONTENTS"); + list<Line>::iterator new_pos = find_trim(hunk, ":OLD_CONTENTS"); indent_all(old_version, new_pos); hunk.splice(new_pos, old_version); hunk.erase(new_pos); @@ -132,7 +172,7 @@ void process_next_hunk(istream& in, const string& directive, list<string>& out) raise << "unknown directive " << cmd << '\n'; } -list<string>::iterator locate_target(list<string>& out, istream& directive_stream) { +list<Line>::iterator locate_target(list<Line>& out, istream& directive_stream) { string pat = next_tangle_token(directive_stream); if (pat == "") return out.end(); @@ -144,13 +184,13 @@ list<string>::iterator locate_target(list<string>& out, istream& directive_strea else if (next_token == "following") { string pat2 = next_tangle_token(directive_stream); if (pat2 == "") return out.end(); - list<string>::iterator intermediate = find_substr(out, pat2); + list<Line>::iterator intermediate = find_substr(out, pat2); if (intermediate == out.end()) return out.end(); return find_substr(out, intermediate, pat); } // second way to do nested pattern: intermediate 'then' pattern else if (next_token == "then") { - list<string>::iterator intermediate = find_substr(out, pat); + list<Line>::iterator intermediate = find_substr(out, pat); if (intermediate == out.end()) return out.end(); string pat2 = next_tangle_token(directive_stream); if (pat2 == "") return out.end(); @@ -161,11 +201,11 @@ list<string>::iterator locate_target(list<string>& out, istream& directive_strea } // indent all lines in l like indentation at exemplar -void indent_all(list<string>& l, list<string>::iterator exemplar) { - string curr_indent = indent(*exemplar); - for (list<string>::iterator p = l.begin(); p != l.end(); ++p) - if (!p->empty()) - p->insert(p->begin(), curr_indent.begin(), curr_indent.end()); +void indent_all(list<Line>& l, list<Line>::iterator exemplar) { + string curr_indent = indent(exemplar->contents); + for (list<Line>::iterator p = l.begin(); p != l.end(); ++p) + if (!p->contents.empty()) + p->contents.insert(p->contents.begin(), curr_indent.begin(), curr_indent.end()); } string next_tangle_token(istream& in) { @@ -207,16 +247,15 @@ void skip_whitespace(istream& in) { in.get(); } -list<string>::iterator balancing_curly(list<string>::iterator orig) { - list<string>::iterator curr = orig; +list<Line>::iterator balancing_curly(list<Line>::iterator curr) { long open_curlies = 0; do { - for (string::iterator p = curr->begin(); p != curr->end(); ++p) { + for (string::iterator p = curr->contents.begin(); p != curr->contents.end(); ++p) { if (*p == '{') ++open_curlies; if (*p == '}') --open_curlies; } ++curr; - // no guard so far against unbalanced curly + // no guard so far against unbalanced curly, including inside comments or strings } while (open_curlies != 0); return curr; } @@ -227,38 +266,62 @@ list<string>::iterator balancing_curly(list<string>::iterator orig) { // followed by one or more lines expected in trace in order ('+') // followed by one or more lines trace shouldn't include ('-') // Remember to update is_input below if you add to this format. -void emit_test(const string& name, list<string>& lines, list<string>& result) { - result.push_back("TEST("+name+")"); +void emit_test(const string& name, list<Line>& lines, list<Line>& result) { + Line tmp; + tmp.line_number = front(lines).line_number-1; // line number of directive + tmp.filename = front(lines).filename; + tmp.contents = "TEST("+name+")"; + result.push_back(tmp); +#define SHIFT(new_contents) { \ + Line tmp; \ + tmp.line_number = front(lines).line_number; \ + tmp.filename = front(lines).filename; \ + tmp.contents = new_contents; \ + result.push_back(tmp); \ + lines.pop_front(); \ +} while (any_non_input_line(lines)) { - if (is_warn(lines.front())) { - result.push_back(" Hide_warnings = true;"); - lines.pop_front(); + if (front(lines).contents == "hide warnings") { + SHIFT(" Hide_warnings = true;"); } - if (starts_with(lines.front(), "dump ")) { - string line = lines.front().substr(strlen("dump ")); - result.push_back(" Trace_stream->dump_layer = \""+line+"\";"); - lines.pop_front(); + if (starts_with(front(lines).contents, "dump ")) { + string line = front(lines).contents.substr(strlen("dump ")); + SHIFT(" Trace_stream->dump_layer = \""+line+"\";"); } - result.push_back(" "+Toplevel+"(\""+input_lines(lines)+"\");"); - if (!lines.empty() && lines.front()[0] == '+') - result.push_back(" CHECK_TRACE_CONTENTS(\""+expected_in_trace(lines)+"\");"); - while (!lines.empty() && lines.front()[0] == '-') { - result.push_back(" CHECK_TRACE_DOESNT_CONTAIN(\""+expected_not_in_trace(lines.front())+"\");"); + result.push_back(input_lines(lines)); + if (!lines.empty() && !front(lines).contents.empty() && front(lines).contents[0] == '+') + result.push_back(expected_in_trace(lines)); + while (!lines.empty() && !front(lines).contents.empty() && front(lines).contents[0] == '-') { + result.push_back(expected_not_in_trace(front(lines))); lines.pop_front(); } - if (!lines.empty() && lines.front() == "===") { - result.push_back(" CLEAR_TRACE;"); + if (!lines.empty() && front(lines).contents == "===") { + Line tmp; + tmp.line_number = front(lines).line_number; + tmp.filename = front(lines).filename; + tmp.contents = " CLEAR_TRACE;"; + result.push_back(tmp); lines.pop_front(); } - if (!lines.empty() && lines.front() == "?") { - result.push_back(" DUMP(\"\");"); + if (!lines.empty() && front(lines).contents == "?") { + Line tmp; + tmp.line_number = front(lines).line_number; + tmp.filename = front(lines).filename; + tmp.contents = " DUMP(\"\");"; + result.push_back(tmp); lines.pop_front(); } } - result.push_back("}"); + Line tmp2; + if (!lines.empty()) { + tmp2.line_number = front(lines).line_number; + tmp2.filename = front(lines).filename; + } + tmp2.contents = "}"; + result.push_back(tmp2); while (!lines.empty() && - (trim(lines.front()).empty() || starts_with(lines.front(), "//"))) + (trim(front(lines).contents).empty() || starts_with(front(lines).contents, "//"))) lines.pop_front(); if (!lines.empty()) { cerr << lines.size() << " unprocessed lines in scenario.\n"; @@ -267,57 +330,60 @@ void emit_test(const string& name, list<string>& lines, list<string>& result) { } bool is_input(const string& line) { + if (line.empty()) return true; return line != "===" && line[0] != '+' && line[0] != '-' && !starts_with(line, "=>"); } -bool is_warn(const string& line) { - return line == "hide warnings"; -} - -bool is_dump(const string& line) { - return starts_with(line, "dump "); -} - -string input_lines(list<string>& hunk) { - string result; - while (!hunk.empty() && is_input(hunk.front())) { - result += hunk.front()+""; // temporary delimiter; replace with escaped newline after escaping other backslashes +Line input_lines(list<Line>& hunk) { + Line result; + result.line_number = hunk.front().line_number; + result.filename = hunk.front().filename; + while (!hunk.empty() && is_input(hunk.front().contents)) { + result.contents += hunk.front().contents+""; // temporary delimiter; replace with escaped newline after escaping other backslashes hunk.pop_front(); } - return escape(result); + result.contents = " "+Toplevel+"(\""+escape(result.contents)+"\");"; + return result; } -string expected_in_trace(list<string>& hunk) { - string result; - while (!hunk.empty() && hunk.front()[0] == '+') { - hunk.front().erase(0, 1); - result += hunk.front()+""; +Line expected_in_trace(list<Line>& hunk) { + Line result; + result.line_number = hunk.front().line_number; + result.filename = hunk.front().filename; + while (!hunk.empty() && !front(hunk).contents.empty() && front(hunk).contents[0] == '+') { + hunk.front().contents.erase(0, 1); + result.contents += hunk.front().contents+""; hunk.pop_front(); } - return escape(result); + result.contents = " CHECK_TRACE_CONTENTS(\""+escape(result.contents)+"\");"; + return result; } -string expected_not_in_trace(const string& line) { - return escape(line.substr(1)); +Line expected_not_in_trace(const Line& line) { + Line result; + result.line_number = line.line_number; + result.filename = line.filename; + result.contents = " CHECK_TRACE_DOESNT_CONTAIN(\""+escape(line.contents.substr(1))+"\");"; + return result; } -list<string>::iterator find_substr(list<string>& in, const string& pat) { - for (list<string>::iterator p = in.begin(); p != in.end(); ++p) - if (p->find(pat) != NOT_FOUND) +list<Line>::iterator find_substr(list<Line>& in, const string& pat) { + for (list<Line>::iterator p = in.begin(); p != in.end(); ++p) + if (p->contents.find(pat) != NOT_FOUND) return p; return in.end(); } -list<string>::iterator find_substr(list<string>& in, list<string>::iterator p, const string& pat) { +list<Line>::iterator find_substr(list<Line>& in, list<Line>::iterator p, const string& pat) { for (; p != in.end(); ++p) - if (p->find(pat) != NOT_FOUND) + if (p->contents.find(pat) != NOT_FOUND) return p; return in.end(); } -list<string>::iterator find_trim(list<string>& in, const string& pat) { - for (list<string>::iterator p = in.begin(); p != in.end(); ++p) - if (trim(*p) == pat) +list<Line>::iterator find_trim(list<Line>& in, const string& pat) { + for (list<Line>::iterator p = in.begin(); p != in.end(); ++p) + if (trim(p->contents) == pat) return p; return in.end(); } @@ -335,15 +401,15 @@ string replace_all(string s, const string& a, const string& b) { return s; } -bool any_line_starts_with(const list<string>& lines, const string& pat) { - for (list<string>::const_iterator p = lines.begin(); p != lines.end(); ++p) - if (starts_with(*p, pat)) return true; +bool any_line_starts_with(const list<Line>& lines, const string& pat) { + for (list<Line>::const_iterator p = lines.begin(); p != lines.end(); ++p) + if (starts_with(p->contents, pat)) return true; return false; } -bool any_non_input_line(const list<string>& lines) { - for (list<string>::const_iterator p = lines.begin(); p != lines.end(); ++p) - if (!is_input(*p)) return true; +bool any_non_input_line(const list<Line>& lines) { + for (list<Line>::const_iterator p = lines.begin(); p != lines.end(); ++p) + if (!is_input(p->contents)) return true; return false; } @@ -386,43 +452,7 @@ string trim(const string& s) { return string(first, last); } -string line_directive(size_t line_number, string filename) { - ostringstream result; - if (filename.empty()) - result << "#line " << line_number; - else - result << "#line " << line_number << " \"" << filename << '"'; - return result.str(); -} - -void scan_up_to_last_line_directive(list<string>::iterator p, list<string>::iterator begin, size_t& line_number, string& filename) { -//? cout << "scan: " << *p << " until " << *begin << '\n'; //? 1 - int delta = 0; - if (p != begin) { - for (--p; p != begin; --p) { -//? cout << " " << *p << ' ' << delta << '\n'; //? 1 - if (starts_with(*p, "#line")) continue; -//? cout << "incrementing\n"; //? 1 - ++delta; - } -//? cout << "delta: " << delta << '\n'; //? 1 - } - if (p == begin) { - assert(starts_with(*p, "#line")); -//? cout << "hit begin\n"; -//? line_number = delta; -//? return; - } - istringstream in(*p); - string directive_; - in >> directive_; - assert(directive_ == "#line"); - in >> line_number; - line_number += delta; -//? cout << line_number << '\n'; //? 1 - if (in.eof()) return; - in >> filename; - if (filename[0] == '"') filename.erase(0, 1); - if (filename[filename.size()-1] == '"') filename.erase(filename.size()-1); -//? cout << filename << '\n'; //? 1 +const Line& front(const list<Line>& l) { + assert(!l.empty()); + return l.front(); } diff --git a/cpp/tangle/030tangle.test.cc b/cpp/tangle/030tangle.test.cc index bdf24a5c..f0abd030 100644 --- a/cpp/tangle/030tangle.test.cc +++ b/cpp/tangle/030tangle.test.cc @@ -1,56 +1,61 @@ void test_tangle() { istringstream in("a\nb\nc\n:(before b)\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "adbc"); } void test_tangle_with_linenumber() { istringstream in("a\nb\nc\n:(before b)\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); - CHECK_TRACE_CONTENTS("tangle", "a#line 5d#line 2bc"); + CHECK_TRACE_CONTENTS("tangle", "#line 1a#line 5d#line 2bc"); + // no other #line directives + CHECK_TRACE_DOESNT_CONTAIN("tangle", "#line 3"); + CHECK_TRACE_DOESNT_CONTAIN("tangle", "#line 4"); } -void test_tangle_with_filename() { +void test_tangle_linenumbers_with_filename() { istringstream in("a\nb\nc\n:(before b)\nd\n"); - list<string> dummy; - Filename = "foo"; - tangle(in, dummy); - Filename = ""; + list<Line> dummy; + tangle(in, "foo", dummy); CHECK_TRACE_CONTENTS("tangle", "a#line 5 \"foo\"dbc"); } -void test_tangle_with_multiple_filenames() { +void test_tangle_linenumbers_with_multiple_filenames() { istringstream in1("a\nb\nc"); - list<string> dummy; - Filename = "foo"; - tangle(in1, dummy); + list<Line> dummy; + tangle(in1, "foo", dummy); CLEAR_TRACE; - Filename = "bar"; istringstream in2(":(before b)\nd\n"); - tangle(in2, dummy); - Filename = ""; + tangle(in2, "bar", dummy); CHECK_TRACE_CONTENTS("tangle", "a#line 2 \"bar\"d#line 2 \"foo\"bc"); } +void test_tangle_linenumbers_with_multiple_directives() { + istringstream in1("a\nb\nc"); + list<Line> dummy; + tangle(in1, "foo", dummy); + CLEAR_TRACE; + istringstream in2(":(before b)\nd\n:(before c)\ne"); + tangle(in2, "bar", dummy); + CHECK_TRACE_CONTENTS("tangle", "a#line 2 \"bar\"d#line 2 \"foo\"b#line 4 \"bar\"e#line 3 \"foo\"c"); +} + void test_tangle_with_multiple_filenames_after() { istringstream in1("a\nb\nc"); - list<string> dummy; - Filename = "foo"; - tangle(in1, dummy); + list<Line> dummy; + tangle(in1, "foo", dummy); CLEAR_TRACE; - Filename = "bar"; istringstream in2(":(after b)\nd\n"); - tangle(in2, dummy); - Filename = ""; + tangle(in2, "bar", dummy); CHECK_TRACE_CONTENTS("tangle", "ab#line 2 \"bar\"d#line 3 \"foo\"c"); //? exit(0); //? 1 } void test_tangle_skip_tanglecomments() { istringstream in("a\nb\nc\n//: 1\n//: 2\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "abcd"); CHECK_TRACE_DOESNT_CONTAIN("tangle", "//: 1"); @@ -58,7 +63,7 @@ void test_tangle_skip_tanglecomments() { void test_tangle_with_tanglecomments_and_directive() { istringstream in("a\n//: 1\nb\nc\n:(before b)\nd\n:(code)\ne\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "a#line 6d#line 3bc#line 8e"); CHECK_TRACE_DOESNT_CONTAIN("tangle", "//: 1"); @@ -66,21 +71,21 @@ void test_tangle_with_tanglecomments_and_directive() { void test_tangle2() { istringstream in("a\nb\nc\n:(after b)\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "abdc"); } void test_tangle_at_end() { istringstream in("a\nb\nc\n:(after c)\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "abcd"); } void test_tangle_indents_hunks_correctly() { istringstream in("a\n b\nc\n:(after b)\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "a b dc"); } @@ -88,7 +93,7 @@ void test_tangle_indents_hunks_correctly() { void test_tangle_warns_on_missing_target() { Hide_warnings = true; istringstream in(":(before)\nabc def\n"); - list<string> lines; + list<Line> lines; tangle(in, lines); CHECK_TRACE_WARNS(); } @@ -96,14 +101,14 @@ void test_tangle_warns_on_missing_target() { void test_tangle_warns_on_unknown_target() { Hide_warnings = true; istringstream in(":(before \"foo\")\nabc def\n"); - list<string> lines; + list<Line> lines; tangle(in, lines); CHECK_TRACE_WARNS(); } void test_tangle_delete_range_of_lines() { istringstream in("a\nb {\nc\n}\n:(delete{} \"b\")\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "a"); CHECK_TRACE_DOESNT_CONTAIN("tangle", "b"); @@ -112,7 +117,7 @@ void test_tangle_delete_range_of_lines() { void test_tangle_replace() { istringstream in("a\nb\nc\n:(replace b)\nd\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "adc"); CHECK_TRACE_DOESNT_CONTAIN("tangle", "b"); @@ -120,7 +125,7 @@ void test_tangle_replace() { void test_tangle_replace_range_of_lines() { istringstream in("a\nb {\nc\n}\n:(replace{} \"b\")\nd\ne\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "ade"); CHECK_TRACE_DOESNT_CONTAIN("tangle", "b {"); @@ -129,7 +134,7 @@ void test_tangle_replace_range_of_lines() { void test_tangle_replace_tracks_old_lines() { istringstream in("a\nb {\nc\n}\n:(replace{} \"b\")\nd\n:OLD_CONTENTS\ne\n"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "adce"); CHECK_TRACE_DOESNT_CONTAIN("tangle", "b {"); @@ -137,14 +142,14 @@ void test_tangle_replace_tracks_old_lines() { void test_tangle_nested_patterns() { istringstream in("a\nc\nb\nc\nd\n:(after \"b\" then \"c\")\ne"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "acbced"); } void test_tangle_nested_patterns2() { istringstream in("a\nc\nb\nc\nd\n:(after \"c\" following \"b\")\ne"); - list<string> dummy; + list<Line> dummy; tangle(in, dummy); CHECK_TRACE_CONTENTS("tangle", "acbced"); } @@ -155,27 +160,30 @@ void test_tangle_nested_patterns2() { void test_tangle_supports_scenarios() { istringstream in(":(scenario does_bar)\nabc def\n+layer1: pqr\n+layer2: xyz"); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: xyz\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: xyz\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } +void test_tangle_handles_empty_lines_in_scenarios() { + istringstream in(":(scenario does_bar)\nabc def\n\n+layer1: pqr\n+layer2: xyz"); + list<Line> lines; + tangle(in, lines); + // no infinite loop +} + void test_tangle_supports_configurable_toplevel() { istringstream in(":(scenarios foo)\n:(scenario does_bar)\nabc def\n+layer1: pqr"); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 3"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " foo(\"abc def\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqr\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " foo(\"abc def\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqr\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); istringstream cleanup(":(scenarios run)\n"); @@ -184,111 +192,95 @@ void test_tangle_supports_configurable_toplevel() { void test_tangle_can_hide_warnings_in_scenarios() { istringstream in(":(scenario does_bar)\nhide warnings\nabc def\n+layer1: pqr\n+layer2: xyz"); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " Hide_warnings = true;"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: xyz\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " Hide_warnings = true;"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: xyz\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_can_dump_traces_in_scenarios() { istringstream in(":(scenario does_bar)\ndump foo\nabc def\n+layer1: pqr\n+layer2: xyz"); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " Trace_stream->dump_layer = \"foo\";"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: xyz\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " Trace_stream->dump_layer = \"foo\";"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: xyz\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_supports_strings_in_scenarios() { istringstream in(":(scenario does_bar)\nabc \"def\"\n+layer1: pqr\n+layer2: \"xyz\""); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc \\\"def\\\"\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"xyz\\\"\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc \\\"def\\\"\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"xyz\\\"\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_supports_strings_in_scenarios2() { istringstream in(":(scenario does_bar)\nabc \"\"\n+layer1: pqr\n+layer2: \"\""); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc \\\"\\\"\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"\\\"\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc \\\"\\\"\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"\\\"\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_supports_multiline_input_in_scenarios() { istringstream in(":(scenario does_bar)\nabc def\n efg\n+layer1: pqr\n+layer2: \"\""); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n efg\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"\\\"\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n efg\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"\\\"\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_supports_reset_in_scenarios() { istringstream in(":(scenario does_bar)\nabc def\n===\nefg\n+layer1: pqr\n+layer2: \"\""); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CLEAR_TRACE;"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"efg\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"\\\"\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CLEAR_TRACE;"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"efg\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqrlayer2: \\\"\\\"\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_can_check_for_absence_at_end_of_scenarios() { istringstream in(":(scenario does_bar)\nabc def\n efg\n+layer1: pqr\n-layer1: xyz"); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n efg\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_CONTENTS(\"layer1: pqr\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_DOESNT_CONTAIN(\"layer1: xyz\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n efg\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_CONTENTS(\"layer1: pqr\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_DOESNT_CONTAIN(\"layer1: xyz\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } void test_tangle_can_check_for_absence_at_end_of_scenarios2() { istringstream in(":(scenario does_bar)\nabc def\n efg\n-layer1: pqr\n-layer1: xyz"); - list<string> lines; + list<Line> lines; tangle(in, lines); - CHECK_EQ(lines.front(), "#line 1"); lines.pop_front(); - CHECK_EQ(lines.front(), "#line 2"); lines.pop_front(); - CHECK_EQ(lines.front(), "TEST(does_bar)"); lines.pop_front(); - CHECK_EQ(lines.front(), " run(\"abc def\\n efg\\n\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_DOESNT_CONTAIN(\"layer1: pqr\");"); lines.pop_front(); - CHECK_EQ(lines.front(), " CHECK_TRACE_DOESNT_CONTAIN(\"layer1: xyz\");"); lines.pop_front(); - CHECK_EQ(lines.front(), "}"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "TEST(does_bar)"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " run(\"abc def\\n efg\\n\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_DOESNT_CONTAIN(\"layer1: pqr\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, " CHECK_TRACE_DOESNT_CONTAIN(\"layer1: xyz\");"); lines.pop_front(); + CHECK_EQ(lines.front().contents, "}"); lines.pop_front(); CHECK(lines.empty()); } diff --git a/cpp/tangle/makefile b/cpp/tangle/makefile index 3d938c09..24485652 100644 --- a/cpp/tangle/makefile +++ b/cpp/tangle/makefile @@ -1,5 +1,5 @@ tangle: makefile type_list function_list file_list test_file_list test_list - g++ -O3 -Wall -Wextra -fno-strict-aliasing boot.cc -o tangle + g++ -g -O3 -Wall -Wextra -fno-strict-aliasing boot.cc -o tangle type_list: boot.cc [0-9]*.cc @# assumes struct decl has space before '{' |