about summary refs log tree commit diff stats
path: root/cpp/054closure_name
blob: 9c61d5cda5c7adc87fa9efd02fadb538f6fd18ec (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//: Writing to a literal (not computed) address of 0 in a recipe chains two
//: spaces together. When a variable has a property of /space:1, it looks up
//: the variable in the chained/surrounding space. /space:2 looks up the
//: surrounding space of the surrounding space, etc.

:(scenario "closure")
recipe main [
  default-space:address:array:location <- new location:type, 30:literal
  1:address:array:location/names:init-counter <- init-counter
#?   $print [AAAAAAAAAAAAAAAA]
#?   $print 1:address:array:location
  2:integer/raw <- increment-counter 1:address:array:location/names:init-counter
  3:integer/raw <- increment-counter 1:address:array:location/names:init-counter
]

recipe init-counter [
  default-space:address:array:location <- new location:type, 30:literal
  x:integer <- copy 23:literal
  y:integer <- copy 3:literal  # variable that will be incremented
  reply default-space:address:array:location
]

recipe increment-counter [
  default-space:address:array:location <- new space:literal, 30:literal
  0:address:array:location/names:init-counter <- next-ingredient  # outer space must be created by 'init-counter' above
  y:integer/space:1 <- add y:integer/space:1, 1:literal  # increment
  y:integer <- copy 234:literal  # dummy
  reply y:integer/space:1
]

+name: recipe increment-counter is surrounded by init-counter
+mem: storing 5 in location 3

//: To make this work, compute the recipe that provides names for the
//: surrounding space of each recipe. This must happen before transform_names.

:(before "End Globals")
unordered_map<recipe_number, recipe_number> Surrounding_space;

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

:(code)
void collect_surrounding_spaces(const recipe_number r) {
  for (size_t i = 0; i < Recipe[r].steps.size(); ++i) {
    const instruction& inst = Recipe[r].steps[i];
    if (inst.is_label) continue;
    for (size_t j = 0; j < inst.products.size(); ++j) {
      if (isa_literal(inst.products[j])) continue;
      if (inst.products[j].name != "0") continue;
      if (inst.products[j].types.size() != 3
          || inst.products[j].types[0] != Type_number["address"]
          || inst.products[j].types[1] != Type_number["array"]
          || inst.products[j].types[2] != Type_number["location"]) {
        raise << "slot 0 should always have type address:array:location, but is " << inst.products[j].to_string() << '\n';
        continue;
      }
      vector<string> s = property(inst.products[j], "names");
      if (s.empty())
        raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << die();
      if (s.size() > 1) raise << "slot 0 should have a single value in /names, got " << inst.products[j].to_string() << '\n';
      string surrounding_recipe_name = s[0];
      if (Surrounding_space.find(r) != Surrounding_space.end()
          && Surrounding_space[r] != Recipe_number[surrounding_recipe_name]) {
        raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n';
        continue;
      }
      trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name;
      Surrounding_space[r] = Recipe_number[surrounding_recipe_name];
    }
  }
}

vector<string> property(const reagent& r, const string& name) {
  for (size_t p = 0; p != r.properties.size(); ++p) {
    if (r.properties[p].first == name)
      return r.properties[p].second;
  }
  return vector<string>();
}

//: Once surrounding spaces are available, transform_names uses them to handle
//: /space properties.

:(replace{} "size_t lookup_name(const reagent& r, const recipe_number default_recipe)")
size_t lookup_name(const reagent& x, const recipe_number default_recipe) {
//?   cout << "AAA " << default_recipe << " " << Recipe[default_recipe].name << '\n'; //? 2
//?   cout << "AAA " << x.to_string() << '\n'; //? 1
  if (!has_property(x, "space")) {
    if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << die();
    return Name[default_recipe][x.name];
  }
  vector<string> p = property(x, "space");
  if (p.size() != 1) raise << "/space property should have exactly one (non-negative integer) value\n";
  int n = to_int(p[0]);
  assert(n >= 0);
  recipe_number surrounding_recipe = lookup_surrounding_recipe(default_recipe, n);
  set<recipe_number> done;
  vector<recipe_number> path;
  return lookup_name(x, surrounding_recipe, done, path);
}

// If the recipe we need to lookup this name in doesn't have names done yet,
// recursively call transform_names on it.
size_t lookup_name(const reagent& x, const recipe_number r, set<recipe_number>& done, vector<recipe_number>& path) {
  if (!Name[r].empty()) return Name[r][x.name];
  if (done.find(r) != done.end()) {
    raise << "can't compute address of " << x.to_string() << " because ";
    for (size_t i = 1; i < path.size(); ++i) {
      raise << path[i-1] << " requires computing names of " << path[i] << '\n';
    }
    raise << path[path.size()-1] << " requires computing names of " << r << "..ad infinitum\n" << die();
    return 0;
  }
  done.insert(r);
  path.push_back(r);
  transform_names(r);  // Not passing 'done' through. Might this somehow cause an infinite loop?
  assert(!Name[r].empty());
  return Name[r][x.name];
}

recipe_number lookup_surrounding_recipe(const recipe_number r, size_t n) {
  if (n == 0) return r;
  if (Surrounding_space.find(r) == Surrounding_space.end()) {
    raise << "don't know surrounding recipe of " << Recipe[r].name << '\n';
    return 0;
  }
  assert(Surrounding_space[r]);
  return lookup_surrounding_recipe(Surrounding_space[r], n-1);
}

//: weaken use-before-set warnings just a tad
:(replace{} "bool already_transformed(const reagent& r, const unordered_map<string, int>& names)")
bool already_transformed(const reagent& r, const unordered_map<string, int>& names) {
  if (has_property(r, "space")) {
    vector<string> p = property(r, "space");
    assert(p.size() == 1);
    if (p[0] != "0") return true;
  }
  return names.find(r.name) != names.end();
}

:(before "End Includes")
#include<set>
using std::set;