diff options
-rw-r--r-- | 027call_ingredient.cc | 2 | ||||
-rw-r--r-- | 076continuation.cc | 24 | ||||
-rw-r--r-- | exception.mu | 63 |
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 +] |