diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2015-05-05 23:50:50 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2015-05-05 23:50:50 -0700 |
commit | 20d1c9057a559ce8db83bbc2787ca91348bcb16f (patch) | |
tree | 56381f7cc68f93014d72019301f623b7993c63ef | |
parent | 8923d6f658d09de800164b8fc6514475f49307b4 (diff) | |
download | mu-20d1c9057a559ce8db83bbc2787ca91348bcb16f.tar.gz |
1278 - support before/after tangle directives
No way to only insert code at a label in a specific recipe. Let's see how that goes.
-rw-r--r-- | 011load.cc | 49 | ||||
-rw-r--r-- | 075scenario_keyboard.cc | 2 | ||||
-rw-r--r-- | 077tangle.cc | 198 | ||||
-rw-r--r-- | tangle.mu | 36 |
4 files changed, 254 insertions, 31 deletions
diff --git a/011load.cc b/011load.cc index 3f0f8e1e..ae793b59 100644 --- a/011load.cc +++ b/011load.cc @@ -24,7 +24,21 @@ vector<recipe_number> load(istream& in) { string command = next_word(in); // Command Handlers if (command == "recipe") { - result.push_back(add_recipe(in)); + string recipe_name = next_word(in); + if (recipe_name.empty()) + raise << "empty recipe name\n"; + if (Recipe_number.find(recipe_name) == Recipe_number.end()) { + Recipe_number[recipe_name] = Next_recipe_number++; + } + if (Recipe.find(Recipe_number[recipe_name]) != Recipe.end()) { + raise << "redefining recipe " << Recipe[Recipe_number[recipe_name]].name << "\n"; + Recipe.erase(Recipe_number[recipe_name]); + } + Recipe[Recipe_number[recipe_name]] = slurp_recipe(in); + Recipe[Recipe_number[recipe_name]].name = recipe_name; + // track added recipes because we may need to undo them in tests; see below + recently_added_recipes.push_back(Recipe_number[recipe_name]); + result.push_back(Recipe_number[recipe_name]); } // End Command Handlers else { @@ -34,43 +48,18 @@ vector<recipe_number> load(istream& in) { return result; } -recipe_number add_recipe(istream& in) { - skip_whitespace_and_comments(in); - string recipe_name = next_word(in); -//? cout << "recipe name: ^" << recipe_name << "$\n"; //? 3 - if (recipe_name.empty()) - raise << "empty recipe name\n"; -//? raise << "empty recipe name in " << in.str() << '\n'; - if (Recipe_number.find(recipe_name) == Recipe_number.end()) { - Recipe_number[recipe_name] = Next_recipe_number++; -//? cout << "AAA: " << recipe_name << " is now " << Recipe_number[recipe_name] << '\n'; //? 1 - } - recipe_number r = Recipe_number[recipe_name]; - if (Recipe.find(r) != Recipe.end()) { - raise << "redefining recipe " << Recipe[r].name << "\n"; - Recipe.erase(r); - } -//? cout << recipe_name << ": adding recipe " << r << '\n'; //? 3 - +recipe slurp_recipe(istream& in) { + recipe result; skip_whitespace(in); if (in.get() != '[') raise << "recipe body must begin with '['\n"; - -//? show_rest_of_stream(in); //? 1 skip_whitespace_and_comments(in); -//? show_rest_of_stream(in); //? 1 - instruction curr; while (next_instruction(in, &curr)) { // End Rewrite Instruction(curr) -//? if (!curr.products.empty()) cout << curr.products[0].to_string() << '\n'; //? 1 - Recipe[r].steps.push_back(curr); + result.steps.push_back(curr); } - Recipe[r].name = recipe_name; -//? cout << "recipe " << recipe_name << " has " << Recipe[r].steps.size() << " steps.\n"; //? 1 - // track added recipes because we may need to undo them in tests; see below - recently_added_recipes.push_back(r); - return r; + return result; } bool next_instruction(istream& in, instruction* curr) { diff --git a/075scenario_keyboard.cc b/075scenario_keyboard.cc index d14e4de7..92e5a7a8 100644 --- a/075scenario_keyboard.cc +++ b/075scenario_keyboard.cc @@ -42,7 +42,7 @@ if (curr.name == "assume-keyboard") { assert(curr.products.empty()); curr.products.push_back(reagent("keyboard:address")); curr.products[0].set_value(KEYBOARD); - Recipe[r].steps.push_back(curr); // hacky that "Rewrite Instruction" is converting to multiple instructions + result.steps.push_back(curr); // hacky that "Rewrite Instruction" is converting to multiple instructions // leave second instruction in curr curr.clear(); curr.operation = Recipe_number["init-fake-keyboard"]; diff --git a/077tangle.cc b/077tangle.cc new file mode 100644 index 00000000..9ec03f68 --- /dev/null +++ b/077tangle.cc @@ -0,0 +1,198 @@ +//: Allow code for recipes to be pulled in from multiple places. +//: +//: TODO: switch recipe.steps to a more efficient data structure. + +:(scenario tangle_before) +recipe main [ + 1:integer <- copy 0:literal + +label1 + 3:integer <- copy 0:literal +] + +before +label1 [ + 2:integer <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 ++mem: storing 0 in location 3 +# nothing else +$mem: 3 + +//: while loading recipes, load before/after fragments + +:(before "End Globals") +map<string /*label*/, recipe> Before_fragments, After_fragments; +:(before "End Setup") +Before_fragments.clear(); +After_fragments.clear(); + +:(before "End Command Handlers") +else if (command == "before") { + string label = next_word(in); + recipe tmp = slurp_recipe(in); + Before_fragments[label].steps.insert(Before_fragments[label].steps.end(), tmp.steps.begin(), tmp.steps.end()); +} +else if (command == "after") { + string label = next_word(in); + recipe tmp = slurp_recipe(in); + After_fragments[label].steps.insert(After_fragments[label].steps.begin(), tmp.steps.begin(), tmp.steps.end()); +} + +//: after all recipes are loaded, insert fragments at appropriate labels + +:(after "int main") + Transform.push_back(insert_fragments); + +:(code) +void insert_fragments(const recipe_number r) { + // Copy into a new vector because insertions invalidate iterators. + // But this way we can't insert into labels created inside before/after. + vector<instruction> result; + for (index_t i = 0; i < Recipe[r].steps.size(); ++i) { + const instruction inst = Recipe[r].steps[i]; + if (!inst.is_label) { + result.push_back(inst); + continue; + } + if (Before_fragments.find(inst.label) != Before_fragments.end()) { + result.insert(result.end(), Before_fragments[inst.label].steps.begin(), Before_fragments[inst.label].steps.end()); + } + result.push_back(inst); + if (After_fragments.find(inst.label) != After_fragments.end()) { + result.insert(result.end(), After_fragments[inst.label].steps.begin(), After_fragments[inst.label].steps.end()); + } + } +//? for (index_t i = 0; i < result.size(); ++i) { //? 1 +//? cout << result[i].to_string() << '\n'; //? 1 +//? } //? 1 + Recipe[r].steps.swap(result); +} + +:(scenario tangle_before_and_after) +recipe main [ + 1:integer <- copy 0:literal + +label1 + 4:integer <- copy 0:literal +] +before +label1 [ + 2:integer <- copy 0:literal +] +after +label1 [ + 3:integer <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 ++mem: storing 0 in location 4 +# nothing else +$mem: 4 + +:(scenario tangle_keeps_labels_separate) +recipe main [ + 1:integer <- copy 0:literal + +label1 + +label2 + 6:integer <- copy 0:literal +] +before +label1 [ + 2:integer <- copy 0:literal +] +after +label1 [ + 3:integer <- copy 0:literal +] +before +label2 [ + 4:integer <- copy 0:literal +] +after +label2 [ + 5:integer <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 +# 'after' fragments for earlier label always go before 'before' fragments for later label ++mem: storing 0 in location 4 +# label2 ++mem: storing 0 in location 5 ++mem: storing 0 in location 6 +# nothing else +$mem: 6 + +:(scenario tangle_stacks_multiple_fragments) +recipe main [ + 1:integer <- copy 0:literal + +label1 + 6:integer <- copy 0:literal +] +before +label1 [ + 2:integer <- copy 0:literal +] +after +label1 [ + 3:integer <- copy 0:literal +] +before +label1 [ + 4:integer <- copy 0:literal +] +after +label1 [ + 5:integer <- copy 0:literal +] ++mem: storing 0 in location 1 +# 'before' fragments stack in order ++mem: storing 0 in location 2 ++mem: storing 0 in location 4 +# label1 +# 'after' fragments stack in reverse order ++mem: storing 0 in location 5 ++mem: storing 0 in location 3 ++mem: storing 0 in location 6 +# nothing else +$mem: 6 + +:(scenario tangle_supports_fragments_with_multiple_instructions) +recipe main [ + 1:integer <- copy 0:literal + +label1 + 6:integer <- copy 0:literal +] +before +label1 [ + 2:integer <- copy 0:literal + 3:integer <- copy 0:literal +] +after +label1 [ + 4:integer <- copy 0:literal + 5:integer <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 ++mem: storing 0 in location 3 +# label1 ++mem: storing 0 in location 4 ++mem: storing 0 in location 5 ++mem: storing 0 in location 6 +# nothing else +$mem: 6 + +:(scenario tangle_tangles_into_all_labels_with_same_name) +recipe main [ + 1:integer <- copy 0:literal + +label1 + +label1 + 4:integer <- copy 0:literal +] +before +label1 [ + 2:integer <- copy 0:literal +] +after +label1 [ + 3:integer <- copy 0:literal +] ++mem: storing 0 in location 1 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 ++mem: storing 0 in location 2 +# label1 ++mem: storing 0 in location 3 ++mem: storing 0 in location 4 +# nothing else +$mem: 6 diff --git a/tangle.mu b/tangle.mu new file mode 100644 index 00000000..db890bc8 --- /dev/null +++ b/tangle.mu @@ -0,0 +1,36 @@ +# To demonstrate tangle directives, we'll construct a factorial function with +# separate base and recursive cases. Compare factorial.mu. +# This isn't a very realistic example, just a simple demonstration of +# possibilities. + +recipe factorial [ + default-space:address:array:location <- new location:type, 30:literal + n:integer <- next-ingredient + { + +base-case + } + +recursive-case +] + +after +base-case [ + # if n=0 return 1 + zero?:boolean <- equal n:integer, 0:literal + break-unless zero?:boolean + reply 1:literal +] + +after +recursive-case [ + # return n * factorial(n - 1) + x:integer <- subtract n:integer, 1:literal + subresult:integer <- factorial x:integer + result:integer <- multiply subresult:integer, n:integer + reply result:integer +] + +recipe main [ + 1:integer <- factorial 5:literal + $print [result: ] + $print 1:integer + $print [ +] +] |