//: Phase 3: Start running a loaded and transformed recipe. //: //: The process of running Mu code: //: load -> transform -> run //: //: So far we've seen recipes as lists of instructions, and instructions point //: at other recipes. To kick things off Mu needs to know how to run certain //: 'primitive' recipes. That will then give the ability to run recipes //: containing these primitives. //: //: This layer defines a skeleton with just two primitive recipes: IDLE which //: does nothing, and COPY, which can copy numbers from one memory location to //: another. Later layers will add more primitives. :(scenario copy_literal) def main [ 1:num <- copy 23 ] +run: {1: "number"} <- copy {23: "literal"} +mem: storing 23 in location 1 :(scenario copy) def main [ 1:num <- copy 23 2:num <- copy 1:num ] +run: {2: "number"} <- copy {1: "number"} +mem: location 1 is 23 +mem: storing 23 in location 2 :(scenario copy_multiple) def main [ 1:num, 2:num <- copy 23, 24 ] +mem: storing 23 in location 1 +mem: storing 24 in location 2 :(before "End Types") // Book-keeping while running a recipe. //: Later layers will replace this to support running multiple routines at once. struct routine { recipe_ordinal running_recipe; int running_step_index; routine(recipe_ordinal r) :running_recipe(r), running_step_index(0) {} bool completed() const; const vector& steps() const; }; :(before "End Globals") routine* Current_routine = NULL; :(before "End Reset") Current_routine = NULL; :(code) void run(const recipe_ordinal r) { routine rr(r); Current_routine = &rr; run_current_routine(); Current_routine = NULL; } void run_current_routine() { while (should_continue_running(Current_routine)) { // beware: may modify Current_routine // Running One Instruction if (current_instruction().is_label) { ++current_step_index(); continue; } trace(Initial_callstack_depth + Trace_stream->callstack_depth, "run") << to_string(current_instruction()) << end(); //? if (Foo) cerr << "run: " << to_string(current_instruction()) << '\n'; if (get_or_insert(Memory, 0) != 0) { raise << "something wrote to location 0; this should never happen\n" << end(); put(Memory, 0, 0); } // read all ingredients from memory, each potentially spanning multiple locations vector > ingredients; if (should_copy_ingredients()) { for (int i = 0; i < SIZE(current_instruction().ingredients); ++i) ingredients.push_back(read_memory(current_instruction().ingredients.at(i))); } // instructions below will write to 'products' vector > products; //: This will be a large switch that later layers will often insert cases //: into. Never call 'continue' within it. Instead, we'll explicitly //: control which of the following stages after the switch we run for each //: instruction. bool write_products = true; bool fall_through_to_next_instruction = true; switch (current_instruction().operation) { // Primitive Recipe Implementations case COPY: { copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin())); break; } // End Primitive Recipe Implementations default: { raise << "not a primitive op: " << current_instruction().operation << '\n' << end(); } } //: used by a later layer if (write_products) { if (SIZE(products) < SIZE(current_instruction().products)) { raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products in '" << to_original_string(current_instruction()) << "'\n" << end(); } else { for (int i = 0; i < SIZE(current_instruction().products); ++i) { // Writing Instruction Product(i) write_memory(current_instruction().products.at(i), products.at(i)); } } } // End Running One Instruction if (fall_through_to_next_instruction) ++current_step_index(); } stop_running_current_routine:; } :(code) //: hook replaced in a later layer bool should_continue_running(const routine* current_routine) { assert(current_routine == Current_routine); // argument passed in just to make caller readable above return !Current_routine->completed(); } bool should_copy_ingredients() { // End should_copy_ingredients Special-cases return true; } bool is_mu_scalar(reagent/*copy*/ r) { return is_mu_scalar(r.type); } bool is_mu_scalar(const type_tree* type) { if (!type) return false; if (is_mu_address(type)) return false; if (!type->atom) return false; if (is_literal(type)) return type->name != "literal-string"; return size_of(type) == 1; } bool is_mu_address(reagent/*copy*/ r) { // End Preprocess is_mu_address(reagent r) return is_mu_address(r.type); } bool is_mu_address(const type_tree* type) { if (!type) return false; if (is_literal(type)) return false; if (type->atom) return false; if (!type->left->atom) { raise << "invalid type " << to_string(type) << '\n' << end(); return false; } return type->left->value == Address_type_ordinal; } //: Some helpers. //: Important that they return references into the current routine. //: hook replaced in a later layer int& current_step_index() { return Current_routine->running_step_index; } //: hook replaced in a later layer recipe_ordinal currently_running_recipe() { return Current_routine->running_recipe; } //: hook replaced in a later layer const string& current_recipe_name() { return get(Recipe, Current_routine->running_recipe).name; } //: hook replaced in a later layer const recipe& current_recipe() { return get(Recipe, Current_routine->running_recipe); } //: hook replaced in a later layer const instruction& current_instruction() { return get(Recipe, Curr
*
!**/
!*.*

