diff options
-rw-r--r-- | 034call.cc | 7 | ||||
-rw-r--r-- | 035call_ingredient.cc | 4 | ||||
-rw-r--r-- | 037recipe.cc | 52 | ||||
-rw-r--r-- | 053continuation.cc | 292 | ||||
-rw-r--r-- | callcc.mu | 17 | ||||
-rw-r--r-- | counters.mu | 16 | ||||
-rw-r--r-- | mu.vim | 1 |
7 files changed, 9 insertions, 380 deletions
diff --git a/034call.cc b/034call.cc index b799b594..ee554ab6 100644 --- a/034call.cc +++ b/034call.cc @@ -90,7 +90,6 @@ if (Recipe.find(inst.operation) == Recipe.end()) { } :(replace{} "default:" following "End Primitive Recipe Implementations") default: { - const instruction& call_instruction = current_instruction(); if (Recipe.find(current_instruction().operation) == Recipe.end()) { // duplicate from Checks // stop running this instruction immediately ++current_step_index(); @@ -103,12 +102,8 @@ default: { assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion } Current_routine->calls.push_front(call(current_instruction().operation)); - finish_call_housekeeping(call_instruction, ingredients); - continue; // not done with caller; don't increment current_step_index() -} -:(code) -void finish_call_housekeeping(const instruction& call_instruction, const vector<vector<double> >& ingredients) { // End Call Housekeeping + continue; // not done with caller; don't increment current_step_index() } :(scenario calling_undefined_recipe_fails) diff --git a/035call_ingredient.cc b/035call_ingredient.cc index c8f6d75a..1291e567 100644 --- a/035call_ingredient.cc +++ b/035call_ingredient.cc @@ -22,7 +22,6 @@ recipe f [ :(before "End call Fields") vector<vector<double> > ingredient_atoms; -vector<type_tree*> ingredient_types; long long int next_ingredient_to_process; :(before "End call Constructor") next_ingredient_to_process = 0; @@ -30,9 +29,6 @@ next_ingredient_to_process = 0; :(before "End Call Housekeeping") for (long long int i = 0; i < SIZE(ingredients); ++i) { Current_routine->calls.front().ingredient_atoms.push_back(ingredients.at(i)); - if (SIZE(Current_routine->calls) > 1) - Current_routine->calls.front().ingredient_types.push_back(call_instruction.ingredients.at(i).type); - // todo: else function is main and ingredient_type is string } :(before "End Primitive Recipe Declarations") diff --git a/037recipe.cc b/037recipe.cc index 51ad79af..a2a95a39 100644 --- a/037recipe.cc +++ b/037recipe.cc @@ -2,27 +2,6 @@ //: also like to make the recipe a variable, pass recipes to "higher-order" //: recipes, return recipes from recipes and so on. -:(scenario call_literal_recipe) -recipe main [ - 1:number <- call f:recipe, 34 -] -recipe f [ - 2:number <- next-ingredient - reply 2:number -] -+mem: storing 34 in location 1 - -:(scenario call_variable) -recipe main [ - 1:recipe-ordinal <- copy f:recipe - 2:number <- call 1:recipe-ordinal, 34 -] -recipe f [ - 3:number <- next-ingredient - reply 3:number -] -+mem: storing 34 in location 2 - :(before "End Mu Types Initialization") // 'recipe' is a literal Type_ordinal["recipe"] = 0; @@ -36,37 +15,6 @@ if (r.properties.at(0).second && r.properties.at(0).second->value == "recipe") { return; } -:(before "End Primitive Recipe Declarations") -CALL, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["call"] = CALL; -:(before "End Primitive Recipe Checks") -case CALL: { - if (inst.ingredients.empty()) { - raise_error << maybe(Recipe[r].name) << "'call' requires at least one ingredient (the recipe to call)\n" << end(); - break; - } - if (!is_mu_recipe(inst.ingredients.at(0))) { - raise_error << maybe(Recipe[r].name) << "first ingredient of 'call' should be a recipe, but got " << inst.ingredients.at(0).original_string << '\n' << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case CALL: { - // Begin Call - if (Trace_stream) { - ++Trace_stream->callstack_depth; - trace("trace") << "indirect 'call': 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(); - Current_routine->calls.push_front(call(ingredients.at(0).at(0))); - ingredients.erase(ingredients.begin()); // drop the callee - finish_call_housekeeping(caller_instruction, ingredients); - continue; -} - :(code) bool is_mu_recipe(reagent r) { if (!r.type) return false; diff --git a/053continuation.cc b/053continuation.cc deleted file mode 100644 index b92217d6..00000000 --- a/053continuation.cc +++ /dev/null @@ -1,292 +0,0 @@ -//: Continuations are a powerful primitive for constructing advanced kinds of -//: control *policies* like back-tracking. They're usually provided using a -//: primitive called 'call-cc': http://en.wikipedia.org/wiki/Call-with-current-continuation) -//: But in mu 'call-cc' is constructed out of a combination of two primitives: -//: 'current-continuation', which returns a continuation, and -//: 'continue-from', which takes a continuation to switch to. - -//: todo: implement continuations in mu's memory -:(before "End Globals") -map<long long int, call_stack> Continuation; -long long int Next_continuation_id = 0; -:(before "End Setup") -Continuation.clear(); -Next_continuation_id = 0; - -:(before "End Mu Types Initialization") -type_ordinal continuation = Type_ordinal["continuation"] = Next_type_ordinal++; -Type[continuation].name = "continuation"; - -:(before "End Primitive Recipe Declarations") -CURRENT_CONTINUATION, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["current-continuation"] = CURRENT_CONTINUATION; -:(before "End Primitive Recipe Checks") -case CURRENT_CONTINUATION: { - break; -} -:(before "End Primitive Recipe Implementations") -case CURRENT_CONTINUATION: { - // copy the current call stack - Continuation[Next_continuation_id] = Current_routine->calls; // deep copy because calls have no pointers - // make sure calling the copy doesn't spawn the same continuation again - ++Continuation[Next_continuation_id].front().running_step_index; - products.resize(1); - products.at(0).push_back(Next_continuation_id); - ++Next_continuation_id; - trace("current-continuation") << "new continuation " << Next_continuation_id << end(); - break; -} - -:(before "End Primitive Recipe Declarations") -CONTINUE_FROM, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["continue-from"] = CONTINUE_FROM; -:(before "End Primitive Recipe Checks") -case CONTINUE_FROM: { - if (!is_mu_continuation(inst.ingredients.at(0))) { - raise_error << maybe(Recipe[r].name) << "first ingredient of 'continue-from' should be a continuation generated by 'current-continuation', but got " << inst.ingredients.at(0).original_string << '\n' << end(); - break; - } - break; -} -:(before "End Primitive Recipe Implementations") -case CONTINUE_FROM: { - long long int c = ingredients.at(0).at(0); - if (Trace_stream) { - Trace_stream->callstack_depth += SIZE(Continuation[c]); - trace("trace") << "continuation; growing callstack depth to " << Trace_stream->callstack_depth << end(); - assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion - } - Current_routine->calls = Continuation[c]; // deep copy; calls have no pointers - continue; // skip rest of this instruction -} - -:(code) -bool is_mu_continuation(const reagent& x) { - if (!x.type) return false; - return x.type->value == Type_ordinal["continuation"]; -} - -:(scenario continuation) -# simulate a loop using continuations -recipe main [ - 1:number <- copy 0 - 2:continuation <- current-continuation - { - 3:boolean <- greater-or-equal 1:number, 3 - break-if 3:boolean - 1:number <- add 1:number, 1 - continue-from 2:continuation # loop - } -] -+mem: storing 1 in location 1 -+mem: storing 2 in location 1 -+mem: storing 3 in location 1 --mem: storing 4 in location 1 -# ensure every iteration doesn't copy the stack over and over -$current-continuation: 1 - -:(scenario continuation_inside_caller) -recipe main [ - 1:number <- copy 0 - 2:continuation <- loop-body - { - 3:boolean <- greater-or-equal 1:number, 3 - break-if 3:boolean - continue-from 2:continuation # loop - } -] - -recipe loop-body [ - 4:continuation <- current-continuation - 1:number <- add 1:number, 1 -] -+mem: storing 1 in location 1 -+mem: storing 2 in location 1 -+mem: storing 3 in location 1 --mem: storing 4 in location 1 - -//:: A variant of continuations is the 'delimited' continuation that can be called like any other recipe. -//: -//: In mu, this is constructed out of two primitives: -//: -//: * 'create-delimited-continuation' lays down a mark on the call -//: stack and calls the provided function (it is sometimes called 'prompt') -//: * 'reply-current-continuation' copies the top of the stack until the -//: mark, and returns it as the response of create-delimited-continuation -//: (which might be a distant ancestor on the call stack; intervening calls -//: don't return) -//: -//: The resulting slice of the stack can now be called just like a regular -//: function. - -:(scenario delimited_continuation) -recipe main [ - 1:continuation <- create-delimited-continuation f:recipe 12 # 12 is an argument to f - 2:number <- copy 5 - { - 2:number <- call 1:continuation, 2:number # 2 is an argument to g, the 'top' of the continuation - 3:boolean <- greater-or-equal 2:number, 8 - break-if 3:boolean - loop - } -] - -recipe f [ - 11:number <- next-ingredient - 12:number <- g 11:number - reply 12:number -] - -recipe g [ - 21:number <- next-ingredient - rewind-ingredients - reply-delimited-continuation - # calls of the continuation start from here - 22:number <- next-ingredient - 23:number <- add 22:number, 1 - reply 23:number -] -# first call of 'g' executes the part before reply-delimited-continuation -+mem: storing 12 in location 21 -+run: 2:number <- copy 5 -+mem: storing 5 in location 2 -# calls of the continuation execute the part after reply-delimited-continuation -+run: 2:number <- call 1:continuation, 2:number -+mem: storing 5 in location 22 -+mem: storing 6 in location 2 -+run: 2:number <- call 1:continuation, 2:number -+mem: storing 6 in location 22 -+mem: storing 7 in location 2 -+run: 2:number <- call 1:continuation, 2:number -+mem: storing 7 in location 22 -+mem: storing 8 in location 2 -# first call of 'g' does not execute the part after reply-delimited-continuation --mem: storing 12 in location 22 -# calls of the continuation don't execute the part before reply-delimited-continuation --mem: storing 5 in location 21 --mem: storing 6 in location 21 --mem: storing 7 in location 21 -# termination --mem: storing 9 in location 2 - -//: 'create-delimited-continuation' is like 'call' except it adds a 'reset' mark to -//: the call stack - -:(before "End call Fields") -bool is_reset; -:(before "End call Constructor") -is_reset = false; - -//: like call, but mark the current call as a 'reset' call before pushing the next one on it - -:(before "End Primitive Recipe Declarations") -CREATE_DELIMITED_CONTINUATION, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["create-delimited-continuation"] = CREATE_DELIMITED_CONTINUATION; -:(before "End Primitive Recipe Checks") -case CREATE_DELIMITED_CONTINUATION: { - break; -} -:(before "End Primitive Recipe Implementations") -case CREATE_DELIMITED_CONTINUATION: { - if (Trace_stream) { - ++Trace_stream->callstack_depth; - 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(); - Current_routine->calls.front().is_reset = true; - Current_routine->calls.push_front(call(Recipe_ordinal[current_instruction().ingredients.at(0).name])); - ingredients.erase(ingredients.begin()); // drop the callee - finish_call_housekeeping(caller_instruction, ingredients); - continue; -} - -//: save the slice of current call stack until the 'create-delimited-continuation' -//: call, and return it as the result. -//: todo: implement delimited continuations in mu's memory -:(before "End Globals") -map<long long int, call_stack> Delimited_continuation; -long long int Next_delimited_continuation_id = 0; -:(before "End Setup") -Delimited_continuation.clear(); -Next_delimited_continuation_id = 0; - -:(before "End Primitive Recipe Declarations") -REPLY_DELIMITED_CONTINUATION, -:(before "End Primitive Recipe Numbers") -Recipe_ordinal["reply-delimited-continuation"] = REPLY_DELIMITED_CONTINUATION; -:(before "End Primitive Recipe Checks") -case REPLY_DELIMITED_CONTINUATION: { - break; -} -:(before "End Primitive Recipe Implementations") -case REPLY_DELIMITED_CONTINUATION: { - // first clear any existing ingredients, to isolate the creation of the - // continuation from its calls - Current_routine->calls.front().ingredient_atoms.clear(); - Current_routine->calls.front().next_ingredient_to_process = 0; - // copy the current call stack until the most recent 'reset' call - call_stack::iterator find_reset(call_stack& c); // manual prototype containing '::' - call_stack::iterator reset = find_reset(Current_routine->calls); - if (reset == Current_routine->calls.end()) { - raise_error << maybe(current_recipe_name()) << "couldn't find a 'reset' call to jump out to\n" << end(); - break; - } - Delimited_continuation[Next_delimited_continuation_id] = call_stack(Current_routine->calls.begin(), reset); - while (Current_routine->calls.begin() != reset) { - if (Trace_stream) { - --Trace_stream->callstack_depth; - assert(Trace_stream->callstack_depth >= 0); - } - Current_routine->calls.pop_front(); - } - // return it as the result of the 'reset' call - products.resize(1); - products.at(0).push_back(Next_delimited_continuation_id); - ++Next_delimited_continuation_id; - break; // continue to process rest of 'reset' call -} - -:(code) -call_stack::iterator find_reset(call_stack& c) { - for (call_stack::iterator p = c.begin(); p != c.end(); ++p) - if (p->is_reset) return p; - return c.end(); -} - -//: overload 'call' for continuations -:(after "Begin Call") - if (!current_instruction().ingredients.at(0).properties.empty() - && current_instruction().ingredients.at(0).properties.at(0).second - && current_instruction().ingredients.at(0).properties.at(0).second->value == "continuation") { - // copy multiple calls on to current call stack - assert(scalar(ingredients.at(0))); - if (Delimited_continuation.find(ingredients.at(0).at(0)) == Delimited_continuation.end()) { - raise_error << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - } - const call_stack& new_calls = Delimited_continuation[ingredients.at(0).at(0)]; - const call& caller = (SIZE(new_calls) > 1) ? *++new_calls.rbegin() : Current_routine->calls.front(); - for (call_stack::const_reverse_iterator p = new_calls.rbegin(); p != new_calls.rend(); ++p) - Current_routine->calls.push_front(*p); - if (Trace_stream) { - Trace_stream->callstack_depth += SIZE(new_calls); - trace("trace") << "calling delimited continuation; growing callstack depth to " << Trace_stream->callstack_depth << end(); - assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion - } - ++current_step_index(); // skip past the reply-delimited-continuation - ingredients.erase(ingredients.begin()); // drop the callee - finish_call_housekeeping(to_instruction(caller), ingredients); - continue; - } - -:(code) -const instruction& to_instruction(const call& c) { - assert(Recipe.find(c.running_recipe) != Recipe.end()); - return Recipe[c.running_recipe].steps.at(c.running_step_index); -} - -:(before "End is_mu_recipe Cases") -if (r.type && r.type->value == Type_ordinal["continuation"]) return true; diff --git a/callcc.mu b/callcc.mu deleted file mode 100644 index 1bf32973..00000000 --- a/callcc.mu +++ /dev/null @@ -1,17 +0,0 @@ -# example program: saving and reusing call-stacks or continuations - -recipe main [ - c:continuation <- f - continue-from c # <-- ..when you hit this -] - -recipe f [ - c:continuation <- g - reply c -] - -recipe g [ - c:continuation <- current-continuation # <-- loop back to here - $print 1 - reply c # threaded through unmodified after first iteration -] diff --git a/counters.mu b/counters.mu index 8a4da073..be1f098a 100644 --- a/counters.mu +++ b/counters.mu @@ -1,18 +1,18 @@ # example program: maintain multiple counters with isolated lexical scopes # (spaces) -recipe new-counter n:number -> default-space:address:array:location [ - default-space <- new location:type, 30 - load-ingredients - reply +recipe new-counter [ + default-space:address:array:location <- new location:type, 30 + n:number <- next-ingredient + reply default-space ] -recipe increment-counter outer:address:array:location/names:new-counter, x:number -> n:number/space:1 [ +recipe increment-counter [ local-scope - load-ingredients - 0:address:array:location/names:new-counter <- copy outer # setup outer space; it *must* come from 'new-counter' + 0:address:array:location/names:new-counter <- next-ingredient # setup outer space; it *must* come from 'new-counter' + x:number <- next-ingredient n:number/space:1 <- add n:number/space:1, x - reply n/space:1 + reply n:number/space:1 ] recipe main [ diff --git a/mu.vim b/mu.vim index bb77b50d..72255940 100644 --- a/mu.vim +++ b/mu.vim @@ -62,7 +62,6 @@ syntax match muGlobal %[^ ]\+:global/\?[^ ,]*% | highlight link muGlobal Special syntax keyword muControl reply reply-if reply-unless jump jump-if jump-unless loop loop-if loop-unless break break-if break-unless current-continuation continue-from create-delimited-continuation reply-delimited-continuation | highlight muControl ctermfg=3 " common keywords syntax keyword muRecipe recipe recipe! before after | highlight muRecipe ctermfg=208 -syntax match muRecipe " -> " syntax keyword muScenario scenario | highlight muScenario ctermfg=34 syntax keyword muData container exclusive-container | highlight muData ctermfg=226 |