about summary refs log tree commit diff stats
path: root/cpp/041name
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/041name')
-rw-r--r--cpp/041name172
1 files changed, 172 insertions, 0 deletions
diff --git a/cpp/041name b/cpp/041name
new file mode 100644
index 00000000..375f9854
--- /dev/null
+++ b/cpp/041name
@@ -0,0 +1,172 @@
+//: A big convenience high-level languages provide is the ability to name memory
+//: locations. In mu, a transform called 'convert-names' provides this
+//: convenience.
+
+:(scenarios run)
+:(scenario "convert_names")
+recipe main [
+  x:integer <- copy 0:literal
+]
++name: assign x 1
++run: instruction main/0
++mem: storing 0 in location 1
+
+:(scenario "convert_names_warns")
+hide warnings
+recipe main [
+  x:integer <- copy y:integer
+]
++warn: use before set: y in main
+
+:(after "int main")
+  Transform.push_back(transform_names);
+
+:(before "End Globals")
+unordered_map<recipe_number, unordered_map<string, int> > Name;
+:(after "Clear Other State For recently_added_recipes")
+for (size_t i = 0; i < recently_added_recipes.size(); ++i) {
+  Name.erase(recently_added_recipes[i]);
+}
+
+:(code)
+void transform_names(const recipe_number r) {
+  unordered_map<string, int>& names = Name[r];
+  int curr_idx = 1;
+//?   cout << "Recipe " << r << '\n'; //? 2
+//?   cout << Recipe[r].steps.size(); //? 1
+  for (size_t i = 0; i < Recipe[r].steps.size(); ++i) {
+//?     cout << "instruction " << i << '\n'; //? 2
+    instruction& inst = Recipe[r].steps[i];
+    // Per-recipe Transforms
+    // map names to addresses
+    for (size_t in = 0; in < inst.ingredients.size(); ++in) {
+//?       cout << "ingredients\n"; //? 2
+      if (is_raw(inst.ingredients[in])) continue;
+//?       cout << "ingredient " << inst.ingredients[in].name << '\n'; //? 2
+      if (inst.ingredients[in].name == "default-space")
+        inst.ingredients[in].initialized = true;
+      assert(!inst.ingredients[in].types.empty());
+      if (inst.ingredients[in].types[0]  // not a literal
+          && !inst.ingredients[in].initialized
+          && inst.ingredients[in].name.find_first_not_of("0123456789-.") != string::npos) {
+        if (!already_transformed(inst.ingredients[in], names)) {
+          raise << "use before set: " << inst.ingredients[in].name << " in " << Recipe[r].name << '\n';
+        }
+        inst.ingredients[in].set_value(lookup_name(inst.ingredients[in], r));
+//?         cout << "lookup ingredient " << Recipe[r].name << "/" << i << ": " << inst.ingredients[in].to_string() << '\n'; //? 1
+      }
+    }
+    for (size_t out = 0; out < inst.products.size(); ++out) {
+//?       cout << "products\n"; //? 1
+      if (is_raw(inst.products[out])) continue;
+//?       cout << "product " << out << '/' << inst.products.size() << " " << inst.products[out].name << '\n'; //? 4
+//?       cout << inst.products[out].types[0] << '\n'; //? 1
+      if (inst.products[out].name == "default-space")
+        inst.products[out].initialized = true;
+      if (inst.products[out].types[0]  // not a literal
+          && !inst.products[out].initialized
+          && inst.products[out].name.find_first_not_of("0123456789-.") != string::npos) {
+        if (names.find(inst.products[out].name) == names.end()) {
+          trace("name") << "assign " << inst.products[out].name << " " << curr_idx;
+          names[inst.products[out].name] = curr_idx;
+          curr_idx += size_of(inst.products[out]);
+        }
+        inst.products[out].set_value(lookup_name(inst.products[out], r));
+//?         cout << "lookup product " << Recipe[r].name << "/" << i << ": " << inst.products[out].to_string() << '\n'; //? 1
+      }
+    }
+  }
+}
+
+bool already_transformed(const reagent& r, const unordered_map<string, int>& names) {
+  return names.find(r.name) != names.end();
+}
+
+size_t lookup_name(const reagent& r, const recipe_number default_recipe) {
+  return Name[default_recipe][r.name];
+}
+
+type_number skip_addresses(const vector<type_number>& types) {
+  for (size_t i = 0; i < types.size(); ++i) {
+    if (types[i] != Type_number["address"]) return types[i];
+  }
+  raise << "expected a container" << '\n' << die();
+  return -1;
+}
+
+int find_element_name(const type_number t, const string& name) {
+  const type_info& container = Type[t];
+//?   cout << "looking for element " << name << " in type " << container.name << " with " << container.element_names.size() << " elements\n"; //? 1
+  for (size_t i = 0; i < container.element_names.size(); ++i) {
+    if (container.element_names[i] == name) return i;
+  }
+  raise << "unknown element " << name << " in container " << t << '\n' << die();
+  return -1;
+}
+
+bool is_raw(const reagent& r) {
+  for (size_t i = /*skip value+type*/1; i < r.properties.size(); ++i) {
+    if (r.properties[i].first == "raw") return true;
+  }
+  return false;
+}
+
+:(scenario "convert_names_passes_dummy")
+# _ is just a dummy result that never gets consumed
+recipe main [
+  _, x:integer <- copy 0:literal
+]
++name: assign x 1
+-name: assign _ 1
+
+//: one reserved word that we'll need later
+:(scenario "convert_names_passes_default_space")
+recipe main [
+  default-space:integer, x:integer <- copy 0:literal
+]
++name: assign x 1
+-name: assign default-space 1
+
+//: an escape hatch to suppress name conversion that we'll use later
+:(scenario "convert_names_passes_raw")
+recipe main [
+  x:integer/raw <- copy 0:literal
+]
+-name: assign x 1
+
+//: update our running example container for the next test
+:(before "End Mu Types Initialization")
+Type[point].element_names.push_back("x");
+Type[point].element_names.push_back("y");
+:(scenario "convert_names_transforms_container_elements")
+recipe main [
+  a:integer <- get 0:point, y:offset
+  b:integer <- get 0:point, x:offset
+]
++name: element y of type point is at offset 1
++name: element x of type point is at offset 0
+
+:(after "Per-recipe Transforms")
+// replace element names of containers with offsets
+if (inst.operation == Recipe_number["get"]
+    || inst.operation == Recipe_number["get-address"]) {
+  // at least 2 args, and second arg is offset
+  assert(inst.ingredients.size() >= 2);
+//?   cout << inst.ingredients[1].to_string() << '\n'; //? 1
+  assert(isa_literal(inst.ingredients[1]));
+  if (inst.ingredients[1].name.find_first_not_of("0123456789") == string::npos) continue;
+  // since first non-address in base type must be a container, we don't have to canonize
+  type_number container = skip_addresses(inst.ingredients[0].types);
+  inst.ingredients[1].set_value(find_element_name(container, inst.ingredients[1].name));
+  trace("name") << "element " << inst.ingredients[1].name << " of type " << Type[container].name << " is at offset " << inst.ingredients[1].value;
+}
+
+//: this test is actually illegal so can't call run
+:(scenarios transform_test)
+:(scenario "convert_names_handles_containers")
+recipe main [
+  a:point <- copy 0:literal
+  b:integer <- copy 0:literal
+]
++name: assign a 1
++name: assign b 3