about summary refs log tree commit diff stats
path: root/tangle
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-03-12 18:56:55 -0700
committerKartik Agaram <vc@akkartik.com>2019-03-12 19:14:12 -0700
commit4a943d4ed313eff001504c2b5c472266e86a38af (patch)
treea5757233a8c81b303a808f251180c7344071ed51 /tangle
parent43711b0e9f18e0225ce14687fb6ea0902aa6fc61 (diff)
downloadmu-4a943d4ed313eff001504c2b5c472266e86a38af.tar.gz
5001 - drop the :(scenario) DSL
I've been saying for a while[1][2][3] that adding extra abstractions makes
things harder for newcomers, and adding new notations doubly so. And then
I notice this DSL in my own backyard. Makes me feel like a hypocrite.

[1] https://news.ycombinator.com/item?id=13565743#13570092
[2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning
[3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2

The implementation of the DSL was also highly hacky:

a) It was happening in the tangle/ tool, but was utterly unrelated to tangling
layers.

b) There were several persnickety constraints on the different kinds of
lines and the specific order they were expected in. I kept finding bugs
where the translator would silently do the wrong thing. Or the error messages
sucked, and readers may be stuck looking at the generated code to figure
out what happened. Fixing error messages would require a lot more code,
which is one of my arguments against DSLs in the first place: they may
be easy to implement, but they're hard to design to go with the grain of
the underlying platform. They require lots of iteration. Is that effort
worth prioritizing in this project?

On the other hand, the DSL did make at least some readers' life easier,
the ones who weren't immediately put off by having to learn a strange syntax.
There were fewer quotes to parse, fewer backslash escapes.

Anyway, since there are also people who dislike having to put up with strange
syntaxes, we'll call that consideration a wash and tear this DSL out.

---