# Cache
nimcache/
rnimcache/
dnimcache/

*.o
!/icons/*.o
*.obj
*.ilk
*.exp
*.pdb
*.lib
*.dll
*.exe
*.so
*.dylib
*.zip
*.iss
*.log
*.pdb

mapping.txt
tags
install.sh
deinstall.sh

doc/html/
doc/*.html
doc/*.pdf
doc/*.idx
/web/upload
/build/*
bin/*

# iOS specific wildcards.
*.mode1v3
*.pbxuser
*.perspective
*.perspectivev3
*.swp
.DS_Store
.tags
project.xcworkspace/
xcuserdata/

# Generated files.
/compile.json
/compiler/nim.dot
/reject.json
/run.json
/tools/dochack/dochack.js
*.json
/pkgstemp/**/*
# for `nim doc foo.nim`
/*.html
lib/**/*.html
#/testresults.html #covered by /*.html

/testresults.json
testament.db
/tests/**/*.json
/tests/**/*.js
/csources
dist/

# Private directories and files (IDEs)
.*/
~*

# testament cruft; TODO: generate these in a gitignore'd dir in the first place.
testresults/
test.txt
/test.ini

tweeter.db
tweeter_test.db
megatest.nim

/outputExpected.txt
/outputGotten.txt

/lib/pure/*.js

!/.builds/

!/.github

# ignore debug dirs generated by dsymutil on OSX
*.dSYM

nimdoc.out.css

# for `nim c -r nimdoc/tester` etc; this can be in multiple places
htmldocs
1: "number"} <- copy {23: "literal"} +run: {2: "number"} <- copy {1: "number"} -run: +foo :(scenario run_dummy) def main [ _ <- copy 0 ] +run: _ <- copy {0: "literal"} :(scenario run_null) def main [ 1:&:num <- copy null ] :(scenario write_to_0_disallowed) % Hide_errors = true; def main [ 0:num <- copy 34 ] -mem: storing 34 in location 0 //: Mu is robust to various combinations of commas and spaces. You just have //: to put spaces around the '<-'. :(scenario comma_without_space) def main [ 1:num, 2:num <- copy 2,2 ] +mem: storing 2 in location 1 :(scenario space_without_comma) def main [ 1:num, 2:num <- copy 2 2 ] +mem: storing 2 in location 1 :(scenario comma_before_space) def main [ 1:num, 2:num <- copy 2, 2 ] +mem: storing 2 in location 1 :(scenario comma_after_space) def main [ 1:num, 2:num <- copy 2 ,2 ] +mem: storing 2 in location 1 //:: Counters for trying to understand where Mu programs are spending their //:: time. :(before "End Globals") bool Run_profiler = false; // We'll key profile information by recipe_ordinal rather than name because // it's more efficient, and because later layers will show more than just the // name of a recipe. // // One drawback: if you're clearing recipes your profile will be inaccurate. // So far that happens in tests, and in 'run-sandboxed' in a later layer. map Instructions_running; :(before "End Commandline Options(*arg)") else if (is_equal(*arg, "--profile")) { Run_profiler = true; } :(after "Running One Instruction") if (Run_profiler) Instructions_running[currently_running_recipe()]++; :(before "End One-time Setup") atexit(dump_profile); :(code) void dump_profile() { if (!Run_profiler) return; if (Run_tests) { cerr << "It's not a good idea to profile a run with tests, since tests can create conflicting recipes and mislead you. To try it anyway, comment out this check in the code.\n"; return; } ofstream fout; fout.open("profile.instructions"); if (fout) { for (map::iterator p = Instructions_running.begin(); p != Instructions_running.end(); ++p) { fout << std::setw(9) << p->second << ' ' << header_label(p->first) << '\n'; } } fout.close(); // End dump_profile } // overridden in a later layer string header_label(const recipe_ordinal r) { return get(Recipe, r).name; }