about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--003trace.cc21
-rw-r--r--003trace.test.cc34
-rw-r--r--011load.cc25
-rw-r--r--013literal_string.cc4
-rw-r--r--020run.cc19
-rw-r--r--023jump.cc10
-rw-r--r--027trace.cc2
-rw-r--r--028assert.cc2
-rw-r--r--029debug.cc4
-rw-r--r--030container.cc36
-rw-r--r--031address.cc2
-rw-r--r--032array.cc24
-rw-r--r--033exclusive_container.cc4
-rw-r--r--034call.cc2
-rw-r--r--036call_reply.cc8
-rw-r--r--038scheduler.cc22
-rw-r--r--039wait.cc14
-rw-r--r--040brace.cc22
-rw-r--r--041jump_label.cc2
-rw-r--r--042name.cc20
-rw-r--r--043new.cc33
-rw-r--r--044space.cc16
-rw-r--r--046closure_name.cc26
-rw-r--r--047global.cc4
-rw-r--r--049continuation.cc2
-rw-r--r--050scenario.cc26
-rw-r--r--072scenario_screen.cc10
-rw-r--r--075scenario_console.cc6
-rw-r--r--082persist.cc8
29 files changed, 205 insertions, 203 deletions
diff --git a/003trace.cc b/003trace.cc
index 11bad7a2..3695703a 100644
--- a/003trace.cc
+++ b/003trace.cc
@@ -114,7 +114,6 @@ struct trace_stream {
 
   ostream& stream(int depth, string layer) {
     if (!collect_layer.empty() && layer != collect_layer) return null_stream;
-    newline();
     curr_stream = new ostringstream;
     curr_layer = layer;
     curr_depth = depth;
@@ -125,6 +124,7 @@ struct trace_stream {
   void newline() {
     if (!curr_stream) return;
     string curr_contents = curr_stream->str();
+    if (curr_contents.empty()) return;
     past_lines.push_back(trace_line(curr_depth, trim(curr_layer), curr_contents));  // preserve indent in contents
     if (curr_layer == dump_layer || curr_layer == "dump" || dump_layer == "all" ||
         (!Hide_warnings && curr_layer == "warn"))
@@ -138,7 +138,6 @@ struct trace_stream {
 
   // Useful for debugging.
   string readable_contents(string layer) {  // missing layer = everything
-    newline();
     ostringstream output;
     layer = trim(layer);
     for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
@@ -161,22 +160,12 @@ trace_stream* Trace_stream = NULL;
 // some unfriendly constraints (they delay printing, they can't nest)
 #define raise  ((!Trace_stream || !Hide_warnings) ? (tb_shutdown(),cerr) /*do print*/ : Trace_stream->stream("warn"))
 
-// A separate helper for debugging. We should only trace domain-specific
-// facts. For everything else use log.
-#define xlog if (false) log
-// To turn on logging replace 'xlog' with 'log'.
-#define log cerr
-
 :(before "End Types")
-// raise << die exits after printing -- unless Hide_warnings is set.
-struct die {};
+struct end {};
 :(before "End Tracing")
-ostream& operator<<(ostream& os, unused die) {
-  if (Hide_warnings) return os;
-  tb_shutdown();
-  os << "dying";
+ostream& operator<<(ostream& os, unused end) {
   if (Trace_stream) Trace_stream->newline();
-  exit(1);
+  return os;
 }
 
 #define CLEAR_TRACE  delete Trace_stream, Trace_stream = new trace_stream;
@@ -217,7 +206,6 @@ bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expecte
   while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
     ++curr_expected_line;
   if (curr_expected_line == SIZE(expected_lines)) return true;
-  Trace_stream->newline();
   string layer, contents;
   split_layer_contents(expected_lines.at(curr_expected_line), &layer, &contents);
   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
@@ -265,7 +253,6 @@ int trace_count(string layer) {
 }
 
 int trace_count(string layer, string line) {
-  Trace_stream->newline();
   long result = 0;
   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
     if (layer == p->label) {
diff --git a/003trace.test.cc b/003trace.test.cc
index 146f9dc2..a16457a1 100644
--- a/003trace.test.cc
+++ b/003trace.test.cc
@@ -1,58 +1,58 @@
 void test_trace_check_compares() {
-  trace("test layer") << "foo";
+  trace("test layer") << "foo" << end();
   CHECK_TRACE_CONTENTS("test layer: foo");
 }
 
 void test_trace_check_ignores_other_layers() {
-  trace("test layer 1") << "foo";
-  trace("test layer 2") << "bar";
+  trace("test layer 1") << "foo" << end();
+  trace("test layer 2") << "bar" << end();
   CHECK_TRACE_CONTENTS("test layer 1: foo");
   CHECK_TRACE_DOESNT_CONTAIN("test layer 2: foo");
 }
 
 void test_trace_check_ignores_other_lines() {
-  trace("test layer 1") << "foo";
-  trace("test layer 1") << "bar";
+  trace("test layer 1") << "foo" << end();
+  trace("test layer 1") << "bar" << end();
   CHECK_TRACE_CONTENTS("test layer 1: foo");
 }
 
 void test_trace_check_ignores_other_lines2() {
-  trace("test layer 1") << "foo";
-  trace("test layer 1") << "bar";
+  trace("test layer 1") << "foo" << end();
+  trace("test layer 1") << "bar" << end();
   CHECK_TRACE_CONTENTS("test layer 1: bar");
 }
 
 void test_trace_ignores_trailing_whitespace() {
-  trace("test layer 1") << "foo\n";
+  trace("test layer 1") << "foo\n" << end();
   CHECK_TRACE_CONTENTS("test layer 1: foo");
 }
 
 void test_trace_ignores_trailing_whitespace2() {
-  trace("test layer 1") << "foo ";
+  trace("test layer 1") << "foo " << end();
   CHECK_TRACE_CONTENTS("test layer 1: foo");
 }
 
 void test_trace_orders_across_layers() {
-  trace("test layer 1") << "foo";
-  trace("test layer 2") << "bar";
-  trace("test layer 1") << "qux";
+  trace("test layer 1") << "foo" << end();
+  trace("test layer 2") << "bar" << end();
+  trace("test layer 1") << "qux" << end();
   CHECK_TRACE_CONTENTS("test layer 1: footest layer 2: bartest layer 1: qux");
 }
 
 void test_trace_supports_count() {
-  trace("test layer 1") << "foo";
-  trace("test layer 1") << "foo";
+  trace("test layer 1") << "foo" << end();
+  trace("test layer 1") << "foo" << end();
   CHECK_EQ(trace_count("test layer 1", "foo"), 2);
 }
 
 void test_trace_supports_count2() {
-  trace("test layer 1") << "foo";
-  trace("test layer 1") << "bar";
+  trace("test layer 1") << "foo" << end();
+  trace("test layer 1") << "bar" << end();
   CHECK_EQ(trace_count("test layer 1"), 2);
 }
 
 void test_trace_count_ignores_trailing_whitespace() {
-  trace("test layer 1") << "foo\n";
+  trace("test layer 1") << "foo\n" << end();
   CHECK(trace_count("test layer 1", "foo") == 1);
 }
 
diff --git a/011load.cc b/011load.cc
index 7ce8e25f..9142429c 100644
--- a/011load.cc
+++ b/011load.cc
@@ -29,7 +29,7 @@ vector<recipe_ordinal> load(istream& in) {
       string recipe_name = next_word(in);
 //?       cerr << "recipe: " << recipe_name << '\n'; //? 1
       if (recipe_name.empty())
-        raise << "empty recipe name\n";
+        raise << "empty recipe name\n" << end();
       if (Recipe_ordinal.find(recipe_name) == Recipe_ordinal.end()) {
         Recipe_ordinal[recipe_name] = Next_recipe_ordinal++;
       }
@@ -44,7 +44,7 @@ vector<recipe_ordinal> load(istream& in) {
     }
     // End Command Handlers
     else {
-      raise << "unknown top-level command: " << command << '\n';
+      raise << "unknown top-level command: " << command << '\n' << end();
     }
   }
   // End Load Sanity Checks
@@ -55,7 +55,7 @@ recipe slurp_recipe(istream& in) {
   recipe result;
   skip_whitespace(in);
   if (in.get() != '[')
-    raise << "recipe body must begin with '['\n";
+    raise << "recipe body must begin with '['\n" << end();
   skip_whitespace_and_comments(in);
   instruction curr;
   while (next_instruction(in, &curr)) {
@@ -96,7 +96,7 @@ bool next_instruction(istream& in, instruction* curr) {
   if (SIZE(words) == 1 && !isalnum(words.at(0).at(0)) && words.at(0).at(0) != '$') {
     curr->is_label = true;
     curr->label = words.at(0);
-    trace("parse") << "label: " << curr->label;
+    trace("parse") << "label: " << curr->label << end();
     return !in.eof();
   }
 
@@ -110,15 +110,18 @@ bool next_instruction(istream& in, instruction* curr) {
     ++p;  // skip <-
   }
 
-  if (p == words.end())
-    raise << "instruction prematurely ended with '<-'\n" << die();
+  if (p == words.end()) {
+    raise << "instruction prematurely ended with '<-'\n" << end() << end();
+    return false;
+  }
   curr->name = *p;
   if (Recipe_ordinal.find(*p) == Recipe_ordinal.end()) {
     Recipe_ordinal[*p] = Next_recipe_ordinal++;
 //?     cout << "AAA: " << *p << " is now " << Recipe_ordinal[*p] << '\n'; //? 1
   }
   if (Recipe_ordinal[*p] == 0) {
-    raise << "Recipe " << *p << " has number 0, which is reserved for IDLE.\n" << die();
+    raise << "Recipe " << *p << " has number 0, which is reserved for IDLE.\n" << end() << end();
+    return false;
   }
   curr->operation = Recipe_ordinal[*p];  ++p;
 
@@ -128,12 +131,12 @@ bool next_instruction(istream& in, instruction* curr) {
 //?     cerr << "ingredient: " << curr->ingredients.back().to_string() << '\n'; //? 1
   }
 
-  trace("parse") << "instruction: " << curr->name;
+  trace("parse") << "instruction: " << curr->name << end();
   for (vector<reagent>::iterator p = curr->ingredients.begin(); p != curr->ingredients.end(); ++p) {
-    trace("parse") << "  ingredient: " << p->to_string();
+    trace("parse") << "  ingredient: " << p->to_string() << end();
   }
   for (vector<reagent>::iterator p = curr->products.begin(); p != curr->products.end(); ++p) {
-    trace("parse") << "  product: " << p->to_string();
+    trace("parse") << "  product: " << p->to_string() << end();
   }
   return !in.eof();
 }
@@ -204,7 +207,7 @@ Hide_redefine_warnings = false;
 :(after "Warn On Redefinition")
 if (!Hide_redefine_warnings
     && Recipe.find(Recipe_ordinal[recipe_name]) != Recipe.end()) {
-  raise << "redefining recipe " << Recipe[Recipe_ordinal[recipe_name]].name << "\n";
+  raise << "redefining recipe " << Recipe[Recipe_ordinal[recipe_name]].name << "\n" << end();
 }
 
 // for debugging
diff --git a/013literal_string.cc b/013literal_string.cc
index 0d4e3573..29a185b2 100644
--- a/013literal_string.cc
+++ b/013literal_string.cc
@@ -80,7 +80,7 @@ void slurp_quoted_comment_oblivious(istream& in, ostringstream& out) {
     if (brace_depth == 0) break;
   }
   if (in.eof() && brace_depth > 0) {
-    raise << "unbalanced '['\n";
+    raise << "unbalanced '['\n" << end();
     out.clear();
   }
 }
@@ -109,7 +109,7 @@ void slurp_quoted_comment_aware(istream& in, ostringstream& out) {
     out << c;
     if (c == ']') return;
   }
-  raise << "unbalanced '['\n";
+  raise << "unbalanced '['\n" << end();
   out.clear();
 }
 
diff --git a/020run.cc b/020run.cc
index 12ae6091..08aa2b74 100644
--- a/020run.cc
+++ b/020run.cc
@@ -60,9 +60,11 @@ void run_current_routine()
 //?     cerr << "AAA 7: " << current_step_index() << '\n'; //? 1
     // Running One Instruction
     if (current_instruction().is_label) { ++current_step_index(); continue; }
-    trace(Initial_callstack_depth+Callstack_depth, "run") << current_instruction().to_string();
-    if (Memory[0] != 0)
-      raise << "something wrote to location 0; this should never happen\n" << die();
+    trace(Initial_callstack_depth+Callstack_depth, "run") << current_instruction().to_string() << end();
+    if (Memory[0] != 0) {
+      raise << "something wrote to location 0; this should never happen\n" << end();
+      break;
+    }
     // Read all ingredients from memory.
     // Each ingredient loads a vector of values rather than a single value; mu
     // permits operating on reagents spanning multiple locations.
@@ -87,7 +89,7 @@ void run_current_routine()
       }
     }
     if (SIZE(products) < SIZE(current_instruction().products))
-      raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string();
+      raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string() << end();
     for (long long int i = 0; i < SIZE(current_instruction().products); ++i) {
       write_memory(current_instruction().products.at(i), products.at(i));
     }
@@ -147,7 +149,7 @@ void load_permanently(string filename) {
 //?   cerr << "AAA: " << filename << ' ' << static_cast<bool>(fin) << ' ' << fin.fail() << '\n'; //? 1
 //?   return; //? 1
   if (!fin) {
-    raise << "no such file " << filename << '\n';
+    raise << "no such file " << filename << '\n' << end();
     return;
   }
   fin >> std::noskipws;
@@ -189,7 +191,7 @@ vector<double> read_memory(reagent x) {
   long long int size = size_of(x);
   for (long long int offset = 0; offset < size; ++offset) {
     double val = Memory[base+offset];
-    trace(Primitive_recipe_depth, "mem") << "location " << base+offset << " is " << val;
+    trace(Primitive_recipe_depth, "mem") << "location " << base+offset << " is " << val << end();
     result.push_back(val);
   }
   return result;
@@ -200,10 +202,11 @@ void write_memory(reagent x, vector<double> data) {
   if (is_literal(x)) return;
   long long int base = x.value;
   if (size_mismatch(x, data)) {
-    raise << current_recipe_name() << ": size mismatch in storing to " << x.to_string() << " at " << current_instruction().to_string() << '\n' << die();
+    raise << current_recipe_name() << ": size mismatch in storing to " << x.to_string() << " at " << current_instruction().to_string() << '\n' << end();
+    return;
   }
   for (long long int offset = 0; offset < SIZE(data); ++offset) {
-    trace(Primitive_recipe_depth, "mem") << "storing " << data.at(offset) << " in location " << base+offset;
+    trace(Primitive_recipe_depth, "mem") << "storing " << data.at(offset) << " in location " << base+offset << end();
     Memory[base+offset] = data.at(offset);
   }
 }
diff --git a/023jump.cc b/023jump.cc
index f53f3079..edccf207 100644
--- a/023jump.cc
+++ b/023jump.cc
@@ -20,7 +20,7 @@ case JUMP: {
   assert(SIZE(ingredients) == 1);
   assert(scalar(ingredients.at(0)));
   current_step_index() += ingredients.at(0).at(0)+1;
-  trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index();
+  trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end();
   continue;  // skip rest of this instruction
 }
 
@@ -49,12 +49,12 @@ case JUMP_IF: {
   assert(SIZE(ingredients) == 2);
   assert(scalar(ingredients.at(0)));
   if (!ingredients.at(0).at(0)) {
-    trace(Primitive_recipe_depth, "run") << "jump-if fell through";
+    trace(Primitive_recipe_depth, "run") << "jump-if fell through" << end();
     break;
   }
   assert(scalar(ingredients.at(1)));
   current_step_index() += ingredients.at(1).at(0)+1;
-  trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index();
+  trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end();
   continue;  // skip rest of this instruction
 }
 
@@ -88,12 +88,12 @@ case JUMP_UNLESS: {
   assert(SIZE(ingredients) == 2);
   assert(scalar(ingredients.at(0)));
   if (ingredients.at(0).at(0)) {
-    trace(Primitive_recipe_depth, "run") << "jump-unless fell through";
+    trace(Primitive_recipe_depth, "run") << "jump-unless fell through" << end();
     break;
   }
   assert(scalar(ingredients.at(1)));
   current_step_index() += ingredients.at(1).at(0)+1;
-  trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index();
+  trace(Primitive_recipe_depth, "run") << "jumping to instruction " << current_step_index() << end();
   continue;  // skip rest of this instruction
 }
 
diff --git a/027trace.cc b/027trace.cc
index 89d6f2e2..12cc7ca3 100644
--- a/027trace.cc
+++ b/027trace.cc
@@ -16,7 +16,7 @@ case TRACE: {
   string label = current_instruction().ingredients.at(0).name;
   assert(is_literal(current_instruction().ingredients.at(1)));
   string message = current_instruction().ingredients.at(1).name;
-  trace(1, label) << message;
+  trace(1, label) << message << end();
   break;
 }
 
diff --git a/028assert.cc b/028assert.cc
index f864d43e..07f68cc8 100644
--- a/028assert.cc
+++ b/028assert.cc
@@ -15,7 +15,7 @@ case ASSERT: {
   assert(scalar(ingredients.at(0)));
   if (!ingredients.at(0).at(0)) {
     assert(is_literal(current_instruction().ingredients.at(1)));
-    raise << current_instruction().ingredients.at(1).name << '\n' << die();
+    raise << current_instruction().ingredients.at(1).name << '\n' << end();
   }
   break;
 }
diff --git a/029debug.cc b/029debug.cc
index f6d77355..b9f653e7 100644
--- a/029debug.cc
+++ b/029debug.cc
@@ -6,7 +6,7 @@ Recipe_ordinal["$print"] = _PRINT;
 case _PRINT: {
   for (long long int i = 0; i < SIZE(ingredients); ++i) {
     if (is_literal(current_instruction().ingredients.at(i))) {
-      trace(Primitive_recipe_depth, "run") << "$print: " << current_instruction().ingredients.at(i).name;
+      trace(Primitive_recipe_depth, "run") << "$print: " << current_instruction().ingredients.at(i).name << end();
       if (has_property(current_instruction().ingredients.at(i), "newline"))
         cout << '\n';
       else
@@ -14,7 +14,7 @@ case _PRINT: {
     }
     else {
       for (long long int j = 0; j < SIZE(ingredients.at(i)); ++j) {
-        trace(Primitive_recipe_depth, "run") << "$print: " << ingredients.at(i).at(j);
+        trace(Primitive_recipe_depth, "run") << "$print: " << ingredients.at(i).at(j) << end();
         if (j > 0) cout << " ";
         cout << ingredients.at(i).at(j);
       }
diff --git a/030container.cc b/030container.cc
index c9042398..79ab71e1 100644
--- a/030container.cc
+++ b/030container.cc
@@ -84,7 +84,7 @@ if (t.kind == container) {
   for (long long int i = 0; i < SIZE(t.elements); ++i) {
     // todo: strengthen assertion to disallow mutual type recursion
     if (types.at(0) == t.elements.at(i).at(0)) {
-      raise << "container " << t.name << " can't include itself as a member\n";
+      raise << "container " << t.name << " can't include itself as a member\n" << end();
       return 0;
     }
     result += size_of(t.elements.at(i));
@@ -108,18 +108,18 @@ Recipe_ordinal["get"] = GET;
 :(before "End Primitive Recipe Implementations")
 case GET: {
   if (ingredients.size() != 2) {
-    raise << current_recipe_name() << ": 'get' expects exactly 2 ingredients in '" << current_instruction().to_string() << "'\n";
+    raise << current_recipe_name() << ": 'get' expects exactly 2 ingredients in '" << current_instruction().to_string() << "'\n" << end();
     break;
   }
   reagent base = current_instruction().ingredients.at(0);
   long long int base_address = base.value;
   type_ordinal base_type = base.types.at(0);
   if (Type[base_type].kind != container) {
-    raise << current_recipe_name () << ": 'get' on a non-container " << base.original_string << '\n';
+    raise << current_recipe_name () << ": 'get' on a non-container " << base.original_string << '\n' << end();
     break;
   }
   if (!is_literal(current_instruction().ingredients.at(1))) {
-    raise << current_recipe_name() << ": expected ingredient 1 of 'get' to have type 'offset', got '" << current_instruction().ingredients.at(1).original_string << "'\n";
+    raise << current_recipe_name() << ": expected ingredient 1 of 'get' to have type 'offset', got '" << current_instruction().ingredients.at(1).original_string << "'\n" << end();
     break;
   }
   assert(scalar(ingredients.at(1)));
@@ -128,14 +128,14 @@ case GET: {
   for (long long int i = 0; i < offset; ++i) {
     src += size_of(Type[base_type].elements.at(i));
   }
-  trace(Primitive_recipe_depth, "run") << "address to copy is " << src;
+  trace(Primitive_recipe_depth, "run") << "address to copy is " << src << end();
   if (offset < 0 || offset >= SIZE(Type[base_type].elements)) {
-    raise << current_recipe_name() << ": invalid offset " << offset << " for " << Type[base_type].name << '\n';
+    raise << current_recipe_name() << ": invalid offset " << offset << " for " << Type[base_type].name << '\n' << end();
     products.resize(1);
     break;
   }
   type_ordinal src_type = Type[base_type].elements.at(offset).at(0);
-  trace(Primitive_recipe_depth, "run") << "its type is " << Type[src_type].name;
+  trace(Primitive_recipe_depth, "run") << "its type is " << Type[src_type].name << end();
   reagent tmp;
   tmp.set_value(src);
   tmp.types.push_back(src_type);
@@ -192,17 +192,17 @@ case GET_ADDRESS: {
   long long int base_address = base.value;
   type_ordinal base_type = base.types.at(0);
   if (Type[base_type].kind != container) {
-    raise << current_recipe_name () << ": 'get-address' on a non-container " << base.original_string << '\n';
+    raise << current_recipe_name () << ": 'get-address' on a non-container " << base.original_string << '\n' << end();
     break;
   }
   if (!is_literal(current_instruction().ingredients.at(1))) {
-    raise << current_recipe_name() << ": expected ingredient 1 of 'get-address' to have type 'offset', got '" << current_instruction().ingredients.at(1).original_string << "'\n";
+    raise << current_recipe_name() << ": expected ingredient 1 of 'get-address' to have type 'offset', got '" << current_instruction().ingredients.at(1).original_string << "'\n" << end();
     break;
   }
   assert(scalar(ingredients.at(1)));
   long long int offset = ingredients.at(1).at(0);
   if (offset < 0 || offset >= SIZE(Type[base_type].elements)) {
-    raise << "invalid offset " << offset << " for " << Type[base_type].name << '\n';
+    raise << "invalid offset " << offset << " for " << Type[base_type].name << '\n' << end();
     products.resize(1);
     break;
   }
@@ -210,7 +210,7 @@ case GET_ADDRESS: {
   for (long long int i = 0; i < offset; ++i) {
     result += size_of(Type[base_type].elements.at(i));
   }
-  trace(Primitive_recipe_depth, "run") << "address to copy is " << result;
+  trace(Primitive_recipe_depth, "run") << "address to copy is " << result << end();
   products.resize(1);
   products.at(0).push_back(result);
   break;
@@ -278,7 +278,7 @@ else if (command == "container") {
 void insert_container(const string& command, kind_of_type kind, istream& in) {
   skip_whitespace(in);
   string name = next_word(in);
-  trace("parse") << "reading " << command << ' ' << name;
+  trace("parse") << "reading " << command << ' ' << name << end();
 //?   cout << name << '\n'; //? 2
 //?   if (Type_ordinal.find(name) != Type_ordinal.end()) //? 1
 //?     cerr << Type_ordinal[name] << '\n'; //? 1
@@ -286,7 +286,7 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
       || Type_ordinal[name] == 0) {
     Type_ordinal[name] = Next_type_ordinal++;
   }
-  trace("parse") << "type number: " << Type_ordinal[name];
+  trace("parse") << "type number: " << Type_ordinal[name] << end();
   skip_bracket(in, "'container' must begin with '['");
   type_info& t = Type[Type_ordinal[name]];
   recently_added_types.push_back(Type_ordinal[name]);
@@ -298,7 +298,7 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
     if (element == "]") break;
     istringstream inner(element);
     t.element_names.push_back(slurp_until(inner, ':'));
-    trace("parse") << "  element name: " << t.element_names.back();
+    trace("parse") << "  element name: " << t.element_names.back() << end();
     vector<type_ordinal> types;
     while (!inner.eof()) {
       string type_name = slurp_until(inner, ':');
@@ -307,7 +307,7 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
         Type_ordinal[type_name] = Next_type_ordinal++;
       }
       types.push_back(Type_ordinal[type_name]);
-      trace("parse") << "  type: " << types.back();
+      trace("parse") << "  type: " << types.back() << end();
     }
     t.elements.push_back(types);
   }
@@ -395,7 +395,7 @@ void check_invalid_types(const reagent& r) {
   for (long long int i = 0; i < SIZE(r.types); ++i) {
     if (r.types.at(i) == 0) continue;
     if (Type.find(r.types.at(i)) == Type.end())
-      raise << "unknown type: " << r.properties.at(0).second.at(i) << '\n';
+      raise << "unknown type: " << r.properties.at(0).second.at(i) << '\n' << end();
   }
 }
 
@@ -431,7 +431,7 @@ void check_container_field_types() {
       for (long long int j = 0; j < SIZE(info.elements.at(i)); ++j) {
         if (info.elements.at(i).at(j) == 0) continue;
         if (Type.find(info.elements.at(i).at(j)) == Type.end())
-          raise << "unknown type for field " << info.element_names.at(i) << " in " << info.name << '\n';
+          raise << "unknown type for field " << info.element_names.at(i) << " in " << info.name << '\n' << end();
       }
     }
   }
@@ -470,5 +470,5 @@ recipe main [
 void skip_bracket(istream& in, string message) {
   skip_whitespace_and_comments(in);
   if (in.get() != '[')
-    raise << message << '\n';
+    raise << message << '\n' << end();
 }
diff --git a/031address.cc b/031address.cc
index 4df2518c..0e2a4f0f 100644
--- a/031address.cc
+++ b/031address.cc
@@ -44,7 +44,7 @@ reagent deref(reagent x) {
 
   // compute value
   result.set_value(Memory[x.value]);
-  trace(Primitive_recipe_depth, "mem") << "location " << x.value << " is " << result.value;
+  trace(Primitive_recipe_depth, "mem") << "location " << x.value << " is " << result.value << end();
 
   // populate types
   copy(++x.types.begin(), x.types.end(), inserter(result.types, result.types.begin()));
diff --git a/032array.cc b/032array.cc
index 9dad84ac..f9134e87 100644
--- a/032array.cc
+++ b/032array.cc
@@ -43,7 +43,7 @@ if (x.types.at(0) == Type_ordinal["array"]) return false;
 :(before "End size_of(reagent) Cases")
   if (r.types.at(0) == Type_ordinal["array"]) {
     if (SIZE(r.types) == 1) {
-      raise << current_recipe_name() << ": '" << r.original_string << "' is an array of what?\n";
+      raise << current_recipe_name() << ": '" << r.original_string << "' is an array of what?\n" << end();
       return 1;
     }
     // skip the 'array' type to get at the element type
@@ -81,26 +81,26 @@ Recipe_ordinal["index"] = INDEX;
 case INDEX: {
 //?   if (Trace_stream) Trace_stream->dump_layer = "run"; //? 1
   reagent base = canonize(current_instruction().ingredients.at(0));
-//?   trace(Primitive_recipe_depth, "run") << "ingredient 0 after canonize: " << base.to_string(); //? 1
+//?   trace(Primitive_recipe_depth, "run") << "ingredient 0 after canonize: " << base.to_string() << end(); //? 1
   long long int base_address = base.value;
   if (base.types.at(0) != Type_ordinal["array"]) {
-    raise << current_recipe_name () << ": 'index' on a non-array " << base.original_string << '\n';
+    raise << current_recipe_name () << ": 'index' on a non-array " << base.original_string << '\n' << end();
     break;
   }
   reagent offset = canonize(current_instruction().ingredients.at(1));
-//?   trace(Primitive_recipe_depth, "run") << "ingredient 1 after canonize: " << offset.to_string(); //? 1
+//?   trace(Primitive_recipe_depth, "run") << "ingredient 1 after canonize: " << offset.to_string() << end(); //? 1
   vector<double> offset_val(read_memory(offset));
   vector<type_ordinal> element_type = array_element(base.types);
-//?   trace(Primitive_recipe_depth, "run") << "offset: " << offset_val.at(0); //? 1
-//?   trace(Primitive_recipe_depth, "run") << "size of elements: " << size_of(element_type); //? 1
+//?   trace(Primitive_recipe_depth, "run") << "offset: " << offset_val.at(0) << end(); //? 1
+//?   trace(Primitive_recipe_depth, "run") << "size of elements: " << size_of(element_type) << end(); //? 1
   if (offset_val.at(0) < 0 || offset_val.at(0) >= Memory[base_address]) {
-    raise << current_recipe_name() << ": invalid index " << offset_val.at(0) << '\n';
+    raise << current_recipe_name() << ": invalid index " << offset_val.at(0) << '\n' << end();
     products.resize(1);
     break;
   }
   long long int src = base_address + 1 + offset_val.at(0)*size_of(element_type);
-  trace(Primitive_recipe_depth, "run") << "address to copy is " << src;
-  trace(Primitive_recipe_depth, "run") << "its type is " << Type[element_type.at(0)].name;
+  trace(Primitive_recipe_depth, "run") << "address to copy is " << src << end();
+  trace(Primitive_recipe_depth, "run") << "its type is " << Type[element_type.at(0)].name << end();
   reagent tmp;
   tmp.set_value(src);
   copy(element_type.begin(), element_type.end(), inserter(tmp.types, tmp.types.begin()));
@@ -175,14 +175,14 @@ case INDEX_ADDRESS: {
   reagent base = canonize(current_instruction().ingredients.at(0));
   long long int base_address = base.value;
   if (base.types.at(0) != Type_ordinal["array"]) {
-    raise << current_recipe_name () << ": 'index-address' on a non-array " << base.original_string << '\n';
+    raise << current_recipe_name () << ": 'index-address' on a non-array " << base.original_string << '\n' << end();
     break;
   }
   reagent offset = canonize(current_instruction().ingredients.at(1));
   vector<double> offset_val(read_memory(offset));
   vector<type_ordinal> element_type = array_element(base.types);
   if (offset_val.at(0) < 0 || offset_val.at(0) >= Memory[base_address]) {
-    raise << current_recipe_name() << ": invalid index " << offset_val.at(0) << '\n';
+    raise << current_recipe_name() << ": invalid index " << offset_val.at(0) << '\n' << end();
     products.resize(1);
     break;
   }
@@ -242,7 +242,7 @@ Recipe_ordinal["length"] = LENGTH;
 case LENGTH: {
   reagent x = canonize(current_instruction().ingredients.at(0));
   if (x.types.at(0) != Type_ordinal["array"]) {
-    raise << "tried to calculate length of non-array " << x.to_string() << '\n';
+    raise << "tried to calculate length of non-array " << x.to_string() << '\n' << end();
     break;
   }
   products.resize(1);
diff --git a/033exclusive_container.cc b/033exclusive_container.cc
index dfdcdd61..c212be79 100644
--- a/033exclusive_container.cc
+++ b/033exclusive_container.cc
@@ -95,11 +95,11 @@ case MAYBE_CONVERT: {
   long long int base_address = base.value;
   type_ordinal base_type = base.types.at(0);
   if (Type[base_type].kind != exclusive_container) {
-    raise << current_recipe_name () << ": 'maybe-convert' on a non-exclusive-container " << base.original_string << '\n';
+    raise << current_recipe_name () << ": 'maybe-convert' on a non-exclusive-container " << base.original_string << '\n' << end();
     break;
   }
   if (!is_literal(current_instruction().ingredients.at(1))) {
-    raise << current_recipe_name() << ": expected ingredient 1 of 'get' to have type 'variant', got '" << current_instruction().ingredients.at(1).original_string << "'\n";
+    raise << current_recipe_name() << ": expected ingredient 1 of 'get' to have type 'variant', got '" << current_instruction().ingredients.at(1).original_string << "'\n" << end();
     break;
   }
   long long int tag = current_instruction().ingredients.at(1).value;
diff --git a/034call.cc b/034call.cc
index 4b841fe0..cafabd53 100644
--- a/034call.cc
+++ b/034call.cc
@@ -81,7 +81,7 @@ inline const instruction& current_instruction() {
 default: {
   // not a primitive; try to look up the book of recipes
   if (Recipe.find(current_instruction().operation) == Recipe.end()) {
-    raise << "undefined operation " << current_instruction().operation << ": " << current_instruction().to_string() << '\n';
+    raise << "undefined operation " << current_instruction().operation << ": " << current_instruction().to_string() << '\n' << end();
     break;
   }
   Current_routine->calls.push_front(call(current_instruction().operation));
diff --git a/036call_reply.cc b/036call_reply.cc
index 618ad651..75587351 100644
--- a/036call_reply.cc
+++ b/036call_reply.cc
@@ -35,20 +35,20 @@ case REPLY: {
   // check that any reply ingredients with /same-as-ingredient connect up
   // the corresponding ingredient and product in the caller.
   if (SIZE(caller_instruction.products) > SIZE(ingredients))
-    raise << "too few values replied from " << callee << '\n';
+    raise << "too few values replied from " << callee << '\n' << end();
   for (long long int i = 0; i < SIZE(caller_instruction.products); ++i) {
 //?     cerr << Recipe[Current_routine->calls.front().running_recipe].name << '\n'; //? 1
-    trace(Primitive_recipe_depth, "run") << "result " << i << " is " << to_string(ingredients.at(i));
+    trace(Primitive_recipe_depth, "run") << "result " << i << " is " << to_string(ingredients.at(i)) << end();
     if (has_property(reply_inst.ingredients.at(i), "same-as-ingredient")) {
       vector<string> tmp = property(reply_inst.ingredients.at(i), "same-as-ingredient");
       assert(SIZE(tmp) == 1);
       long long int ingredient_index = to_integer(tmp.at(0));
       if (ingredient_index >= SIZE(caller_instruction.ingredients))
-        raise << current_recipe_name() << ": 'same-as-ingredient' metadata overflows ingredients in: " << caller_instruction.to_string() << '\n';
+        raise << current_recipe_name() << ": 'same-as-ingredient' metadata overflows ingredients in: " << caller_instruction.to_string() << '\n' << end();
 //?       cerr << caller_instruction.products.size() << ' ' << i << ' ' << caller_instruction.ingredients.size() << ' ' << ingredient_index << '\n'; //? 1
 //?       cerr << caller_instruction.to_string() << '\n'; //? 1
       if (!is_dummy(caller_instruction.products.at(i)) && caller_instruction.products.at(i).value != caller_instruction.ingredients.at(ingredient_index).value)
-        raise << current_recipe_name() << ": 'same-as-ingredient' result " << caller_instruction.products.at(i).value << " from call to " << callee << " must be location " << caller_instruction.ingredients.at(ingredient_index).value << '\n';
+        raise << current_recipe_name() << ": 'same-as-ingredient' result " << caller_instruction.products.at(i).value << " from call to " << callee << " must be location " << caller_instruction.ingredients.at(ingredient_index).value << '\n' << end();
     }
   }
   // End Reply
diff --git a/038scheduler.cc b/038scheduler.cc
index 2f53e884..5334009c 100644
--- a/038scheduler.cc
+++ b/038scheduler.cc
@@ -56,9 +56,9 @@ void run(recipe_ordinal r) {
 //?     cout << "scheduler: " << Current_routine_index << '\n'; //? 1
     assert(Current_routine);
     assert(Current_routine->state == RUNNING);
-    trace("schedule") << current_routine_label();
-//?     trace("schedule") << Current_routine_index << ": " << current_recipe_name(); //? 1
-//?     trace("schedule") << Current_routine->id << " " << current_recipe_name(); //? 1
+    trace("schedule") << current_routine_label() << end();
+//?     trace("schedule") << Current_routine_index << ": " << current_recipe_name() << end(); //? 1
+//?     trace("schedule") << Current_routine->id << " " << current_recipe_name() << end(); //? 1
     run_current_routine(Scheduling_interval);
     // Scheduler State Transitions
 //?     cerr << "AAA completed? " << Current_routine->completed() << '\n'; //? 1
@@ -252,29 +252,29 @@ recipe f1 [
 -schedule: f1
 
 :(before "End Scheduler Cleanup")
-//? trace("schedule") << "Before cleanup"; //? 1
+//? trace("schedule") << "Before cleanup" << end(); //? 1
 //? for (long long int i = 0; i < SIZE(Routines); ++i) { //? 1
-//?   trace("schedule") << i << ": " << Routines.at(i)->id << ' ' << Routines.at(i)->state << ' ' << Routines.at(i)->parent_index << ' ' << Routines.at(i)->state; //? 1
+//?   trace("schedule") << i << ": " << Routines.at(i)->id << ' ' << Routines.at(i)->state << ' ' << Routines.at(i)->parent_index << ' ' << Routines.at(i)->state << end(); //? 1
 //? } //? 1
 for (long long int i = 0; i < SIZE(Routines); ++i) {
   if (Routines.at(i)->state == COMPLETED) continue;
   if (Routines.at(i)->parent_index < 0) continue;  // root thread
-//?   trace("schedule") << "AAA " << i; //? 1
+//?   trace("schedule") << "AAA " << i << end(); //? 1
   if (has_completed_parent(i)) {
-//?     trace("schedule") << "BBB " << i; //? 1
+//?     trace("schedule") << "BBB " << i << end(); //? 1
     Routines.at(i)->state = COMPLETED;
   }
 }
-//? trace("schedule") << "After cleanup"; //? 1
+//? trace("schedule") << "After cleanup" << end(); //? 1
 //? for (long long int i = 0; i < SIZE(Routines); ++i) { //? 1
-//?   trace("schedule") << i << ": " << Routines.at(i)->id << ' ' << Routines.at(i)->state << ' ' << Routines.at(i)->parent_index << ' ' << Routines.at(i)->state; //? 1
+//?   trace("schedule") << i << ": " << Routines.at(i)->id << ' ' << Routines.at(i)->state << ' ' << Routines.at(i)->parent_index << ' ' << Routines.at(i)->state << end(); //? 1
 //? } //? 1
 
 :(code)
 bool has_completed_parent(long long int routine_index) {
-//?   trace("schedule") << "CCC " << routine_index << '\n'; //? 2
+//?   trace("schedule") << "CCC " << routine_index << '\n' << end(); //? 2
   for (long long int j = routine_index; j >= 0; j = Routines.at(j)->parent_index) {
-//?     trace("schedule") << "DDD " << j << '\n'; //? 2
+//?     trace("schedule") << "DDD " << j << '\n' << end(); //? 2
     if (Routines.at(j)->state == COMPLETED)
       return true;
   }
diff --git a/039wait.cc b/039wait.cc
index cbfb4664..8a40dc8a 100644
--- a/039wait.cc
+++ b/039wait.cc
@@ -40,8 +40,8 @@ case WAIT_FOR_LOCATION: {
   Current_routine->state = WAITING;
   Current_routine->waiting_on_location = loc.value;
   Current_routine->old_value_of_waiting_location = Memory[loc.value];
-  trace(Primitive_recipe_depth, "run") << "waiting for location " << loc.value << " to change from " << Memory[loc.value];
-//?   trace("schedule") << Current_routine->id << ": waiting for location " << loc.value << " to change from " << Memory[loc.value]; //? 2
+  trace(Primitive_recipe_depth, "run") << "waiting for location " << loc.value << " to change from " << Memory[loc.value] << end();
+//?   trace("schedule") << Current_routine->id << ": waiting for location " << loc.value << " to change from " << Memory[loc.value] << end(); //? 2
   break;
 }
 
@@ -49,15 +49,15 @@ case WAIT_FOR_LOCATION: {
 
 :(before "End Scheduler State Transitions")
 for (long long int i = 0; i < SIZE(Routines); ++i) {
-//?   trace("schedule") << "wake up loop 1: routine " << Routines.at(i)->id << " has state " << Routines.at(i)->state; //? 1
+//?   trace("schedule") << "wake up loop 1: routine " << Routines.at(i)->id << " has state " << Routines.at(i)->state << end(); //? 1
   if (Routines.at(i)->state != WAITING) continue;
-//?   trace("schedule") << "waiting on location: " << Routines.at(i)->waiting_on_location; //? 1
+//?   trace("schedule") << "waiting on location: " << Routines.at(i)->waiting_on_location << end(); //? 1
 //?   if (Routines.at(i)->waiting_on_location) //? 2
 //?     trace("schedule") << "checking routine " << Routines.at(i)->id << " waiting on location " //? 2
 //?       << Routines.at(i)->waiting_on_location << ": " << Memory[Routines.at(i)->waiting_on_location] << " vs " << Routines.at(i)->old_value_of_waiting_location; //? 2
   if (Routines.at(i)->waiting_on_location &&
       Memory[Routines.at(i)->waiting_on_location] != Routines.at(i)->old_value_of_waiting_location) {
-    trace("schedule") << "waking up routine\n";
+    trace("schedule") << "waking up routine\n" << end();
     Routines.at(i)->state = RUNNING;
     Routines.at(i)->waiting_on_location = Routines.at(i)->old_value_of_waiting_location = 0;
   }
@@ -99,7 +99,7 @@ case WAIT_FOR_ROUTINE: {
   Current_routine->state = WAITING;
   assert(scalar(ingredients.at(0)));
   Current_routine->waiting_on_routine = ingredients.at(0).at(0);
-  trace(Primitive_recipe_depth, "run") << "waiting for routine " << ingredients.at(0).at(0);
+  trace(Primitive_recipe_depth, "run") << "waiting for routine " << ingredients.at(0).at(0) << end();
   break;
 }
 
@@ -114,7 +114,7 @@ for (long long int i = 0; i < SIZE(Routines); ++i) {
   assert(id != Routines.at(i)->id);
   for (long long int j = 0; j < SIZE(Routines); ++j) {
     if (Routines.at(j)->id == id && Routines.at(j)->state != RUNNING) {
-      trace("schedule") << "waking up routine " << Routines.at(i)->id;
+      trace("schedule") << "waking up routine " << Routines.at(i)->id << end();
       Routines.at(i)->state = RUNNING;
       Routines.at(i)->waiting_on_routine = 0;
     }
diff --git a/040brace.cc b/040brace.cc
index 0ed25262..29d3e024 100644
--- a/040brace.cc
+++ b/040brace.cc
@@ -45,16 +45,16 @@ void transform_braces(const recipe_ordinal r) {
   for (long long int index = 0; index < SIZE(Recipe[r].steps); ++index) {
     const instruction& inst = Recipe[r].steps.at(index);
     if (inst.label == "{") {
-      trace("brace") << r << ": push (open, " << index << ")";
+      trace("brace") << r << ": push (open, " << index << ")" << end();
       braces.push_back(pair<int,long long int>(OPEN, index));
     }
     if (inst.label == "}") {
-      trace("brace") << "push (close, " << index << ")";
+      trace("brace") << "push (close, " << index << ")" << end();
       braces.push_back(pair<int,long long int>(CLOSE, index));
     }
   }
   stack</*step*/long long int> open_braces;
-  trace("after-brace") << "recipe " << Recipe[r].name;
+  trace("after-brace") << "recipe " << Recipe[r].name << end();
   for (long long int index = 0; index < SIZE(Recipe[r].steps); ++index) {
     instruction& inst = Recipe[r].steps.at(index);
     if (inst.label == "{") {
@@ -72,7 +72,7 @@ void transform_braces(const recipe_ordinal r) {
          && inst.operation != Recipe_ordinal["break"]
          && inst.operation != Recipe_ordinal["break-if"]
          && inst.operation != Recipe_ordinal["break-unless"]) {
-      trace("after-brace") << inst.name << " ...";
+      trace("after-brace") << inst.name << " ..." << end();
       continue;
     }
     // update instruction operation
@@ -86,14 +86,14 @@ void transform_braces(const recipe_ordinal r) {
     if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) {
       // conditional branches check arg 1
       if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) {
-        trace("after-brace") << "jump " << inst.ingredients.at(1).name << ":offset";
+        trace("after-brace") << "jump " << inst.ingredients.at(1).name << ":offset" << end();
         continue;
       }
     }
     else {
       // unconditional branches check arg 0
       if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) {
-        trace("after-brace") << "jump " << inst.ingredients.at(0).name << ":offset";
+        trace("after-brace") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
         continue;
       }
     }
@@ -102,7 +102,7 @@ void transform_braces(const recipe_ordinal r) {
     target.types.push_back(Type_ordinal["offset"]);
     target.set_value(0);
     if (open_braces.empty())
-      raise << inst.name << " wasn't inside {}\n";
+      raise << inst.name << " wasn't inside {}\n" << end();
     else if (inst.name.find("loop") != string::npos)
       target.set_value(open_braces.top()-index);
     else  // break instruction
@@ -110,11 +110,11 @@ void transform_braces(const recipe_ordinal r) {
     inst.ingredients.push_back(target);
     // log computed target
     if (inst.name.find("-if") != string::npos)
-      trace("after-brace") << "jump-if " << inst.ingredients.at(0).name << ", " << target.value << ":offset";
+      trace("after-brace") << "jump-if " << inst.ingredients.at(0).name << ", " << target.value << ":offset" << end();
     else if (inst.name.find("-unless") != string::npos)
-      trace("after-brace") << "jump-unless " << inst.ingredients.at(0).name << ", " << target.value << ":offset";
+      trace("after-brace") << "jump-unless " << inst.ingredients.at(0).name << ", " << target.value << ":offset" << end();
     else
-      trace("after-brace") << "jump " << target.value << ":offset";
+      trace("after-brace") << "jump " << target.value << ":offset" << end();
   }
 }
 
@@ -127,7 +127,7 @@ long long int matching_brace(long long int index, const list<pair<int, long long
     stacksize += (p->first ? 1 : -1);
     if (stacksize == 0) return p->second;
   }
-  raise << Recipe[r].name << ": unbalanced '{'\n";
+  raise << Recipe[r].name << ": unbalanced '{'\n" << end();
   return SIZE(Recipe[r].steps);  // exit current routine
 }
 
diff --git a/041jump_label.cc b/041jump_label.cc
index dca2e37a..2527e813 100644
--- a/041jump_label.cc
+++ b/041jump_label.cc
@@ -54,7 +54,7 @@ void replace_offset(reagent& x, /*const*/ map<string, long long int>& offset, co
   if (is_integer(x.name)) return;  // non-labels will be handled like other number operands
 //?   cerr << "DDD " << x.to_string() << '\n'; //? 1
   if (offset.find(x.name) == offset.end())
-    raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n';
+    raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n' << end();
   x.set_value(offset[x.name]-current_offset);
 }
 
diff --git a/042name.cc b/042name.cc
index ed322d54..283bcbd7 100644
--- a/042name.cc
+++ b/042name.cc
@@ -45,7 +45,7 @@ void transform_names(const recipe_ordinal r) {
       if (is_named_location(inst.ingredients.at(in))) names_used = true;
       if (disqualified(inst.ingredients.at(in), inst)) continue;
       if (!already_transformed(inst.ingredients.at(in), names)) {
-        raise << "use before set: " << inst.ingredients.at(in).name << " in " << Recipe[r].name << '\n';
+        raise << "use before set: " << inst.ingredients.at(in).name << " in " << Recipe[r].name << '\n' << end();
       }
       inst.ingredients.at(in).set_value(lookup_name(inst.ingredients.at(in), r));
     }
@@ -55,7 +55,7 @@ void transform_names(const recipe_ordinal r) {
       if (is_named_location(inst.products.at(out))) names_used = true;
       if (disqualified(inst.products.at(out), inst)) continue;
       if (names.find(inst.products.at(out).name) == names.end()) {
-        trace("name") << "assign " << inst.products.at(out).name << " " << curr_idx;
+        trace("name") << "assign " << inst.products.at(out).name << " " << curr_idx << end();
         names[inst.products.at(out).name] = curr_idx;
         curr_idx += size_of(inst.products.at(out));
       }
@@ -63,7 +63,7 @@ void transform_names(const recipe_ordinal r) {
     }
   }
   if (names_used && numeric_locations_used && r != Recipe_ordinal["interactive"])
-    raise << "mixing variable names and numeric addresses in " << Recipe[r].name << '\n';
+    raise << "mixing variable names and numeric addresses in " << Recipe[r].name << '\n' << end();
 }
 
 void check_metadata(map<string, vector<type_ordinal> >& metadata, const reagent& x, const recipe_ordinal r) {
@@ -75,13 +75,13 @@ void check_metadata(map<string, vector<type_ordinal> >& metadata, const reagent&
   if (metadata.find(x.name) == metadata.end())
     metadata[x.name] = x.types;
   if (metadata[x.name] != x.types)
-    raise << x.name << " used with multiple types in " << Recipe[r].name << '\n';
+    raise << x.name << " used with multiple types in " << Recipe[r].name << '\n' << end();
 }
 
 bool disqualified(/*mutable*/ reagent& x, const instruction& inst) {
 //?   cerr << x.to_string() << '\n'; //? 1
   if (x.types.empty()) {
-    raise << "missing type in '" << inst.to_string() << "'\n";
+    raise << "missing type in '" << inst.to_string() << "'\n" << end();
     return true;
   }
   if (is_raw(x)) return true;
@@ -104,7 +104,7 @@ type_ordinal skip_addresses(const vector<type_ordinal>& types) {
   for (long long int i = 0; i < SIZE(types); ++i) {
     if (types.at(i) != Type_ordinal["address"]) return types.at(i);
   }
-  raise << "expected a container" << '\n' << die();
+  raise << "expected a container" << '\n' << end();
   return -1;
 }
 
@@ -114,7 +114,7 @@ int find_element_name(const type_ordinal t, const string& name) {
   for (long long int i = 0; i < SIZE(container.element_names); ++i) {
     if (container.element_names.at(i) == name) return i;
   }
-  raise << "unknown element " << name << " in container " << Type[t].name << '\n';
+  raise << "unknown element " << name << " in container " << Type[t].name << '\n' << end();
   return -1;
 }
 
@@ -223,12 +223,12 @@ if (inst.operation == Recipe_ordinal["get"]
   assert(SIZE(inst.ingredients) >= 2);
 //?   cout << inst.ingredients.at(1).to_string() << '\n'; //? 1
   if (!is_literal(inst.ingredients.at(1)))
-    raise << Recipe[r].name << ": expected ingredient 1 of " << (inst.operation == Recipe_ordinal["get"] ? "'get'" : "'get-address'") << " to have type 'offset'; got " << inst.ingredients.at(1).original_string << '\n' << die();
+    raise << Recipe[r].name << ": expected ingredient 1 of " << (inst.operation == Recipe_ordinal["get"] ? "'get'" : "'get-address'") << " to have type 'offset'; got " << inst.ingredients.at(1).original_string << '\n' << end();
   if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) {
     // since first non-address in base type must be a container, we don't have to canonize
     type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types);
     inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name));
-    trace("name") << "element " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " is at offset " << inst.ingredients.at(1).value;
+    trace("name") << "element " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " is at offset " << inst.ingredients.at(1).value << end();
   }
 }
 
@@ -265,6 +265,6 @@ if (inst.operation == Recipe_ordinal["maybe-convert"]) {
     // since first non-address in base type must be an exclusive container, we don't have to canonize
     type_ordinal base_type = skip_addresses(inst.ingredients.at(0).types);
     inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name));
-    trace("name") << "variant " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " has tag " << inst.ingredients.at(1).value;
+    trace("name") << "variant " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " has tag " << inst.ingredients.at(1).value << end();
   }
 }
diff --git a/043new.cc b/043new.cc
index 672b4bb1..08c68eec 100644
--- a/043new.cc
+++ b/043new.cc
@@ -23,7 +23,7 @@ long long int alloc, alloc_max;
 alloc = Memory_allocated_until;
 Memory_allocated_until += Initial_memory_per_routine;
 alloc_max = Memory_allocated_until;
-trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << alloc << " to " << alloc_max;
+trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << alloc << " to " << alloc_max << end();
 
 //:: First handle 'type' operands.
 
@@ -36,14 +36,14 @@ if (inst.operation == Recipe_ordinal["new"]) {
   // first arg must be of type 'type'
   assert(SIZE(inst.ingredients) >= 1);
   if (!is_literal(inst.ingredients.at(0)))
-    raise << "expected literal, got " << inst.ingredients.at(0).original_string << '\n' << die();
+    raise << "expected literal, got " << inst.ingredients.at(0).original_string << '\n' << end();
   if (inst.ingredients.at(0).properties.at(0).second.at(0) != "type")
-    raise << "tried to allocate non-type " << inst.ingredients.at(0).to_string() << " in recipe " << Recipe[r].name << '\n' << die();
+    raise << "tried to allocate non-type " << inst.ingredients.at(0).to_string() << " in recipe " << Recipe[r].name << '\n' << end();
   if (Type_ordinal.find(inst.ingredients.at(0).name) == Type_ordinal.end())
-    raise << "unknown type " << inst.ingredients.at(0).name << " in recipe " << Recipe[r].name << '\n' << die();
+    raise << "unknown type " << inst.ingredients.at(0).name << " in recipe " << Recipe[r].name << '\n' << end();
 //?   cerr << "type " << inst.ingredients.at(0).name << " => " << Type_ordinal[inst.ingredients.at(0).name] << '\n'; //? 1
   inst.ingredients.at(0).set_value(Type_ordinal[inst.ingredients.at(0).name]);
-  trace(Primitive_recipe_depth, "new") << inst.ingredients.at(0).name << " -> " << inst.ingredients.at(0).name;
+  trace(Primitive_recipe_depth, "new") << inst.ingredients.at(0).name << " -> " << inst.ingredients.at(0).name << end();
   end_new_transform:;
 }
 
@@ -63,11 +63,11 @@ case NEW: {
     vector<type_ordinal> type;
     assert(is_literal(current_instruction().ingredients.at(0)));
     type.push_back(current_instruction().ingredients.at(0).value);
-//?     trace(Primitive_recipe_depth, "mem") << "type " << current_instruction().ingredients.at(0).to_string() << ' ' << type.size() << ' ' << type.back() << " has size " << size_of(type); //? 1
+//?     trace(Primitive_recipe_depth, "mem") << "type " << current_instruction().ingredients.at(0).to_string() << ' ' << type.size() << ' ' << type.back() << " has size " << size_of(type) << end(); //? 1
     if (SIZE(current_instruction().ingredients) > 1) {
       // array
       array_length = ingredients.at(1).at(0);
-      trace(Primitive_recipe_depth, "mem") << "array size is " << array_length;
+      trace(Primitive_recipe_depth, "mem") << "array size is " << array_length << end();
       size = array_length*size_of(type) + /*space for length*/1;
     }
     else {
@@ -81,8 +81,8 @@ case NEW: {
   // really crappy at the moment
   ensure_space(size);
   const long long int result = Current_routine->alloc;
-  trace(Primitive_recipe_depth, "mem") << "new alloc: " << result;
-//?   trace(Primitive_recipe_depth, "mem") << "size: " << size << " locations"; //? 1
+  trace(Primitive_recipe_depth, "mem") << "new alloc: " << result << end();
+//?   trace(Primitive_recipe_depth, "mem") << "size: " << size << " locations" << end(); //? 1
   // save result
   products.resize(1);
   products.at(0).push_back(result);
@@ -121,7 +121,7 @@ void ensure_space(long long int size) {
     Current_routine->alloc = Memory_allocated_until;
     Memory_allocated_until += Initial_memory_per_routine;
     Current_routine->alloc_max = Memory_allocated_until;
-    trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max;
+    trace(Primitive_recipe_depth, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end();
   }
 }
 
@@ -209,13 +209,13 @@ Recipe_ordinal["abandon"] = ABANDON;
 :(before "End Primitive Recipe Implementations")
 case ABANDON: {
   if (!scalar(ingredients.at(0))) {
-    raise << "abandon's ingredient should be scalar\n";
+    raise << "abandon's ingredient should be scalar\n" << end();
     break;
   }
   long long int address = ingredients.at(0).at(0);
   reagent types = canonize(current_instruction().ingredients.at(0));
   if (types.types.at(0) != Type_ordinal["address"]) {
-    raise << "abandon's ingredient should be an address\n";
+    raise << "abandon's ingredient should be an address\n" << end();
     break;
   }
   reagent target_type = deref(types);
@@ -240,9 +240,12 @@ void abandon(long long int address, long long int size) {
 if (Free_list[size]) {
   long long int result = Free_list[size];
   Free_list[size] = Memory[result];
-  for (long long int curr = result+1; curr < result+size; ++curr)
-    if (Memory[curr] != 0)
-      raise << current_recipe_name() << ": memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << die();
+  for (long long int curr = result+1; curr < result+size; ++curr) {
+    if (Memory[curr] != 0) {
+      raise << current_recipe_name() << ": memory in free list was not zeroed out: " << curr << '/' << result << "; somebody wrote to us after free!!!\n" << end();
+      break;  // always fatal
+    }
+  }
   if (SIZE(current_instruction().ingredients) > 1)
     Memory[result] = array_length;
   else
diff --git a/044space.cc b/044space.cc
index 557ee04c..17ca4d2a 100644
--- a/044space.cc
+++ b/044space.cc
@@ -51,8 +51,10 @@ reagent r = absolutize(x);
 reagent absolutize(reagent x) {
 //?   cout << "absolutize " << x.to_string() << '\n'; //? 4
   if (is_raw(x) || is_dummy(x)) return x;
-  if (!x.initialized)
-    raise << current_instruction().to_string() << ": reagent not initialized: " << x.original_string << die();
+  if (!x.initialized) {
+    raise << current_instruction().to_string() << ": reagent not initialized: " << x.original_string << end();
+    return x;
+  }
   reagent r = x;
   r.set_value(address(r.value, space_base(r)));
   r.properties.push_back(pair<string, vector<string> >("raw", vector<string>()));
@@ -131,13 +133,13 @@ if (curr.name == "new-default-space") {
     vector<double> result;
     result.push_back(Name[Recipe_ordinal[current_recipe_name()]][""]);
     if (result.back() == 0)
-      raise << "no space allocated for default-space in recipe " << current_recipe_name() << "; are you using names\n";
+      raise << "no space allocated for default-space in recipe " << current_recipe_name() << "; are you using names\n" << end();
     return result;
   }
 :(after "void write_memory(reagent x, vector<double> data)")
   if (x.name == "number-of-locals") {
     assert(scalar(data));
-    raise << "can't write to special variable number-of-locals\n";
+    raise << "can't write to special variable number-of-locals\n" << end();
     return;
   }
 
@@ -186,11 +188,11 @@ void try_reclaim_locals() {
 void rewrite_default_space_instruction(instruction& curr) {
   curr.operation = Recipe_ordinal["new"];
   if (!curr.ingredients.empty())
-    raise << "new-default-space can't take any ingredients\n";
+    raise << "new-default-space can't take any ingredients\n" << end();
   curr.ingredients.push_back(reagent("location:type"));
   curr.ingredients.push_back(reagent("number-of-locals:literal"));
   if (!curr.products.empty())
-    raise << "new-default-space can't take any results\n";
+    raise << "new-default-space can't take any results\n" << end();
   curr.products.push_back(reagent("default-space:address:array:location"));
 }
 
@@ -206,7 +208,7 @@ long long int address(long long int offset, long long int base) {
 //?   cout << base << '\n'; //? 2
   if (offset >= static_cast<long long int>(Memory[base])) {
     // todo: test
-    raise << "location " << offset << " is out of bounds " << Memory[base] << " at " << base << '\n';
+    raise << "location " << offset << " is out of bounds " << Memory[base] << " at " << base << '\n' << end();
   }
   return base+1 + offset;
 }
diff --git a/046closure_name.cc b/046closure_name.cc
index a9a7575c..bc7460f5 100644
--- a/046closure_name.cc
+++ b/046closure_name.cc
@@ -52,20 +52,22 @@ void collect_surrounding_spaces(const recipe_ordinal r) {
           || inst.products.at(j).types.at(0) != Type_ordinal["address"]
           || inst.products.at(j).types.at(1) != Type_ordinal["array"]
           || inst.products.at(j).types.at(2) != Type_ordinal["location"]) {
-        raise << "slot 0 should always have type address:array:location, but is " << inst.products.at(j).to_string() << '\n';
+        raise << "slot 0 should always have type address:array:location, but is " << inst.products.at(j).to_string() << '\n' << end();
         continue;
       }
       vector<string> s = property(inst.products.at(j), "names");
-      if (s.empty())
-        raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << die();
-      if (SIZE(s) > 1) raise << "slot 0 should have a single value in /names, got " << inst.products.at(j).to_string() << '\n';
+      if (s.empty()) {
+        raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << end();
+        continue;
+      }
+      if (SIZE(s) > 1) raise << "slot 0 should have a single value in /names, got " << inst.products.at(j).to_string() << '\n' << end();
       string surrounding_recipe_name = s.at(0);
       if (Surrounding_space.find(r) != Surrounding_space.end()
           && Surrounding_space[r] != Recipe_ordinal[surrounding_recipe_name]) {
-        raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n';
+        raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n' << end();
         continue;
       }
-      trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name;
+      trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name << end();
       Surrounding_space[r] = Recipe_ordinal[surrounding_recipe_name];
     }
   }
@@ -79,11 +81,11 @@ long long int lookup_name(const reagent& x, const recipe_ordinal default_recipe)
 //?   cout << "AAA " << default_recipe << " " << Recipe[default_recipe].name << '\n'; //? 2
 //?   cout << "AAA " << x.to_string() << '\n'; //? 1
   if (!has_property(x, "space")) {
-    if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << die();
+    if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << end();
     return Name[default_recipe][x.name];
   }
   vector<string> p = property(x, "space");
-  if (SIZE(p) != 1) raise << "/space property should have exactly one (non-negative integer) value\n";
+  if (SIZE(p) != 1) raise << "/space property should have exactly one (non-negative integer) value\n" << end();
   long long int n = to_integer(p.at(0));
   assert(n >= 0);
   recipe_ordinal surrounding_recipe = lookup_surrounding_recipe(default_recipe, n);
@@ -97,11 +99,11 @@ long long int lookup_name(const reagent& x, const recipe_ordinal default_recipe)
 long long int lookup_name(const reagent& x, const recipe_ordinal r, set<recipe_ordinal>& done, vector<recipe_ordinal>& path) {
   if (!Name[r].empty()) return Name[r][x.name];
   if (done.find(r) != done.end()) {
-    raise << "can't compute address of " << x.to_string() << " because ";
+    raise << "can't compute address of " << x.to_string() << " because " << end();
     for (long long int i = 1; i < SIZE(path); ++i) {
-      raise << path.at(i-1) << " requires computing names of " << path.at(i) << '\n';
+      raise << path.at(i-1) << " requires computing names of " << path.at(i) << '\n' << end();
     }
-    raise << path.at(SIZE(path)-1) << " requires computing names of " << r << "..ad infinitum\n" << die();
+    raise << path.at(SIZE(path)-1) << " requires computing names of " << r << "..ad infinitum\n" << end();
     return 0;
   }
   done.insert(r);
@@ -114,7 +116,7 @@ long long int lookup_name(const reagent& x, const recipe_ordinal r, set<recipe_o
 recipe_ordinal lookup_surrounding_recipe(const recipe_ordinal r, long long int n) {
   if (n == 0) return r;
   if (Surrounding_space.find(r) == Surrounding_space.end()) {
-    raise << "don't know surrounding recipe of " << Recipe[r].name << '\n';
+    raise << "don't know surrounding recipe of " << Recipe[r].name << '\n' << end();
     return 0;
   }
   assert(Surrounding_space[r]);
diff --git a/047global.cc b/047global.cc
index e3dbe356..6ce4b78f 100644
--- a/047global.cc
+++ b/047global.cc
@@ -33,7 +33,7 @@ global_space = 0;
   if (x.name == "global-space") {
     assert(scalar(data));
     if (Current_routine->global_space)
-      raise << "routine already has a global-space; you can't over-write your globals";
+      raise << "routine already has a global-space; you can't over-write your globals" << end();
     Current_routine->global_space = data.at(0);
     return;
   }
@@ -42,7 +42,7 @@ global_space = 0;
 :(after "long long int space_base(const reagent& x)")
   if (is_global(x)) {
     if (!Current_routine->global_space)
-      raise << "routine has no global space\n" << die();
+      raise << "routine has no global space\n" << end();
     return Current_routine->global_space;
   }
 
diff --git a/049continuation.cc b/049continuation.cc
index c292f44b..e625e7b7 100644
--- a/049continuation.cc
+++ b/049continuation.cc
@@ -30,7 +30,7 @@ case CURRENT_CONTINUATION: {
   products.resize(1);
   products.at(0).push_back(Next_continuation_id);
   ++Next_continuation_id;
-  trace("current-continuation") << "new continuation " << Next_continuation_id;
+  trace("current-continuation") << "new continuation " << Next_continuation_id << end();
   break;
 }
 
diff --git a/050scenario.cc b/050scenario.cc
index 8f3ec77e..358c767c 100644
--- a/050scenario.cc
+++ b/050scenario.cc
@@ -257,16 +257,16 @@ void check_memory(const string& s) {
     skip_whitespace_and_comments(in);
     int value = 0;  in >> value;
     if (locations_checked.find(address) != locations_checked.end())
-      raise << "duplicate expectation for location " << address << '\n';
-    trace(Primitive_recipe_depth, "run") << "checking location " << address;
+      raise << "duplicate expectation for location " << address << '\n' << end();
+    trace(Primitive_recipe_depth, "run") << "checking location " << address << end();
     if (Memory[address] != value) {
       if (Current_scenario && !Scenario_testing_scenario) {
         // genuine test in a mu file
-        raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n';
+        raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n' << end();
       }
       else {
         // just testing scenario support
-        raise << "expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n';
+        raise << "expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n' << end();
       }
       if (!Scenario_testing_scenario) {
         Passed = false;
@@ -294,16 +294,16 @@ void check_type(const string& lhs, istream& in) {
     check_string(address, literal);
     return;
   }
-  raise << "don't know how to check memory for " << lhs << '\n';
+  raise << "don't know how to check memory for " << lhs << '\n' << end();
 }
 
 void check_string(long long int address, const string& literal) {
-  trace(Primitive_recipe_depth, "run") << "checking string length at " << address;
+  trace(Primitive_recipe_depth, "run") << "checking string length at " << address << end();
   if (Memory[address] != SIZE(literal)) {
     if (Current_scenario && !Scenario_testing_scenario)
-      raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << Memory[address] << '\n';
+      raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << Memory[address] << '\n' << end();
     else
-      raise << "expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << Memory[address] << '\n';
+      raise << "expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << Memory[address] << '\n' << end();
     if (!Scenario_testing_scenario) {
       Passed = false;
       ++Num_failures;
@@ -312,15 +312,15 @@ void check_string(long long int address, const string& literal) {
   }
   ++address;  // now skip length
   for (long long int i = 0; i < SIZE(literal); ++i) {
-    trace(Primitive_recipe_depth, "run") << "checking location " << address+i;
+    trace(Primitive_recipe_depth, "run") << "checking location " << address+i << end();
     if (Memory[address+i] != literal.at(i)) {
       if (Current_scenario && !Scenario_testing_scenario) {
         // genuine test in a mu file
-        raise << "\nF - " << Current_scenario->name << ": expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n';
+        raise << "\nF - " << Current_scenario->name << ": expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n' << end();
       }
       else {
         // just testing scenario support
-        raise << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n';
+        raise << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n' << end();
       }
       if (!Scenario_testing_scenario) {
         Passed = false;
@@ -421,7 +421,7 @@ bool check_trace(const string& expected) {
   }
 
   raise << "missing [" << expected_lines.at(curr_expected_line).contents << "] "
-        << "in trace layer " << expected_lines.at(curr_expected_line).label << '\n';
+        << "in trace layer " << expected_lines.at(curr_expected_line).label << '\n' << end();
   Passed = false;
   return false;
 }
@@ -502,7 +502,7 @@ bool check_trace_missing(const string& in) {
   vector<trace_line> lines = parse_trace(in);
   for (long long int i = 0; i < SIZE(lines); ++i) {
     if (trace_count(lines.at(i).label, lines.at(i).contents) != 0) {
-      raise << "unexpected [" << lines.at(i).contents << "] in trace layer " << lines.at(i).label << '\n';
+      raise << "unexpected [" << lines.at(i).contents << "] in trace layer " << lines.at(i).label << '\n' << end();
       Passed = false;
       return false;
     }
diff --git a/072scenario_screen.cc b/072scenario_screen.cc
index 8e3f0d9c..b3642f1a 100644
--- a/072scenario_screen.cc
+++ b/072scenario_screen.cc
@@ -236,11 +236,11 @@ void check_screen(const string& expected_contents, const int color) {
         // contents match but color is off
         if (Current_scenario && !Scenario_testing_scenario) {
           // genuine test in a mu file
-          raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ", address " << addr << ", value " << Memory[addr] << ") to be in color " << color << " instead of " << Memory[addr+cell_color_offset] << "\n";
+          raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ", address " << addr << ", value " << Memory[addr] << ") to be in color " << color << " instead of " << Memory[addr+cell_color_offset] << "\n" << end();
         }
         else {
           // just testing check_screen
-          raise << "expected screen location (" << row << ", " << column << ") to be in color " << color << " instead of " << Memory[addr+cell_color_offset] << '\n';
+          raise << "expected screen location (" << row << ", " << column << ") to be in color " << color << " instead of " << Memory[addr+cell_color_offset] << '\n' << end();
         }
         if (!Scenario_testing_scenario) {
           Passed = false;
@@ -265,12 +265,12 @@ void check_screen(const string& expected_contents, const int color) {
 
       if (Current_scenario && !Scenario_testing_scenario) {
         // genuine test in a mu file
-        raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << " instead of " << Memory[addr] << actual_pretty << '\n';
+        raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << " instead of " << Memory[addr] << actual_pretty << '\n' << end();
         dump_screen();
       }
       else {
         // just testing check_screen
-        raise << "expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << " instead of " << Memory[addr] << actual_pretty << '\n';
+        raise << "expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << " instead of " << Memory[addr] << actual_pretty << '\n' << end();
       }
       if (!Scenario_testing_scenario) {
         Passed = false;
@@ -291,7 +291,7 @@ bool raw_string_stream::at_end() const {
 //?   cerr << buf << "$\n"; //? 1
   if (index >= max) return true;
   if (tb_utf8_char_length(buf[index]) > max-index) {
-    raise << "unicode string seems corrupted at index "<< index << " character " << static_cast<int>(buf[index]) << '\n';
+    raise << "unicode string seems corrupted at index "<< index << " character " << static_cast<int>(buf[index]) << '\n' << end();
     return true;
   }
   return false;
diff --git a/075scenario_console.cc b/075scenario_console.cc
index c5946617..bf58c46c 100644
--- a/075scenario_console.cc
+++ b/075scenario_console.cc
@@ -167,8 +167,10 @@ Recipe_ordinal["replace-in-console"] = REPLACE_IN_CONSOLE;
 case REPLACE_IN_CONSOLE: {
   assert(scalar(ingredients.at(0)));
 //?   cerr << "console: " << Memory[CONSOLE] << '\n'; //? 1
-  if (!Memory[CONSOLE])
-    raise << "console not initialized\n" << die();
+  if (!Memory[CONSOLE]) {
+    raise << "console not initialized\n" << end();
+    break;
+  }
   long long int console_data = Memory[Memory[CONSOLE]+1];
 //?   cerr << "console data starts at " << console_data << '\n'; //? 1
   long long int size = Memory[console_data];  // array size
diff --git a/082persist.cc b/082persist.cc
index b4817dc0..9d1fa8b2 100644
--- a/082persist.cc
+++ b/082persist.cc
@@ -9,7 +9,7 @@ Recipe_ordinal["restore"] = RESTORE;
 :(before "End Primitive Recipe Implementations")
 case RESTORE: {
   if (!scalar(ingredients.at(0)))
-    raise << "restore: illegal operand " << current_instruction().ingredients.at(0).to_string() << '\n';
+    raise << "restore: illegal operand " << current_instruction().ingredients.at(0).to_string() << '\n' << end();
   products.resize(1);
   string filename = current_instruction().ingredients.at(0).name;
   if (!is_literal(current_instruction().ingredients.at(0)))
@@ -48,13 +48,13 @@ Recipe_ordinal["save"] = SAVE;
 :(before "End Primitive Recipe Implementations")
 case SAVE: {
   if (!scalar(ingredients.at(0)))
-    raise << "save: illegal operand 0 " << current_instruction().ingredients.at(0).to_string() << '\n';
+    raise << "save: illegal operand 0 " << current_instruction().ingredients.at(0).to_string() << '\n' << end();
   string filename = current_instruction().ingredients.at(0).name;
   if (!is_literal(current_instruction().ingredients.at(0)))
     filename = to_string(ingredients.at(0).at(0));
   ofstream fout(("lesson/"+filename).c_str());
   if (!scalar(ingredients.at(1)))
-    raise << "save: illegal operand 1 " << current_instruction().ingredients.at(1).to_string() << '\n';
+    raise << "save: illegal operand 1 " << current_instruction().ingredients.at(1).to_string() << '\n' << end();
   string contents = read_mu_string(ingredients.at(1).at(0));
   fout << contents;
   fout.close();
@@ -62,7 +62,7 @@ case SAVE: {
   // bug in git: git diff -q messes up --exit-code
   int status = system("cd lesson; git add .; git diff HEAD --exit-code >/dev/null || git commit -a -m . >/dev/null");
   if (status != 0)
-    raise << "error in commit: contents " << contents << '\n';
+    raise << "error in commit: contents " << contents << '\n' << end();
   break;
 }