This commit was sheer drudgery. Hopefully it won't need to be redone with
a new DSL because I grow sick of backslashes.
Diffstat (limited to 'tangle')
-rw-r--r--tangle/001trace.cc2
-rw-r--r--tangle/001trace.test.cc9
-rw-r--r--tangle/003tangle.cc149
-rw-r--r--tangle/003tangle.test.cc530
4 files changed, 107 insertions, 583 deletions
diff --git a/tangle/001trace.cc b/tangle/001trace.cc
index e78c0010..520801b7 100644
--- a/tangle/001trace.cc
+++ b/tangle/001trace.cc
@@ -69,7 +69,7 @@ struct lease_tracer {
 #define START_TRACING_UNTIL_END_OF_SCOPE  lease_tracer leased_tracer;
 
 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string layer, string expected) {  // empty layer == everything
-  vector<string> expected_lines = split(expected, "");
+  vector<string> expected_lines = split(expected, "\n");
   size_t curr_expected_line = 0;
   while (curr_expected_line < expected_lines.size() && expected_lines[curr_expected_line].empty())
     ++curr_expected_line;
diff --git a/tangle/001trace.test.cc b/tangle/001trace.test.cc
index 5f0a696e..2d1b54d1 100644
--- a/tangle/001trace.test.cc
+++ b/tangle/001trace.test.cc
@@ -28,18 +28,21 @@ void test_trace_check_treats_empty_layers_as_wildcards() {
 void test_trace_check_multiple_lines_at_once() {
   trace("test layer 1") << "foo";
   trace("test layer 2") << "bar";
-  CHECK_TRACE_CONTENTS("", "foobar");
+  CHECK_TRACE_CONTENTS("", "foo\n"
+                           "bar\n");
 }
 
 void test_trace_check_always_finds_empty_lines2() {
-  CHECK_TRACE_CONTENTS("test layer 1", "");
+  CHECK_TRACE_CONTENTS("test layer 1", "\n\n\n");
 }
 
 void test_trace_orders_across_layers() {
   trace("test layer 1") << "foo";
   trace("test layer 2") << "bar";
   trace("test layer 1") << "qux";
-  CHECK_TRACE_CONTENTS("", "foobarqux");
+  CHECK_TRACE_CONTENTS("", "foo\n"
+                           "bar\n"
+                           "qux\n");
 }
 
 void test_trace_supports_count() {
diff --git a/tangle/003tangle.cc b/tangle/003tangle.cc
index bcb3c315..64c841f1 100644
--- a/tangle/003tangle.cc
+++ b/tangle/003tangle.cc
@@ -100,7 +100,6 @@ void process_next_hunk(istream& in, const string& directive, const string& filen
 
   // first slurp all lines until next directive
   list<Line> hunk;
-  bool end_of_scenario_input = false;
   {
     string curr_line;
     while (!in.eof()) {
@@ -115,22 +114,6 @@ void process_next_hunk(istream& in, const string& directive, const string& filen
         ++line_number;
         continue;
       }
-      if (cmd == "scenario") {
-        // ignore mu comments in scenarios, but only after the end of input
-        if (!starts_with(curr_line, "#") && !is_input(curr_line)) {
-          // remaining lines are checks
-          end_of_scenario_input = true;
-        }
-        else if (end_of_scenario_input && starts_with(curr_line, "#")) {
-          ++line_number;
-          continue;
-        }
-        if (trim(curr_line).empty()) {
-          // ignore empty lines in scenarios, whether in input of after
-          ++line_number;
-          continue;
-        }
-      }
       hunk.push_back(Line(curr_line, filename, line_number));
       ++line_number;
     }
@@ -141,21 +124,6 @@ void process_next_hunk(istream& in, const string& directive, const string& filen
     return;
   }
 
-  if (cmd == "scenarios") {
-    Toplevel = next_tangle_token(directive_stream);
-    return;
-  }
-
-  if (cmd == "scenario") {
-    list<Line> result;
-    string name = next_tangle_token(directive_stream);
-    emit_test(name, hunk, result);
-//?     cerr << out.size() << " " << result.size() << '\n';
-    out.insert(out.end(), result.begin(), result.end());
-//?     cerr << out.size() << " " << result.size() << '\n';
-    return;
-  }
-
   if (cmd == "before" || cmd == "after" || cmd == "replace" || cmd == "replace{}" || cmd == "delete" || cmd == "delete{}") {
     list<Line>::iterator target = locate_target(out, directive_stream);
     if (target == out.end()) {
@@ -291,111 +259,6 @@ list<Line>::iterator balancing_curly(list<Line>::iterator curr) {
   return curr;
 }
 
-// A scenario is one or more sessions separated by calls to CLEAR_TRACE ('===')
-//   A session is:
-//     one or more lines of escaped setup in C/C++ ('%')
-//   followed by one or more lines of input,
-//   followed optionally by (in order):
-//     one or more lines expected in trace in order ('+') and one or more lines trace shouldn't include ('-')
-//     one or more lines expressing counts of specific layers emitted in trace ('$')
-//     a directive to print the trace just for debugging ('?')
-// Remember to update is_input below if you add to this format.
-//
-// Allowing interleaving of '+' and '-' lines is a kludgy way to indicate that
-// two sets of trace lines can occur in any order. We should come up with a
-// better way to specify order-independence.
-void emit_test(const string& name, list<Line>& lines, list<Line>& result) {
-  result.push_back(Line("void test_"+name+"() {", front(lines).filename, front(lines).line_number-1));  // use line number of directive
-//?   result.push_back("cerr << \""+name+"\\n\";");  // debug: uncomment this to print scenario names as you run them
-  while (!lines.empty()) {
-    while (!lines.empty() && starts_with(front(lines).contents, "% ")) {
-      result.push_back(Line("  "+front(lines).contents.substr(strlen("% ")), front(lines)));
-      lines.pop_front();
-    }
-    if (lines.empty()) break;
-    emit_input_lines(lines, result);
-    emit_expected_in_trace(lines, result);
-    while (!lines.empty() && !front(lines).contents.empty() && front(lines).contents.at(0) == '-') {
-      result.push_back(expected_not_in_trace(front(lines)));
-      lines.pop_front();
-    }
-    if (!lines.empty() && front(lines).contents.at(0) == '$') {
-      const string& in = front(lines).contents;
-      size_t pos = in.find(": ");
-      string layer = in.substr(1, pos-1);
-      string count = in.substr(pos+2);
-      result.push_back(Line("  CHECK_TRACE_COUNT(\""+layer+"\", "+count+");", front(lines)));
-      lines.pop_front();
-    }
-    if (!lines.empty() && front(lines).contents == "===") {
-      result.push_back(Line("  CLEAR_TRACE;", front(lines)));
-      lines.pop_front();
-    }
-    if (!lines.empty() && front(lines).contents == "?") {
-      result.push_back(Line("  DUMP(\"\");", front(lines)));
-      lines.pop_front();
-    }
-  }
-  result.push_back(Line("}"));
-}
-
-bool is_input(const string& line) {
-  if (line.empty()) return true;
-  return line != "===" && line.find_first_of("+-$?%") != 0;
-}
-
-void emit_input_lines(list<Line>& hunk, list<Line>& out) {
-  assert(!hunk.empty());
-  if (!is_input(front(hunk).contents)) return;
-  Line curr_out;
-  curr_out.line_number = hunk.front().line_number;
-  curr_out.filename = hunk.front().filename;
-  curr_out.contents = "  "+Toplevel+"(";
-  out.push_back(curr_out);
-  for (/*nada*/;  !hunk.empty() && is_input(front(hunk).contents);  hunk.pop_front()) {
-    Line curr_out;
-    curr_out.line_number = front(hunk).line_number;
-    curr_out.filename = front(hunk).filename;
-    curr_out.contents = "      \""+escape(front(hunk).contents+'')+"\"";
-    out.push_back(curr_out);
-  }
-  curr_out.line_number = out.back().line_number;
-  curr_out.filename = out.back().filename;
-  curr_out.contents = "  );";
-  out.push_back(curr_out);
-}
-
-// pull lines starting with '+' out of 'hunk', and append translated lines to 'out'
-void emit_expected_in_trace(list<Line>& hunk, list<Line>& out) {
-  if (hunk.empty()) return;
-  if (front(hunk).contents.empty()) return;
-  if (front(hunk).contents.at(0) != '+') return;
-  Line curr_out;
-  curr_out.line_number = front(hunk).line_number;
-  curr_out.filename = front(hunk).filename;
-  curr_out.contents = "  CHECK_TRACE_CONTENTS(";
-  out.push_back(curr_out);
-  for (/*nada*/;  !hunk.empty() && front(hunk).contents.at(0) == '+';  hunk.pop_front()) {
-    Line curr_out;
-    curr_out.line_number = front(hunk).line_number;
-    curr_out.filename = front(hunk).filename;
-    curr_out.contents = "      \""+escape(front(hunk).contents.substr(1))+"\"";
-    out.push_back(curr_out);
-  }
-  curr_out.line_number = out.back().line_number;
-  curr_out.filename = out.back().filename;
-  curr_out.contents = "  );";
-  out.push_back(curr_out);
-}
-
-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<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) != string::npos)
@@ -430,18 +293,6 @@ string replace_all(string s, const string& a, const string& b) {
   return s;
 }
 
-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<Line>& lines) {
-  for (list<Line>::const_iterator p = lines.begin(); p != lines.end(); ++p)
-    if (!is_input(p->contents)) return true;
-  return false;
-}
-
 // does s start with pat, after skipping whitespace?
 // pat can't start with whitespace
 bool starts_with(const string& s, const string& pat) {
diff --git a/tangle/003tangle.test.cc b/tangle/003tangle.test.cc
index 732c8f2e..c7cc96b4 100644
--- a/tangle/003tangle.test.cc
+++ b/tangle/003tangle.test.cc
@@ -6,10 +6,10 @@ void test_tangle() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "d"
-                                 "b"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "d\n"
+                                 "b\n"
+                                 "c\n");
 }
 
 void test_tangle_with_linenumber() {
@@ -20,13 +20,13 @@ void test_tangle_with_linenumber() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "#line 1"
-                                 "a"
-                                 "#line 5"
-                                 "d"
-                                 "#line 2"
-                                 "b"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "#line 1\n"
+                                 "a\n"
+                                 "#line 5\n"
+                                 "d\n"
+                                 "#line 2\n"
+                                 "b\n"
+                                 "c\n");
   // no other #line directives
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "#line 3");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "#line 4");
@@ -40,11 +40,11 @@ void test_tangle_linenumbers_with_filename() {
                    "d\n");
   list<Line> dummy;
   tangle(in, "foo", dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "#line 5 \"foo\""
-                                 "d"
-                                 "b"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "#line 5 \"foo\"\n"
+                                 "d\n"
+                                 "b\n"
+                                 "c\n");
 }
 
 void test_tangle_line_numbers_with_multiple_filenames() {
@@ -57,12 +57,12 @@ void test_tangle_line_numbers_with_multiple_filenames() {
   istringstream in2(":(before b)\n"
                     "d\n");
   tangle(in2, "bar", dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "#line 2 \"bar\""
-                                 "d"
-                                 "#line 2 \"foo\""
-                                 "b"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "#line 2 \"bar\"\n"
+                                 "d\n"
+                                 "#line 2 \"foo\"\n"
+                                 "b\n"
+                                 "c\n");
 }
 
 void test_tangle_linenumbers_with_multiple_directives() {
@@ -77,15 +77,15 @@ void test_tangle_linenumbers_with_multiple_directives() {
                     ":(before c)\n"
                     "e");
   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");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "#line 2 \"bar\"\n"
+                                 "d\n"
+                                 "#line 2 \"foo\"\n"
+                                 "b\n"
+                                 "#line 4 \"bar\"\n"
+                                 "e\n"
+                                 "#line 3 \"foo\"\n"
+                                 "c\n");
 }
 
 void test_tangle_with_multiple_filenames_after() {
@@ -98,12 +98,12 @@ void test_tangle_with_multiple_filenames_after() {
   istringstream in2(":(after b)\n"
                     "d\n");
   tangle(in2, "bar", dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "b"
-                                 "#line 2 \"bar\""
-                                 "d"
-                                 "#line 3 \"foo\""
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "b\n"
+                                 "#line 2 \"bar\"\n"
+                                 "d\n"
+                                 "#line 3 \"foo\"\n"
+                                 "c\n");
 }
 
 void test_tangle_skip_tanglecomments() {
@@ -115,12 +115,12 @@ void test_tangle_skip_tanglecomments() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "b"
-                                 "c"
-                                 ""
-                                 ""
-                                 "d");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "b\n"
+                                 "c\n"
+                                 "\n"
+                                 "\n"
+                                 "d\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "//: 1");
 }
 
