//: Allow code for recipes to be pulled in from multiple places and inserted //: at special labels called 'waypoints' using two new top-level commands: //: before //: after //: Most labels are local: they must be unique to a recipe, and are invisible //: outside the recipe. However, waypoints are global: a recipe can have //: multiple of them, you can't use them as jump targets. :(before "End is_jump_target Special-cases") if (is_waypoint(label)) return false; //: Waypoints are always surrounded by '<>', e.g. . :(code) bool is_waypoint(string label) { return *label.begin() == '<' && *label.rbegin() == '>'; } void test_tangle_before() { run( "def main [\n" " 1:num <- copy 0\n" " \n" " 3:num <- copy 0\n" "]\n" "before [\n" " 2:num <- copy 0\n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 0 in location 1\n" "mem: storing 0 in location 2\n" "mem: storing 0 in location 3\n" ); // nothing else CHECK_TRACE_COUNT("mem", 3); } //: while loading recipes, load before/after fragments :(before "End Globals") map Before_fragments, After_fragments; set Fragments_used; :(before "End Reset") Before_fragments.clear(); After_fragments.clear(); Fragments_used.clear(); :(before "End Command Handlers") else if (command == "before") { string label = next_word(in); if (label.empty()) { assert(!has_data(in)); raise << "incomplete 'before' block at end of file\n" << end(); return result; } recipe tmp; slurp_body(in, tmp); if (is_waypoint(label)) Before_fragments[label].steps.insert(Before_fragments[label].steps.end(), tmp.steps.begin(), tmp.steps.end()); else raise << "can't tangle before non-waypoint " << label << '\n' << end(); // End before Command Handler } else if (command == "after") { string label = next_word(in); if (label.empty()) { assert(!has_data(in)); raise << "incomplete 'after' block at end of file\n" << end(); return result; } recipe tmp; slurp_body(in, tmp); if (is_waypoint(label)) After_fragments[label].steps.insert(After_fragments[label].steps.begin(), tmp.steps.begin(), tmp.steps.end()); else raise << "can't tangle after non-waypoint " << label << '\n' << end(); // End after Command Handler } //: after all recipes are loaded, insert fragments at appropriate labels. :(after "Begin Instruction Inserting/Deleting Transforms") Transform.push_back(insert_fragments); // NOT idempotent //: We might need to perform multiple passes, in case inserted fragments //: include more labels that need further insertions. Track which labels we've //: already processed using an extra field. :(before "End instruction Fields") mutable bool tangle_done; :(before "End instruction Constructor") tangle_done = false; :(code) void insert_fragments(const recipe_ordinal r) { insert_fragments(get(Recipe, r)); } void insert_fragments(recipe& r) { trace(101, "transform") << "--- insert fragments into recipe " << r.name << end(); bool made_progress = true; int pass = 0; while (made_progress) { made_progress = false; // create a new vector because insertions invalidate iterators vector result; for (int i = 0; i < SIZE(r.steps); ++i) { const instruction
@echo off
rem build development version of the compiler; can be rerun safely
if not exist csources (
  git clone --depth 1 https://github.com/nim-lang/csources.git
)
if not exist bin\nim.exe (
  cd csources
  if PROCESSOR_ARCHITECTURE == AMD64 (
    SET ARCH=64
  )
  CALL build.bat
  cd ..
)
bin\nim.exe c --skipUserCfg --skipParentCfg koch
koch.exe boot -d:release
koch.exe tools
"def main [\n" " 1:num <- copy 10\n" " \n" " 4:num <- copy 10\n" " recipe2\n" "]\n" "def recipe2 [\n" " 1:num <- copy 11\n" " \n" " 4:num <- copy 11\n" "]\n" "before [\n" " 2:num <- copy 12\n" "]\n" "after [\n" " 3:num <- copy 12\n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 10 in location 1\n" "mem: storing 12 in location 2\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 10 in location 4\n" // recipe2 "mem: storing 11 in location 1\n" "mem: storing 12 in location 2\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 11 in location 4\n" ); // nothing else CHECK_TRACE_COUNT("mem", 8); } void test_tangle_tangles_into_all_labels_with_same_name_2() { run( "def main [\n" " 1:num <- copy 10\n" " \n" " \n" " 4:num <- copy 10\n" "]\n" "before [\n" " 2:num <- copy 12\n" "]\n" "after [\n" " 3:num <- copy 12\n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 10 in location 1\n" "mem: storing 12 in location 2\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 12 in location 2\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 10 in location 4\n" ); // nothing else CHECK_TRACE_COUNT("mem", 6); } void test_tangle_tangles_into_all_labels_with_same_name_3() { run( "def main [\n" " 1:num <- copy 10\n" " \n" " \n" " 4:num <- copy 10\n" "]\n" "before [\n" " 2:num <- copy 12\n" "]\n" "after [\n" " 3:num <- copy 12\n" "]\n" "after [\n" " \n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 10 in location 1\n" "mem: storing 12 in location 2\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 12 in location 2\n" // foo/label1 "mem: storing 12 in location 3\n" "mem: storing 10 in location 4\n" ); // nothing else CHECK_TRACE_COUNT("mem", 6); } void test_tangle_handles_jump_target_inside_fragment() { run( "def main [\n" " 1:num <- copy 10\n" " \n" " 4:num <- copy 10\n" "]\n" "before [\n" " jump +label2:label\n" " 2:num <- copy 12\n" " +label2\n" " 3:num <- copy 12\n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 10 in location 1\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 10 in location 4\n" ); // ignored by jump CHECK_TRACE_DOESNT_CONTAIN("mem: storing 12 in label 2"); // nothing else CHECK_TRACE_COUNT("mem", 3); } void test_tangle_renames_jump_target() { run( "def main [\n" " 1:num <- copy 10\n" " \n" " +label2\n" " 4:num <- copy 10\n" "]\n" "before [\n" " jump +label2:label\n" " 2:num <- copy 12\n" " +label2 # renamed\n" " 3:num <- copy 12\n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 10 in location 1\n" // label1 "mem: storing 12 in location 3\n" "mem: storing 10 in location 4\n" ); // ignored by jump CHECK_TRACE_DOESNT_CONTAIN("mem: storing 12 in label 2"); // nothing else CHECK_TRACE_COUNT("mem", 3); } void test_tangle_jump_to_base_recipe() { run( "def main [\n" " 1:num <- copy 10\n" " \n" " +label2\n" " 4:num <- copy 10\n" "]\n" "before [\n" " jump +label2:label\n" " 2:num <- copy 12\n" " 3:num <- copy 12\n" "]\n" ); CHECK_TRACE_CONTENTS( "mem: storing 10 in location 1\n" // label1 "mem: storing 10 in location 4\n" ); // ignored by jump CHECK_TRACE_DOESNT_CONTAIN("mem: storing 12 in label 2"); CHECK_TRACE_DOESNT_CONTAIN("mem: storing 12 in location 3"); // nothing else CHECK_TRACE_COUNT("mem", 2); } //: ensure that there are no new fragments created for a label after it's already been inserted to void test_new_fragment_after_tangle() { // define a recipe load("def foo [\n" " local-scope\n" "