//: A big convenience high-level languages provide is the ability to name memory
//: locations. In mu, a transform called 'transform_names' provides this
//: convenience.

:(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 = true;
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")
map<recipe_number, map<string, index_t> > Name;
:(after "Clear Other State For recently_added_recipes")
for (index_t i = 0; i < recently_added_recipes.size(); ++i) {
  Name.erase(recently_added_recipes[i]);
}

:(code)
void transform_names(const recipe_number r) {
  map<string, index_t>& names = Name[r];
  // store the indices 'used' so far in the map
  index_t& curr_idx = names[""];
  ++curr_idx;  // avoid using index 0, benign skip in some other cases
//?   cout << "Recipe " << r << ": " << Recipe[r].name << '\n'; //? 3
//?   cout << Recipe[r].steps.size() << '\n'; //? 2
  for (index_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 (index_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'; //? 3
//?       cout << "ingredient " << inst.ingredients[in].to_string() << '\n'; //? 1
      if (inst.ingredients[in].name == "default-space")
        inst.ingredients[in].initialized = true;
      if (inst.ingredients[in].types.empty())
        raise << "missing type in " << inst.to_string() << '\n';
      assert(!inst.ingredients[in].types.empty());
      if (inst.ingredients[in].types[0]  // not a literal
          && !inst.ingredients[in].initialized
          && !is_number(inst.ingredients[in].name)) {
        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 (index_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
          && !is_number(inst.products[out].name)) {
        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 map<string, index_t>& names) {
  return names.find(r.name) != names.end();
}

index_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 (index_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) {
  co
//: Clean syntax to manipulate and check the console in scenarios.
//: Instruction 'assume-console' implicitly creates a variable called
//: 'console' that is accessible inside other 'run' instructions in the
//: scenario. Like with the fake screen, 'assume-console' transparently
//: supports unicode.

//: first make sure we don't mangle this instruction in other transforms
:(before "End initialize_transform_rewrite_literal_string_to_text()")
recipes_taking_literal_strings.insert("assume-console");

:(code)
void test_keyboard_in_scenario() {
  run_mu_scenario(
      "scenario keyboard-in-scenario [\n"
      "  assume-console [\n"
      "    type [abc]\n"
      "  ]\n"
      "  run [\n"
      "    1:char, 2:bool <- read-key console\n"
      "    3:char, 4:bool <- read-key console\n"
      "    5:char, 6:bool <- read-key console\n"
      "    7:char, 8:bool, 9:bool <- read-key console\n"
      "  ]\n"
      "  memory-should-contain [\n"
      "    1 <- 97\n"  // 'a'
      "    2 <- 1\n"
      "    3 <- 98\n"  // 'b'
      "    4 <- 1\n"
      "    5 <- 99\n"  // 'c'
      "    6 <- 1\n"
      "    7 <- 0\n"  // unset
      "    8 <- 1\n"
      "    9 <- 1\n"  // end of test events
      "  ]\n"
      "]\n"
  );
}

:(before "End Scenario Globals")
extern const int CONSOLE = next_predefined_global_for_scenarios(/*size_of(address:console)*/2);
//: give 'console' a fixed location in scenarios
:(before "End Special Scenario Variable Names(r)")
Name[r]["console"] = CONSOLE;
//: make 'console' always a raw location in scenarios
:(before "End is_special_name Special-cases")
if (s == "console") return true;

:(before