@@ -135,14 +135,14 @@ void test_tangle_with_tanglecomments_and_directive() {
                    "e\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "#line 6"
-                                 "d"
-                                 "#line 3"
-                                 "b"
-                                 "c"
-                                 "#line 8"
-                                 "e");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "#line 6\n"
+                                 "d\n"
+                                 "#line 3\n"
+                                 "b\n"
+                                 "c\n"
+                                 "#line 8\n"
+                                 "e\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "//: 1");
 }
 
@@ -158,14 +158,14 @@ void test_tangle_with_tanglecomments_inside_directive() {
                    "e\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "#line 7"
-                                 "d"
-                                 "#line 3"
-                                 "b"
-                                 "c"
-                                 "#line 9"
-                                 "e");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "#line 7\n"
+                                 "d\n"
+                                 "#line 3\n"
+                                 "b\n"
+                                 "c\n"
+                                 "#line 9\n"
+                                 "e\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "//: 1");
 }
 
@@ -176,9 +176,9 @@ void test_tangle_with_multiword_directives() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a b"
-                                 "d"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a b\n"
+                                 "d\n"
+                                 "c\n");
 }
 
 void test_tangle_with_quoted_multiword_directives() {
@@ -188,9 +188,9 @@ void test_tangle_with_quoted_multiword_directives() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a \"b\""
-                                 "d"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a \"b\"\n"
+                                 "d\n"
+                                 "c\n");
 }
 
 void test_tangle2() {
@@ -201,10 +201,10 @@ void test_tangle2() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "b"
-                                 "d"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "b\n"
+                                 "d\n"
+                                 "c\n");
 }
 
 void test_tangle_at_end() {
@@ -215,10 +215,10 @@ void test_tangle_at_end() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "b"
-                                 "c"
-                                 "d");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "b\n"
+                                 "c\n"
+                                 "d\n");
 }
 
 void test_tangle_indents_hunks_correctly() {
@@ -229,10 +229,10 @@ void test_tangle_indents_hunks_correctly() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "  b"
-                                 "  d"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "  b\n"
+                                 "  d\n"
+                                 "c\n");
 }
 
 void test_tangle_warns_on_missing_target() {
@@ -261,7 +261,7 @@ void test_tangle_delete_range_of_lines() {
                    ":(delete{} \"b\")\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a");
+  CHECK_TRACE_CONTENTS("tangle", "a\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "b");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "c");
 }
@@ -274,9 +274,9 @@ void test_tangle_replace() {
                    "d\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "d"
-                                 "c");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "d\n"
+                                 "c\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "b");
 }
 
