about summary refs log tree commit diff stats
path: root/034call.cc
blob: 6ab18ba45870bcee43d2f924e479bd5962ae11a2 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//: So far the recipes we define can't run each other. Let's fix that.

:(scenario calling_recipe)
recipe main [
  f
]
recipe f [
  3:number <- add 2, 2
]
+mem: storing 4 in location 3

:(scenario return_on_fallthrough)
recipe main [
  f
  1:number <- copy 0
  2:number <- copy 0
  3:number <- copy 0
]
recipe f [
  4:number <- copy 0
  5:number <- copy 0
]
+run: f
# running f
+run: 4:number <- copy 0
+run: 5:number <- copy 0
# back out to main
+run: 1:number <- copy 0
+run: 2:number <- copy 0
+run: 3:number <- copy 0

:(before "struct routine {")
// Everytime a recipe runs another, we interrupt it and start running the new
// recipe. When that finishes, we continue this one where we left off.
// This requires maintaining a 'stack' of interrupted recipes or 'calls'.
struct call {
  recipe_ordinal running_recipe;
  long long int running_step_index;
  // End call Fields
  call(recipe_ordinal r) {
    running_recipe = r;
    running_step_index = 0;
    // End call Constructor
  }
  ~call() {
    // End call Destructor
  }
};
typedef list<call> call_stack;

:(replace{} "struct routine")
struct routine {
  call_stack calls;
  // End routine Fields
  routine(recipe_ordinal r);
  bool completed() const;
  const vector<instruction>& steps() const;
};
:(code)
routine::routine(recipe_ordinal r) {
  if (Trace_stream) {
    ++Trace_stream->callstack_depth;
    trace(9999, "trace") << "new routine; incrementing callstack depth to " << Trace_stream->callstack_depth << end();
    assert(Trace_stream->callstack_depth < 9000);  // 9998-101 plus cushion
  }
  calls.push_front(call(r));
  // End routine Constructor
}

:(code)
inline call& current_call() {
  return Current_routine->calls.front();
}

//:: now update routine's helpers

:(replace{} "inline long long int& current_step_index()")
inline long long int& current_step_index() {
  assert(!Current_routine->calls.empty());
  return current_call().running_step_index;
}
:(replace{} "inline const string& current_recipe_name()")
inline const string& current_recipe_name() {
  assert(!Current_routine->calls.empty());
  return get(Recipe, current_call().running_recipe).name;
}
:(replace{} "inline const instruction& current_instruction()")
inline const instruction& current_instruction() {
  assert(!Current_routine->calls.empty());
  return to_instruction(current_call());
}
:(code)
inline const instruction& to_instruction(const call& call) {
  return get(Recipe, call.running_recipe).steps.at(call.running_step_index);
}

:(after "Defined Recipe Checks")
// not a primitive; check that it's present in the book of recipes
if (!contains_key(Recipe, inst.operation)) {
  raise_error << maybe(get(Recipe, r).name) << "undefined operation in '" << to_string(inst) << "'\n" << end();
  break;
}
:(replace{} "default:" following "End Primitive Recipe Implementations")
default: {
  const instruction& call_instruction = current_instruction();
  if (Recipe.find(current_instruction().operation) == Recipe.end()) {  // duplicate from Checks
    // stop running this instruction immediately
    ++current_step_index();
    continue;
  }
  // not a primitive; look up the book of recipes
  if (Trace_stream) {
    ++Trace_stream->callstack_depth;
    trace(9999, "trace") << "incrementing callstack depth to " << Trace_stream->callstack_depth << end();
    assert(Trace_stream->callstack_depth < 9000);  // 9998-101 plus cushion
  }
  Current_routine->calls.push_front(call(current_instruction().operation));
  finish_call_housekeeping(call_instruction, ingredients);
  continue;  // not done with caller; don't increment step_index of caller
}
:(code)
void finish_call_housekeeping(const instruction& call_instruction, const vector<vector<double> >& ingredients) {
  // End Call Housekeeping
}

:(scenario calling_undefined_recipe_fails)
% Hide_errors = true;
recipe main [
  foo
]
+error: main: undefined operation in 'foo '

:(scenario calling_undefined_recipe_handles_missing_result)
% Hide_errors = true;
recipe main [
  x:number <- foo
]
+error: main: undefined operation in 'x:number <- foo '

//:: finally, we need to fix the termination conditions for the run loop

:(replace{} "inline bool routine::completed() const")
inline bool routine::completed() const {
  return calls.empty();
}

inline const vector<instruction>& routine::steps() const {
  assert(!calls.empty());
  return get(Recipe, calls.front().running_recipe).steps;
}

:(before "Running One Instruction")
// when we reach the end of one call, we may reach the end of the one below
// it, and the one below that, and so on
while (current_step_index() >= SIZE(Current_routine->steps())) {
  // Falling Through End Of Recipe
  if (Trace_stream) {
    trace(9999, "trace") << "fall-through: exiting " << current_recipe_name() << "; decrementing callstack depth from " << Trace_stream->callstack_depth << end();
    --Trace_stream->callstack_depth;
    assert(Trace_stream->callstack_depth >= 0);
  }
  Current_routine->calls.pop_front();
  if (Current_routine->calls.empty()) return;
  // Complete Call Fallthrough
  // todo: fail if no products returned
  ++current_step_index();
}