https://github.com/akkartik/mu/blob/master/020run.cc
  1 //: Phase 3: Start running a loaded and transformed recipe.
  2 //:
  3 //:   The process of running Mu code:
  4 //:     load -> transform -> run
  5 //:
  6 //: So far we've seen recipes as lists of instructions, and instructions point
  7 //: at other recipes. To kick things off Mu needs to know how to run certain
  8 //: 'primitive' recipes. That will then give the ability to run recipes
  9 //: containing these primitives.
 10 //:
 11 //: This layer defines a skeleton with just two primitive recipes: IDLE which
 12 //: does nothing, and COPY, which can copy numbers from one memory location to
 13 //: another. Later layers will add more primitives.
 14 
 15 :(scenario copy_literal)
 16 def main [
 17   1:num <- copy 23
 18 ]
 19 +run: {1: "number"} <- copy {23: "literal"}
 20 +mem: storing 23 in location 1
 21 
 22 :(scenario copy)
 23 def main [
 24   1:num <- copy 23
 25   2:num <- copy 1:num
 26 ]
 27 +run: {2: "number"} <- copy {1: "number"}
 28 +mem: location 1 is 23
 29 +mem: storing 23 in location 2
 30 
 31 :(scenario copy_multiple)
 32 def main [
 33   1:num, 2:num <- copy 23, 24
 34 ]
 35 +mem: storing 23 in location 1
 36 +mem: storing 24 in location 2
 37 
 38 :(before "End Types")
 39 // Book-keeping while running a recipe.
 40 //: Later layers will replace this to support running multiple routines at once.
 41 struct routine {
 42   recipe_ordinal running_recipe;
 43   int running_step_index;
 44   routine(recipe_ordinal r) :running_recipe(r), running_step_index(0) {}
 45   bool completed() const;
 46   const vector<instruction>& steps() const;
 47 };
 48 
 49 :(before "End Globals")
 50 routine* Current_routine = NULL;
 51 :(before "End Reset")
 52 Current_routine = NULL;
 53 
 54 :(code)
 55 void run(const recipe_ordinal r) {
 56   routine rr(r);
 57   Current_routine = &rr;
 58   run_current_routine();
 59   Current_routine = NULL;
 60 }
 61 
 62 void run_current_routine() {
 63   pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
; a screen is an array of pointers to lines, in turn arrays of characters

(function new-screen [
  (default-space:space-address <- new space:literal 30:literal)
  (nrows:integer <- next-input)
  (ncols:integer <- next-input)
  (result:screen-address <- new screen:literal nrows:integer)
  (rowidx:integer <- copy 0:literal)
  { begin
    (curr-line-address-address:line-address-address <- index-address result:screen-address/deref rowidx:integer)
    (curr-line-address-address:line-address-address/deref <- new line:literal ncols:integer)
    (curr-line-address:line-address <- copy curr-line-address-address:line-address-address/deref)
    (rowidx:integer <- add rowidx:integer 1:literal)
    (x:boolean <- not-equal rowidx:integer nrows:integer)
    (loop-if x:boolean)
  }
  (reply result:screen-address)
])
Delimiter">(r.type); 130 } 131 bool is_mu_scalar(const type_tree* type) { 132 if (!type) return false; 133 if (is_mu_address(type)) return false; 134 if (!type->atom) return false; 135 if (is_literal(type)) 136 return type->name != "literal-string"; 137 return size_of(type) == 1; 138 } 139 140 bool is_mu_address(reagent/*copy*/ r) { 141 // End Preprocess is_mu_address(reagent r) 142 return is_mu_address(r.type); 143 } 144 bool is_mu_address(const type_tree* type) { 145 if (!type) return false; 146 if (is_literal(type)) return false; 147 if (type->atom) return false; 148 if (!type->left->atom) { 149 raise << "invalid type " << to_string(type) << '\n' << end(); 150 return false; 151 } 152 return type->left->value == Address_type_ordinal; 153 } 154 155 //: Some helpers. 156 //: Important that they return references into the current routine. 157 158 //: hook replaced in a later layer 159 int& current_step_index() { 160 return Current_routine->running_step_index; 161 } 162 163 //: hook replaced in a later layer 164 recipe_ordinal currently_running_recipe() { 165 return Current_routine->running_recipe; 166 } 167 168 //: hook replaced in a later layer 169 const string& current_recipe_name() { 170 return get(Recipe, Current_routine->running_recipe).name; 171 } 172 173 //: hook replaced in a later layer 174 const recipe& current_recipe() { 175 return get(Recipe, Current_routine->running_recipe); 176 } 177 178 //: hook replaced in a later layer 179 const instruction& current_instruction() { 180 return get(Recipe, Current_routine->running_recipe).steps.at(Current_routine->running_step_index); 181 } 182 183 //: hook replaced in a later layer 184 bool routine::completed() const { 185 return running_step_index >= SIZE(get(Recipe, running_recipe).steps); 186 } 187 188 //: hook replaced in a later layer 189 const vector<instruction>& routine::steps() const { 190 return get(Recipe, running_recipe).steps; 191 } 192 193 //:: Startup flow 194 195 :(before "End Mu Prelude") 196 load_file_or_directory("core.mu"); 197 //? DUMP(""); 198 //? exit(0); 199 200 //: Step 2: load any .mu files provided at the commandline 201 :(before "End Commandline Parsing") 202 // Check For .mu Files 203 if (argc > 1) { 204 // skip argv[0] 205 ++argv; 206 --argc; 207 while (argc > 0) { 208 // ignore argv past '--'; that's commandline args for 'main' 209 if (string(*argv) == "--") break; 210 if (starts_with(*argv, "--")) 211 cerr << "treating " << *argv << " as a file rather than an option\n"; 212 load_file_or_directory(*argv); 213 --argc; 214 ++argv; 215 } 216 if (Run_tests) Recipe.erase(get(Recipe_ordinal, "main")); 217 } 218 transform_all(); 219 //? cerr << to_original_string(get(Type_ordinal, "editor")) << '\n'; 220 //? cerr << to_original_string(get(Recipe, get(Recipe_ordinal, "event-loop"))) << '\n'; 221 //? DUMP(""); 222 //? exit(0); 223 if (trace_contains_errors()) return 1; 224 if (Trace_stream && Run_tests) { 225 // We'll want a trace per test. Clear the trace. 226 delete Trace_stream; 227 Trace_stream = NULL; 228 } 229 save_snapshots(); 230 231 //: Step 3: if we aren't running tests, locate a recipe called 'main' and 232 //: start running it. 233 :(before "End Main") 234 if (!Run_tests && contains_key(Recipe_ordinal, "main") && contains_key(Recipe, get(Recipe_ordinal, "main"))) { 235 // Running Main 236 reset(); 237 trace(2, "run") << "=== Starting to run" << end(); 238 assert(Num_calls_to_transform_all == 1); 239 run_main(argc, argv); 240 } 241 :(code) 242 void run_main(int argc, char* argv[]) { 243 recipe_ordinal r = get(Recipe_ordinal, "main"); 244 if (r) run(r); 245 } 246 247 :(code) 248 void load_file_or_directory(string filename) { 249 if (is_directory(filename)) { 250 load_all(filename); 251 return; 252 } 253 ifstream fin(filename.c_str()); 254 if (!fin) { 255 cerr << "no such file '" << filename << "'\n" << end(); // don't raise, just warn. just in case it's just a name for a scenario to run. 256 return; 257 } 258 trace(9990, "load") << "=== " << filename << end(); 259 load(fin); 260 fin.close(); 261 } 262 263 bool is_directory(string path) { 264 struct stat info; 265 if (stat(path.c_str(), &info)) return false; // error 266 return info.st_mode & S_IFDIR; 267 } 268 269 void load_all(string dir) { 270 dirent** files; 271 int num_files = scandir(dir.c_str(), &files, NULL, alphasort); 272 for (int i = 0; i < num_files; ++i) { 273 string curr_file = files[i]->d_name; 274 if (isdigit(curr_file.at(0)) && ends_with(curr_file, ".mu")) 275 load_file_or_directory(dir+'/'+curr_file); 276 free(files[i]); 277 files[i] = NULL; 278 } 279 free(files); 280 } 281 282 bool ends_with(const string& s, const string& pat) { 283 for (string::const_reverse_iterator p = s.rbegin(), q = pat.rbegin(); q != pat.rend(); ++p, ++q) { 284 if (p == s.rend()) return false; // pat too long 285 if (*p != *q) return false; 286 } 287 return true; 288 } 289 290 :(before "End Includes") 291 #include <dirent.h> 292 #include <sys/stat.h> 293 294 //:: Reading from memory, writing to memory. 295 296 :(code) 297 vector<double> read_memory(reagent/*copy*/ x) { 298 // Begin Preprocess read_memory(x) 299 vector<double> result; 300 if (x.name == "null") result.push_back(/*alloc id*/0); 301 if (is_literal(x)) { 302 result.push_back(x.value); 303 return result; 304 } 305 // End Preprocess read_memory(x) 306 int size = size_of(x); 307 for (int offset = 0; offset < size; ++offset) { 308 double val = get_or_insert(Memory, x.value+offset); 309 trace("mem") << "location " << x.value+offset << " is " << no_scientific(val) << end(); 310 result.push_back(val); 311 } 312 return result; 313 } 314 315 void write_memory(reagent/*copy*/ x, const vector<double>& data) { 316 assert(Current_routine); // run-time only 317 // Begin Preprocess write_memory(x, data) 318 if (!x.type) { 319 raise << "can't write to '" << to_string(x) << "'; no type\n" << end(); 320 return; 321 } 322 if (is_dummy(x)) return; 323 if (is_literal(x)) return; 324 // End Preprocess write_memory(x, data) 325 if (x.value == 0) { 326 raise << "can't write to location 0 in '" << to_original_string(current_instruction()) << "'\n" << end(); 327 return; 328 } 329 if (size_mismatch(x, data)) { 330 raise << maybe(current_recipe_name()) << "size mismatch in storing to '" << x.original_string << "' (" << size_of(x) << " vs " << SIZE(data) << ") at '" << to_original_string(current_instruction()) << "'\n" << end(); 331 return; 332 } 333 // End write_memory(x) Special-cases 334 for (int offset = 0; offset < SIZE(data); ++offset) { 335 assert(x.value+offset > 0); 336 trace("mem") << "storing " << no_scientific(data.at(offset)) << " in location " << x.value+offset << end(); 337 //? if (Foo) cerr << "mem: storing " << no_scientific(data.at(offset)) << " in location " << x.value+offset << '\n'; 338 put(Memory, x.value+offset, data.at(offset)); 339 } 340 } 341 342 :(code) 343 int size_of(const reagent& r) { 344 if (!r.type) return 0; 345 // End size_of(reagent r) Special-cases 346 return size_of(r.type); 347 } 348 int size_of(const type_tree* type) { 349 if (!type) return 0; 350 if (type->atom) { 351 if (type->value == -1) return 1; // error value, but we'll raise it elsewhere 352 if (type->value == 0) return 1; 353 // End size_of(type) Atom Special-cases 354 } 355 else { 356 if (!type->left->atom) { 357 raise << "invalid type " << to_string(type) << '\n' << end(); 358 return 0; 359 } 360 if (type->left->value == Address_type_ordinal) return 2; // address and alloc id 361 // End size_of(type) Non-atom Special-cases 362 } 363 // End size_of(type) Special-cases 364 return 1; 365 } 366 367 bool size_mismatch(const reagent& x, const vector<double>& data) { 368 if (!x.type) return true; 369 // End size_mismatch(x) Special-cases 370 //? if (size_of(x) != SIZE(data)) cerr << size_of(x) << " vs " << SIZE(data) << '\n'; 371 return size_of(x) != SIZE(data); 372 } 373 374 bool is_literal(const reagent& r) { 375 return is_literal(r.type); 376 } 377 bool is_literal(const type_tree* type) { 378 if (!type) return false; 379 if (!type->atom) return false; 380 return type->value == 0; 381 } 382 383 bool scalar(const vector<int>& x) { 384 return SIZE(x) == 1; 385 } 386 bool scalar(const vector<double>& x) { 387 return SIZE(x) == 1; 388 } 389 390 // helper for tests 391 void run(const string& form) { 392 vector<recipe_ordinal> tmp = load(form); 393 transform_all(); 394 if (tmp.empty()) return; 395 if (trace_contains_errors()) { 396 if (Save_trace && Trace_stream) Trace_stream->save(); 397 return; 398 } 399 // if a test defines main, it probably wants to start there regardless of 400 // definition order 401 if (contains_key(Recipe, get(Recipe_ordinal, "main"))) 402 run(get(Recipe_ordinal, "main")); 403 else 404 run(tmp.front()); 405 } 406 407 :(scenario run_label) 408 def main [ 409 +foo 410 1:num <- copy 23 411 2:num <- copy 1:num 412 ] 413 +run: {1: "number"} <- copy {23: "literal"} 414 +run: {2: "number"} <- copy {1: "number"} 415 -run: +foo 416 417 :(scenario run_dummy) 418 def main [ 419 _ <- copy 0 420 ] 421 +run: _ <- copy {0: "literal"} 422 423 :(scenario run_null) 424 def main [ 425 1:&:num <- copy null 426 ] 427 428 :(scenario write_to_0_disallowed) 429 % Hide_errors = true; 430 def main [ 431 0:num <- copy 34 432 ] 433 -mem: storing 34 in location 0 434 435 //: Mu is robust to various combinations of commas and spaces. You just have 436 //: to put spaces around the '<-'. 437 438 :(scenario comma_without_space) 439 def main [ 440 1:num, 2:num <- copy 2,2 441 ] 442 +mem: storing 2 in location 1 443 444 :(scenario space_without_comma) 445 def main [ 446 1:num, 2:num <- copy 2 2 447 ] 448 +mem: storing 2 in location 1 449 450 :(scenario comma_before_space) 451 def main [ 452 1:num, 2:num <- copy 2, 2 453 ] 454 +mem: storing 2 in location 1 455 456 :(scenario comma_after_space) 457 def main [ 458 1:num, 2:num <- copy 2 ,2 459 ] 460 +mem: storing 2 in location 1 461 462 //:: Counters for trying to understand where Mu programs are spending their 463 //:: time. 464 465 :(before "End Globals") 466 bool Run_profiler = false; 467 // We'll key profile information by recipe_ordinal rather than name because 468 // it's more efficient, and because later layers will show more than just the 469 // name of a recipe. 470 // 471 // One drawback: if you're clearing recipes your profile will be inaccurate. 472 // So far that happens in tests, and in 'run-sandboxed' in a later layer. 473 map<recipe_ordinal, int> Instructions_running; 474 :(before "End Commandline Options(*arg)") 475 else if (is_equal(*arg, "--profile")) { 476 Run_profiler = true; 477 } 478 :(after "Running One Instruction") 479 if (Run_profiler) Instructions_running[currently_running_recipe()]++; 480 :(before "End One-time Setup") 481 atexit(dump_profile); 482 :(code) 483 void dump_profile() { 484 if (!Run_profiler) return; 485 if (Run_tests) { 486 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"; 487 return; 488 } 489 ofstream fout; 490 fout.open("profile.instructions"); 491 if (fout) { 492 for (map<recipe_ordinal, int>::iterator p = Instructions_running.begin(); p != Instructions_running.end(); ++p) { 493 fout << std::setw(9) << p->second << ' ' << header_label(p->first) << '\n'; 494 } 495 } 496 fout.close(); 497 // End dump_profile 498 } 499 500 // overridden in a later layer 501 string header_label(const recipe_ordinal r) { 502 return get(Recipe, r).name; 503 }