@@ -290,9 +290,9 @@ void test_tangle_replace_range_of_lines() {
                    "e\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "d"
-                                 "e");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "d\n"
+                                 "e\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "b {");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "c");
 }
@@ -308,10 +308,10 @@ void test_tangle_replace_tracks_old_lines() {
                    "e\n");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "d"
-                                 "c"
-                                 "e");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "d\n"
+                                 "c\n"
+                                 "e\n");
   CHECK_TRACE_DOESNT_CONTAIN("tangle", "b {");
 }
 
@@ -325,12 +325,12 @@ void test_tangle_nested_patterns() {
                    "e");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "c"
-                                 "b"
-                                 "c"
-                                 "e"
-                                 "d");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "c\n"
+                                 "b\n"
+                                 "c\n"
+                                 "e\n"
+                                 "d\n");
 }
 
 void test_tangle_nested_patterns2() {
@@ -343,346 +343,16 @@ void test_tangle_nested_patterns2() {
                    "e");
   list<Line> dummy;
   tangle(in, dummy);
-  CHECK_TRACE_CONTENTS("tangle", "a"
-                                 "c"
-                                 "b"
-                                 "c"
-                                 "e"
-                                 "d");
+  CHECK_TRACE_CONTENTS("tangle", "a\n"
+                                 "c\n"
+                                 "b\n"
+                                 "c\n"
+                                 "e\n"
+                                 "d\n");
 }
 
 // todo: include line numbers in tangle errors
 
