mu/archive/1.vm/076continuation.cc

407 lines
16 KiB
C++
Raw Normal View History

//: Continuations are a powerful primitive for constructing advanced kinds of
//: control *policies* like back-tracking.
//:
//: In Mu, continuations are first-class and delimited, and are constructed
//: out of two primitives:
//:
//: * 'call-with-continuation-mark' marks the top of the call stack and then
2017-11-04 00:56:35 +00:00
//: calls the provided recipe.
//: * 'return-continuation-until-mark' copies the top of the stack
//: until the mark, and returns it as the result of
//: 'call-with-continuation-mark' (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
2017-11-04 00:56:35 +00:00
//: recipe.
//:
//: See the example programs continuation*.mu to get a sense for the
//: possibilities.
//:
//: Refinements:
//: * You can call a single continuation multiple times, and it will preserve
//: the state of its local variables at each stack frame between calls.
//: The stack frames of a continuation are not destroyed until the
2017-11-04 00:56:35 +00:00
//: continuation goes out of scope. See continuation2.mu.
//: * 'return-continuation-until-mark' doesn't consume the mark, so you can
//: return multiple continuations based on a single mark. In combination
//: with the fact that 'return-continuation-until-mark' can return from
//: regular calls, just as long as there was an earlier call to
//: 'call-with-continuation-mark', this gives us a way to create resumable
2017-11-04 00:56:35 +00:00
//: recipes. See continuation3.mu.
2017-11-05 18:52:33 +00:00
//: * 'return-continuation-until-mark' can take ingredients to return just
//: like other 'return' instructions. It just implicitly also returns a
//: continuation as the first result. See continuation4.mu.
//: * Conversely, you can pass ingredients to a continuation when calling it,
//: to make it available to products of 'return-continuation-until-mark'.
2017-11-06 10:35:54 +00:00
//: 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.
//: https://www.cs.indiana.edu/~sabry/papers/yield.pdf
//:
//: Caveats:
//: * At the moment we can't statically type-check continuations. So we raise
//: runtime errors for a call that doesn't return a continuation when the
//: caller expects, or one that returns a continuation when the caller
//: doesn't expect it. This shouldn't cause memory corruption, though.
//: There should still be no way to lookup addresses that aren't allocated.
:(before "End Mu Types Initialization")
type_ordinal continuation = Type_ordinal["continuation"] = Next_type_ordinal++;
Type[continuation].name = "continuation";
//: A continuation can be called like a recipe.
:(before "End is_mu_recipe Atom Cases(r)")
if (r.type->name == "continuation") return true;
//: However, it can't be type-checked like most recipes. Pretend it's like a
//: header-less recipe.
:(after "Begin Reagent->Recipe(r, recipe_header)")
if (r.type->atom && r.type->name == "continuation") {
result_header.has_header = false;
return result_header;
}
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
:(code)
void test_delimited_continuation() {
run(
"recipe main [\n"
" 1:continuation <- call-with-continuation-mark 233/mark, f, 77\n" // 77 is an argument to f
" 2:num <- copy 5\n"
" {\n"
" 2:num <- call 1:continuation, 2:num\n" // jump to 'return-continuation-until-mark' below
" 3:bool <- greater-or-equal 2:num, 8\n"
" break-if 3:bool\n"
" loop\n"
" }\n"
"]\n"
"recipe f [\n"
" 11:num <- next-ingredient\n"
" 12:num <- g 11:num\n"
" return 12:num\n"
"]\n"
"recipe g [\n"
" 21:num <- next-ingredient\n"
" 22:num <- return-continuation-until-mark 233/mark\n"
" 23:num <- add 22:num, 1\n"
" return 23:num\n"
"]\n"
);
// first call of 'g' executes the part before return-continuation-until-mark
CHECK_TRACE_CONTENTS(
// first call of 'g' executes the part before return-continuation-until-mark
"mem: storing 77 in location 21\n"
"run: {2: \"number\"} <- copy {5: \"literal\"}\n"
"mem: storing 5 in location 2\n"
// calls of the continuation execute the part after return-continuation-until-mark
"run: {2: \"number\"} <- call {1: \"continuation\"}, {2: \"number\"}\n"
"mem: storing 5 in location 22\n"
"mem: storing 6 in location 2\n"
"run: {2: \"number\"} <- call {1: \"continuation\"}, {2: \"number\"}\n"
"mem: storing 6 in location 22\n"
"mem: storing 7 in location 2\n"
"run: {2: \"number\"} <- call {1: \"continuation\"}, {2: \"number\"}\n"
"mem: storing 7 in location 22\n"
"mem: storing 8 in location 2\n"
);
// first call of 'g' does not execute the part after return-continuation-until-mark
CHECK_TRACE_DOESNT_CONTAIN("mem: storing 77 in location 22");
// calls of the continuation don't execute the part before return-continuation-until-mark
CHECK_TRACE_DOESNT_CONTAIN("mem: storing 5 in location 21");
CHECK_TRACE_DOESNT_CONTAIN("mem: storing 6 in location 21");
CHECK_TRACE_DOESNT_CONTAIN("mem: storing 7 in location 21");
// termination
CHECK_TRACE_DOESNT_CONTAIN("mem: storing 9 in location 2");
}
:(before "End call Fields")
2018-03-16 04:54:53 +00:00
int continuation_mark_tag;
:(before "End call Constructor")
2018-03-16 04:54:53 +00:00
continuation_mark_tag = 0;
:(before "End Primitive Recipe Declarations")
CALL_WITH_CONTINUATION_MARK,
:(before "End Primitive Recipe Numbers")
Recipe_ordinal["call-with-continuation-mark"] = CALL_WITH_CONTINUATION_MARK;
:(before "End Primitive Recipe Checks")
case CALL_WITH_CONTINUATION_MARK: {
2018-02-21 06:09:12 +00:00
if (SIZE(inst.ingredients) < 2) {
raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires at least two ingredients: a mark number and a recipe to call\n" << end();
}
break;
}
:(before "End Primitive Recipe Implementations")
case CALL_WITH_CONTINUATION_MARK: {
2017-09-01 08:50:32 +00:00
// like call, but mark the current call as a 'base of continuation' call
// before pushing the next one on it
trace(Callstack_depth+1, "trace") << "delimited continuation; incrementing callstack depth to " << Callstack_depth << end();
++Callstack_depth;
assert(Callstack_depth < Max_depth);
instruction/*copy*/ caller_instruction = current_instruction();
2018-03-16 04:54:53 +00:00
Current_routine->calls.front().continuation_mark_tag = current_instruction().ingredients.at(0).value;
Current_routine->calls.push_front(call(ingredients.at(1).at(0)));
// 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;
}
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
:(code)
void test_next_ingredient_inside_continuation() {
run(
"recipe main [\n"
" call-with-continuation-mark 233/mark, f, true\n"
"]\n"
"recipe f [\n"
" 10:bool <- next-input\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"mem: storing 1 in location 10\n"
);
}
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
void test_delimited_continuation_out_of_recipe_variable() {
run(
"recipe main [\n"
" x:recipe <- copy f\n"
" call-with-continuation-mark 233/mark, x, true\n"
"]\n"
"recipe f [\n"
" 10:bool <- next-input\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"mem: storing 1 in location 10\n"
);
}
//: 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
:(before "End Types")
struct delimited_continuation {
call_stack frames;
int nrefs;
delimited_continuation(call_stack::iterator begin, call_stack::iterator end) :frames(call_stack(begin, end)), nrefs(0) {}
};
:(before "End Globals")
map<long long int, delimited_continuation> Delimited_continuation;
long long int Next_delimited_continuation_id = 1; // 0 is null just like an address
:(before "End Reset")
Delimited_continuation.clear();
Next_delimited_continuation_id = 1;
:(before "End Primitive Recipe Declarations")
RETURN_CONTINUATION_UNTIL_MARK,
:(before "End Primitive Recipe Numbers")
Recipe_ordinal["return-continuation-until-mark"] = RETURN_CONTINUATION_UNTIL_MARK;
:(before "End Primitive Recipe Checks")
case RETURN_CONTINUATION_UNTIL_MARK: {
2018-03-22 04:52:53 +00:00
if (inst.ingredients.empty()) {
raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires at least one ingredient: a mark tag (number)\n" << end();
}
break;
}
:(before "End Primitive Recipe Implementations")
case RETURN_CONTINUATION_UNTIL_MARK: {
2017-12-08 00:04:47 +00:00
// I don't know how to think about next-ingredient in combination with
// continuations, so seems cleaner to just kill it. Functions have to read
// their inputs before ever returning a continuation.
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 marked call
call_stack::iterator find_base_of_continuation(call_stack&, int); // manual prototype containing '::'
2018-03-16 04:54:53 +00:00
call_stack::iterator base = find_base_of_continuation(Current_routine->calls, /*mark tag*/current_instruction().ingredients.at(0).value);
if (base == Current_routine->calls.end()) {
raise << maybe(current_recipe_name()) << "couldn't find a 'call-with-continuation-mark' to return to with tag " << current_instruction().ingredients.at(0).original_string << '\n' << end();
2017-09-01 08:50:32 +00:00
raise << maybe(current_recipe_name()) << "call stack:\n" << end();
for (call_stack::iterator p = Current_routine->calls.begin(); p != Current_routine->calls.end(); ++p)
raise << maybe(current_recipe_name()) << " " << get(Recipe, p->running_recipe).name << '\n' << end();
break;
}
trace(Callstack_depth+1, "run") << "creating continuation " << Next_delimited_continuation_id << end();
put(Delimited_continuation, Next_delimited_continuation_id, delimited_continuation(Current_routine->calls.begin(), base));
while (Current_routine->calls.begin() != base) {
--Callstack_depth;
assert(Callstack_depth >= 0);
Current_routine->calls.pop_front();
}
// return it as the result of the marked call
products.resize(1);
products.at(0).push_back(Next_delimited_continuation_id);
// return any other ingredients passed in
2018-03-16 04:54:53 +00:00
copy(/*skip mark tag*/++ingredients.begin(), ingredients.end(), inserter(products, products.end()));
++Next_delimited_continuation_id;
break; // continue to process rest of marked call
}
:(code)
2018-03-16 04:54:53 +00:00
call_stack::iterator find_base_of_continuation(call_stack& c, int mark_tag) {
for (call_stack::iterator p = c.begin(); p != c.end(); ++p)
2018-03-16 04:54:53 +00:00
if (p->continuation_mark_tag == mark_tag) return p;
return c.end();
}
//: overload 'call' for continuations
:(after "Begin Call")
2017-11-19 10:35:43 +00:00
if (is_mu_continuation(current_instruction().ingredients.at(0))) {
2017-09-01 09:23:45 +00:00
// copy multiple calls on to current call stack
assert(scalar(ingredients.at(0)));
trace(Callstack_depth+1, "run") << "calling continuation " << ingredients.at(0).at(0) << end();
2017-11-19 11:18:11 +00:00
if (!contains_key(Delimited_continuation, ingredients.at(0).at(0)))
2017-09-01 09:23:45 +00:00
raise << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end();
const call_stack& new_frames = get(Delimited_continuation, ingredients.at(0).at(0)).frames;
for (call_stack::const_reverse_iterator p = new_frames.rbegin(); p != new_frames.rend(); ++p)
2017-09-01 09:23:45 +00:00
Current_routine->calls.push_front(*p);
trace(Callstack_depth+1, "trace") << "calling delimited continuation; growing callstack depth to " << Callstack_depth+SIZE(new_frames) << end();
Callstack_depth += SIZE(new_frames);
assert(Callstack_depth < Max_depth);
// no call housekeeping; continuations don't support next-ingredient
copy(/*drop continuation*/++ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
break; // record results of resuming 'return-continuation-until-mark' instruction
2017-09-01 09:23:45 +00:00
}
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
:(code)
void test_continuations_can_return_values() {
run(
"def main [\n"
" local-scope\n"
" k:continuation, 1:num/raw <- call-with-continuation-mark 233/mark, f\n"
"]\n"
"def f [\n"
" local-scope\n"
" g\n"
"]\n"
"def g [\n"
" local-scope\n"
" return-continuation-until-mark 233/mark, 34\n"
" stash [continuation called]\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"mem: storing 34 in location 1\n"
);
}
2017-11-19 12:18:31 +00:00
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
void test_continuations_continue_to_matching_mark() {
run(
"def main [\n"
" local-scope\n"
" k:continuation, 1:num/raw <- call-with-continuation-mark 233/mark, f\n"
" add 1, 1\n"
"]\n"
"def f [\n"
" local-scope\n"
" k2:continuation <- call-with-continuation-mark 234/mark, g\n"
" add 2, 2\n"
"]\n"
"def g [\n"
" local-scope\n"
" return-continuation-until-mark 233/mark, 34\n"
" stash [continuation called]\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"run: add {1: \"literal\"}, {1: \"literal\"}\n"
);
CHECK_TRACE_DOESNT_CONTAIN("run: add {2: \"literal\"}, {2: \"literal\"}");
}
//: Allow shape-shifting recipes to return continuations.
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
void test_call_shape_shifting_recipe_with_continuation_mark() {
run(
"def main [\n"
" 1:num <- call-with-continuation-mark 233/mark, f, 34\n"
"]\n"
"def f x:_elem -> y:_elem [\n"
" local-scope\n"
" load-ingredients\n"
" y <- copy x\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"mem: storing 34 in location 1\n"
);
}
:(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases")
if (inst.name == "call-with-continuation-mark") {
if (SIZE(inst.ingredients) > 1 && is_recipe_literal(inst.ingredients.at(/*skip mark*/1))) {
resolve_indirect_continuation_call(r, index, inst, caller_recipe);
return;
}
}
:(code)
void resolve_indirect_continuation_call(const recipe_ordinal r, int index, instruction& inst, const recipe& caller_recipe) {
instruction inst2;
inst2.name = inst.ingredients.at(/*skip mark*/1).name;
for (int i = /*skip mark and recipe*/2; i < SIZE(inst.ingredients); ++i)
inst2.ingredients.push_back(inst.ingredients.at(i));
for (int i = /*skip continuation*/1; i < SIZE(inst.products); ++i)
inst2.products.push_back(inst.products.at(i));
resolve_ambiguous_call(r, index, inst2, caller_recipe);
inst.ingredients.at(/*skip mark*/1).name = inst2.name;
inst.ingredients.at(/*skip mark*/1).set_value(get(Recipe_ordinal, inst2.name));
}
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
void test_call_shape_shifting_recipe_with_continuation_mark_and_no_outputs() {
run(
"def main [\n"
" 1:continuation <- call-with-continuation-mark 233/mark, f, 34\n"
"]\n"
"def f x:_elem [\n"
" local-scope\n"
" load-ingredients\n"
" return-continuation-until-mark 233/mark\n"
"]\n"
);
CHECK_TRACE_COUNT("error", 0);
}
2017-12-08 00:01:43 +00:00
5001 - drop the :(scenario) DSL I've been saying for a while[1][2][3] that adding extra abstractions makes things harder for newcomers, and adding new notations doubly so. And then I notice this DSL in my own backyard. Makes me feel like a hypocrite. [1] https://news.ycombinator.com/item?id=13565743#13570092 [2] https://lobste.rs/s/to8wpr/configuration_files_are_canary_warning [3] https://lobste.rs/s/mdmcdi/little_languages_by_jon_bentley_1986#c_3miuf2 The implementation of the DSL was also highly hacky: a) It was happening in the tangle/ tool, but was utterly unrelated to tangling layers. b) There were several persnickety constraints on the different kinds of lines and the specific order they were expected in. I kept finding bugs where the translator would silently do the wrong thing. Or the error messages sucked, and readers may be stuck looking at the generated code to figure out what happened. Fixing error messages would require a lot more code, which is one of my arguments against DSLs in the first place: they may be easy to implement, but they're hard to design to go with the grain of the underlying platform. They require lots of iteration. Is that effort worth prioritizing in this project? On the other hand, the DSL did make at least some readers' life easier, the ones who weren't immediately put off by having to learn a strange syntax. There were fewer quotes to parse, fewer backslash escapes. Anyway, since there are also people who dislike having to put up with strange syntaxes, we'll call that consideration a wash and tear this DSL out. --- This commit was sheer drudgery. Hopefully it won't need to be redone with a new DSL because I grow sick of backslashes.
2019-03-13 01:56:55 +00:00
void test_continuation1() {
run(
"def main [\n"
" local-scope\n"
" k:continuation <- call-with-continuation-mark 233/mark, create-yielder\n"
" 10:num/raw <- call k\n"
"]\n"
"def create-yielder -> n:num [\n"
" local-scope\n"
" load-inputs\n"
" return-continuation-until-mark 233/mark\n"
" return 1\n"
"]\n"
);
CHECK_TRACE_CONTENTS(
"mem: storing 1 in location 10\n"
);
CHECK_TRACE_COUNT("error", 0);
}
:(code)
bool is_mu_continuation(reagent/*copy*/ x) {
canonize_type(x);
return x.type && x.type->atom && x.type->value == get(Type_ordinal, "continuation");
}
2017-12-07 21:45:01 +00:00
// helper for debugging
void dump(const int continuation_id) {
if (!contains_key(Delimited_continuation, continuation_id)) {
raise << "missing delimited continuation: " << continuation_id << '\n' << end();
return;
}
delimited_continuation& curr = get(Delimited_continuation, continuation_id);
dump(curr.frames);
}