//: Allow code for recipes to be pulled in from multiple places and inserted
//: at special labels called 'waypoints'. Unlike jump targets, a recipe can
//: have multiple ambiguous waypoints with the same name. Any 'before' and
//: 'after' fragments will simply be inserted at all applicable waypoints.
//: Waypoints are always surrounded by '<>', e.g. <handle-request>.
//:
//: TODO: switch recipe.steps to a more efficient data structure.

:(scenario tangle_before)
recipe main [
  1:number <- copy 0
  <label1>
  3:number <- copy 0
]

before <label1> [
  2:number <- copy 0
]
+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;
set<string /*label*/> Fragments_used;
:(before "End Setup")
Before_fragments.clear();
After_fragments.clear();
Fragments_used.clear();

:(before "End Command Handlers")
else if (command == "before") {
  string label = next_word(in);
  recipe tmp = slurp_body(in);
  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 label " << label << '\n' << end();
}
else if (command == "after") {
  string label = next_word(in);
  recipe tmp = slurp_body(in);
  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 label " << label << '\n' << end();
}

//: after all recipes are loaded, insert fragments at appropriate labels.

:(after "int main")
  Transform.push_back(insert_fragments);

//; 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) {
  bool made_progress = true;
  long long int pass = 0;
  while (made_progress) {
    made_progress = false;
    // create a new vector because insertions invalidate iterators
    vector<instruction> result;
    for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) {
      const instruction& inst = Recipe[r].steps.at(i);
      if (!inst.is_label || !is_waypoint(inst.label) || inst.tangle_done) {
        result.push_back(inst);
        continue;
      }
      inst.tangle_done = true;
      made_progress = true;
      Fragments_used.insert(inst.label);
      ostringstream prefix;
      prefix << '+' << Recipe[r].name << '_' << pass << '_' << i;
      if (Before_fragments.find(inst.label) != Before_fragments.end()) {
        append_fragment(result, Before_fragments[inst.label].steps, prefix.str());
      }
      result.push_back(inst);
      if (After_fragments.find(inst.label) != After_fragments.end()) {
        append_fragment(result, After_fragments[inst.label].steps, prefix.str());
      }
    }
    Recipe[r].steps.swap(result);
    ++pass;
  }
}

void append_fragment(vector<instruction>& base, const vector<instruction>& patch, const string prefix) {
  // append 'patch' to 'base' while keeping 'base' oblivious to any new jump
  // targets in 'patch' oblivious to 'base' by prepending 'prefix' to them.
  // we might tangle the same fragment at multiple points in a single recipe,
  // and we need to avoid duplicate jump targets.
  // so we'll keep jump targets local to the specific before/after fragment
  // that introduces them.
  set<string> jump_targets;
  for (long long int i = 0; i < SIZE(<2">
<meta name="syntax" content="none">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=,use_input_for_pc=fallback">
<meta name="colorscheme" content="minimal-light">
<style>
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #000000; background-color: #ffffd7; }
body { font-size:12pt; font-family: monospace; color: #000000; background-color: #ffffd7; }
a { color:inherit; }
* { font-size:12pt; font-size: 1em; }
.subxFunction { color: #af5f00; text-decoration: underline; }
.SpecialChar { color: #d70000; }
.LineNr { }
.subxS1Comment { color: #0000af; }
.subxComment { color: #005faf; }
.Constant { color: #008787; }
-->
</style>

<script>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
  var lineNum;
  lineNum = window.location.hash;
  lineNum = lineNum.substr(1); /* strip off '#' */

  if (lineNum.indexOf('L') == -1) {
    lineNum = 'L'+lineNum;
  }
  var lineElem = document.getElementById(lineNum);
  /* Always jump to new location even if the line was hidden inside a fold, or
   * we corrected the raw number to a line ID.
   */
  if (lineElem) {
    lineElem.scrollIntoView(true);
  }
  return true;
}
if ('onhashchange' in window) {
  window.onhashchange = JumpToLine;
}

-->
</script>
</head>
<body onload='JumpToLine();'>
<a href='https://github.com/akkartik/mu/blob/main/312copy.subx'>https://github.com/akkartik/mu/blob/main/312copy.subx</a>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr"> 1 </span>== code
<span id="L2" class="LineNr"> 2 </span>
<span id="L3" class="LineNr"> 3 </span><span class="subxFunction">copy-array-object</span>:  <span class="subxComment"># src: (addr array T), dest-ah: (addr handle array T)</span>
<span id="L4" class="LineNr"> 4 </span>    <span class="subxS1Comment"># . prologue</span>
<span id="L5" class="LineNr"> 5 </span>    55/push-ebp
<span id="L6" class="LineNr"> 6 </span>    89/&lt;- %ebp 4/r32/esp
<span id="L7" class="LineNr"> 7 </span>    <span class="subxComment">#</span>
<span id="L8" class="LineNr"> 8 </span>    (<a href='120allocate.subx.html#L690'>copy-array</a> <span class="SpecialChar"><a href='120allocate.subx.html#L27'>Heap</a></span> *(ebp+8) *(ebp+0xc))
<span id="L9" class="LineNr"> 9 </span><span class="Constant">$copy-array-object:end</span>:
<span id="L10" class="LineNr">10 </span>    <span class="subxS1Comment"># . epilogue</span>
<span id="L11" class="LineNr">11 </span>    89/&lt;- %esp 5/r32/ebp
<span id="L12" class="LineNr">12 </span>    5d/pop-to-ebp
<span id="L13" class="LineNr">13 </span>    c3/return
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->
3:number <- copy 12 ] +mem: storing 10 in location 1 # label1 +mem: storing 12 in location 3 +mem: storing 10 in location 4 # ignored by jump -mem: storing 12 in label 2 # nothing else $mem: 3 :(scenario tangle_renames_jump_target) recipe main [ 1:number <- copy 10 <label1> +label2 4:number <- copy 10 ] before <label1> [ jump +label2:label 2:number <- copy 12 +label2 # renamed 3:number <- copy 12 ] +mem: storing 10 in location 1 # label1 +mem: storing 12 in location 3 +mem: storing 10 in location 4 # ignored by jump -mem: storing 12 in label 2 # nothing else $mem: 3 :(scenario tangle_jump_to_base_recipe) recipe main [ 1:number <- copy 10 <label1> +label2 4:number <- copy 10 ] before <label1> [ jump +label2:label 2:number <- copy 12 3:number <- copy 12 ] +mem: storing 10 in location 1 # label1 +mem: storing 10 in location 4 # ignored by jump -mem: storing 12 in label 2 -mem: storing 12 in location 3 # nothing else $mem: 2