-//// scenarios
-
-void test_tangle_supports_scenarios() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "+layer1: pqr\n"
-                   "+layer2: xyz");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: xyz\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-}
-
-void test_tangle_ignores_empty_lines_in_scenarios() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "+layer1: pqr\n"
-                   "  \n"
-                   "+layer2: xyz");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: xyz\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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)\n"
-                   "abc 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)\n"
-                   "abc def\n"
-                   "+layer1: pqr");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  foo(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-
-  istringstream cleanup(":(scenarios run)\n");
-  tangle(cleanup, lines);
-}
-
-void test_tangle_can_hide_warnings_in_scenarios() {
-  istringstream in(":(scenario does_bar)\n"
-                   "% Hide_warnings = true;\n"
-                   "abc def\n"
-                   "+layer1: pqr\n"
-                   "+layer2: xyz");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  Hide_warnings = true;");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: xyz\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-}
-
-void test_tangle_can_include_c_code_at_end_of_scenario() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "+layer1: pqr\n"
-                   "+layer2: xyz\n"
-                   "% int x = 1;");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: xyz\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  int x = 1;");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-}
-
-void test_tangle_can_include_c_code_at_end_of_scenario_without_trace_expectations() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "% int x = 1;");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  int x = 1;");  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)\n"
-                   "abc \"def\"\n"
-                   "+layer1: pqr\n"
-                   "+layer2: \"xyz\"");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc \\\"def\\\"\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: \\\"xyz\\\"\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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)\n"
-                   "abc \"\"\n"
-                   "+layer1: pqr\n"
-                   "+layer2: \"\"");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc \\\"\\\"\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: \\\"\\\"\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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)\n"
-                   "abc def\n"
-                   "  efg\n"
-                   "+layer1: pqr\n"
-                   "+layer2: \"\"");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"  efg\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: \\\"\\\"\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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)\n"
-                   "abc def\n"
-                   "===\n"
-                   "efg\n"
-                   "+layer1: pqr\n"
-                   "+layer2: \"\"");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CLEAR_TRACE;");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"efg\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: \\\"\\\"\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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)\n"
-                   "abc def\n"
-                   "  efg\n"
-                   "+layer1: pqr\n"
-                   "-layer1: xyz");
-  list<Line> lines;
-  tangle(in, lines);
-//?   for (list<Line>::iterator p = lines.begin();  p != lines.end();  ++p)
-//?     cerr << p->contents << '\n';
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"  efg\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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)\n"
-                   "abc def\n"
-                   "  efg\n"
-                   "-layer1: pqr\n"
-                   "-layer1: xyz");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"  efg\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  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());
-}
-
-void test_tangle_can_check_for_count_in_scenario() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "  efg\n"
-                   "$layer1: 2");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"  efg\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_COUNT(\"layer1\", 2);");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-}
-
-void test_tangle_can_handle_mu_comments_in_scenario() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "# comment1\n"
-                   "  efg\n"
-                   "  # indented comment 2\n"
-                   "+layer1: pqr\n"
-                   "# comment inside expected_trace\n"
-                   "+layer1: xyz\n"
-                   "# comment after expected trace\n"
-                   "-layer1: z\n"
-                   "# comment before trace count\n"
-                   "$layer1: 2\n"
-                   "# comment at end\n"
-                   "\n");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"# comment1\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"  efg\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"  # indented comment 2\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: xyz\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_DOESNT_CONTAIN(\"layer1: z\");");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_COUNT(\"layer1\", 2);");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-}
-
-void test_tangle_can_interleave_present_and_absent_lines_to_kludgily_avoid_specifying_order() {
-  istringstream in(":(scenario does_bar)\n"
-                   "abc def\n"
-                   "+layer1: pqr\n"
-                   "-absent\n"
-                   "+layer2: xyz");
-  list<Line> lines;
-  tangle(in, lines);
-  CHECK_EQ(lines.front().contents, "void test_does_bar() {");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  run(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"abc def\\n\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer1: pqr\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_DOESNT_CONTAIN(\"absent\");");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  CHECK_TRACE_CONTENTS(");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "      \"layer2: xyz\"");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "  );");  lines.pop_front();
-  CHECK_EQ(lines.front().contents, "}");  lines.pop_front();
-  CHECK(lines.empty());
-}
-
 //// helpers
 
 void test_trim() {