about summary refs log tree commit diff stats
path: root/034call.cc
blob: ebfb9534c63547a4817382c05aa19ad313b4c9f6 (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
//: 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
  }
};
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) {
  calls.push_front(call(r));
  // End routine Constructor
}

//:: 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_routine->calls.front().running_step_index;
}
:(replace{} "inline const string& current_recipe_name()")
inline const string& current_recipe_name() {
  assert(!Current_routine->calls.empty());
  return Recipe[Current_routine->calls.front().running_recipe].name;
}
:(replace{} "inline const instruction& current_instruction()")
inline const instruction& current_instruction() {
  assert(!Current_routine->calls.empty());
  return Recipe[Current_routine->calls.front().running_recipe].steps.at(Current_routine->calls.front().running_step_index);
}

:(after "Defined Recipe Checks")
// not a primitive; check that it's present in the book of recipes
if (Recipe.find(inst.operation) == Recipe.end()) {
  raise_error << maybe(Recipe[r].name) << "undefined operation in '" << inst.to_string() << "'\n" << end();
  break;
}
:(replace{} "default:" following "End Primitive Recipe Implementations")
default: {
  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
  Current_routine->calls.push_front(call(current_instruction().operation));
  call_housekeeping:
  ++Callstack_depth;
  assert(Callstack_depth < 9000);  // 9998-101 plus cushion
  continue;  // not done with caller; don't increment current_step_index()
}

:(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 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
  --Callstack_depth;
  Current_routine->calls.pop_front();
  if (Current_routine->calls.empty()) return;
  // Complete Call Fallthrough
  // todo: fail if no products returned
  ++current_step_index();
}

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