about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--027call_ingredient.cc2
-rw-r--r--076continuation.cc24
-rw-r--r--exception.mu63
3 files changed, 85 insertions, 4 deletions
diff --git a/027call_ingredient.cc b/027call_ingredient.cc
index 99d20b17..06a18b10 100644
--- a/027call_ingredient.cc
+++ b/027call_ingredient.cc
@@ -63,7 +63,7 @@ case NEXT_INGREDIENT: {
     }
     else if (!types_coercible(product,
                               current_call().ingredients.at(current_call().next_ingredient_to_process))) {
-      raise << maybe(current_recipe_name()) << "wrong type for ingredient '" << product.original_string << "'\n" << end();
+      raise << maybe(current_recipe_name()) << "wrong type for ingredient '" << product.original_string << "': " << current_call().ingredients.at(current_call().next_ingredient_to_process).original_string << '\n' << end();
       // End next-ingredient Type Mismatch Error
     }
     products.push_back(
diff --git a/076continuation.cc b/076continuation.cc
index 8a7b3b87..c08e741a 100644
--- a/076continuation.cc
+++ b/076continuation.cc
@@ -34,6 +34,11 @@
 //:  * Conversely, you can pass ingredients to a continuation when calling it,
 //:    to make it available to products of 'return-continuation-until-mark'.
 //:    See continuation5.mu.
+//:  * There can be multiple continuation marks on the stack at once;
+//:    'call-with-continuation-mark' and 'return-continuation-until-mark' both
+//:    need to pass in a tag to coordinate on the correct mark. This allows us
+//:    to save multiple continuations for different purposes (say if one is
+//:    for exceptions) with overlapping stack frames. See exception.mu.
 //:
 //: Inspired by James and Sabry, "Yield: Mainstream delimited continuations",
 //: Workshop on the Theory and Practice of Delimited Continuations, 2011.
@@ -132,15 +137,28 @@ case CALL_WITH_CONTINUATION_MARK: {
     trace("trace") << "delimited continuation; incrementing callstack depth to " << Trace_stream->callstack_depth << end();
     assert(Trace_stream->callstack_depth < 9000);  // 9998-101 plus cushion
   }
-  const instruction& caller_instruction = current_instruction();
+  instruction/*copy*/ caller_instruction = current_instruction();
   Current_routine->calls.front().continuation_mark_tag = current_instruction().ingredients.at(0).value;
   Current_routine->calls.push_front(call(Recipe_ordinal[current_instruction().ingredients.at(1).name]));
-  ingredients.erase(ingredients.begin());  // drop the mark
-  ingredients.erase(ingredients.begin());  // drop the callee
+  // drop the mark
+  caller_instruction.ingredients.erase(caller_instruction.ingredients.begin());
+  ingredients.erase(ingredients.begin());
+  // drop the callee
+  caller_instruction.ingredients.erase(caller_instruction.ingredients.begin());
+  ingredients.erase(ingredients.begin());
   finish_call_housekeeping(caller_instruction, ingredients);
   continue;
 }
 
+:(scenario next_ingredient_inside_continuation)
+recipe main [
+  call-with-continuation-mark 233/mark, f, 1/true
+]
+recipe f [
+  10:bool <- next-input
+]
++mem: storing 1 in location 10
+
 //: save the slice of current call stack until the 'call-with-continuation-mark'
 //: call, and return it as the result.
 //: todo: implement delimited continuations in Mu's memory
diff --git a/exception.mu b/exception.mu
new file mode 100644
index 00000000..73c7ca78
--- /dev/null
+++ b/exception.mu
@@ -0,0 +1,63 @@
+# Example program showing exceptions built out of delimited continuations.
+
+# Since Mu is statically typed, we can't build an all-purpose higher-order
+# function called 'try'; it wouldn't know how many arguments the function
+# passed to it needs to take, what their types are, etc. Instead, until Mu
+# gets macros we'll directly use the continuation primitives.
+
+def main [
+  local-scope
+  no-exception:bool <- copy 0/false
+  foo no-exception
+  raise-exception:bool <- copy 1/true
+  foo raise-exception
+]
+
+# example showing exception handling
+def foo raise-exception?:bool [
+  local-scope
+  load-inputs
+  # To run an instruction of the form:
+  #   try f ...
+  # write this:
+  #   call-with-continuation-mark 999/exception-tag, f, ...
+  # By convention we reserve tag 999 for exceptions.
+  #
+  # 'f' above may terminate at either a regular 'return' or a 'return-with-continuation-mark'.
+  # We never re-call the continuation returned in the latter case;
+  # its existence merely signals that an exception was raised.
+  # So just treat it as a boolean.
+  # The other inputs and outputs to 'call-with-continuation-mark' depend on
+  # the function it is called with.
+  exception-raised?:bool, err:text, result:num <- call-with-continuation-mark 999/exception-tag, f, raise-exception?
+  {
+    break-if exception-raised?
+    $print [normal exit; result ] result 10/newline
+  }
+  {
+    break-unless exception-raised?
+    $print [error caught: ] err 10/newline
+  }
+]
+
+# A callee function that can raise an exception has some weird constraints at
+# the moment.
+#
+# The caller's 'call-with-continuation-mark' instruction may return with
+# either a regular 'return' or a 'return-continuation-until-mark'.
+# To handle both cases, regular 'return' instructions in the callee must
+# prepend an extra 0 result, in place of the continuation that may have been
+# returned.
+# This change to number of outputs violates our type system, so the call has
+# to be dynamically typed. The callee can't have a header.
+def f [
+  local-scope
+  raise-exception?:bool <- next-input
+  {
+    break-unless raise-exception?
+    # throw/raise: 2 results + implicit continuation (ignoring the continuation tag)
+    return-continuation-until-mark 999/exception-tag, [error will robinson!], 0/unused
+  }
+  # normal return: 3 results including 0 continuation placeholder at start
+  return 0/continuation-placeholder, 0/no-error, 34/regular-result
+]