:(scenarios run) :(scenario copy_literal) recipe main [ 1:integer <- copy 23:literal ] +run: instruction main/0 +run: ingredient 0 is 23 +mem: storing in location 1 :(scenario copy) recipe main [ 1:integer <- copy 23:literal 2:integer <- copy 1:integer ] +run: instruction main/1 +run: ingredient 0 is 1 +mem: location 1 is 23 +mem: storing in location 2 :(before "End Types") // Each recipe can be 'called' many many times in a program. Each call needs a // little extra information. struct call { recipe_number running_recipe; size_t pc; vector > incoming_atoms; size_t next_ingredient_to_process; vector > outgoing_atoms; call(recipe_number r) :running_recipe(r), pc(0), next_ingredient_to_process(0) {} }; typedef stack call_stack; struct routine { size_t alloc; size_t alloc_max; call_stack calls; size_t limit; size_t running_since; // todo: sleep conditions }; :(code) void run(string form) { recipe_number r = add_recipes(form); routine rr; rr.calls.push(call(r)); run(rr); } void run(routine rr) { // #defines save us the trouble of updating aliases when dependent variables // change. #define TOP_RECIPE Recipe[rr.calls.top().running_recipe] #define instructions TOP_RECIPE.steps while (!rr.calls.empty()) { while (rr.calls.top().pc >= instructions.size()) { rr.calls.pop(); if (rr.calls.empty()) return; // todo: no results returned warning ++rr.calls.top().pc; } size_t& pc = rr.calls.top().pc; //? cout << "instruction " << TOP_RECIPE.name << '/' << pc << '\n'; //? 2 trace("run") << "instruction " << TOP_RECIPE.name << '/' << pc; switch (instructions[pc].operation) { // Primitive Recipe Implementations. case COPY: { trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name; vector data = read_memory(instructions[pc].ingredients[0]); write_memory(instructions[pc].products[0], data); break; } // End Primitive Recipe Implementations. default: { // unknown op = try to call a defined recipe // TODO: this should be later, in the same layer as next_ingredient //? cout << "non primitive op: " << instructions[pc].operation << '\n'; //? 1 if (Recipe.find(instructions[pc].operation) == Recipe.end()) { raise << "undefined operation " << instructions[pc].operation << '\n'; break; } //? cout << "calling " << instructions[pc].operation << '\n'; //? 2 call callee(instructions[pc].operation); for (vector::iterator p = instructions[pc].ingredients.begin(); p != instructions[pc].ingredients.end(); ++p) { //? cout << "push back: " << p->to_string() << '\n'; //? 1 callee.incoming_atoms.push_back(read_memory(*p)); } rr.calls.push(callee); continue; // not done with caller; don't increment pc }} ++pc; } #undef TOP_RECIPE #undef instructions } vector read_memory(reagent x) { //? cout << "read_memory: " << x.to_string() << '\n'; //? 1 vector result; if (x.types[0] == 0) { // literal result.push_back(to_int(x.name)); return result; } int base = to_int(x.name); for (size_t offset = 0; offset < Type[x.types[0]].size; ++offset) { int val = Memory[base+offset]; trace("mem") << "location " << base+offset << " is " << val; result.push_back(val); } return result; } void write_memory(reagent x, vector data) { int base = to_int(x.name); size_t size = size_of(x); if (size != data.size()) raise << "size mismatch in storing to " << x.to_string(); for (size_t offset = 0; offset < size; ++offset) { trace("mem") << "storing in location " << base+offset; Memory[base+offset] = data[offset]; } } :(code) int to_int(string n) { char* end = NULL; int result = strtol(n.c_str(), &end, /*any base*/0); assert(*end == '\0'); return result; } size_t size_of(reagent r) { type_info t = Type[r.types[0]]; if (!t.is_record && !t.is_array) return t.size; return t.size; // TODO }