From 4fe9f5e8257770a6b1de1aa94748609acd37f0f6 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 3 Aug 2015 00:49:38 -0700 Subject: 1925 --- html/001help.cc.html | 2 +- html/003trace.cc.html | 12 +- html/003trace.test.cc.html | 2 +- html/010vm.cc.html | 2 +- html/011load.cc.html | 2 +- html/013literal_string.cc.html | 9 +- html/014literal_noninteger.cc.html | 2 +- html/020run.cc.html | 17 +- html/021arithmetic.cc.html | 4 +- html/023jump.cc.html | 4 +- html/024compare.cc.html | 2 +- html/029tools.cc.html | 29 +- html/030container.cc.html | 14 +- html/031address.cc.html | 17 +- html/032array.cc.html | 20 +- html/033exclusive_container.cc.html | 6 +- html/034call.cc.html | 22 +- html/035call_ingredient.cc.html | 2 +- html/036call_reply.cc.html | 2 +- html/037recipe.cc.html | 2 +- html/038scheduler.cc.html | 4 +- html/039wait.cc.html | 2 +- html/040brace.cc.html | 2 +- html/041jump_label.cc.html | 8 +- html/042name.cc.html | 11 +- html/043new.cc.html | 3 +- html/044space.cc.html | 6 +- html/045space_surround.cc.html | 2 +- html/046closure_name.cc.html | 2 +- html/047global.cc.html | 2 +- html/048typecheck.cc.html | 15 +- html/050scenario.cc.html | 4 +- html/053continuation.cc.html | 2 +- html/060string.mu.html | 68 +- html/061channel.mu.html | 66 +- html/062array.mu.html | 8 +- html/063list.mu.html | 12 +- html/064random.cc.html | 2 +- html/065duplex_list.mu.html | 120 +-- html/066stream.mu.html | 18 +- html/071print.mu.html | 114 +-- html/072scenario_screen.cc.html | 4 +- html/074console.mu.html | 18 +- html/075scenario_console.cc.html | 2 +- html/080trace_browser.cc.html | 2 +- html/081run_interactive.cc.html | 53 +- html/082persist.cc.html | 45 +- html/callcc.mu.html | 2 +- html/channel.mu.html | 8 +- html/chessboard.mu.html | 50 +- html/console.mu.html | 2 +- html/counters.mu.html | 4 +- html/edit.mu.html | 1886 +++++++++++++++++++++++------------ html/factorial.mu.html | 4 +- html/fork.mu.html | 6 +- html/global.mu.html | 2 +- html/tangle.mu.html | 4 +- 57 files changed, 1710 insertions(+), 1025 deletions(-) (limited to 'html') diff --git a/html/001help.cc.html b/html/001help.cc.html index b7f03457..9affa16c 100644 --- a/html/001help.cc.html +++ b/html/001help.cc.html @@ -13,8 +13,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .SalientComment { color: #00ffff; } diff --git a/html/003trace.cc.html b/html/003trace.cc.html index ea09cda6..22764804 100644 --- a/html/003trace.cc.html +++ b/html/003trace.cc.html @@ -136,7 +136,7 @@ struct trace_stream { string curr_layer; int curr_depth; string dump_layer; - string collect_layer; // if set, ignore all other layers + set<string> collect_layers; // if not empty, ignore all absent layers ofstream null_stream; // never opens a file, so writes silently fail trace_stream() :curr_stream(NULL), curr_depth(0) {} ~trace_stream() { if (curr_stream) delete curr_stream; } @@ -146,13 +146,21 @@ struct trace_stream { } ostream& stream(int depth, string layer) { - if (!collect_layer.empty() && layer != collect_layer) return null_stream; + if (!is_collecting(layer)) return null_stream; curr_stream = new ostringstream; curr_layer = layer; curr_depth = depth; return *curr_stream; } + bool is_collecting(const string& layer) { + return collect_layers.empty() || collect_layers.find(layer) != collect_layers.end(); + } + + bool is_narrowly_collecting(const string& layer) { + return collect_layers.find(layer) != collect_layers.end(); + } + // be sure to call this before messing with curr_stream or curr_layer void newline() { if (!curr_stream) return; diff --git a/html/003trace.test.cc.html b/html/003trace.test.cc.html index e800a8cc..839fb91a 100644 --- a/html/003trace.test.cc.html +++ b/html/003trace.test.cc.html @@ -13,8 +13,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.cSpecial { color: #008000; } .Delimiter { color: #a04060; } +.cSpecial { color: #008000; } .Constant { color: #00a0a0; } .Comment { color: #9090ff; } --> diff --git a/html/010vm.cc.html b/html/010vm.cc.html index 4d1ed5fc..cb1f962b 100644 --- a/html/010vm.cc.html +++ b/html/010vm.cc.html @@ -13,8 +13,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .SalientComment { color: #00ffff; } diff --git a/html/011load.cc.html b/html/011load.cc.html index 73e924c9..97ae9cea 100644 --- a/html/011load.cc.html +++ b/html/011load.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/013literal_string.cc.html b/html/013literal_string.cc.html index b6c539cf..906c367c 100644 --- a/html/013literal_string.cc.html +++ b/html/013literal_string.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -166,11 +166,14 @@ if (s.at//: b) Escape newlines in the string to make it more friendly to trace(). :(after "string reagent::to_string()") - if (!properties.at(0).second.empty() && properties.at(0).second.at(0) == "literal-string") { + if (is_literal_string(*this)) return emit_literal_string(name); - } :(code) +bool is_literal_string(const reagent& x) { + return !x.properties.at(0).second.empty() && x.properties.at(0).second.at(0) == "literal-string"; +} + string emit_literal_string(string name) { size_t pos = 0; while (pos != string::npos) diff --git a/html/014literal_noninteger.cc.html b/html/014literal_noninteger.cc.html index 7235bf47..7275c135 100644 --- a/html/014literal_noninteger.cc.html +++ b/html/014literal_noninteger.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/020run.cc.html b/html/020run.cc.html index bf21779f..dbbfce8a 100644 --- a/html/020run.cc.html +++ b/html/020run.cc.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -99,7 +99,7 @@ void run_current_routine() trace(Initial_callstack_depth+Callstack_depth, "run") << current_instruction().to_string() << end(); if (Memory[0] != 0) { raise << "something wrote to location 0; this should never happen\n" << end(); - break; + Memory[0] = 0; } // Read all ingredients from memory. // Each ingredient loads a vector of values rather than a single value; mu @@ -124,10 +124,13 @@ void run_current_routine() cout << "not a primitive op: " << current_instruction().operation << '\n'; } } - if (SIZE(products) < SIZE(current_instruction().products)) + if (SIZE(products) < SIZE(current_instruction().products)) { raise << SIZE(products) << " vs " << SIZE(current_instruction().products) << ": failed to write to all products! " << current_instruction().to_string() << end(); - for (long long int i = 0; i < SIZE(current_instruction().products); ++i) { - write_memory(current_instruction().products.at(i), products.at(i)); + } + else { + for (long long int i = 0; i < SIZE(current_instruction().products); ++i) { + write_memory(current_instruction().products.at(i), products.at(i)); + } } // End of Instruction ++current_step_index(); @@ -169,7 +172,7 @@ if (!Run_tests) (); //? Trace_file = "interactive"; //? 2 //? START_TRACING_UNTIL_END_OF_SCOPE; //? 2 -//? Trace_stream->collect_layer = "app"; //? 1 +//? Trace_stream->collect_layer.insert("app"); //? 1 transform_all(); recipe_ordinal r = Recipe_ordinal[string("main")]; if (r) run(r); diff --git a/html/021arithmetic.cc.html b/html/021arithmetic.cc.html index b43cb286..c3e35229 100644 --- a/html/021arithmetic.cc.html +++ b/html/021arithmetic.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -197,13 +197,13 @@ DIVIDE_WITH_REMAINDER, Recipe_ordinal["divide-with-remainder"] = DIVIDE_WITH_REMAINDER; :(before "End Primitive Recipe Implementations") case DIVIDE_WITH_REMAINDER: { + products.resize(2); if (SIZE(ingredients) != 2) { raise << current_recipe_name() << ": 'divide-with-remainder' requires exactly two ingredients, but got " << current_instruction().to_string() << '\n' << end(); break; } long long int quotient = ingredients.at(0).at(0) / ingredients.at(1).at(0); long long int remainder = static_cast<long long int>(ingredients.at(0).at(0)) % static_cast<long long int>(ingredients.at(1).at(0)); - products.resize(2); // very large integers will lose precision products.at(0).push_back(quotient); products.at(1).push_back(remainder); diff --git a/html/023jump.cc.html b/html/023jump.cc.html index 472a964b..b25b74f7 100644 --- a/html/023jump.cc.html +++ b/html/023jump.cc.html @@ -13,10 +13,10 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } -.traceAbsent { color: #c00000; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/024compare.cc.html b/html/024compare.cc.html index 7944d4ff..df30ac7b 100644 --- a/html/024compare.cc.html +++ b/html/024compare.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/029tools.cc.html b/html/029tools.cc.html index 58b813e1..0f8ba6cd 100644 --- a/html/029tools.cc.html +++ b/html/029tools.cc.html @@ -14,14 +14,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } -.Identifier { color: #804000; } .CommentedCode { color: #6c6c6c; } +.Identifier { color: #804000; } --> @@ -47,15 +47,22 @@ TRACE, Recipe_ordinal["trace"] = TRACE; :(before "End Primitive Recipe Implementations") case TRACE: { - if (SIZE(ingredients) != 2) { - raise << current_recipe_name() << ": 'trace' takes exactly two ingredients rather than '" << current_instruction().to_string() << "'\n" << end(); - break; + if (SIZE(ingredients) == 2) { + assert(is_literal(current_instruction().ingredients.at(0))); + string label = current_instruction().ingredients.at(0).name; + assert(is_literal(current_instruction().ingredients.at(1))); + string message = current_instruction().ingredients.at(1).name; + trace(1, label) << message << end(); + } + else if (SIZE(ingredients) == 1) { + assert(is_literal(current_instruction().ingredients.at(0))); + string message = current_instruction().ingredients.at(0).name; +//? cerr << "tracing " << message << '\n'; //? 1 + trace(1, "app") << message << end(); + } + else { + raise << current_recipe_name() << ": 'trace' takes one or two ingredients rather than '" << current_instruction().to_string() << "'\n" << end(); } - assert(is_literal(current_instruction().ingredients.at(0))); - string label = current_instruction().ingredients.at(0).name; - assert(is_literal(current_instruction().ingredients.at(1))); - string message = current_instruction().ingredients.at(1).name; - trace(1, label) << message << end(); break; } @@ -221,8 +228,8 @@ case _SYSTEM: { raise << current_recipe_name() << ": '$system' requires exactly one ingredient, but got none\n" << end(); break; } - products.resize(1); int status = system(current_instruction().ingredients.at(0).name.c_str()); + products.resize(1); products.at(0).push_back(status); break; } diff --git a/html/030container.cc.html b/html/030container.cc.html index 5d939c14..f56bcc74 100644 --- a/html/030container.cc.html +++ b/html/030container.cc.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -149,6 +149,10 @@ case GET: { } reagent base = current_instruction().ingredients.at(0); long long int base_address = base.value; + if (base_address == 0) { + raise << current_recipe_name() << ": tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + break; + } type_ordinal base_type = base.types.at(0); if (Type[base_type].kind != container) { raise << current_recipe_name () << ": first ingredient of 'get' should be a container, but got " << base.original_string << '\n' << end(); @@ -167,7 +171,6 @@ case GET: { trace(Primitive_recipe_depth, "run") << "address to copy is " << src << end(); if (offset < 0 || offset >= SIZE(Type[base_type].elements)) { raise << current_recipe_name() << ": invalid offset " << offset << " for " << Type[base_type].name << '\n' << end(); - products.resize(1); break; } type_ordinal src_type = Type[base_type].elements.at(offset).at(0); @@ -226,6 +229,10 @@ Recipe_ordinal["get-address"] = GET_ADDR case GET_ADDRESS: { reagent base = current_instruction().ingredients.at(0); long long int base_address = base.value; + if (base_address == 0) { + raise << current_recipe_name() << ": tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + break; + } type_ordinal base_type = base.types.at(0); if (Type[base_type].kind != container) { raise << current_recipe_name () << ": first ingredient of 'get-address' should be a container, but got " << base.original_string << '\n' << end(); @@ -239,7 +246,6 @@ case GET_ADDRESS: { long long int offset = ingredients.at(1).at(0); if (offset < 0 || offset >= SIZE(Type[base_type].elements)) { raise << "invalid offset " << offset << " for " << Type[base_type].name << '\n' << end(); - products.resize(1); break; } long long int result = base_address; diff --git a/html/031address.cc.html b/html/031address.cc.html index 1709e1a1..06c08de7 100644 --- a/html/031address.cc.html +++ b/html/031address.cc.html @@ -13,9 +13,10 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } @@ -59,6 +60,20 @@ recipe main [ :(before "long long int base = x.value" following "void write_memory(reagent x, vector<double> data)") x = canonize(x); +if (x.value == 0) { + raise << "can't write to location 0 in '" << current_instruction().to_string() << "'\n" << end(); + return; +} + +//: writes to address 0 always loudly fail +:(scenario store_to_0_warns) +% Hide_warnings = true; +recipe main [ + 1:address:number <- copy 0 + 1:address:number/lookup <- copy 34 +] +-mem: storing 34 in location 0 ++warn: can't write to location 0 in '1:address:number/lookup <- copy 34' :(code) reagent canonize(reagent x) { diff --git a/html/032array.cc.html b/html/032array.cc.html index 6086d339..249453b3 100644 --- a/html/032array.cc.html +++ b/html/032array.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } @@ -118,17 +118,20 @@ case INDEX: { break; } reagent base = canonize(current_instruction().ingredients.at(0)); - long long int base_address = base.value; if (base.types.at(0) != Type_ordinal["array"]) { raise << current_recipe_name () << ": 'index' on a non-array " << base.original_string << '\n' << end(); break; } + long long int base_address = base.value; + if (base_address == 0) { + raise << current_recipe_name() << ": tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + break; + } reagent offset = canonize(current_instruction().ingredients.at(1)); vector<double> offset_val(read_memory(offset)); vector<type_ordinal> element_type = array_element(base.types); if (offset_val.at(0) < 0 || offset_val.at(0) >= Memory[base_address]) { raise << current_recipe_name() << ": invalid index " << offset_val.at(0) << '\n' << end(); - products.resize(1); break; } long long int src = base_address + 1 + offset_val.at(0)*size_of(element_type); @@ -210,17 +213,20 @@ case INDEX_ADDRESS: { break; } reagent base = canonize(current_instruction().ingredients.at(0)); - long long int base_address = base.value; if (base.types.at(0) != Type_ordinal["array"]) { raise << current_recipe_name () << ": 'index-address' on a non-array " << base.original_string << '\n' << end(); break; } + long long int base_address = base.value; + if (base_address == 0) { + raise << current_recipe_name() << ": tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + break; + } reagent offset = canonize(current_instruction().ingredients.at(1)); vector<double> offset_val(read_memory(offset)); vector<type_ordinal> element_type = array_element(base.types); if (offset_val.at(0) < 0 || offset_val.at(0) >= Memory[base_address]) { raise << current_recipe_name() << ": invalid index " << offset_val.at(0) << '\n' << end(); - products.resize(1); break; } long long int result = base_address + 1 + offset_val.at(0)*size_of(element_type); @@ -286,6 +292,10 @@ case LENGTH: { raise << "tried to calculate length of non-array " << x.original_string << '\n' << end(); break; } + if (x.value == 0) { + raise << current_recipe_name() << ": tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + break; + } products.resize(1); products.at(0).push_back(Memory[x.value]); break; diff --git a/html/033exclusive_container.cc.html b/html/033exclusive_container.cc.html index 2aaf30fb..efccf02f 100644 --- a/html/033exclusive_container.cc.html +++ b/html/033exclusive_container.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } @@ -123,6 +123,10 @@ case MAYBE_CONVERT: { } reagent base = canonize(current_instruction().ingredients.at(0)); long long int base_address = base.value; + if (base_address == 0) { + raise << current_recipe_name() << ": tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + break; + } type_ordinal base_type = base.types.at(0); if (Type[base_type].kind != exclusive_container) { raise << current_recipe_name () << ": first ingredient of 'maybe-convert' should be an exclusive-container, but got " << base.original_string << '\n' << end(); diff --git a/html/034call.cc.html b/html/034call.cc.html index 5e9d93b0..e408b840 100644 --- a/html/034call.cc.html +++ b/html/034call.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } @@ -117,8 +117,10 @@ inline const instruction& current_instruction(){ // not a primitive; try to look up the book of recipes if (Recipe.find(current_instruction().operation) == Recipe.end()) { - raise << "undefined operation " << current_instruction().operation << ": " << current_instruction().to_string() << '\n' << end(); - break; + raise << current_recipe_name() << ": undefined operation in '" << current_instruction().to_string() << "'\n" << end(); + // stop running this instruction immediately + ++current_step_index(); + continue; } Current_routine->calls.push_front(call(current_instruction().operation)); call_housekeeping: @@ -127,6 +129,20 @@ default: { continue; // not done with caller; don't increment current_step_index() } +:(scenario calling_undefined_recipe_warns) +% Hide_warnings = true; +recipe main [ + foo +] ++warn: main: undefined operation in 'foo ' + +:(scenario calling_undefined_recipe_handles_missing_result) +% Hide_warnings = true; +recipe main [ + x:number <- foo +] ++warn: 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") diff --git a/html/035call_ingredient.cc.html b/html/035call_ingredient.cc.html index 14c718a6..0435c8f9 100644 --- a/html/035call_ingredient.cc.html +++ b/html/035call_ingredient.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/036call_reply.cc.html b/html/036call_reply.cc.html index b293886c..3404cd37 100644 --- a/html/036call_reply.cc.html +++ b/html/036call_reply.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/037recipe.cc.html b/html/037recipe.cc.html index e0d7a2c4..4fe59bb0 100644 --- a/html/037recipe.cc.html +++ b/html/037recipe.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/038scheduler.cc.html b/html/038scheduler.cc.html index 667ad6e4..d6cb3c3f 100644 --- a/html/038scheduler.cc.html +++ b/html/038scheduler.cc.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/039wait.cc.html b/html/039wait.cc.html index 311f37ca..2b5eeac4 100644 --- a/html/039wait.cc.html +++ b/html/039wait.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/040brace.cc.html b/html/040brace.cc.html index 7d468a9d..c2928477 100644 --- a/html/040brace.cc.html +++ b/html/040brace.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/041jump_label.cc.html b/html/041jump_label.cc.html index 05493404..960c7294 100644 --- a/html/041jump_label.cc.html +++ b/html/041jump_label.cc.html @@ -13,15 +13,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.traceAbsent { color: #c00000; } -.cSpecial { color: #008000; } -.Constant { color: #00a0a0; } .traceContains { color: #008000; } +.Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } -.Identifier { color: #804000; } +.traceAbsent { color: #c00000; } .CommentedCode { color: #6c6c6c; } +.Identifier { color: #804000; } --> diff --git a/html/042name.cc.html b/html/042name.cc.html index 1a0cc746..106b8e63 100644 --- a/html/042name.cc.html +++ b/html/042name.cc.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -45,6 +45,7 @@ recipe main [ +name: assign x 1 +mem: storing 0 in location 1 +:(scenarios transform) :(scenario transform_names_warns) % Hide_warnings = true; recipe main [ @@ -176,12 +177,16 @@ recipe main [ -name: assign _ 1 //: an escape hatch to suppress name conversion that we'll use later +:(scenarios run) :(scenario transform_names_passes_raw) +% Hide_warnings = true; recipe main [ x:number/raw <- copy 0 ] -name: assign x 1 ++warn: can't write to location 0 in 'x:number/raw <- copy 0' +:(scenarios transform) :(scenario transform_names_warns_when_mixing_names_and_numeric_locations) % Hide_warnings = true; recipe main [ @@ -233,7 +238,7 @@ recipe main [ if (inst.operation == Recipe_ordinal["get"] || inst.operation == Recipe_ordinal["get-address"]) { if (SIZE(inst.ingredients) != 2) { - raise << Recipe[r].name << ": exactly 2 ingredients expected in '" << current_instruction().to_string() << "'\n" << end(); + raise << Recipe[r].name << ": exactly 2 ingredients expected in '" << inst.to_string() << "'\n" << end(); break; } if (!is_literal(inst.ingredients.at(1))) diff --git a/html/043new.cc.html b/html/043new.cc.html index 6175f3dc..e35772db 100644 --- a/html/043new.cc.html +++ b/html/043new.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } @@ -97,6 +97,7 @@ case NEW: { } if (!scalar(ingredients.at(0))) { raise << current_recipe_name() << ": first ingredient of 'new' should be a type, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + break; } // compute the space we need long long int size = 0; diff --git a/html/044space.cc.html b/html/044space.cc.html index 7f4e742e..3f5b1b8c 100644 --- a/html/044space.cc.html +++ b/html/044space.cc.html @@ -14,15 +14,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } -.Identifier { color: #804000; } +.traceAbsent { color: #c00000; } .CommentedCode { color: #6c6c6c; } +.Identifier { color: #804000; } --> diff --git a/html/045space_surround.cc.html b/html/045space_surround.cc.html index 1b356165..87f5ff46 100644 --- a/html/045space_surround.cc.html +++ b/html/045space_surround.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/046closure_name.cc.html b/html/046closure_name.cc.html index b406f481..44854c80 100644 --- a/html/046closure_name.cc.html +++ b/html/046closure_name.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/047global.cc.html b/html/047global.cc.html index 941f8c90..6ff1dacd 100644 --- a/html/047global.cc.html +++ b/html/047global.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/048typecheck.cc.html b/html/048typecheck.cc.html index aafbd8f5..fe5be341 100644 --- a/html/048typecheck.cc.html +++ b/html/048typecheck.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -90,7 +90,8 @@ void deduce_missing_type(map<string(metadata.find(x.name) == metadata.end()) return; copy(metadata[x.name].begin(), metadata[x.name].end(), inserter(x.types, x.types.begin())); assert(x.properties.at(0).second.empty()); - x.properties.at(0).second.push_back("as-before"); + x.properties.at(0).second.resize(metadata[x.name].size()); + x.properties.push_back(pair<string, vector<string> >("as-before", vector<string>())); } :(scenario transform_types_fills_in_missing_types_in_product) @@ -113,8 +114,14 @@ recipe main [ x:number <- copy 2 ] +warn: missing type in 'x <- copy 1' -+warn: x <- copy 1: reagent not initialized: x -+warn: main: size mismatch in storing to x at 'x <- copy 1' + +:(scenario typo_in_address_type_warns) +% Hide_warnings = true; +recipe main [ + y:address:charcter <- new character:type + *y <- copy 67 +] ++warn: unknown type: charcter diff --git a/html/050scenario.cc.html b/html/050scenario.cc.html index dff6c56f..6126a5ce 100644 --- a/html/050scenario.cc.html +++ b/html/050scenario.cc.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/053continuation.cc.html b/html/053continuation.cc.html index 1118064c..171c0b7d 100644 --- a/html/053continuation.cc.html +++ b/html/053continuation.cc.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } .cSpecial { color: #008000; } .Constant { color: #00a0a0; } .SalientComment { color: #00ffff; } -.traceAbsent { color: #c00000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/060string.mu.html b/html/060string.mu.html index 9b30c7fa..9f3c3c99 100644 --- a/html/060string.mu.html +++ b/html/060string.mu.html @@ -13,14 +13,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .muScenario { color: #00af00; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } .CommentedCode { color: #6c6c6c; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -68,7 +68,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } scenario string-equal-reflexive [ run [ - default-space:address:array:location <- new location:type, 30 + default-space:address:array:location <- new location:type, 30 x:address:array:character <- new [abc] 3:boolean/raw <- string-equal x, x ] @@ -79,7 +79,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } scenario string-equal-identical [ run [ - default-space:address:array:location <- new location:type, 30 + default-space:address:array:location <- new location:type, 30 x:address:array:character <- new [abc] y:address:array:character <- new [abc] 3:boolean/raw <- string-equal x, y @@ -91,7 +91,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } scenario string-equal-distinct-lengths [ run [ - default-space:address:array:location <- new location:type, 30 + default-space:address:array:location <- new location:type, 30 x:address:array:character <- new [abc] y:address:array:character <- new [abcd] 3:boolean/raw <- string-equal x, y @@ -109,7 +109,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } scenario string-equal-with-empty [ run [ - default-space:address:array:location <- new location:type, 30 + default-space:address:array:location <- new location:type, 30 x:address:array:character <- new [] y:address:array:character <- new [abcd] 3:boolean/raw <- string-equal x, y @@ -121,7 +121,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } scenario string-equal-common-lengths-but-distinct [ run [ - default-space:address:array:location <- new location:type, 30 + default-space:address:array:location <- new location:type, 30 x:address:array:character <- new [abc] y:address:array:character <- new [abd] 3:boolean/raw <- string-equal x, y @@ -140,13 +140,13 @@ container buffer [ recipe new-buffer [ local-scope #? $print default-space:address:array:location, 10/newline - result:address:buffer <- new buffer:type - len:address:number <- get-address *result, length:offset + result:address:buffer <- new buffer:type + len:address:number <- get-address *result, length:offset *len:address:number <- copy 0 - s:address:address:array:character <- get-address *result, data:offset + s:address:address:array:character <- get-address *result, data:offset capacity:number, found?:boolean <- next-ingredient assert found?, [new-buffer must get a capacity argument] - *s <- new character:type, capacity + *s <- new character:type, capacity #? $print *s, 10/newline reply result ] @@ -155,11 +155,11 @@ container buffer [ local-scope in:address:buffer <- next-ingredient # double buffer size - x:address:address:array:character <- get-address *in, data:offset + x:address:address:array:character <- get-address *in, data:offset oldlen:number <- length **x newlen:number <- multiply oldlen, 2 olddata:address:array:character <- copy *x - *x <- new character:type, newlen + *x <- new character:type, newlen # copy old contents i:number <- copy 0 { @@ -177,8 +177,8 @@ container buffer [ recipe buffer-full? [ local-scope in:address:buffer <- next-ingredient - len:number <- get *in, length:offset - s:address:array:character <- get *in, data:offset + len:number <- get *in, length:offset + s:address:array:character <- get *in, data:offset capacity:number <- length *s result:boolean <- greater-or-equal len, capacity reply result @@ -189,7 +189,7 @@ container buffer [ local-scope in:address:buffer <- next-ingredient c:character <- next-ingredient - len:address:number <- get-address *in, length:offset + len:address:number <- get-address *in, length:offset { # backspace? just drop last character if it exists and return backspace?:boolean <- equal c, 8/backspace @@ -205,7 +205,7 @@ container buffer [ break-unless full? in <- grow-buffer in } - s:address:array:character <- get *in, data:offset + s:address:array:character <- get *in, data:offset #? $print [array underlying buf: ], s, 10/newline #? $print [index: ], *len, 10/newline dest:address:character <- index-address *s, *len @@ -219,18 +219,18 @@ container buffer [ run [ local-scope x:address:buffer <- new-buffer 3 - s1:address:array:character <- get *x:address:buffer, data:offset + s1:address:array:character <- get *x:address:buffer, data:offset x:address:buffer <- buffer-append x:address:buffer, 97 # 'a' x:address:buffer <- buffer-append x:address:buffer, 98 # 'b' x:address:buffer <- buffer-append x:address:buffer, 99 # 'c' - s2:address:array:character <- get *x:address:buffer, data:offset + s2:address:array:character <- get *x:address:buffer, data:offset 1:boolean/raw <- equal s1:address:array:character, s2:address:array:character 2:array:character/raw <- copy *s2:address:array:character +buffer-filled x:address:buffer <- buffer-append x:address:buffer, 100 # 'd' - s3:address:array:character <- get *x:address:buffer, data:offset + s3:address:array:character <- get *x:address:buffer, data:offset 10:boolean/raw <- equal s1:address:array:character, s3:address:array:character - 11:number/raw <- get *x:address:buffer, length:offset + 11:number/raw <- get *x:address:buffer, length:offset 12:array:character/raw <- copy *s3:address:array:character ] memory-should-contain [ @@ -305,9 +305,9 @@ container buffer [ tmp <- buffer-append tmp, 45 # '-' } # reverse buffer into string result - len:number <- get *tmp, length:offset - buf:address:array:character <- get *tmp, data:offset - result:address:array:character <- new character:type, len + len:number <- get *tmp, length:offset + buf:address:array:character <- get *tmp, data:offset + result:address:array:character <- new character:type, len i:number <- subtract len, 1 # source index, decreasing j:number <- copy 0 # destination index, increasing { @@ -333,11 +333,11 @@ container buffer [ break-if in reply 0 } - len:number <- get *in, length:offset + len:number <- get *in, length:offset #? $print [size ], len, 10/newline - s:address:array:character <- get *in, data:offset + s:address:array:character <- get *in, data:offset # we can't just return s because it is usually the wrong length - result:address:array:character <- new character:type, len + result:address:array:character <- new character:type, len i:number <- copy 0 { #? $print i #? 1 @@ -393,7 +393,7 @@ container buffer [ b:address:array:character <- next-ingredient b-len:number <- length *b result-len:number <- add a-len, b-len - result:address:array:character <- new character:type, result-len + result:address:array:character <- new character:type, result-len # copy a into result result-idx:number <- copy 0 i:number <- copy 0 @@ -459,7 +459,7 @@ container buffer [ #? $print tem-len, [ ], $result-len, 10/newline rewind-ingredients _ <- next-ingredient # skip template - result:address:array:character <- new character:type, result-len + result:address:array:character <- new character:type, result-len # repeatedly copy sections of template and 'holes' into result result-idx:number <- copy 0 i:number <- copy 0 @@ -632,7 +632,7 @@ container buffer [ { at-end?:boolean <- greater-or-equal start, len break-unless at-end? - result:address:array:character <- new character:type, 0 + result:address:array:character <- new character:type, 0 reply result } curr:character <- index *s, start @@ -654,7 +654,7 @@ container buffer [ } # result = new character[end+1 - start] new-len:number <- subtract end, start, -1 - result:address:array:character <- new character:type, new-len + result:address:array:character <- new character:type, new-len # i = start, j = 0 i:number <- copy start j:number <- copy 0 @@ -1050,7 +1050,7 @@ container buffer [ { empty?:boolean <- equal len, 0 break-unless empty? - result:address:array:address:array:character <- new location:type, 0 + result:address:array:address:array:character <- new location:type, 0 reply result } # count #pieces we need room for @@ -1065,7 +1065,7 @@ container buffer [ loop } # allocate space - result:address:array:address:array:character <- new location:type, count + result:address:array:address:array:character <- new location:type, count # repeatedly copy slices start..end until delimiter into result[curr-result] curr-result:number <- copy 0 start:number <- copy 0 @@ -1216,7 +1216,7 @@ container buffer [ end:number <- min len, end # allocate space for result len <- subtract end, start - result:address:array:character <- new character:type, len + result:address:array:character <- new character:type, len # copy start..end into result[curr-result] src-idx:number <- copy start dest-idx:number <- copy 0 diff --git a/html/061channel.mu.html b/html/061channel.mu.html index 89df9b83..b8cfae4c 100644 --- a/html/061channel.mu.html +++ b/html/061channel.mu.html @@ -13,14 +13,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.muScenario { color: #00af00; } -.Delimiter { color: #a04060; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } +.muScenario { color: #00af00; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -69,18 +69,18 @@ container channel [ recipe new-channel [ local-scope # result = new channel - result:address:channel <- new channel:type + result:address:channel <- new channel:type # result.first-full = 0 - full:address:number <- get-address *result, first-full:offset + full:address:number <- get-address *result, first-full:offset *full <- copy 0 # result.first-free = 0 - free:address:number <- get-address *result, first-free:offset + free:address:number <- get-address *result, first-free:offset *free <- copy 0 # result.data = new location[ingredient+1] capacity:number <- next-ingredient capacity <- add capacity, 1 # unused slot for 'full?' below - dest:address:address:array:location <- get-address *result, data:offset - *dest <- new location:type, capacity + dest:address:address:array:location <- get-address *result, data:offset + *dest <- new location:type, capacity reply result ] @@ -93,12 +93,12 @@ container channel [ # block if chan is full full:boolean <- channel-full? chan break-unless full - full-address:address:number <- get-address *chan, first-full:offset + full-address:address:number <- get-address *chan, first-full:offset wait-for-location *full-address } # store val - circular-buffer:address:array:location <- get *chan, data:offset - free:address:number <- get-address *chan, first-free:offset + circular-buffer:address:array:location <- get *chan, data:offset + free:address:number <- get-address *chan, first-free:offset dest:address:location <- index-address *circular-buffer, *free *dest <- copy val # mark its slot as filled @@ -121,12 +121,12 @@ container channel [ # block if chan is empty empty?:boolean <- channel-empty? chan break-unless empty? - free-address:address:number <- get-address *chan, first-free:offset + free-address:address:number <- get-address *chan, first-free:offset wait-for-location *free-address } # read result - full:address:number <- get-address *chan, first-full:offset - circular-buffer:address:array:location <- get *chan, data:offset + full:address:number <- get-address *chan, first-full:offset + circular-buffer:address:array:location <- get *chan, data:offset result:location <- index *circular-buffer, *full # increment full *full <- add *full, 1 @@ -154,8 +154,8 @@ container channel [ scenario channel-initialization [ run [ 1:address:channel <- new-channel 3/capacity - 2:number <- get *1:address:channel, first-full:offset - 3:number <- get *1:address:channel, first-free:offset + 2:number <- get *1:address:channel, first-full:offset + 3:number <- get *1:address:channel, first-free:offset ] memory-should-contain [ 2 <- 0 # first-full @@ -167,8 +167,8 @@ container channel [ run [ 1:address:channel <- new-channel 3/capacity 1:address:channel <- write 1:address:channel, 34 - 2:number <- get *1:address:channel, first-full:offset - 3:number <- get *1:address:channel, first-free:offset + 2:number <- get *1:address:channel, first-full:offset + 3:number <- get *1:address:channel, first-free:offset ] memory-should-contain [ 2 <- 0 # first-full @@ -181,8 +181,8 @@ container channel [ 1:address:channel <- new-channel 3/capacity 1:address:channel <- write 1:address:channel, 34 _, 1:address:channel <- read 1:address:channel - 2:number <- get *1:address:channel, first-full:offset - 3:number <- get *1:address:channel, first-free:offset + 2:number <- get *1:address:channel, first-full:offset + 3:number <- get *1:address:channel, first-free:offset ] memory-should-contain [ 2 <- 1 # first-full @@ -198,14 +198,14 @@ container channel [ 1:address:channel <- write 1:address:channel, 34 _, 1:address:channel <- read 1:address:channel # first-free will now be 1 - 2:number <- get *1:address:channel, first-free:offset - 3:number <- get *1:address:channel, first-free:offset + 2:number <- get *1:address:channel, first-free:offset + 3:number <- get *1:address:channel, first-free:offset # write second value, verify that first-free wraps 1:address:channel <- write 1:address:channel, 34 - 4:number <- get *1:address:channel, first-free:offset + 4:number <- get *1:address:channel, first-free:offset # read second value, verify that first-full wraps _, 1:address:channel <- read 1:address:channel - 5:number <- get *1:address:channel, first-full:offset + 5:number <- get *1:address:channel, first-full:offset ] memory-should-contain [ 2 <- 1 # first-free after first write @@ -222,8 +222,8 @@ container channel [ local-scope chan:address:channel <- next-ingredient # return chan.first-full == chan.first-free - full:number <- get *chan, first-full:offset - free:number <- get *chan, first-free:offset + full:number <- get *chan, first-full:offset + free:number <- get *chan, first-free:offset result:boolean <- equal full, free reply result ] @@ -234,7 +234,7 @@ container channel [ local-scope chan:address:channel <- next-ingredient # tmp = chan.first-free + 1 - tmp:number <- get *chan, first-free:offset + tmp:number <- get *chan, first-free:offset tmp <- add tmp, 1 { # if tmp == chan.capacity, tmp = 0 @@ -244,7 +244,7 @@ container channel [ tmp <- copy 0 } # return chan.first-full == tmp - full:number <- get *chan, first-full:offset + full:number <- get *chan, first-full:offset result:boolean <- equal full, tmp reply result ] @@ -253,7 +253,7 @@ container channel [ recipe channel-capacity [ local-scope chan:address:channel <- next-ingredient - q:address:array:location <- get *chan, data:offset + q:address:array:location <- get *chan, data:offset result:number <- length *q reply result ] @@ -330,7 +330,7 @@ container channel [ break-unless backspace? # drop previous character { - buffer-length:address:number <- get-address *line, length:offset + buffer-length:address:number <- get-address *line, length:offset buffer-empty?:boolean <- equal *buffer-length, 0 break-if buffer-empty? *buffer-length <- subtract *buffer-length, 1 @@ -349,8 +349,8 @@ container channel [ } # copy line into 'out' i:number <- copy 0 - line-contents:address:array:character <- get *line, data:offset - max:number <- get *line, length:offset + line-contents:address:array:character <- get *line, data:offset + max:number <- get *line, length:offset { done?:boolean <- greater-or-equal i, max break-if done? @@ -372,7 +372,7 @@ container channel [ assert 3:boolean, [ F buffer-lines-blocks-until-newline: channel should be empty after init] # buffer stdin into buffered-stdin, try to read from buffered-stdin - 4:number/buffer-routine <- start-running buffer-lines:recipe, 1:address:channel/stdin, 2:address:channel/buffered-stdin + 4:number/buffer-routine <- start-running buffer-lines:recipe, 1:address:channel/stdin, 2:address:channel/buffered-stdin wait-for-routine 4:number/buffer-routine 5:boolean <- channel-empty? 2:address:channel/buffered-stdin assert 5:boolean, [ diff --git a/html/062array.mu.html b/html/062array.mu.html index 55169dfe..32af078f 100644 --- a/html/062array.mu.html +++ b/html/062array.mu.html @@ -13,13 +13,13 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.muScenario { color: #00af00; } -.Delimiter { color: #a04060; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } +.muScenario { color: #00af00; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -55,7 +55,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } capacity <- add capacity, 1 loop } - result:address:array:location <- new location:type, capacity + result:address:array:location <- new location:type, capacity rewind-ingredients i:number <- copy 0 { diff --git a/html/063list.mu.html b/html/063list.mu.html index 50060a5f..7e0f6e61 100644 --- a/html/063list.mu.html +++ b/html/063list.mu.html @@ -13,13 +13,13 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } .muScenario { color: #00af00; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } .CommentedCode { color: #6c6c6c; } -.muControl { color: #c0a020; } --> @@ -46,10 +46,10 @@ container list [ local-scope x:location <- next-ingredient in:address:list <- next-ingredient - result:address:list <- new list:type - val:address:location <- get-address *result, value:offset + result:address:list <- new list:type + val:address:location <- get-address *result, value:offset *val <- copy x - next:address:address:list <- get-address *result, next:offset + next:address:address:list <- get-address *result, next:offset *next <- copy in reply result ] @@ -58,7 +58,7 @@ container list [ recipe first [ local-scope in:address:list <- next-ingredient - result:location <- get *in, value:offset + result:location <- get *in, value:offset reply result ] @@ -66,7 +66,7 @@ container list [ recipe rest [ local-scope in:address:list <- next-ingredient - result:address:list <- get *in, next:offset + result:address:list <- get *in, next:offset reply result ] diff --git a/html/064random.cc.html b/html/064random.cc.html index 8735a580..5cfdf676 100644 --- a/html/064random.cc.html +++ b/html/064random.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .PreProc { color: #c000c0; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } diff --git a/html/065duplex_list.mu.html b/html/065duplex_list.mu.html index cbae7303..50ccbc35 100644 --- a/html/065duplex_list.mu.html +++ b/html/065duplex_list.mu.html @@ -13,14 +13,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .muScenario { color: #00af00; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } .CommentedCode { color: #6c6c6c; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -45,13 +45,13 @@ container duplex-list [ local-scope x:location <- next-ingredient in:address:duplex-list <- next-ingredient - result:address:duplex-list <- new duplex-list:type - val:address:location <- get-address *result, value:offset + result:address:duplex-list <- new duplex-list:type + val:address:location <- get-address *result, value:offset *val <- copy x - next:address:address:duplex-list <- get-address *result, next:offset + next:address:address:duplex-list <- get-address *result, next:offset *next <- copy in reply-unless in, result - prev:address:address:duplex-list <- get-address *in, prev:offset + prev:address:address:duplex-list <- get-address *in, prev:offset *prev <- copy result reply result ] @@ -61,7 +61,7 @@ container duplex-list [ local-scope in:address:duplex-list <- next-ingredient reply-unless in, 0 - result:location <- get *in, value:offset + result:location <- get *in, value:offset reply result ] @@ -70,7 +70,7 @@ container duplex-list [ local-scope in:address:duplex-list <- next-ingredient reply-unless in, 0 - result:address:duplex-list <- get *in, next:offset + result:address:duplex-list <- get *in, next:offset reply result ] @@ -79,7 +79,7 @@ container duplex-list [ local-scope in:address:duplex-list <- next-ingredient reply-unless in, 0 - result:address:duplex-list <- get *in, prev:offset + result:address:duplex-list <- get *in, prev:offset reply result ] @@ -93,19 +93,19 @@ container duplex-list [ 3:address:duplex-list <- push-duplex 4, 3:address:duplex-list 3:address:duplex-list <- push-duplex 5, 3:address:duplex-list 4:address:duplex-list <- copy 3:address:duplex-list - 5:number <- first 4:address:duplex-list + 5:number <- first-duplex 4:address:duplex-list 4:address:duplex-list <- next-duplex 4:address:duplex-list - 6:number <- first 4:address:duplex-list + 6:number <- first-duplex 4:address:duplex-list 4:address:duplex-list <- next-duplex 4:address:duplex-list - 7:number <- first 4:address:duplex-list + 7:number <- first-duplex 4:address:duplex-list 8:address:duplex-list <- next-duplex 4:address:duplex-list - 9:number <- first 8:address:duplex-list + 9:number <- first-duplex 8:address:duplex-list 10:address:duplex-list <- next-duplex 8:address:duplex-list 11:address:duplex-list <- prev-duplex 8:address:duplex-list 4:address:duplex-list <- prev-duplex 4:address:duplex-list - 12:number <- first 4:address:duplex-list + 12:number <- first-duplex 4:address:duplex-list 4:address:duplex-list <- prev-duplex 4:address:duplex-list - 13:number <- first 4:address:duplex-list + 13:number <- first-duplex 4:address:duplex-list 14:boolean <- equal 3:address:duplex-list, 4:address:duplex-list #? $dump-trace #? 1 ] @@ -132,23 +132,23 @@ container duplex-list [ local-scope x:location <- next-ingredient in:address:duplex-list <- next-ingredient - new-node:address:duplex-list <- new duplex-list:type - val:address:location <- get-address *new-node, value:offset + new-node:address:duplex-list <- new duplex-list:type + val:address:location <- get-address *new-node, value:offset *val <- copy x - next-node:address:duplex-list <- get *in, next:offset + next-node:address:duplex-list <- get *in, next:offset # in.next = new-node - y:address:address:duplex-list <- get-address *in, next:offset + y:address:address:duplex-list <- get-address *in, next:offset *y <- copy new-node # new-node.prev = in - y <- get-address *new-node, prev:offset + y <- get-address *new-node, prev:offset *y <- copy in # new-node.next = next-node - y <- get-address *new-node, next:offset + y <- get-address *new-node, next:offset *y <- copy next-node # if next-node is not null reply-unless next-node, new-node # next-node.prev = new-node - y <- get-address *next-node, prev:offset + y <- get-address *next-node, prev:offset *y <- copy new-node reply new-node # just signalling something changed; don't rely on the result ] @@ -163,19 +163,19 @@ container duplex-list [ 2:address:duplex-list <- insert-duplex 6, 2:address:duplex-list # check structure like before 2:address:duplex-list <- copy 1:address:duplex-list - 3:number <- first 2:address:duplex-list + 3:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 4:number <- first 2:address:duplex-list + 4:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 5:number <- first 2:address:duplex-list + 5:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 6:number <- first 2:address:duplex-list + 6:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 7:number <- first 2:address:duplex-list + 7:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 8:number <- first 2:address:duplex-list + 8:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 9:number <- first 2:address:duplex-list + 9:number <- first-duplex 2:address:duplex-list 10:boolean <- equal 1:address:duplex-list, 2:address:duplex-list ] memory-should-contain [ @@ -201,19 +201,19 @@ container duplex-list [ 2:address:duplex-list <- insert-duplex 6, 2:address:duplex-list # check structure like before 2:address:duplex-list <- copy 1:address:duplex-list - 3:number <- first 2:address:duplex-list + 3:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 4:number <- first 2:address:duplex-list + 4:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 5:number <- first 2:address:duplex-list + 5:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 6:number <- first 2:address:duplex-list + 6:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 7:number <- first 2:address:duplex-list + 7:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 8:number <- first 2:address:duplex-list + 8:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 9:number <- first 2:address:duplex-list + 9:number <- first-duplex 2:address:duplex-list 10:boolean <- equal 1:address:duplex-list, 2:address:duplex-list ] memory-should-contain [ @@ -237,19 +237,19 @@ container duplex-list [ 2:address:duplex-list <- insert-duplex 6, 1:address:duplex-list # check structure like before 2:address:duplex-list <- copy 1:address:duplex-list - 3:number <- first 2:address:duplex-list + 3:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 4:number <- first 2:address:duplex-list + 4:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 5:number <- first 2:address:duplex-list + 5:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 6:number <- first 2:address:duplex-list + 6:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 7:number <- first 2:address:duplex-list + 7:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 8:number <- first 2:address:duplex-list + 8:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 9:number <- first 2:address:duplex-list + 9:number <- first-duplex 2:address:duplex-list 10:boolean <- equal 1:address:duplex-list, 2:address:duplex-list ] memory-should-contain [ @@ -275,25 +275,25 @@ container duplex-list [ in:address:duplex-list <- next-ingredient # if 'in' is null, return reply-unless in, in - next-node:address:duplex-list <- get *in, next:offset - prev-node:address:duplex-list <- get *in, prev:offset + next-node:address:duplex-list <- get *in, next:offset + prev-node:address:duplex-list <- get *in, prev:offset # null in's pointers - x:address:address:duplex-list <- get-address *in, next:offset + x:address:address:duplex-list <- get-address *in, next:offset *x <- copy 0 - x <- get-address *in, prev:offset + x <- get-address *in, prev:offset *x <- copy 0 { # if next-node is not null break-unless next-node # next-node.prev = prev-node - x <- get-address *next-node, prev:offset + x <- get-address *next-node, prev:offset *x <- copy prev-node } { # if prev-node is not null break-unless prev-node # prev-node.next = next-node - x <- get-address *prev-node, next:offset + x <- get-address *prev-node, next:offset *x <- copy next-node reply prev-node } @@ -311,12 +311,12 @@ container duplex-list [ 3:boolean <- equal 2:address:duplex-list, 0 # check structure like before 2:address:duplex-list <- copy 1:address:duplex-list - 4:number <- first 2:address:duplex-list + 4:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 5:number <- first 2:address:duplex-list + 5:number <- first-duplex 2:address:duplex-list 6:address:duplex-list <- next-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 7:number <- first 2:address:duplex-list + 7:number <- first-duplex 2:address:duplex-list 8:boolean <- equal 1:address:duplex-list, 2:address:duplex-list ] memory-should-contain [ @@ -339,12 +339,12 @@ container duplex-list [ 1:address:duplex-list <- remove-duplex 1:address:duplex-list # check structure like before 2:address:duplex-list <- copy 1:address:duplex-list - 3:number <- first 2:address:duplex-list + 3:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 4:number <- first 2:address:duplex-list + 4:number <- first-duplex 2:address:duplex-list 5:address:duplex-list <- next-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 6:number <- first 2:address:duplex-list + 6:number <- first-duplex 2:address:duplex-list 7:boolean <- equal 1:address:duplex-list, 2:address:duplex-list ] memory-should-contain [ @@ -369,12 +369,12 @@ container duplex-list [ 3:boolean <- equal 2:address:duplex-list, 0 # check structure like before 2:address:duplex-list <- copy 1:address:duplex-list - 4:number <- first 2:address:duplex-list + 4:number <- first-duplex 2:address:duplex-list 2:address:duplex-list <- next-duplex 2:address:duplex-list - 5:number <- first 2:address:duplex-list + 5:number <- first-duplex 2:address:duplex-list 6:address:duplex-list <- next-duplex 2:address:duplex-list 2:address:duplex-list <- prev-duplex 2:address:duplex-list - 7:number <- first 2:address:duplex-list + 7:number <- first-duplex 2:address:duplex-list 8:boolean <- equal 1:address:duplex-list, 2:address:duplex-list ] memory-should-contain [ @@ -392,8 +392,8 @@ container duplex-list [ 1:address:duplex-list <- copy 0 # 1 points to singleton list 1:address:duplex-list <- push-duplex 3, 1:address:duplex-list 2:address:duplex-list <- remove-duplex 1:address:duplex-list - 3:address:duplex-list <- get *1:address:duplex-list, next:offset - 4:address:duplex-list <- get *1:address:duplex-list, prev:offset + 3:address:duplex-list <- get *1:address:duplex-list, next:offset + 4:address:duplex-list <- get *1:address:duplex-list, prev:offset ] memory-should-contain [ 2 <- 0 # remove returned null diff --git a/html/066stream.mu.html b/html/066stream.mu.html index ad7aebdf..2535cd8f 100644 --- a/html/066stream.mu.html +++ b/html/066stream.mu.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } --> @@ -37,10 +37,10 @@ container stream [ recipe new-stream [ local-scope - result:address:stream <- new stream:type - i:address:number <- get-address *result, index:offset + result:address:stream <- new stream:type + i:address:number <- get-address *result, index:offset *i <- copy 0 - d:address:address:array:character <- get-address *result, data:offset + d:address:address:array:character <- get-address *result, data:offset *d <- next-ingredient reply result ] @@ -48,7 +48,7 @@ container stream [ recipe rewind-stream [ local-scope in:address:stream <- next-ingredient - x:address:number <- get-address *in, index:offset + x:address:number <- get-address *in, index:offset *x <- copy 0 reply in/same-as-arg:0 ] @@ -56,8 +56,8 @@ container stream [ recipe read-line [ local-scope in:address:stream <- next-ingredient - idx:address:number <- get-address *in, index:offset - s:address:array:character <- get *in, data:offset + idx:address:number <- get-address *in, index:offset + s:address:array:character <- get *in, data:offset next-idx:number <- find-next s, 10/newline, *idx result:address:array:character <- string-copy s, *idx, next-idx *idx <- add next-idx, 1 # skip newline @@ -67,8 +67,8 @@ container stream [ recipe end-of-stream? [ local-scope in:address:stream <- next-ingredient - idx:address:number <- get *in, index:offset - s:address:array:character <- get *in, data:offset + idx:address:number <- get *in, index:offset + s:address:array:character <- get *in, data:offset len:number <- length *s result:boolean <- greater-or-equal idx, len reply result diff --git a/html/071print.mu.html b/html/071print.mu.html index fb8d0ae7..8e21896f 100644 --- a/html/071print.mu.html +++ b/html/071print.mu.html @@ -13,14 +13,14 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .muScenario { color: #00af00; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } .CommentedCode { color: #6c6c6c; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -50,18 +50,18 @@ container screen-cell [ recipe new-fake-screen [ local-scope - result:address:screen <- new screen:type - width:address:number <- get-address *result, num-columns:offset + result:address:screen <- new screen:type + width:address:number <- get-address *result, num-columns:offset *width <- next-ingredient - height:address:number <- get-address *result, num-rows:offset + height:address:number <- get-address *result, num-rows:offset *height <- next-ingredient - row:address:number <- get-address *result, cursor-row:offset + row:address:number <- get-address *result, cursor-row:offset *row <- copy 0 - column:address:number <- get-address *result, cursor-column:offset + column:address:number <- get-address *result, cursor-column:offset *column <- copy 0 bufsize:number <- multiply *width, *height - buf:address:address:array:screen-cell <- get-address *result, data:offset - *buf <- new screen-cell:type, bufsize + buf:address:address:array:screen-cell <- get-address *result, data:offset + *buf <- new screen-cell:type, bufsize clear-screen result reply result ] @@ -73,24 +73,24 @@ container screen-cell [ { break-unless sc # clear fake screen - buf:address:array:screen-cell <- get *sc, data:offset + buf:address:array:screen-cell <- get *sc, data:offset max:number <- length *buf i:number <- copy 0 { done?:boolean <- greater-or-equal i, max break-if done? curr:address:screen-cell <- index-address *buf, i - curr-content:address:character <- get-address *curr, contents:offset + curr-content:address:character <- get-address *curr, contents:offset *curr-content <- copy [ ] - curr-color:address:character <- get-address *curr, color:offset + curr-color:address:character <- get-address *curr, color:offset *curr-color <- copy 7/white i <- add i, 1 loop } # reset cursor - x:address:number <- get-address *sc, cursor-row:offset + x:address:number <- get-address *sc, cursor-row:offset *x <- copy 0 - x <- get-address *sc, cursor-column:offset + x <- get-address *sc, cursor-column:offset *x <- copy 0 reply sc/same-as-ingredient:0 } @@ -103,14 +103,14 @@ container screen-cell [ local-scope sc:address:screen <- next-ingredient reply-unless sc, 1/true - buf:address:array:screen-cell <- get *sc, data:offset + buf:address:array:screen-cell <- get *sc, data:offset i:number <- copy 0 len:number <- length *buf { done?:boolean <- greater-or-equal i, len break-if done? curr:screen-cell <- index *buf, i - curr-contents:character <- get curr, contents:offset + curr-contents:character <- get curr, contents:offset i <- add i, 1 loop-unless curr-contents # not 0 @@ -140,15 +140,15 @@ container screen-cell [ # if x exists # (handle special cases exactly like in the real screen) break-unless sc - width:number <- get *sc, num-columns:offset - height:number <- get *sc, num-rows:offset + width:number <- get *sc, num-columns:offset + height:number <- get *sc, num-rows:offset # if cursor is out of bounds, silently exit - row:address:number <- get-address *sc, cursor-row:offset + row:address:number <- get-address *sc, cursor-row:offset legal?:boolean <- greater-or-equal *row, 0 reply-unless legal?, sc legal? <- lesser-than *row, height reply-unless legal?, sc - column:address:number <- get-address *sc, cursor-column:offset + column:address:number <- get-address *sc, cursor-column:offset legal? <- greater-or-equal *column, 0 reply-unless legal?, sc legal? <- lesser-than *column, width @@ -172,7 +172,7 @@ container screen-cell [ # save character in fake screen index:number <- multiply *row, width index <- add index, *column - buf:address:array:screen-cell <- get *sc, data:offset + buf:address:array:screen-cell <- get *sc, data:offset len:number <- length *buf # special-case: backspace { @@ -186,18 +186,18 @@ container screen-cell [ *column <- subtract *column, 1 index <- subtract index, 1 cursor:address:screen-cell <- index-address *buf, index - cursor-contents:address:character <- get-address *cursor, contents:offset + cursor-contents:address:character <- get-address *cursor, contents:offset *cursor-contents <- copy 32/space - cursor-color:address:number <- get-address *cursor, color:offset + cursor-color:address:number <- get-address *cursor, color:offset *cursor-color <- copy 7/white } reply sc/same-as-ingredient:0 } #? $print [saving character ], c, [ to fake screen ], cursor, 10/newline cursor:address:screen-cell <- index-address *buf, index - cursor-contents:address:character <- get-address *cursor, contents:offset + cursor-contents:address:character <- get-address *cursor, contents:offset *cursor-contents <- copy c - cursor-color:address:number <- get-address *cursor, color:offset + cursor-color:address:number <- get-address *cursor, color:offset *cursor-color <- copy color # increment column unless it's already all the way to the right { @@ -218,7 +218,7 @@ container screen-cell [ #? $start-tracing #? 3 1:address:screen <- new-fake-screen 3/width, 2/height 1:address:screen <- print-character 1:address:screen, 97 # 'a' - 2:address:array:screen-cell <- get *1:address:screen, data:offset + 2:address:array:screen-cell <- get *1:address:screen, data:offset 3:array:screen-cell <- copy *2:address:array:screen-cell ] memory-should-contain [ @@ -233,7 +233,7 @@ container screen-cell [ run [ 1:address:screen <- new-fake-screen 3/width, 2/height 1:address:screen <- print-character 1:address:screen, 97/a, 1/red - 2:address:array:screen-cell <- get *1:address:screen, data:offset + 2:address:array:screen-cell <- get *1:address:screen, data:offset 3:array:screen-cell <- copy *2:address:array:screen-cell ] memory-should-contain [ @@ -250,8 +250,8 @@ container screen-cell [ 1:address:screen <- new-fake-screen 3/width, 2/height 1:address:screen <- print-character 1:address:screen, 97 # 'a' 1:address:screen <- print-character 1:address:screen, 8 # backspace - 2:number <- get *1:address:screen, cursor-column:offset - 3:address:array:screen-cell <- get *1:address:screen, data:offset + 2:number <- get *1:address:screen, cursor-column:offset + 3:address:array:screen-cell <- get *1:address:screen, data:offset 4:array:screen-cell <- copy *3:address:array:screen-cell ] memory-should-contain [ @@ -269,8 +269,8 @@ container screen-cell [ 1:address:screen <- print-character 1:address:screen, 97 # 'a' 1:address:screen <- print-character 1:address:screen, 8 # backspace 1:address:screen <- print-character 1:address:screen, 8 # backspace - 2:number <- get *1:address:screen, cursor-column:offset - 3:address:array:screen-cell <- get *1:address:screen, data:offset + 2:number <- get *1:address:screen, cursor-column:offset + 3:address:array:screen-cell <- get *1:address:screen, data:offset 4:array:screen-cell <- copy *3:address:array:screen-cell ] memory-should-contain [ @@ -288,8 +288,8 @@ container screen-cell [ 1:address:screen <- print-character 1:address:screen, 97 # 'a' 1:address:screen <- print-character 1:address:screen, 98 # 'b' 1:address:screen <- print-character 1:address:screen, 99 # 'c' - 2:number <- get *1:address:screen, cursor-column:offset - 3:address:array:screen-cell <- get *1:address:screen, data:offset + 2:number <- get *1:address:screen, cursor-column:offset + 3:address:array:screen-cell <- get *1:address:screen, data:offset 4:array:screen-cell <- copy *3:address:array:screen-cell ] memory-should-contain [ @@ -309,9 +309,9 @@ container screen-cell [ 1:address:screen <- new-fake-screen 3/width, 2/height 1:address:screen <- print-character 1:address:screen, 97 # 'a' 1:address:screen <- print-character 1:address:screen, 10/newline - 2:number <- get *1:address:screen, cursor-row:offset - 3:number <- get *1:address:screen, cursor-column:offset - 4:address:array:screen-cell <- get *1:address:screen, data:offset + 2:number <- get *1:address:screen, cursor-row:offset + 3:number <- get *1:address:screen, cursor-column:offset + 4:address:array:screen-cell <- get *1:address:screen, data:offset 5:array:screen-cell <- copy *4:address:array:screen-cell ] memory-should-contain [ @@ -330,8 +330,8 @@ container screen-cell [ 1:address:screen <- print-character 1:address:screen, 10/newline 1:address:screen <- print-character 1:address:screen, 10/newline 1:address:screen <- print-character 1:address:screen, 10/newline - 2:number <- get *1:address:screen, cursor-row:offset - 3:number <- get *1:address:screen, cursor-column:offset + 2:number <- get *1:address:screen, cursor-row:offset + 3:number <- get *1:address:screen, cursor-column:offset ] memory-should-contain [ 2 <- 1 # cursor row @@ -348,9 +348,9 @@ container screen-cell [ 1:address:screen <- print-character 1:address:screen, 99 # 'c' 1:address:screen <- print-character 1:address:screen, 10/newline 1:address:screen <- print-character 1:address:screen, 100 # 'd' - 2:number <- get *1:address:screen, cursor-row:offset - 3:number <- get *1:address:screen, cursor-column:offset - 4:address:array:screen-cell <- get *1:address:screen, data:offset + 2:number <- get *1:address:screen, cursor-row:offset + 3:number <- get *1:address:screen, cursor-column:offset + 4:address:array:screen-cell <- get *1:address:screen, data:offset 5:array:screen-cell <- copy *4:address:array:screen-cell ] memory-should-contain [ @@ -375,8 +375,8 @@ container screen-cell [ # if x exists, clear line in fake screen { break-unless sc - width:number <- get *sc, num-columns:offset - column:address:number <- get-address *sc, cursor-column:offset + width:number <- get *sc, num-columns:offset + column:address:number <- get-address *sc, cursor-column:offset original-column:number <- copy *column # space over the entire line #? $start-tracing #? 1 @@ -403,8 +403,8 @@ container screen-cell [ # if x exists, lookup cursor in fake screen { break-unless sc - row:number <- get *sc, cursor-row:offset - column:number <- get *sc, cursor-column:offset + row:number <- get *sc, cursor-row:offset + column:number <- get *sc, cursor-column:offset reply row, column, sc/same-as-ingredient:0 } row, column <- cursor-position-on-display @@ -419,9 +419,9 @@ container screen-cell [ # if x exists, move cursor in fake screen { break-unless sc - row:address:number <- get-address *sc, cursor-row:offset + row:address:number <- get-address *sc, cursor-row:offset *row <- copy new-row - column:address:number <- get-address *sc, cursor-column:offset + column:address:number <- get-address *sc, cursor-column:offset *column <- copy new-column reply sc/same-as-ingredient:0 } @@ -440,7 +440,7 @@ container screen-cell [ 1:address:screen <- move-cursor 1:address:screen, 0/row, 0/column # clear line 1:address:screen <- clear-line 1:address:screen - 2:address:array:screen-cell <- get *1:address:screen, data:offset + 2:address:array:screen-cell <- get *1:address:screen, data:offset 3:array:screen-cell <- copy *2:address:array:screen-cell ] # screen should be blank @@ -469,8 +469,8 @@ container screen-cell [ break-unless sc { # if row < height-1 - height:number <- get *sc, num-rows:offset - row:address:number <- get-address *sc, cursor-row:offset + height:number <- get *sc, num-rows:offset + row:address:number <- get-address *sc, cursor-row:offset max:number <- subtract height, 1 at-bottom?:boolean <- greater-or-equal *row, max break-if at-bottom? @@ -492,7 +492,7 @@ container screen-cell [ break-unless sc { # if row > 0 - row:address:number <- get-address *sc, cursor-row:offset + row:address:number <- get-address *sc, cursor-row:offset at-top?:boolean <- lesser-or-equal *row, 0 break-if at-top? # row = row-1 @@ -513,8 +513,8 @@ container screen-cell [ break-unless sc { # if column < width-1 - width:number <- get *sc, num-columns:offset - column:address:number <- get-address *sc, cursor-column:offset + width:number <- get *sc, num-columns:offset + column:address:number <- get-address *sc, cursor-column:offset max:number <- subtract width, 1 at-bottom?:boolean <- greater-or-equal *column, max break-if at-bottom? @@ -536,7 +536,7 @@ container screen-cell [ break-unless sc { # if column > 0 - column:address:number <- get-address *sc, cursor-column:offset + column:address:number <- get-address *sc, cursor-column:offset at-top?:boolean <- lesser-or-equal *column, 0 break-if at-top? # column = column-1 @@ -572,7 +572,7 @@ container screen-cell [ # if x exists, move cursor in fake screen { break-unless sc - width:number <- get *sc, num-columns:offset + width:number <- get *sc, num-columns:offset reply width } # otherwise, real screen @@ -586,7 +586,7 @@ container screen-cell [ # if x exists, move cursor in fake screen { break-unless sc - height:number <- get *sc, num-rows:offset + height:number <- get *sc, num-rows:offset reply height } # otherwise, real screen @@ -680,7 +680,7 @@ container screen-cell [ 1:address:screen <- new-fake-screen 3/width, 2/height 2:address:array:character <- new [abcd] 1:address:screen <- print-string 1:address:screen, 2:address:array:character - 3:address:array:screen-cell <- get *1:address:screen, data:offset + 3:address:array:screen-cell <- get *1:address:screen, data:offset 4:array:screen-cell <- copy *3:address:array:screen-cell ] memory-should-contain [ diff --git a/html/072scenario_screen.cc.html b/html/072scenario_screen.cc.html index 44757729..62c58b4a 100644 --- a/html/072scenario_screen.cc.html +++ b/html/072scenario_screen.cc.html @@ -13,10 +13,10 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.traceAbsent { color: #c00000; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } -.traceAbsent { color: #c00000; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/074console.mu.html b/html/074console.mu.html index 53c01cfe..d292d918 100644 --- a/html/074console.mu.html +++ b/html/074console.mu.html @@ -13,12 +13,12 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -53,10 +53,10 @@ container console [ recipe new-fake-console [ local-scope - result:address:console <- new console:type - buf:address:address:array:character <- get-address *result, data:offset + result:address:console <- new console:type + buf:address:address:array:character <- get-address *result, data:offset *buf <- next-ingredient - idx:address:number <- get-address *result, index:offset + idx:address:number <- get-address *result, index:offset *idx <- copy 0 reply result ] @@ -66,13 +66,13 @@ container console [ x:address:console <- next-ingredient { break-unless x - idx:address:number <- get-address *x, index:offset - buf:address:array:event <- get *x, data:offset + idx:address:number <- get-address *x, index:offset + buf:address:array:event <- get *x, data:offset { max:number <- length *buf done?:boolean <- greater-or-equal *idx, max break-unless done? - dummy:address:event <- new event:type + dummy:address:event <- new event:type reply *dummy, x/same-as-ingredient:0, 1/found, 1/quit } result:event <- index *buf, *idx @@ -94,7 +94,7 @@ container console [ x:event, console, found?:boolean, quit?:boolean <- read-event console reply-if quit?, 0, console/same-as-ingredient:0, found?, quit? reply-unless found?, 0, console/same-as-ingredient:0, found?, quit? - c:address:character <- maybe-convert x, text:variant + c:address:character <- maybe-convert x, text:variant reply-unless c, 0, console/same-as-ingredient:0, 0/found, 0/quit reply *c, console/same-as-ingredient:0, 1/found, 0/quit ] diff --git a/html/075scenario_console.cc.html b/html/075scenario_console.cc.html index 3b965fdb..ed5c2a19 100644 --- a/html/075scenario_console.cc.html +++ b/html/075scenario_console.cc.html @@ -13,8 +13,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } diff --git a/html/080trace_browser.cc.html b/html/080trace_browser.cc.html index f2139f3f..2f47c417 100644 --- a/html/080trace_browser.cc.html +++ b/html/080trace_browser.cc.html @@ -13,8 +13,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Identifier { color: #804000; } diff --git a/html/081run_interactive.cc.html b/html/081run_interactive.cc.html index 03c1f523..4ece36af 100644 --- a/html/081run_interactive.cc.html +++ b/html/081run_interactive.cc.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background- body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } .traceContains { color: #008000; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } @@ -53,6 +53,7 @@ recipe main [ //: stringified output in case we want to print it to screen //: any warnings encountered //: simulated screen any prints went to +//: any 'app' layer traces generated :(before "End Primitive Recipe Declarations") RUN_INTERACTIVE, :(before "End Primitive Recipe Numbers") @@ -68,12 +69,13 @@ case RUN_INTERACTIVE: { raise << current_recipe_name() << ": first ingredient of 'run-interactive' should be a literal string, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); break; } - products.resize(3); bool new_code_pushed_to_stack = run_interactive(ingredients.at(0).at(0)); if (!new_code_pushed_to_stack) { + products.resize(4); products.at(0).push_back(0); - products.at(1).push_back(warnings_from_trace()); + products.at(1).push_back(trace_contents("warn")); products.at(2).push_back(0); + products.at(3).push_back(trace_contents("app")); clean_up_interactive(); break; // done with this instruction } @@ -115,7 +117,8 @@ bool run_interactive(long long int address(!Trace_stream) { Trace_file = ""; // if there wasn't already a stream we don't want to save it Trace_stream = new trace_stream; - Trace_stream->collect_layer = "warn"; + Trace_stream->collect_layers.insert("warn"); + Trace_stream->collect_layers.insert("app"); } // call run(string) but without the scheduling // we won't create a local scope so that we can get to the new screen after @@ -183,7 +186,15 @@ void record_products(const instruction& instr for (long long int i = 0; i < SIZE(products); ++i) { // string if (i < SIZE(instruction.products)) { - if (is_string(instruction.products.at(i))) { + if (is_mu_string(instruction.products.at(i))) { + if (!scalar(products.at(i))) { + tb_shutdown(); + cerr << read_mu_string(trace_contents("warn")) << '\n'; + cerr << SIZE(products.at(i)) << ": "; + for (long long int j = 0; j < SIZE(products.at(i)); ++j) + cerr << products.at(i).at(j) << ' '; + cerr << '\n'; + } assert(scalar(products.at(i))); out << read_mu_string(products.at(i).at(0)) << '\n'; continue; @@ -198,7 +209,7 @@ void record_products(const instruction& instr } :(before "Complete Call Fallthrough") if (current_instruction().operation == RUN_INTERACTIVE && !current_instruction().products.empty()) { - assert(SIZE(current_instruction().products) <= 3); + assert(SIZE(current_instruction().products) <= 4); // Send the results of the most recently executed instruction, regardless of // call depth, to be converted to string and potentially printed to string. vector<double> result; @@ -206,7 +217,7 @@ if (current_instruction() write_memory(current_instruction().products.at(0), result); if (SIZE(current_instruction().products) >= 2) { vector<double> warnings; - warnings.push_back(warnings_from_trace()); + warnings.push_back(trace_contents("warn")); write_memory(current_instruction().products.at(1), warnings); } if (SIZE(current_instruction().products) >= 3) { @@ -214,6 +225,12 @@ if (current_instruction() screen.push_back(Memory[SCREEN]); write_memory(current_instruction().products.at(2), screen); } + if (SIZE(current_instruction().products) >= 4) { +//? cerr << "emitting trace\n"; //? 1 + vector<double> trace; + trace.push_back(trace_contents("app")); + write_memory(current_instruction().products.at(3), trace); + } } //: clean up reply after we've popped it off the call-stack @@ -231,8 +248,8 @@ void clean_up_interactive() ->newline(); // flush trace Hide_warnings = false; Running_interactive = false; - // hack: assume collect_layer isn't set anywhere else - if (Trace_stream->collect_layer == "warn") { + // hack: assume collect_layers isn't set anywhere else + if (Trace_stream->is_narrowly_collecting("warn")) { delete Trace_stream; Trace_stream = NULL; } @@ -275,23 +292,26 @@ long long int stringified_value_of_location(long return new_mu_string(out.str()); } -bool is_string(const reagent& x) { +bool is_mu_string(const reagent& x) { return SIZE(x.types) == 3 && x.types.at(0) == Type_ordinal["address"] && x.types.at(1) == Type_ordinal["array"] && x.types.at(2) == Type_ordinal["character"]; } -long long int warnings_from_trace() { +long long int trace_contents(const string& layer) { if (!Trace_stream) return 0; - if (trace_count("warn") <= 0) return 0; +//? cerr << "trace stream exists\n"; //? 1 + if (trace_count(layer) <= 0) return 0; +//? cerr << layer << " has something\n"; //? 1 ostringstream out; for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { - if (p->label != "warn") continue; + if (p->label != layer) continue; out << p->contents; if (*--p->contents.end() != '\n') out << '\n'; } assert(!out.str().empty()); +//? cerr << layer << ":\n" << out.str() << "\n--\n"; //? 1 return new_mu_string(out.str()); } @@ -314,7 +334,7 @@ case RELOAD: { if (!Trace_stream) { Trace_file = ""; // if there wasn't already a stream we don't want to save it Trace_stream = new trace_stream; - Trace_stream->collect_layer = "warn"; + Trace_stream->collect_layers.insert("warn"); } Hide_warnings = true; Disable_redefine_warnings = true; @@ -324,8 +344,9 @@ case RELOAD: { Disable_redefine_warnings = false; Hide_warnings = false; products.resize(1); - products.at(0).push_back(warnings_from_trace()); - if (Trace_stream->collect_layer == "warn") { + products.at(0).push_back(trace_contents("warn")); + // hack: assume collect_layers isn't set anywhere else + if (Trace_stream->is_narrowly_collecting("warn")) { delete Trace_stream; Trace_stream = NULL; } diff --git a/html/082persist.cc.html b/html/082persist.cc.html index bb5582ee..0984d488 100644 --- a/html/082persist.cc.html +++ b/html/082persist.cc.html @@ -13,8 +13,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } -.cSpecial { color: #008000; } .Constant { color: #00a0a0; } +.cSpecial { color: #008000; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Identifier { color: #804000; } @@ -45,14 +45,20 @@ case RESTORE: { raise << current_recipe_name() << ": 'restore' requires exactly one ingredient, but got " << current_instruction().to_string() << '\n' << end(); break; } - if (!scalar(ingredients.at(0))) - raise << current_recipe_name() << ": first ingredient of 'restore' should be a literal string, but got " << current_instruction().ingredients.at(0).to_string() << '\n' << end(); - products.resize(1); + string filename; + if (is_literal_string(current_instruction().ingredients.at(0))) { + filename = current_instruction().ingredients.at(0).name; + } + else if (is_mu_string(current_instruction().ingredients.at(0))) { + filename = read_mu_string(ingredients.at(0).at(0)); + } + else { + raise << current_recipe_name() << ": first ingredient of 'restore' should be a string, but got " << current_instruction().ingredients.at(0).to_string() << '\n' << end(); + break; + } if (Current_scenario) break; // do nothing in tests - string filename = current_instruction().ingredients.at(0).name; - if (!is_literal(current_instruction().ingredients.at(0))) - filename = to_string(ingredients.at(0).at(0)); string contents = slurp("lesson/"+filename); + products.resize(1); if (contents.empty()) products.at(0).push_back(0); else @@ -89,21 +95,30 @@ case SAVE: { raise << current_recipe_name() << ": 'save' requires exactly two ingredients, but got " << current_instruction().to_string() << '\n' << end(); break; } - if (!scalar(ingredients.at(0))) - raise << current_recipe_name() << ": first ingredient of 'save' should be a literal string, but got " << current_instruction().ingredients.at(0).to_string() << '\n' << end(); - if (!scalar(ingredients.at(1))) - raise << current_recipe_name() << ": second ingredient of 'save' should be an address:array:character, but got " << current_instruction().ingredients.at(1).to_string() << '\n' << end(); if (Current_scenario) break; // do nothing in tests - string filename = current_instruction().ingredients.at(0).name; - if (!is_literal(current_instruction().ingredients.at(0))) - filename = to_string(ingredients.at(0).at(0)); + string filename; + if (is_literal_string(current_instruction().ingredients.at(0))) { + filename = current_instruction().ingredients.at(0).name; + } + else if (is_mu_string(current_instruction().ingredients.at(0))) { + filename = read_mu_string(ingredients.at(0).at(0)); + } + else { + raise << current_recipe_name() << ": first ingredient of 'save' should be a string, but got " << current_instruction().ingredients.at(0).to_string() << '\n' << end(); + break; + } + if (!scalar(ingredients.at(1))) { + raise << current_recipe_name() << ": second ingredient of 'save' should be an address:array:character, but got " << current_instruction().ingredients.at(1).to_string() << '\n' << end(); + break; + } ofstream fout(("lesson/"+filename).c_str()); string contents = read_mu_string(ingredients.at(1).at(0)); fout << contents; fout.close(); if (!exists("lesson/.git")) break; // bug in git: git diff -q messes up --exit-code - int status = system("cd lesson; git add .; git diff HEAD --exit-code >/dev/null || git commit -a -m . >/dev/null"); + // explicitly say '--all' for git 1.9 + int status = system("cd lesson; git add --all .; git diff HEAD --exit-code >/dev/null || git commit -a -m . >/dev/null"); if (status != 0) raise << "error in commit: contents " << contents << '\n' << end(); break; diff --git a/html/callcc.mu.html b/html/callcc.mu.html index 20ebd83c..5e817501 100644 --- a/html/callcc.mu.html +++ b/html/callcc.mu.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } --> diff --git a/html/channel.mu.html b/html/channel.mu.html index 45450112..91f55497 100644 --- a/html/channel.mu.html +++ b/html/channel.mu.html @@ -13,12 +13,12 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -68,8 +68,8 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } local-scope chan:address:channel <- new-channel 3 # create two background 'routines' that communicate by a channel - routine1:number <- start-running producer:recipe, chan - routine2:number <- start-running consumer:recipe, chan + routine1:number <- start-running producer:recipe, chan + routine2:number <- start-running consumer:recipe, chan wait-for-routine routine1 wait-for-routine routine2 ] diff --git a/html/chessboard.mu.html b/html/chessboard.mu.html index d04f4df0..39ecd0bf 100644 --- a/html/chessboard.mu.html +++ b/html/chessboard.mu.html @@ -13,15 +13,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .muScenario { color: #00af00; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } .CommentedCode { color: #6c6c6c; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -108,10 +108,10 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } board:address:array:address:array:character <- initial-position # hook up stdin stdin:address:channel <- new-channel 10/capacity - start-running send-keys-to-channel:recipe, console, stdin, screen + start-running send-keys-to-channel:recipe, console, stdin, screen # buffer lines in stdin buffered-stdin:address:channel <- new-channel 10/capacity - start-running buffer-lines:recipe, stdin, buffered-stdin + start-running buffer-lines:recipe, stdin, buffered-stdin { msg:address:array:character <- new [Stupid text-mode chessboard. White pieces in uppercase; black pieces in lowercase. No checking for legal moves. ] @@ -152,7 +152,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } correct-length?:boolean <- equal len, 64 assert correct-length?, [chessboard had incorrect size] # board is an array of pointers to files; file is an array of characters - board:address:array:address:array:character <- new location:type, 8 + board:address:array:address:array:character <- new location:type, 8 col:number <- copy 0 { done?:boolean <- equal col, 8 @@ -170,7 +170,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } position:address:array:number <- next-ingredient index:number <- next-ingredient index <- multiply index, 8 - result:address:array:character <- new character:type, 8 + result:address:array:character <- new character:type, 8 row:number <- copy 0 { done?:boolean <- equal row, 8 @@ -299,20 +299,20 @@ container move [ reply-if quit?, 0/dummy, quit?, error? reply-if error?, 0/dummy, quit?, error? # construct the move object - result:address:move <- new move:type - x:address:number <- get-address *result, from-file:offset + result:address:move <- new move:type + x:address:number <- get-address *result, from-file:offset *x <- copy from-file - x <- get-address *result, from-rank:offset + x <- get-address *result, from-rank:offset *x, quit?, error? <- read-rank stdin, screen reply-if quit?, 0/dummy, quit?, error? reply-if error?, 0/dummy, quit?, error? error? <- expect-from-channel stdin, 45/dash, screen reply-if error?, 0/dummy, 0/quit, error? - x <- get-address *result, to-file:offset + x <- get-address *result, to-file:offset *x, quit?, error? <- read-file stdin, screen reply-if quit?:boolean, 0/dummy, quit?:boolean, error?:boolean reply-if error?:boolean, 0/dummy, quit?:boolean, error?:boolean - x:address:number <- get-address *result, to-rank:offset + x:address:number <- get-address *result, to-rank:offset *x, quit?, error? <- read-rank stdin, screen reply-if quit?, 0/dummy, quit?, error? reply-if error?, 0/dummy, quit?, error? @@ -439,7 +439,7 @@ container move [ assume-screen 20/width, 2/height run [ 1:address:channel <- new-channel 2 - 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address + 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address # 'read-move' is waiting for input wait-for-routine 2:number 3:number <- routine-state 2:number/id @@ -511,7 +511,7 @@ F read-move-blocking: routine failed to terminate on newline] assume-screen 20/width, 2/height run [ 1:address:channel <- new-channel 2 - 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address + 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address # 'read-move' is waiting for input wait-for-routine 2:number 3:number <- routine-state 2:number/id @@ -538,7 +538,7 @@ F read-move-quit: routine failed to terminate on 'q'] assume-screen 20/width, 2/height run [ 1:address:channel <- new-channel 2 - 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address + 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address # 'read-move' is waiting for input wait-for-routine 2:number 3:number <- routine-state 2:number/id @@ -559,7 +559,7 @@ F read-move-file: routine failed to pause after co assume-screen 20/width, 2/height run [ 1:address:channel <- new-channel 2 - 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address + 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address # 'read-move' is waiting for input wait-for-routine 2:number 3:number <- routine-state 2:number/id @@ -581,7 +581,7 @@ F read-move-file: routine failed to pause after co assume-screen 20/width, 2/height run [ 1:address:channel <- new-channel 2 - 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address + 2:number/routine <- start-running read-move:recipe, 1:address:channel, screen:address # 'read-move' is waiting for input wait-for-routine 2:number 3:number <- routine-state 2:number/id @@ -603,10 +603,10 @@ F read-move-file: routine failed to pause after co local-scope b:address:array:address:array:character <- next-ingredient m:address:move <- next-ingredient - from-file:number <- get *m, from-file:offset - from-rank:number <- get *m, from-rank:offset - to-file:number <- get *m, to-file:offset - to-rank:number <- get *m, to-rank:offset + from-file:number <- get *m, from-file:offset + from-rank:number <- get *m, from-rank:offset + to-file:number <- get *m, to-file:offset + to-rank:number <- get *m, to-rank:offset f:address:array:character <- index *b, from-file src:address:character/square <- index-address *f, from-rank f <- index *b, to-file @@ -620,14 +620,14 @@ F read-move-file: routine failed to pause after co assume-screen 30/width, 12/height run [ 2:address:array:address:array:character/board <- initial-position - 3:address:move <- new move:type - 4:address:number <- get-address *3:address:move, from-file:offset + 3:address:move <- new move:type + 4:address:number <- get-address *3:address:move, from-file:offset *4:address:number <- copy 6/g - 5:address:number <- get-address *3:address:move, from-rank:offset + 5:address:number <- get-address *3:address:move, from-rank:offset *5:address:number <- copy 1/'2' - 6:address:number <- get-address *3:address:move, to-file:offset + 6:address:number <- get-address *3:address:move, to-file:offset *6:address:number <- copy 6/g - 7:address:number <- get-address *3:address:move, to-rank:offset + 7:address:number <- get-address *3:address:move, to-rank:offset *7:address:number <- copy 3/'4' 2:address:array:address:array:character/board <- make-move 2:address:array:address:array:character/board, 3:address:move screen:address <- print-board screen:address, 2:address:array:address:array:character/board diff --git a/html/console.mu.html b/html/console.mu.html index 8e0f4284..e30185f7 100644 --- a/html/console.mu.html +++ b/html/console.mu.html @@ -13,12 +13,12 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } .Constant { color: #00a0a0; } .Comment { color: #9090ff; } .Delimiter { color: #a04060; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } --> diff --git a/html/counters.mu.html b/html/counters.mu.html index 926eca68..122724c0 100644 --- a/html/counters.mu.html +++ b/html/counters.mu.html @@ -13,11 +13,11 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } -.muControl { color: #c0a020; } --> @@ -33,7 +33,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; } # (spaces) recipe new-counter [ - default-space:address:array:location <- new location:type, 30 + default-space:address:array:location <- new location:type, 30 n:number <- next-ingredient reply default-space ] diff --git a/html/edit.mu.html b/html/edit.mu.html index fa1b442f..2e612a7c 100644 --- a/html/edit.mu.html +++ b/html/edit.mu.html @@ -13,15 +13,15 @@ pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; } body { font-family: monospace; color: #eeeeee; background-color: #080808; } * { font-size: 1.05em; } +.muControl { color: #c0a020; } .muRecipe { color: #ff8700; } -.Delimiter { color: #a04060; } .muScenario { color: #00af00; } .SalientComment { color: #00ffff; } .Comment { color: #9090ff; } .Constant { color: #00a0a0; } .Special { color: #ff6060; } .CommentedCode { color: #6c6c6c; } -.muControl { color: #c0a020; } +.Delimiter { color: #a04060; } --> @@ -33,7 +33,10 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; }
-# Environment for learning programming using mu.
+# Environment for learning programming using mu: http://akkartik.name/post/mu
+#
+# Consists of one editor on the left for recipes and one on the right for the
+# sandbox.
 
 recipe main [
   local-scope
@@ -48,44 +51,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; }
   # never gets here
 ]
 
-container programming-environment-data [
-  recipes:address:editor-data
-  recipe-warnings:address:array:character
-  current-sandbox:address:editor-data
-  sandbox:address:sandbox-data
-  sandbox-in-focus?:boolean  # false => focus in recipes; true => focus in current-sandbox
-]
-
-recipe new-programming-environment [
-  local-scope
-  screen:address <- next-ingredient
-  initial-recipe-contents:address:array:character <- next-ingredient
-  initial-sandbox-contents:address:array:character <- next-ingredient
-  width:number <- screen-width screen
-  height:number <- screen-height screen
-  # top menu
-  result:address:programming-environment-data <- new programming-environment-data:type
-  draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
-  button-start:number <- subtract width, 20
-  button-on-screen?:boolean <- greater-or-equal button-start, 0
-  assert button-on-screen?, [screen too narrow for menu]
-  move-cursor screen, 0/row, button-start
-  run-button:address:array:character <- new [ run (F4) ]
-  print-string screen, run-button, 255/white, 161/reddish
-  # dotted line down the middle
-  divider:number, _ <- divide-with-remainder width, 2
-  draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
-  # recipe editor on the left
-  recipes:address:address:editor-data <- get-address *result, recipes:offset
-  *recipes <- new-editor initial-recipe-contents, screen, 0/left, divider/right
-  # sandbox editor on the right
-  new-left:number <- add divider, 1
-  new-right:number <- add new-left, 5
-  current-sandbox:address:address:editor-data <- get-address *result, current-sandbox:offset
-  *current-sandbox <- new-editor initial-sandbox-contents, screen, new-left, width/right
-  screen <- render-all screen, result
-  reply result
-]
+## the basic editor data structure, and how it displays text to the screen
 
 scenario editor-initially-prints-string-to-screen [
   assume-screen 10/width, 5/height
@@ -100,9 +66,6 @@ container programming-environment-data [
   ]
 ]
 
-## In which we introduce the editor data structure, and show how it displays
-## text to the screen.
-
 container editor-data [
   # editable text: doubly linked list of characters (head contains a special sentinel)
   data:address:duplex-list
@@ -110,7 +73,7 @@ container editor-data [
   before-cursor:address:duplex-list
 
   # raw bounds of display area on screen
-  # always displays from row 1 and at most until bottom of screen
+  # always displays from row 1 (leaving row 0 for a menu) and at most until bottom of screen
   left:number
   right:number
   # raw screen coordinates of cursor
@@ -130,44 +93,52 @@ container editor-data [
   left:number <- next-ingredient
   right:number <- next-ingredient
   right <- subtract right, 1
-  result:address:editor-data <- new editor-data:type
+  result:address:editor-data <- new editor-data:type
   # initialize screen-related fields
-  x:address:number <- get-address *result, left:offset
+  x:address:number <- get-address *result, left:offset
   *x <- copy left
-  x <- get-address *result, right:offset
+  x <- get-address *result, right:offset
   *x <- copy right
   # initialize cursor
-  x <- get-address *result, cursor-row:offset
+  x <- get-address *result, cursor-row:offset
   *x <- copy 1/top
-  x <- get-address *result, cursor-column:offset
+  x <- get-address *result, cursor-column:offset
   *x <- copy left
-  init:address:address:duplex-list <- get-address *result, data:offset
+  init:address:address:duplex-list <- get-address *result, data:offset
   *init <- push-duplex 167/§, 0/tail
-  y:address:address:duplex-list <- get-address *result, before-cursor:offset
+  y:address:address:duplex-list <- get-address *result, before-cursor:offset
   *y <- copy *init
-  # early exit if s is empty
-  reply-unless s, result
-  len:number <- length *s
-  reply-unless len, result
+  result <- insert-text result, s
+  # initialize cursor to top of screen
+  y <- get-address *result, before-cursor:offset
+  *y <- copy *init
+  # initial render to screen, just for some old tests
+  _, screen <- render screen, result
+  reply result
+]
+
+recipe insert-text [
+  local-scope
+  editor:address:editor-data <- next-ingredient
+  text:address:array:character <- next-ingredient
+  # early exit if text is empty
+  reply-unless text, editor/same-as-ingredient:0
+  len:number <- length *text
+  reply-unless len, editor/same-as-ingredient:0
   idx:number <- copy 0
   # now we can start appending the rest, character by character
-  curr:address:duplex-list <- copy *init
+  curr:address:duplex-list <- get *editor, data:offset
   {
     done?:boolean <- greater-or-equal idx, len
     break-if done?
-    c:character <- index *s, idx
+    c:character <- index *text, idx
     insert-duplex c, curr
     # next iter
     curr <- next-duplex curr
     idx <- add idx, 1
     loop
   }
-  # initialize cursor to top of screen
-  y <- get-address *result, before-cursor:offset
-  *y <- copy *init
-  # initial render to screen, just for some old tests
-  _, screen <- render screen, result
-  reply result
+  reply editor/same-as-ingredient:0
 ]
 
 scenario editor-initializes-without-data [
@@ -197,23 +168,22 @@ container editor-data [
   screen:address <- next-ingredient
   editor:address:editor-data <- next-ingredient
   reply-unless editor, 1/top, screen/same-as-ingredient:0
-  left:number <- get *editor, left:offset
+  left:number <- get *editor, left:offset
   screen-height:number <- screen-height screen
-  right:number <- get *editor, right:offset
+  right:number <- get *editor, right:offset
   hide-screen screen
-  # highlight mu code with color
-  color:number <- copy 7/white
-  highlighting-state:number <- copy 0/normal
   # traversing editor
-  curr:address:duplex-list <- get *editor, data:offset
+  curr:address:duplex-list <- get *editor, data:offset
   prev:address:duplex-list <- copy curr
   curr <- next-duplex curr
   # traversing screen
+  +render-loop-initialization
+  color:number <- copy 7/white
   row:number <- copy 1/top
   column:number <- copy left
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  cursor-row:address:number <- get-address *editor, cursor-row:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
   move-cursor screen, row, column
   {
     +next-character
@@ -230,8 +200,8 @@ container editor-data [
       break-unless at-cursor?
       *before-cursor <- prev-duplex curr
     }
-    c:character <- get *curr, value:offset
-    color, highlighting-state <- get-color color, highlighting-state, c
+    c:character <- get *curr, value:offset
+    +character-c-recived
     {
       # newline? move to left rather than 0
       newline?:boolean <- equal c, 10/newline
@@ -370,75 +340,6 @@ container editor-data [
   reply row/same-as-ingredient:5, screen/same-as-ingredient:0
 ]
 
-# row:number, screen:address <- render-screen screen:address, sandbox-screen:address, left:number, right:number, row:number
-# print the fake sandbox screen to 'screen' with appropriate delimiters
-# leave cursor at start of next line
-recipe render-screen [
-  local-scope
-  screen:address <- next-ingredient
-  s:address:screen <- next-ingredient
-  left:number <- next-ingredient
-  right:number <- next-ingredient
-  row:number <- next-ingredient
-  row <- add row, 1
-  reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0
-  # print 'screen:'
-  header:address:array:character <- new [screen:]
-  row <- subtract row, 1  # compensate for render-string below
-  row <- render-string screen, header, left, right, 245/grey, row
-  # newline
-  row <- add row, 1
-  move-cursor screen, row, left
-  # start printing s
-  column:number <- copy left
-  s-width:number <- screen-width s
-  s-height:number <- screen-height s
-  buf:address:array:screen-cell <- get *s, data:offset
-  stop-printing:number <- add left, s-width, 3
-  max-column:number <- min stop-printing, right
-  i:number <- copy 0
-  len:number <- length *buf
-  screen-height:number <- screen-height screen
-  {
-    done?:boolean <- greater-or-equal i, len
-    break-if done?
-    done? <- greater-or-equal row, screen-height
-    break-if done?
-    column <- copy left
-    move-cursor screen, row, column
-    # initial leader for each row: two spaces and a '.'
-    print-character screen, 32/space, 245/grey
-    print-character screen, 32/space, 245/grey
-    print-character screen, 46/full-stop, 245/grey
-    column <- add left, 3
-    {
-      # print row
-      row-done?:boolean <- greater-or-equal column, max-column
-      break-if row-done?
-      curr:screen-cell <- index *buf, i
-      c:character <- get curr, contents:offset
-      print-character screen, c, 245/grey
-      column <- add column, 1
-      i <- add i, 1
-      loop
-    }
-    # print final '.'
-    print-character screen, 46/full-stop, 245/grey
-    column <- add column, 1
-    {
-      # clear rest of current line
-      line-done?:boolean <- greater-than column, right
-      break-if line-done?
-      print-character screen, 32/space
-      column <- add column, 1
-      loop
-    }
-    row <- add row, 1
-    loop
-  }
-  reply row/same-as-ingredient:4, screen/same-as-ingredient:0
-]
-
 recipe clear-line-delimited [
   local-scope
   screen:address <- next-ingredient
@@ -544,8 +445,8 @@ container editor-data [
   run [
     1:address:array:character <- new []
     2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .     .
@@ -558,7 +459,7 @@ container editor-data [
   ]
 ]
 
-## highlighting mu code
+# just a little color for mu code
 
 scenario render-colors-comments [
   assume-screen 5/width, 5/height
@@ -591,11 +492,15 @@ container editor-data [
   ]
 ]
 
-# color:number, highlighting-state:number <- get-color color:number, highlighting-state:number, c:character
+after +character-c-recived [
+  color <- get-color color, c
+]
+
+# color:number <- get-color color:number, c:character
+# so far the previous color is all the information we need; that may change
 recipe get-color [
   local-scope
   color:number <- next-ingredient
-  highlighting-state:number <- next-ingredient
   c:character <- next-ingredient
   color-is-white?:boolean <- equal color, 7/white
 #?   $print [character: ], c, 10/newline #? 1
@@ -637,7 +542,7 @@ container editor-data [
   }
   # otherwise no change
   +exit
-  reply color, highlighting-state
+  reply color
 ]
 
 scenario render-colors-assignment [
@@ -666,100 +571,6 @@ container editor-data [
 
 ## handling events from the keyboard, mouse, touch screen, ...
 
-recipe event-loop [
-  local-scope
-  screen:address <- next-ingredient
-  console:address <- next-ingredient
-  env:address:programming-environment-data <- next-ingredient
-  recipes:address:editor-data <- get *env, recipes:offset
-  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
-  sandbox-in-focus?:address:boolean <- get-address *env, sandbox-in-focus?:offset
-  {
-    # looping over each (keyboard or touch) event as it occurs
-    +next-event
-    e:event, console, found?:boolean, quit?:boolean <- read-event console
-    loop-unless found?
-    break-if quit?  # only in tests
-    trace [app], [next-event]
-    # check for global events that will trigger regardless of which editor has focus
-    {
-      k:address:number <- maybe-convert e:event, keycode:variant
-      break-unless k
-      # F4? load all code and run all sandboxes.
-      {
-        do-run?:boolean <- equal *k, 65532/F4
-        break-unless do-run?
-        run-sandboxes env
-        # F4 might update warnings and results on both sides
-        screen <- render-all screen, env
-        update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
-        show-screen screen
-        loop +next-event:label
-      }
-    }
-    {
-      c:address:character <- maybe-convert e:event, text:variant
-      break-unless c
-      # ctrl-n? - switch focus
-      {
-        ctrl-n?:boolean <- equal *c, 14/ctrl-n
-        break-unless ctrl-n?
-        *sandbox-in-focus? <- not *sandbox-in-focus?
-        update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
-        show-screen screen
-        loop +next-event:label
-      }
-    }
-    # 'touch' event
-    {
-      t:address:touch-event <- maybe-convert e:event, touch:variant
-      break-unless t
-      # ignore all but 'left-click' events for now
-      # todo: test this
-      touch-type:number <- get *t, type:offset
-      is-left-click?:boolean <- equal touch-type, 65513/mouse-left
-      loop-unless is-left-click?, +next-event:label
-      # on a sandbox delete icon? process delete
-      {
-        was-delete?:boolean <- delete-sandbox *t, env
-        break-unless was-delete?
-#?         trace [app], [delete clicked] #? 1
-        screen <- render-sandbox-side screen, env, 1/clear
-        update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
-        show-screen screen
-        loop +next-event:label
-      }
-      # if not, send to both editors
-      _ <- move-cursor-in-editor screen, recipes, *t
-      *sandbox-in-focus? <- move-cursor-in-editor screen, current-sandbox, *t
-      jump +continue:label
-    }
-    # if it's not global, send to appropriate editor
-    {
-      {
-        break-if *sandbox-in-focus?
-        handle-event screen, console, recipes, e:event
-      }
-      {
-        break-unless *sandbox-in-focus?
-        handle-event screen, console, current-sandbox, e:event
-      }
-    }
-    +continue
-    # if no more events currently left to process, render.
-    # we rely on 'render' to update 'before-cursor' on pointer events, but
-    # they won't usually come fast enough to trigger this.
-    # todo: test this
-    {
-      more-events?:boolean <- has-more-events? console
-      break-if more-events?
-      render-minimal screen, env
-    }
-    loop
-  }
-]
-
-# helper for testing a single editor
 recipe editor-event-loop [
   local-scope
   screen:address <- next-ingredient
@@ -774,18 +585,18 @@ container editor-data [
     trace [app], [next-event]
     # 'touch' event - send to both editors
     {
-      t:address:touch-event <- maybe-convert e:event, touch:variant
+      t:address:touch-event <- maybe-convert e, touch:variant
       break-unless t
       move-cursor-in-editor screen, editor, *t
       jump +continue:label
     }
     # other events - send to appropriate editor
-    handle-event screen, console, editor, e:event
+    handle-event screen, console, editor, e
     +continue
     row:number, screen <- render screen, editor
     # clear next line, in case we just processed a backspace
-    left:number <- get *editor, left:offset
-    right:number <- get *editor, right:offset
+    left:number <- get *editor, left:offset
+    right:number <- get *editor, right:offset
     row <- add row, 1
     move-cursor screen, row, left
     clear-line-delimited screen, left, right
@@ -802,7 +613,7 @@ container editor-data [
   reply-unless editor
   # character
   {
-    c:address:character <- maybe-convert e:event, text:variant
+    c:address:character <- maybe-convert e, text:variant
     break-unless c
     ## check for special characters
     # backspace - delete character before cursor
@@ -843,7 +654,7 @@ container editor-data [
     # tab - insert two spaces
     {
       tab?:boolean <- equal *c, 9/tab
-      break-unless tab?:boolean
+      break-unless tab?
       insert-at-cursor editor, 32/space, screen
       insert-at-cursor editor, 32/space, screen
       reply
@@ -853,15 +664,15 @@ container editor-data [
     reply
   }
   # otherwise it's a special key
-  k:address:number <- maybe-convert e:event, keycode:variant
+  k:address:number <- maybe-convert e:event, keycode:variant
   assert k, [event was of unknown type; neither keyboard nor mouse]
-  d:address:duplex-list <- get *editor, data:offset
-  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  d:address:duplex-list <- get *editor, data:offset
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  cursor-row:address:number <- get-address *editor, cursor-row:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
   screen-height:number <- screen-height screen
-  left:number <- get *editor, left:offset
-  right:number <- get *editor, right:offset
+  left:number <- get *editor, left:offset
+  right:number <- get *editor, right:offset
   # arrows; update cursor-row and cursor-column, leave before-cursor to 'render'.
   # right arrow
   {
@@ -874,7 +685,7 @@ container editor-data [
     *before-cursor <- copy old-cursor
     # if crossed a newline, move cursor to start of next row
     {
-      old-cursor-character:character <- get **before-cursor, value:offset
+      old-cursor-character:character <- get **before-cursor, value:offset
       was-at-newline?:boolean <- equal old-cursor-character, 10/newline
       break-unless was-at-newline?
       *cursor-row <- add *cursor-row, 1
@@ -894,7 +705,7 @@ container editor-data [
       # and if next character isn't newline
       new-cursor:address:duplex-list <- next-duplex old-cursor
       break-unless new-cursor
-      next-character:character <- get *new-cursor, value:offset
+      next-character:character <- get *new-cursor, value:offset
       newline?:boolean <- equal next-character, 10/newline
       break-if newline?
       *cursor-row <- add *cursor-row, 1
@@ -915,32 +726,7 @@ container editor-data [
     # if not at start of text (before-cursor at § sentinel)
     prev:address:duplex-list <- prev-duplex *before-cursor
     break-unless prev
-    # if cursor not at left margin, move one character left
-    {
-      at-left-margin?:boolean <- equal *cursor-column, 0
-      break-if at-left-margin?
-#?       trace [app], [decrementing] #? 1
-      *cursor-column <- subtract *cursor-column, 1
-      reply
-    }
-    # if at left margin, there's guaranteed to be a previous line, since we're
-    # not at start of text
-    {
-      # if before-cursor is at newline, figure out how long the previous line is
-      prevc:character <- get **before-cursor, value:offset
-      previous-character-is-newline?:boolean <- equal prevc, 10/newline
-      break-unless previous-character-is-newline?
-#?       trace [app], [previous line] #? 1
-      # compute length of previous line
-      end-of-line:number <- previous-line-length *before-cursor, d
-      *cursor-row <- subtract *cursor-row, 1
-      *cursor-column <- copy end-of-line
-      reply
-    }
-    # if before-cursor is not at newline, we're just at a wrapped line
-    assert *cursor-row, [unimplemented: moving cursor above top of screen]
-    *cursor-row <- subtract *cursor-row, 1
-    *cursor-column <- subtract right, 1  # leave room for wrap icon
+    editor <- move-cursor-coordinates-left editor
   }
   # down arrow
   {
@@ -980,6 +766,14 @@ container editor-data [
     move-to-end-of-line editor
     reply
   }
+  # delete
+  {
+    delete?:boolean <- equal *k, 65522/delete
+    break-unless delete?
+    curr:address:duplex-list <- get **before-cursor, next:offset
+    _ <- remove-duplex curr
+    reply
+  }
 ]
 
 # process click, return if it was on current editor
@@ -990,18 +784,18 @@ container editor-data [
   editor:address:editor-data <- next-ingredient
   t:touch-event <- next-ingredient
   reply-unless editor, 0/false
-  click-column:number <- get t, column:offset
-  left:number <- get *editor, left:offset
+  click-column:number <- get t, column:offset
+  left:number <- get *editor, left:offset
   too-far-left?:boolean <- lesser-than click-column, left
   reply-if too-far-left?, 0/false
-  right:number <- get *editor, right:offset
+  right:number <- get *editor, right:offset
   too-far-right?:boolean <- greater-than click-column, right
   reply-if too-far-right?, 0/false
   # update cursor
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  *cursor-row <- get t, row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  *cursor-column <- get t, column:offset
+  cursor-row:address:number <- get-address *editor, cursor-row:offset
+  *cursor-row <- get t, row:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  *cursor-column <- get t, column:offset
   # gain focus
   reply 1/true
 ]
@@ -1012,13 +806,13 @@ container editor-data [
   c:character <- next-ingredient
   screen:address <- next-ingredient
 #?   $print [insert ], c, 10/newline #? 1
-  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
   insert-duplex c, *before-cursor
   *before-cursor <- next-duplex *before-cursor
-  cursor-row:address:number <- get-address *editor, cursor-row:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
-  left:number <- get *editor, left:offset
-  right:number <- get *editor, right:offset
+  cursor-row:address:number <- get-address *editor, cursor-row:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  left:number <- get *editor, left:offset
+  right:number <- get *editor, right:offset
   # update cursor: if newline, move cursor to start of next line
   # todo: bottom of screen
   {
@@ -1028,7 +822,7 @@ container editor-data [
     *cursor-column <- copy left
     # indent if necessary
 #?     $print [computing indent], 10/newline #? 1
-    d:address:duplex-list <- get *editor, data:offset
+    d:address:duplex-list <- get *editor, data:offset
     end-of-previous-line:address:duplex-list <- prev-duplex *before-cursor
     indent:number <- line-indent end-of-previous-line, d
 #?     $print indent, 10/newline #? 1
@@ -1068,19 +862,49 @@ container editor-data [
 recipe delete-before-cursor [
   local-scope
   editor:address:editor-data <- next-ingredient
-  before-cursor:address:address:duplex-list <- get-address *editor:address:editor-data, before-cursor:offset
-  d:address:duplex-list <- get *editor:address:editor-data, data:offset
-  # unless already at start
-  at-start?:boolean <- equal *before-cursor:address:address:duplex-list, d:address:duplex-list
-  reply-if at-start?:boolean
-  # delete character
-  prev:address:duplex-list <- prev-duplex *before-cursor:address:address:duplex-list
-  remove-duplex *before-cursor:address:address:duplex-list
-  # update cursor
-  *before-cursor:address:address:duplex-list <- copy prev:address:duplex-list
-  cursor-column:address:number <- get-address *editor:address:editor-data, cursor-column:offset
-  *cursor-column:address:number <- subtract *cursor-column:address:number, 1
-#?   $print [delete-before-cursor: ], *cursor-column:address:number, 10/newline
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  # if at start of text (before-cursor at § sentinel), return
+  prev:address:duplex-list <- prev-duplex *before-cursor
+  reply-unless prev
+  editor <- move-cursor-coordinates-left editor
+  remove-duplex *before-cursor
+  *before-cursor <- copy prev
+]
+
+recipe move-cursor-coordinates-left [
+  local-scope
+  editor:address:editor-data <- next-ingredient
+  before-cursor:address:duplex-list <- get *editor, before-cursor:offset
+  cursor-row:address:number <- get-address *editor, cursor-row:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  # if not at left margin, move one character left
+  {
+    at-left-margin?:boolean <- equal *cursor-column, 0
+    break-if at-left-margin?
+#?     trace [app], [decrementing cursor column] #? 1
+    *cursor-column <- subtract *cursor-column, 1
+    reply editor/same-as-ingredient:0
+  }
+  # if at left margin, we must move to previous row:
+  assert *cursor-row, [unimplemented: moving cursor above top of screen]
+  *cursor-row <- subtract *cursor-row, 1
+  {
+    # case 1: if previous character was newline, figure out how long the previous line is
+    previous-character:character <- get *before-cursor, value:offset
+    previous-character-is-newline?:boolean <- equal previous-character, 10/newline
+    break-unless previous-character-is-newline?
+    # compute length of previous line
+#?     trace [app], [switching to previous line] #? 1
+    d:address:duplex-list <- get *editor, data:offset
+    end-of-line:number <- previous-line-length before-cursor, d
+    *cursor-column <- copy end-of-line
+    reply editor/same-as-ingredient:0
+  }
+  # case 2: if previous-character was not newline, we're just at a wrapped line
+#?   trace [app], [wrapping to previous line] #? 1
+  right:number <- get *editor, right:offset
+  *cursor-column <- subtract right, 1  # leave room for wrap icon
+  reply editor/same-as-ingredient:0
 ]
 
 # takes a pointer 'curr' into the doubly-linked list and its sentinel, counts
@@ -1098,7 +922,7 @@ container editor-data [
     break-unless curr
     at-start?:boolean <- equal curr, start
     break-if at-start?
-    c:character <- get *curr, value:offset
+    c:character <- get *curr, value:offset
     at-newline?:boolean <- equal c, 10/newline
     break-if at-newline?
     result <- add result, 1
@@ -1122,7 +946,7 @@ container editor-data [
     break-unless curr
     at-start?:boolean <- equal curr, start
     break-if at-start?
-    c:character <- get *curr, value:offset
+    c:character <- get *curr, value:offset
     at-newline?:boolean <- equal c, 10/newline
     break-if at-newline?
     # if c is a space, increment result
@@ -1145,17 +969,17 @@ container editor-data [
   local-scope
   editor:address:editor-data <- next-ingredient
   # update cursor column
-  left:number <- get *editor, left:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  left:number <- get *editor, left:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
   *cursor-column <- copy left
   # update before-cursor
-  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
-  init:address:duplex-list <- get *editor, data:offset
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  init:address:duplex-list <- get *editor, data:offset
   # while not at start of line, move 
   {
     at-start-of-text?:boolean <- equal *before-cursor, init
     break-if at-start-of-text?
-    prev:character <- get **before-cursor, value:offset
+    prev:character <- get **before-cursor, value:offset
     at-start-of-line?:boolean <- equal prev, 10/newline
     break-if at-start-of-line?
     *before-cursor <- prev-duplex *before-cursor
@@ -1167,13 +991,13 @@ container editor-data [
 recipe move-to-end-of-line [
   local-scope
   editor:address:editor-data <- next-ingredient
-  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
   # while not at start of line, move 
   {
     next:address:duplex-list <- next-duplex *before-cursor
     break-unless next  # end of text
-    nextc:character <- get *next, value:offset
+    nextc:character <- get *next, value:offset
     at-end-of-line?:boolean <- equal nextc, 10/newline
     break-if at-end-of-line?
     *before-cursor <- copy next
@@ -1188,14 +1012,14 @@ container editor-data [
   local-scope
   editor:address:editor-data <- next-ingredient
   # compute range to delete
-  init:address:duplex-list <- get *editor, data:offset
-  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
+  init:address:duplex-list <- get *editor, data:offset
+  before-cursor:address:address:duplex-list <- get-address *editor, before-cursor:offset
   start:address:duplex-list <- copy *before-cursor
   end:address:duplex-list <- next-duplex *before-cursor
   {
     at-start-of-text?:boolean <- equal start, init
     break-if at-start-of-text?
-    curr:character <- get *start, value:offset
+    curr:character <- get *start, value:offset
     at-start-of-line?:boolean <- equal curr, 10/newline
     break-if at-start-of-line?
     start <- prev-duplex start
@@ -1203,14 +1027,14 @@ container editor-data [
     loop
   }
   # snip it out
-  start-next:address:address:duplex-list <- get-address *start, next:offset
+  start-next:address:address:duplex-list <- get-address *start, next:offset
   *start-next <- copy end
-  end-prev:address:address:duplex-list <- get-address *end, prev:offset
+  end-prev:address:address:duplex-list <- get-address *end, prev:offset
   *end-prev <- copy start
   # adjust cursor
   *before-cursor <- prev-duplex end
-  left:number <- get *editor, left:offset
-  cursor-column:address:number <- get-address *editor, cursor-column:offset
+  left:number <- get *editor, left:offset
+  cursor-column:address:number <- get-address *editor, cursor-column:offset
   *cursor-column <- copy left
 ]
 
@@ -1218,142 +1042,40 @@ container editor-data [
   local-scope
   editor:address:editor-data <- next-ingredient
   # compute range to delete
-  start:address:duplex-list <- get *editor, before-cursor:offset
+  start:address:duplex-list <- get *editor, before-cursor:offset
   end:address:duplex-list <- next-duplex start
   {
     at-end-of-text?:boolean <- equal end, 0/null
     break-if at-end-of-text?
-    curr:character <- get *end, value:offset
+    curr:character <- get *end, value:offset
     at-end-of-line?:boolean <- equal curr, 10/newline
     break-if at-end-of-line?
     end <- next-duplex end
     loop
   }
   # snip it out
-  start-next:address:address:duplex-list <- get-address *start, next:offset
+  start-next:address:address:duplex-list <- get-address *start, next:offset
   *start-next <- copy end
   {
     break-unless end
-    end-prev:address:address:duplex-list <- get-address *end, prev:offset
+    end-prev:address:address:duplex-list <- get-address *end, prev:offset
     *end-prev <- copy start
   }
 ]
 
-recipe render-all [
-  local-scope
-  screen:address <- next-ingredient
-  env:address:programming-environment-data <- next-ingredient
-  screen <- render-recipes screen, env, 1/clear-below
-  screen <- render-sandbox-side screen, env, 1/clear-below
-  recipes:address:editor-data <- get *env, recipes:offset
-  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
-  sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
-  update-cursor screen, recipes, current-sandbox, sandbox-in-focus?
-  show-screen screen
-  reply screen/same-as-ingredient:0
-]
-
-recipe render-minimal [
-  local-scope
-  screen:address <- next-ingredient
-  env:address:programming-environment-data <- next-ingredient
-  recipes:address:editor-data <- get *env, recipes:offset
-  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
-  sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
-  {
-    break-if sandbox-in-focus?
-    screen <- render-recipes screen, env
-    cursor-row:number <- get *recipes, cursor-row:offset
-    cursor-column:number <- get *recipes, cursor-column:offset
-  }
-  {
-    break-unless sandbox-in-focus?
-    screen <- render-sandbox-side screen, env
-    cursor-row:number <- get *current-sandbox, cursor-row:offset
-    cursor-column:number <- get *current-sandbox, cursor-column:offset
-  }
-  move-cursor screen, cursor-row, cursor-column
-  show-screen screen
-  reply screen/same-as-ingredient:0
-]
-
-recipe render-recipes [
-  local-scope
-  screen:address <- next-ingredient
-  env:address:programming-environment-data <- next-ingredient
-  clear:boolean <- next-ingredient
-  recipes:address:editor-data <- get *env, recipes:offset
-  # render recipes
-  left:number <- get *recipes, left:offset
-  right:number <- get *recipes, right:offset
-  row:number, screen <- render screen, recipes
-  recipe-warnings:address:array:character <- get *env, recipe-warnings:offset
-  {
-    # print any warnings
-    break-unless recipe-warnings
-    row, screen <- render-string screen, recipe-warnings, left, right, 1/red, row
-  }
-  {
-    # no warnings? move to next line
-    break-if recipe-warnings
-    row <- add row, 1
-  }
-  # draw dotted line after recipes
-  draw-horizontal screen, row, left, right, 9480/horizontal-dotted
-  # clear next line, in case we just processed a backspace
-  row <- add row, 1
-  move-cursor screen, row, left
-  clear-line-delimited screen, left, right
-  # clear rest of screen in this column, if requested
-  reply-unless clear, screen/same-as-ingredient:0
-  screen-height:number <- screen-height screen
-  {
-    at-bottom-of-screen?:boolean <- greater-or-equal row, screen-height
-    break-if at-bottom-of-screen?
-    move-cursor screen, row, left
-    clear-line-delimited screen, left, right
-    row <- add row, 1
-    loop
-  }
-  reply screen/same-as-ingredient:0
-]
-
-recipe update-cursor [
-  local-scope
-  screen:address <- next-ingredient
-  recipes:address:editor-data <- next-ingredient
-  current-sandbox:address:editor-data <- next-ingredient
-  sandbox-in-focus?:boolean <- next-ingredient
-  {
-    break-if sandbox-in-focus?
-#?     $print [recipes in focus
-#? ] #? 1
-    cursor-row:number <- get *recipes, cursor-row:offset
-    cursor-column:number <- get *recipes, cursor-column:offset
-  }
-  {
-    break-unless sandbox-in-focus?
-#?     $print [sandboxes in focus
-#? ] #? 1
-    cursor-row:number <- get *current-sandbox, cursor-row:offset
-    cursor-column:number <- get *current-sandbox, cursor-column:offset
-  }
-  move-cursor screen, cursor-row, cursor-column
-]
-
-scenario editor-handles-empty-event-queue [
-  assume-screen 10/width, 5/height
-  1:address:array:character <- new [abc]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
-  assume-console []
-  run [
-    editor-event-loop screen:address, console:address, 2:address:editor-data
-  ]
-  screen-should-contain [
-    .          .
-    .abc       .
-    .          .
-  ]
+scenario editor-handles-empty-event-queue [
+  assume-screen 10/width, 5/height
+  1:address:array:character <- new [abc]
+  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
+  assume-console []
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  screen-should-contain [
+    .          .
+    .abc       .
+    .          .
+  ]
 ]
 
 scenario editor-handles-mouse-clicks [
@@ -1365,8 +1087,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -1388,8 +1110,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 1  # cursor row
@@ -1407,8 +1129,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 1  # cursor row
@@ -1426,8 +1148,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 2  # cursor row
@@ -1446,8 +1168,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -1633,8 +1355,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -1658,8 +1380,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -1754,8 +1476,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor should be below start of previous line
   memory-should-contain [
@@ -1764,6 +1486,34 @@ container editor-data [
   ]
 ]
 
+scenario editor-handles-delete-key [
+  assume-screen 10/width, 5/height
+  1:address:array:character <- new [abc]
+  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
+  assume-console [
+    press 65522  # delete
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  screen-should-contain [
+    .          .
+    .bc        .
+    .          .
+  ]
+  assume-console [
+    press 65522  # delete
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+  ]
+  screen-should-contain [
+    .          .
+    .c         .
+    .          .
+  ]
+]
+
 scenario editor-handles-backspace-key [
   assume-screen 10/width, 5/height
   1:address:array:character <- new [abc]
@@ -1776,8 +1526,8 @@ container editor-data [
   replace-in-console 171/«, 3:event/backspace
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    4:number <- get *2:address:editor-data, cursor-row:offset
-    5:number <- get *2:address:editor-data, cursor-column:offset
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -1795,7 +1545,7 @@ container editor-data [
   # just one character in final line
   1:address:array:character <- new [ab
 cd]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 5/right
+  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
   assume-console [
     left-click 2, 0  # cursor at only character in final line
     type [«]
@@ -1804,12 +1554,18 @@ container editor-data [
   replace-in-console 171/«, 3:event/backspace
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
     .abcd      .
     .          .
   ]
+  memory-should-contain [
+    4 <- 1
+    5 <- 2
+  ]
 ]
 
 scenario editor-inserts-two-spaces-on-tab [
@@ -1907,8 +1663,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -1934,8 +1690,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 2
@@ -1947,8 +1703,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 2
@@ -1966,8 +1722,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .          .
@@ -2034,8 +1790,8 @@ container editor-data [
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 1
@@ -2136,8 +1892,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 1  # previous row
@@ -2156,8 +1912,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 1
@@ -2176,8 +1932,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # ..and ends at (2, 0)
   memory-should-contain [
@@ -2197,8 +1953,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 1
@@ -2217,8 +1973,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     3 <- 2
@@ -2240,8 +1996,8 @@ d]
   replace-in-console 97/a, 3:event/ctrl-a
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    4:number <- get *2:address:editor-data, cursor-row:offset
-    5:number <- get *2:address:editor-data, cursor-column:offset
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to start of line
   memory-should-contain [
@@ -2264,8 +2020,8 @@ d]
   replace-in-console 97/a, 3:event/ctrl-a
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    4:number <- get *2:address:editor-data, cursor-row:offset
-    5:number <- get *2:address:editor-data, cursor-column:offset
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to start of line
   memory-should-contain [
@@ -2286,8 +2042,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to start of line
   memory-should-contain [
@@ -2308,8 +2064,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to start of line
   memory-should-contain [
@@ -2332,8 +2088,8 @@ d]
   replace-in-console 101/e, 3:event/ctrl-e
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    4:number <- get *2:address:editor-data, cursor-row:offset
-    5:number <- get *2:address:editor-data, cursor-column:offset
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to end of line
   memory-should-contain [
@@ -2346,8 +2102,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    4:number <- get *2:address:editor-data, cursor-row:offset
-    5:number <- get *2:address:editor-data, cursor-column:offset
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     4 <- 1
@@ -2375,8 +2131,8 @@ d]
   replace-in-console 101/e, 3:event/ctrl-e
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    4:number <- get *2:address:editor-data, cursor-row:offset
-    5:number <- get *2:address:editor-data, cursor-column:offset
+    4:number <- get *2:address:editor-data, cursor-row:offset
+    5:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to end of line
   memory-should-contain [
@@ -2397,8 +2153,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to end of line
   memory-should-contain [
@@ -2419,8 +2175,8 @@ d]
   ]
   run [
     editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:number <- get *2:address:editor-data, cursor-row:offset
-    4:number <- get *2:address:editor-data, cursor-column:offset
+    3:number <- get *2:address:editor-data, cursor-row:offset
+    4:number <- get *2:address:editor-data, cursor-column:offset
   ]
   # cursor moves to end of line
   memory-should-contain [
@@ -2645,6 +2401,119 @@ d]
   ]
 ]
 
+## putting the environment together out of editors
+
+container programming-environment-data [
+  recipes:address:editor-data
+  recipe-warnings:address:array:character
+  current-sandbox:address:editor-data
+  sandbox:address:sandbox-data  # list of sandboxes, from top to bottom
+  sandbox-in-focus?:boolean  # false => cursor in recipes; true => cursor in current-sandbox
+]
+
+recipe new-programming-environment [
+  local-scope
+  screen:address <- next-ingredient
+  initial-recipe-contents:address:array:character <- next-ingredient
+  initial-sandbox-contents:address:array:character <- next-ingredient
+  width:number <- screen-width screen
+  height:number <- screen-height screen
+  # top menu
+  result:address:programming-environment-data <- new programming-environment-data:type
+  draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
+  button-start:number <- subtract width, 20
+  button-on-screen?:boolean <- greater-or-equal button-start, 0
+  assert button-on-screen?, [screen too narrow for menu]
+  move-cursor screen, 0/row, button-start
+  run-button:address:array:character <- new [ run (F4) ]
+  print-string screen, run-button, 255/white, 161/reddish
+  # dotted line down the middle
+  divider:number, _ <- divide-with-remainder width, 2
+  draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
+  # recipe editor on the left
+  recipes:address:address:editor-data <- get-address *result, recipes:offset
+  *recipes <- new-editor initial-recipe-contents, screen, 0/left, divider/right
+  # sandbox editor on the right
+  new-left:number <- add divider, 1
+  new-right:number <- add new-left, 5
+  current-sandbox:address:address:editor-data <- get-address *result, current-sandbox:offset
+  *current-sandbox <- new-editor initial-sandbox-contents, screen, new-left, width/right
+  screen <- render-all screen, result
+  reply result
+]
+
+recipe event-loop [
+  local-scope
+  screen:address <- next-ingredient
+  console:address <- next-ingredient
+  env:address:programming-environment-data <- next-ingredient
+  recipes:address:editor-data <- get *env, recipes:offset
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  sandbox-in-focus?:address:boolean <- get-address *env, sandbox-in-focus?:offset
+  {
+    # looping over each (keyboard or touch) event as it occurs
+    +next-event
+    e:event, console, found?:boolean, quit?:boolean <- read-event console
+    loop-unless found?
+    break-if quit?  # only in tests
+    trace [app], [next-event]
+    # check for global events that will trigger regardless of which editor has focus
+    {
+      k:address:number <- maybe-convert e:event, keycode:variant
+      break-unless k
+      +global-keypress
+    }
+    {
+      c:address:character <- maybe-convert e:event, text:variant
+      break-unless c
+      +global-type
+      # ctrl-n? - switch focus
+      {
+        ctrl-n?:boolean <- equal *c, 14/ctrl-n
+        break-unless ctrl-n?
+        *sandbox-in-focus? <- not *sandbox-in-focus?
+        update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
+        show-screen screen
+        loop +next-event:label
+      }
+    }
+    # 'touch' event - send to both sides, see what picks it up
+    {
+      t:address:touch-event <- maybe-convert e:event, touch:variant
+      break-unless t
+      # ignore all but 'left-click' events for now
+      # todo: test this
+      touch-type:number <- get *t, type:offset
+      is-left-click?:boolean <- equal touch-type, 65513/mouse-left
+      loop-unless is-left-click?, +next-event:label
+      # later exceptions for non-editor touches will go here
+      +global-touch
+      # send to both editors
+      _ <- move-cursor-in-editor screen, recipes, *t
+      *sandbox-in-focus? <- move-cursor-in-editor screen, current-sandbox, *t
+      render-minimal screen, env
+      loop +next-event:label
+    }
+    # if it's not global and not a touch event, send to appropriate editor
+    {
+      {
+        break-if *sandbox-in-focus?
+        handle-event screen, console, recipes, e:event
+      }
+      {
+        break-unless *sandbox-in-focus?
+        handle-event screen, console, current-sandbox, e:event
+      }
+      # optimization: refresh screen only if no more events
+      # todo: test this
+      more-events?:boolean <- has-more-events? console
+      break-if more-events?
+      render-minimal screen, env
+    }
+    loop
+  }
+]
+
 scenario point-at-multiple-editors [
   $close-trace
   assume-screen 30/width, 5/height
@@ -2660,10 +2529,10 @@ d]
   # check cursor column in each
   run [
     event-loop screen:address, console:address, 3:address:programming-environment-data
-    4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
-    5:number <- get *4:address:editor-data, cursor-column:offset
-    6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
-    7:number <- get *6:address:editor-data, cursor-column:offset
+    4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
+    5:number <- get *4:address:editor-data, cursor-column:offset
+    6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
+    7:number <- get *6:address:editor-data, cursor-column:offset
   ]
   memory-should-contain [
     5 <- 1
@@ -2687,10 +2556,10 @@ d]
   ]
   run [
     event-loop screen:address, console:address, 3:address:programming-environment-data
-    4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
-    5:number <- get *4:address:editor-data, cursor-column:offset
-    6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
-    7:number <- get *6:address:editor-data, cursor-column:offset
+    4:address:editor-data <- get *3:address:programming-environment-data, recipes:offset
+    5:number <- get *4:address:editor-data, cursor-column:offset
+    6:address:editor-data <- get *3:address:programming-environment-data, current-sandbox:offset
+    7:number <- get *6:address:editor-data, cursor-column:offset
   ]
   screen-should-contain [
     .           run (F4)           .  # this line has a different background, but we don't test that yet
@@ -2769,64 +2638,169 @@ d]
   ]
 ]
 
-## Running code from the editors
+recipe render-all [
+  local-scope
+  screen:address <- next-ingredient
+  env:address:programming-environment-data <- next-ingredient
+  screen <- render-recipes screen, env
+  screen <- render-sandbox-side screen, env
+  recipes:address:editor-data <- get *env, recipes:offset
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
+  update-cursor screen, recipes, current-sandbox, sandbox-in-focus?
+  show-screen screen
+  reply screen/same-as-ingredient:0
+]
 
-container sandbox-data [
-  data:address:array:character
-  response:address:array:character
-  warnings:address:array:character
-  starting-row-on-screen:number  # to track clicks on delete
-  screen:address:screen  # prints in the sandbox go here
-  next-sandbox:address:sandbox-data
+recipe render-minimal [
+  local-scope
+  screen:address <- next-ingredient
+  env:address:programming-environment-data <- next-ingredient
+  recipes:address:editor-data <- get *env, recipes:offset
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  sandbox-in-focus?:boolean <- get *env, sandbox-in-focus?:offset
+  {
+    break-if sandbox-in-focus?
+    screen <- render-recipes screen, env
+    cursor-row:number <- get *recipes, cursor-row:offset
+    cursor-column:number <- get *recipes, cursor-column:offset
+  }
+  {
+    break-unless sandbox-in-focus?
+    screen <- render-sandbox-side screen, env
+    cursor-row:number <- get *current-sandbox, cursor-row:offset
+    cursor-column:number <- get *current-sandbox, cursor-column:offset
+  }
+  move-cursor screen, cursor-row, cursor-column
+  show-screen screen
+  reply screen/same-as-ingredient:0
 ]
 
-scenario run-and-show-results [
-  $close-trace  # trace too long for github
-  assume-screen 100/width, 15/height
-  # recipe editor is empty
-  1:address:array:character <- new []
-  # sandbox editor contains an instruction without storing outputs
-  2:address:array:character <- new [divide-with-remainder 11, 3]
-  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
-  # run the code in the editors
-  assume-console [
-    press 65532  # F4
-  ]
-  run [
-    event-loop screen:address, console:address, 3:address:programming-environment-data
-  ]
-  # check that screen prints the results
-  screen-should-contain [
-    .                                                                                 run (F4)           .
-    .                                                  ┊                                                 .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .                                                  ┊                                                x.
-    .                                                  ┊divide-with-remainder 11, 3                      .
-    .                                                  ┊3                                                .
-    .                                                  ┊2                                                .
-    .                                                  ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .                                                  ┊                                                 .
-  ]
-  screen-should-contain-in-color 7/white, [
-    .                                                                                                    .
-    .                                                                                                    .
-    .                                                                                                    .
-    .                                                                                                    .
-    .                                                   divide-with-remainder 11, 3                      .
-    .                                                                                                    .
-    .                                                                                                    .
-    .                                                                                                    .
-    .                                                                                                    .
-  ]
-  screen-should-contain-in-color 245/grey, [
-    .                                                                                                    .
-    .                                                  ┊                                                 .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
-    .                                                  ┊                                                x.
-    .                                                  ┊                                                 .
-    .                                                  ┊3                                                .
-    .                                                  ┊2                                                .
-    .                                                  ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
+recipe render-recipes [
+  local-scope
+  screen:address <- next-ingredient
+  env:address:programming-environment-data <- next-ingredient
+  recipes:address:editor-data <- get *env, recipes:offset
+  # render recipes
+  left:number <- get *recipes, left:offset
+  right:number <- get *recipes, right:offset
+  row:number, screen <- render screen, recipes
+  recipe-warnings:address:array:character <- get *env, recipe-warnings:offset
+  {
+    # print any warnings
+    break-unless recipe-warnings
+    row, screen <- render-string screen, recipe-warnings, left, right, 1/red, row
+  }
+  {
+    # no warnings? move to next line
+    break-if recipe-warnings
+    row <- add row, 1
+  }
+  # draw dotted line after recipes
+  draw-horizontal screen, row, left, right, 9480/horizontal-dotted
+  # clear rest of screen
+  row <- add row, 1
+  move-cursor screen, row, left
+  screen-height:number <- screen-height screen
+  {
+    at-bottom-of-screen?:boolean <- greater-or-equal row, screen-height
+    break-if at-bottom-of-screen?
+    move-cursor screen, row, left
+    clear-line-delimited screen, left, right
+    row <- add row, 1
+    loop
+  }
+  reply screen/same-as-ingredient:0
+]
+
+# helper for testing a single editor
+
+recipe update-cursor [
+  local-scope
+  screen:address <- next-ingredient
+  recipes:address:editor-data <- next-ingredient
+  current-sandbox:address:editor-data <- next-ingredient
+  sandbox-in-focus?:boolean <- next-ingredient
+  {
+    break-if sandbox-in-focus?
+#?     $print [recipes in focus
+#? ] #? 1
+    cursor-row:number <- get *recipes, cursor-row:offset
+    cursor-column:number <- get *recipes, cursor-column:offset
+  }
+  {
+    break-unless sandbox-in-focus?
+#?     $print [sandboxes in focus
+#? ] #? 1
+    cursor-row:number <- get *current-sandbox, cursor-row:offset
+    cursor-column:number <- get *current-sandbox, cursor-column:offset
+  }
+  move-cursor screen, cursor-row, cursor-column
+]
+
+## running code from the editor and creating sandboxes
+
+container sandbox-data [
+  data:address:array:character
+  response:address:array:character
+  warnings:address:array:character
+  trace:address:array:character
+  expected-response:address:array:character
+  # coordinates to track clicks
+  starting-row-on-screen:number
+  response-starting-row-on-screen:number
+  display-trace?:boolean
+  screen:address:screen  # prints in the sandbox go here
+  next-sandbox:address:sandbox-data
+]
+
+scenario run-and-show-results [
+  $close-trace  # trace too long for github
+  assume-screen 100/width, 15/height
+  # recipe editor is empty
+  1:address:array:character <- new []
+  # sandbox editor contains an instruction without storing outputs
+  2:address:array:character <- new [divide-with-remainder 11, 3]
+  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
+  # run the code in the editors
+  assume-console [
+    press 65532  # F4
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # check that screen prints the results
+  screen-should-contain [
+    .                                                                                 run (F4)           .
+    .                                                  ┊                                                 .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
+    .                                                  ┊                                                x.
+    .                                                  ┊divide-with-remainder 11, 3                      .
+    .                                                  ┊3                                                .
+    .                                                  ┊2                                                .
+    .                                                  ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
+    .                                                  ┊                                                 .
+  ]
+  screen-should-contain-in-color 7/white, [
+    .                                                                                                    .
+    .                                                                                                    .
+    .                                                                                                    .
+    .                                                                                                    .
+    .                                                   divide-with-remainder 11, 3                      .
+    .                                                                                                    .
+    .                                                                                                    .
+    .                                                                                                    .
+    .                                                                                                    .
+  ]
+  screen-should-contain-in-color 245/grey, [
+    .                                                                                                    .
+    .                                                  ┊                                                 .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
+    .                                                  ┊                                                x.
+    .                                                  ┊                                                 .
+    .                                                  ┊3                                                .
+    .                                                  ┊2                                                .
+    .                                                  ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━.
     .                                                  ┊                                                 .
   ]
   # run another command
@@ -2856,60 +2830,91 @@ container sandbox-data [
   ]
 ]
 
+# hook into event-loop recipe: read non-unicode keypress from k, process it if
+# necessary, then go to next level
+after +global-keypress [
+  # F4? load all code and run all sandboxes.
+  {
+    do-run?:boolean <- equal *k, 65532/F4
+    break-unless do-run?
+    run-sandboxes env
+    # F4 might update warnings and results on both sides
+    screen <- render-all screen, env
+    update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
+    show-screen screen
+    loop +next-event:label
+  }
+]
+
 recipe run-sandboxes [
   local-scope
   env:address:programming-environment-data <- next-ingredient
-  recipes:address:editor-data <- get *env, recipes:offset
-  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  recipes:address:editor-data <- get *env, recipes:offset
   # copy code from recipe editor, persist, load into mu, save any warnings
   in:address:array:character <- editor-contents recipes
   save [recipes.mu], in
-  recipe-warnings:address:address:array:character <- get-address *env, recipe-warnings:offset
+  recipe-warnings:address:address:array:character <- get-address *env, recipe-warnings:offset
   *recipe-warnings <- reload in
   # if recipe editor has errors, stop
   reply-if *recipe-warnings
   # check contents of right editor (sandbox)
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
   {
     sandbox-contents:address:array:character <- editor-contents current-sandbox
     break-unless sandbox-contents
     # if contents exist, first save them
     # run them and turn them into a new sandbox-data
-    new-sandbox:address:sandbox-data <- new sandbox-data:type
-    data:address:address:array:character <- get-address *new-sandbox, data:offset
+    new-sandbox:address:sandbox-data <- new sandbox-data:type
+    data:address:address:array:character <- get-address *new-sandbox, data:offset
     *data <- copy sandbox-contents
     # push to head of sandbox list
-    dest:address:address:sandbox-data <- get-address *env, sandbox:offset
-    next:address:address:sandbox-data <- get-address *new-sandbox, next-sandbox:offset
+    dest:address:address:sandbox-data <- get-address *env, sandbox:offset
+    next:address:address:sandbox-data <- get-address *new-sandbox, next-sandbox:offset
     *next <- copy *dest
     *dest <- copy new-sandbox
     # clear sandbox editor
-    init:address:address:duplex-list <- get-address *current-sandbox, data:offset
+    init:address:address:duplex-list <- get-address *current-sandbox, data:offset
     *init <- push-duplex 167/§, 0/tail
   }
   # save all sandboxes before running, just in case we die when running
-  # first clear previous versions, in case we deleted some sandbox
-  $system [rm lesson/[0-9]*]
-  curr:address:sandbox-data <- get *env, sandbox:offset
-  filename:number <- copy 0
+  save-sandboxes env
+  # run all sandboxes
+  curr:address:sandbox-data <- get *env, sandbox:offset
   {
     break-unless curr
-    data:address:address:array:character <- get-address *curr, data:offset
-    save filename, *data
-    filename <- add filename, 1
-    curr <- get *curr, next-sandbox:offset
+    data <- get-address *curr, data:offset
+    response:address:address:array:character <- get-address *curr, response:offset
+    warnings:address:address:array:character <- get-address *curr, warnings:offset
+    trace:address:address:array:character <- get-address *curr, trace:offset
+    fake-screen:address:address:screen <- get-address *curr, screen:offset
+    *response, *warnings, *fake-screen, *trace <- run-interactive *data
+    curr <- get *curr, next-sandbox:offset
     loop
   }
-  # run all sandboxes
-  curr <- get *env, sandbox:offset
+]
+
+recipe save-sandboxes [
+  local-scope
+  env:address:programming-environment-data <- next-ingredient
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  # first clear previous versions, in case we deleted some sandbox
+  $system [rm lesson/[0-9]* >/dev/null 2>/dev/null]  # some shells can't handle '>&'
+  curr:address:sandbox-data <- get *env, sandbox:offset
+  suffix:address:array:character <- new [.out]
+  idx:number <- copy 0
   {
     break-unless curr
-    data <- get-address *curr, data:offset
-    response:address:address:array:character <- get-address *curr, response:offset
-    warnings:address:address:array:character <- get-address *curr, warnings:offset
-    fake-screen:address:address:screen <- get-address *curr, screen:offset
-    *response, *warnings, *fake-screen <- run-interactive *data
-#?     $print *warnings, [ ], **warnings, 10/newline
-    curr <- get *curr, next-sandbox:offset
+    data:address:array:character <- get *curr, data:offset
+    filename:address:array:character <- integer-to-decimal-string idx
+    save filename, data
+    {
+      expected-response:address:array:character <- get *curr, expected-response:offset
+      break-unless expected-response
+      filename <- string-append filename, suffix
+      save filename, expected-response
+    }
+    idx <- add idx, 1
+    curr <- get *curr, next-sandbox:offset
     loop
   }
 ]
@@ -2918,21 +2923,18 @@ container sandbox-data [
   local-scope
   screen:address <- next-ingredient
   env:address:programming-environment-data <- next-ingredient
-  clear:boolean <- next-ingredient
 #?   trace [app], [render sandbox side] #? 1
-  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
-  left:number <- get *current-sandbox, left:offset
-  right:number <- get *current-sandbox, right:offset
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  left:number <- get *current-sandbox, left:offset
+  right:number <- get *current-sandbox, right:offset
   row:number, screen <- render screen, current-sandbox
   row <- add row, 1
   draw-horizontal screen, row, left, right, 9473/horizontal-double
-  sandbox:address:sandbox-data <- get *env, sandbox:offset
+  sandbox:address:sandbox-data <- get *env, sandbox:offset
   row, screen <- render-sandboxes screen, sandbox, left, right, row
-  # clear next line, in case we just processed a backspace
+  # clear rest of screen
   row <- add row, 1
   move-cursor screen, row, left
-  clear-line-delimited screen, left, right
-  reply-unless clear, screen/same-as-ingredient:0
   screen-height:number <- screen-height screen
   {
     at-bottom-of-screen?:boolean <- greater-or-equal row, screen-height
@@ -2962,17 +2964,20 @@ container sandbox-data [
   clear-line-delimited screen, left, right
   print-character screen, 120/x, 245/grey
   # save menu row so we can detect clicks to it later
-  starting-row:address:number <- get-address *sandbox, starting-row-on-screen:offset
+  starting-row:address:number <- get-address *sandbox, starting-row-on-screen:offset
   *starting-row <- copy row
   # render sandbox contents
-  sandbox-data:address:array:character <- get *sandbox, data:offset
+  sandbox-data:address:array:character <- get *sandbox, data:offset
   row, screen <- render-string screen, sandbox-data, left, right, 7/white, row
   # render sandbox warnings, screen or response, in that order
-  sandbox-response:address:array:character <- get *sandbox, response:offset
-  sandbox-warnings:address:array:character <- get *sandbox, warnings:offset
-  sandbox-screen:address <- get *sandbox, screen:offset
+  response-starting-row:address:number <- get-address *sandbox, response-starting-row-on-screen:offset
+  sandbox-response:address:array:character <- get *sandbox, response:offset
+  sandbox-warnings:address:array:character <- get *sandbox, warnings:offset
+  sandbox-screen:address <- get *sandbox, screen:offset
+  +render-sandbox-results
   {
     break-unless sandbox-warnings
+    *response-starting-row <- copy 0  # no response
     row, screen <- render-string screen, sandbox-warnings, left, right, 1/red, row
   }
   {
@@ -2984,14 +2989,18 @@ container sandbox-data [
   {
     break-if sandbox-warnings
     break-unless empty-screen?
+#?     $print [display response from ], row, 10/newline #? 1
+    *response-starting-row <- add row, 1
+    +render-sandbox-response
     row, screen <- render-string screen, sandbox-response, left, right, 245/grey, row
   }
+  +render-sandbox-end
   at-bottom?:boolean <- greater-or-equal row, screen-height
   reply-if at-bottom?, row/same-as-ingredient:4, screen/same-as-ingredient:0
   # draw solid line after sandbox
   draw-horizontal screen, row, left, right, 9473/horizontal-double
   # draw next sandbox
-  next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
+  next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
   row, screen <- render-sandboxes screen, next-sandbox, left, right, row
   reply row/same-as-ingredient:4, screen/same-as-ingredient:0
 ]
@@ -3001,52 +3010,100 @@ container sandbox-data [
   local-scope
   env:address:programming-environment-data <- next-ingredient
   # read all scenarios, pushing them to end of a list of scenarios
-  filename:number <- copy 0
-  curr:address:address:sandbox-data <- get-address *env, sandbox:offset
+  suffix:address:array:character <- new [.out]
+  idx:number <- copy 0
+  curr:address:address:sandbox-data <- get-address *env, sandbox:offset
   {
+    filename:address:array:character <- integer-to-decimal-string idx
     contents:address:array:character <- restore filename
     break-unless contents  # stop at first error; assuming file didn't exist
     # create new sandbox for file
-    *curr <- new sandbox-data:type
-    data:address:address:array:character <- get-address **curr, data:offset
+    *curr <- new sandbox-data:type
+    data:address:address:array:character <- get-address **curr, data:offset
     *data <- copy contents
+    # restore expected output for sandbox if it exists
+    {
+      filename <- string-append filename, suffix
+      contents <- restore filename
+      break-unless contents
+      expected-response:address:address:array:character <- get-address **curr, expected-response:offset
+      *expected-response <- copy contents
+    }
     # increment loop variables
-    filename <- add filename, 1
-    curr <- get-address **curr, next-sandbox:offset
+    idx <- add idx, 1
+    curr <- get-address **curr, next-sandbox:offset
     loop
   }
   reply env/same-as-ingredient:0
 ]
 
-# was-deleted?:boolean <- delete-sandbox t:touch-event, env:address:programming-environment-data
-recipe delete-sandbox [
+# row:number, screen:address <- render-screen screen:address, sandbox-screen:address, left:number, right:number, row:number
+# print the fake sandbox screen to 'screen' with appropriate delimiters
+# leave cursor at start of next line
+recipe render-screen [
   local-scope
-  t:touch-event <- next-ingredient
-  env:address:programming-environment-data <- next-ingredient
-  click-column:number <- get t, column:offset
-  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
-  right:number <- get *current-sandbox, right:offset
-  at-right?:boolean <- equal click-column, right
-  reply-unless at-right?, 0/false
-  click-row:number <- get t, row:offset
-  prev:address:address:sandbox-data <- get-address *env, sandbox:offset
-  curr:address:sandbox-data <- get *env, sandbox:offset
+  screen:address <- next-ingredient
+  s:address:screen <- next-ingredient
+  left:number <- next-ingredient
+  right:number <- next-ingredient
+  row:number <- next-ingredient
+  row <- add row, 1
+  reply-unless s, row/same-as-ingredient:4, screen/same-as-ingredient:0
+  # print 'screen:'
+  header:address:array:character <- new [screen:]
+  row <- subtract row, 1  # compensate for render-string below
+  row <- render-string screen, header, left, right, 245/grey, row
+  # newline
+  row <- add row, 1
+  move-cursor screen, row, left
+  # start printing s
+  column:number <- copy left
+  s-width:number <- screen-width s
+  s-height:number <- screen-height s
+  buf:address:array:screen-cell <- get *s, data:offset
+  stop-printing:number <- add left, s-width, 3
+  max-column:number <- min stop-printing, right
+  i:number <- copy 0
+  len:number <- length *buf
+  screen-height:number <- screen-height screen
   {
-    break-unless curr
-    # more sandboxes to check
+    done?:boolean <- greater-or-equal i, len
+    break-if done?
+    done? <- greater-or-equal row, screen-height
+    break-if done?
+    column <- copy left
+    move-cursor screen, row, column
+    # initial leader for each row: two spaces and a '.'
+    print-character screen, 32/space, 245/grey
+    print-character screen, 32/space, 245/grey
+    print-character screen, 46/full-stop, 245/grey
+    column <- add left, 3
     {
-      target-row:number <- get *curr, starting-row-on-screen:offset
-      delete-curr?:boolean <- equal target-row, click-row
-      break-unless delete-curr?
-      # delete this sandbox, rerender and stop
-      *prev <- get *curr, next-sandbox:offset
-      reply 1/true
+      # print row
+      row-done?:boolean <- greater-or-equal column, max-column
+      break-if row-done?
+      curr:screen-cell <- index *buf, i
+      c:character <- get curr, contents:offset
+      print-character screen, c, 245/grey
+      column <- add column, 1
+      i <- add i, 1
+      loop
     }
-    prev <- get-address *curr, next-sandbox:offset
-    curr <- get *curr, next-sandbox:offset
+    # print final '.'
+    print-character screen, 46/full-stop, 245/grey
+    column <- add column, 1
+    {
+      # clear rest of current line
+      line-done?:boolean <- greater-than column, right
+      break-if line-done?
+      print-character screen, 32/space
+      column <- add column, 1
+      loop
+    }
+    row <- add row, 1
     loop
   }
-  reply 0/false
+  reply row/same-as-ingredient:4, screen/same-as-ingredient:0
 ]
 
 scenario run-updates-results [
@@ -3188,6 +3245,154 @@ container sandbox-data [
   ]
 ]
 
+recipe editor-contents [
+  local-scope
+  editor:address:editor-data <- next-ingredient
+  buf:address:buffer <- new-buffer 80
+  curr:address:duplex-list <- get *editor, data:offset
+  # skip § sentinel
+  assert curr, [editor without data is illegal; must have at least a sentinel]
+  curr <- next-duplex curr
+  reply-unless curr, 0
+  {
+    break-unless curr
+    c:character <- get *curr, value:offset
+    buffer-append buf, c
+    curr <- next-duplex curr
+    loop
+  }
+  result:address:array:character <- buffer-to-array buf
+  reply result
+]
+
+scenario editor-provides-edited-contents [
+  assume-screen 10/width, 5/height
+  1:address:array:character <- new [abc]
+  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
+  assume-console [
+    left-click 1, 2
+    type [def]
+  ]
+  run [
+    editor-event-loop screen:address, console:address, 2:address:editor-data
+    3:address:array:character <- editor-contents 2:address:editor-data
+    4:array:character <- copy *3:address:array:character
+  ]
+  memory-should-contain [
+    4:string <- [abdefc]
+  ]
+]
+
+## editing sandboxes after they've been created
+
+scenario clicking-on-a-sandbox-moves-it-to-editor [
+  $close-trace
+  assume-screen 40/width, 10/height
+  # basic recipe
+  1:address:array:character <- new [ 
+recipe foo [
+  add 2, 2
+]]
+  # run it
+  2:address:array:character <- new [foo]
+  assume-console [
+    press 65532  # F4
+  ]
+  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
+  event-loop screen:address, console:address, 3:address:programming-environment-data
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  add 2, 2          ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                  .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click somewhere on the sandbox
+  assume-console [
+    left-click 3, 30
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # it pops back into editor
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊foo                .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  add 2, 2          ┊                   .
+    .]                   ┊                   .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                   .
+    .                    ┊                   .
+    .                    ┊                   .
+  ]
+]
+
+after +global-touch [
+  # right side of screen and below sandbox editor? pop appropriate sandbox
+  # contents back into sandbox editor provided it's empty
+  {
+    sandbox-left-margin:number <- get *current-sandbox, left:offset
+    click-column:number <- get *t, column:offset
+    on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
+    break-unless on-sandbox-side?
+    first-sandbox:address:sandbox-data <- get *env, sandbox:offset
+    break-unless first-sandbox
+    first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
+    click-row:number <- get *t, row:offset
+    below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
+    break-unless below-sandbox-editor?
+    empty-sandbox-editor?:boolean <- empty-editor? current-sandbox
+    break-unless empty-sandbox-editor?  # make the user hit F4 before editing a new sandbox
+    # identify the sandbox to edit and remove it from the sandbox list
+    sandbox:address:sandbox-data <- extract-sandbox env, click-row
+    text:address:array:character <- get *sandbox, data:offset
+    current-sandbox <- insert-text current-sandbox, text
+    screen <- render-sandbox-side screen, env
+    update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
+    show-screen screen
+    loop +next-event:label
+  }
+]
+
+recipe empty-editor? [
+  local-scope
+  editor:address:editor-data <- next-ingredient
+  head:address:duplex-list <- get *editor, data:offset
+  first:address:duplex-list <- next-duplex head
+  result:boolean <- not first
+  reply result
+]
+
+recipe extract-sandbox [
+  local-scope
+  env:address:programming-environment-data <- next-ingredient
+  click-row:number <- next-ingredient
+  # assert click-row >= sandbox.starting-row-on-screen
+  sandbox:address:address:sandbox-data <- get-address *env, sandbox:offset
+  start:number <- get **sandbox, starting-row-on-screen:offset
+  clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
+  assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
+  {
+    next-sandbox:address:sandbox-data <- get **sandbox, next-sandbox:offset
+    break-unless next-sandbox
+    # if click-row < sandbox.next-sandbox.starting-row-on-screen, break
+    next-start:number <- get *next-sandbox, starting-row-on-screen:offset
+    found?:boolean <- lesser-than click-row, next-start
+    break-if found?
+    sandbox <- get-address **sandbox, next-sandbox:offset
+    loop
+  }
+  # snip sandbox out of its list
+  result:address:sandbox-data <- copy *sandbox
+  *sandbox <- copy next-sandbox
+  reply result
+]
+
+## deleting sandboxes
+
 scenario deleting-sandboxes [
   $close-trace  # trace too long for github
   assume-screen 100/width, 15/height
@@ -3254,6 +3459,50 @@ container sandbox-data [
   ]
 ]
 
+after +global-touch [
+  # on a sandbox delete icon? process delete
+  {
+    was-delete?:boolean <- delete-sandbox *t, env
+    break-unless was-delete?
+#?     trace [app], [delete clicked] #? 1
+    screen <- render-sandbox-side screen, env
+    update-cursor screen, recipes, current-sandbox, *sandbox-in-focus?
+    show-screen screen
+    loop +next-event:label
+  }
+]
+
+# was-deleted?:boolean <- delete-sandbox t:touch-event, env:address:programming-environment-data
+recipe delete-sandbox [
+  local-scope
+  t:touch-event <- next-ingredient
+  env:address:programming-environment-data <- next-ingredient
+  click-column:number <- get t, column:offset
+  current-sandbox:address:editor-data <- get *env, current-sandbox:offset
+  right:number <- get *current-sandbox, right:offset
+  at-right?:boolean <- equal click-column, right
+  reply-unless at-right?, 0/false
+  click-row:number <- get t, row:offset
+  prev:address:address:sandbox-data <- get-address *env, sandbox:offset
+  curr:address:sandbox-data <- get *env, sandbox:offset
+  {
+    break-unless curr
+    # more sandboxes to check
+    {
+      target-row:number <- get *curr, starting-row-on-screen:offset
+      delete-curr?:boolean <- equal target-row, click-row
+      break-unless delete-curr?
+      # delete this sandbox, rerender and stop
+      *prev <- get *curr, next-sandbox:offset
+      reply 1/true
+    }
+    prev <- get-address *curr, next-sandbox:offset
+    curr <- get *curr, next-sandbox:offset
+    loop
+  }
+  reply 0/false
+]
+
 scenario run-instruction-manages-screen-per-sandbox [
   $close-trace  # trace too long for github #? 1
   assume-screen 100/width, 20/height
@@ -3288,42 +3537,357 @@ container sandbox-data [
   ]
 ]
 
-recipe editor-contents [
+## clicking on sandbox results to 'fix' them and turn sandboxes into tests
+
+scenario sandbox-click-on-result-toggles-color-to-green [
+  $close-trace
+  assume-screen 40/width, 10/height
+  # basic recipe
+  1:address:array:character <- new [ 
+recipe foo [
+  add 2, 2
+]]
+  # run it
+  2:address:array:character <- new [foo]
+  assume-console [
+    press 65532  # F4
+  ]
+  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
+  event-loop screen:address, console:address, 3:address:programming-environment-data
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  add 2, 2          ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                  .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click on the '4' in the result
+  assume-console [
+    left-click 5, 21
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # color toggles to green
+  screen-should-contain-in-color 2/green, [
+    .                                        .
+    .                                        .
+    .                                        .
+    .                                        .
+    .                                        .
+    .                     4                  .
+    .                                        .
+    .                                        .
+  ]
+  # now change the second arg of the 'add'
+  # then rerun
+  assume-console [
+    left-click 3, 11  # cursor to end of line
+    type [«3]  # turn '2' into '3'
+    press 65532  # F4
+  ]
+  4:event/backspace <- merge 0/text, 8/backspace, 0/dummy, 0/dummy
+  replace-in-console 171/«, 4:event/backspace
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # result turns red
+  screen-should-contain-in-color 1/red, [
+    .                                        .
+    .                                        .
+    .                                        .
+    .                                        .
+    .                                        .
+    .                     5                  .
+    .                                        .
+    .                                        .
+  ]
+]
+
+# clicks on sandbox responses save it as 'expected'
+after +global-touch [
+  # right side of screen? check if it's inside the output of any sandbox
+  {
+    sandbox-left-margin:number <- get *current-sandbox, left:offset
+    click-column:number <- get *t, column:offset
+    on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
+    break-unless on-sandbox-side?
+    first-sandbox:address:sandbox-data <- get *env, sandbox:offset
+    break-unless first-sandbox
+    first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
+    click-row:number <- get *t, row:offset
+    below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
+    break-unless below-sandbox-editor?
+    # identify the sandbox whose output is being clicked on
+    sandbox:address:sandbox-data <- find-click-in-sandbox-output env, click-row
+    break-unless sandbox
+    # toggle its expected-response, and save session
+    sandbox <- toggle-expected-response sandbox
+    save-sandboxes env
+    screen <- render-sandbox-side screen, env, 1/clear
+    # no change in cursor
+    show-screen screen
+    loop +next-event:label
+  }
+]
+
+recipe find-click-in-sandbox-output [
   local-scope
-  editor:address:editor-data <- next-ingredient
-  buf:address:buffer <- new-buffer 80
-  curr:address:duplex-list <- get *editor, data:offset
-  # skip § sentinel
-  assert curr, [editor without data is illegal; must have at least a sentinel]
-  curr <- next-duplex curr
-  reply-unless curr, 0
+  env:address:programming-environment-data <- next-ingredient
+  click-row:number <- next-ingredient
+  # assert click-row >= sandbox.starting-row-on-screen
+  sandbox:address:sandbox-data <- get *env, sandbox:offset
+  start:number <- get *sandbox, starting-row-on-screen:offset
+  clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
+  assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
+  # while click-row < sandbox.next-sandbox.starting-row-on-screen
   {
-    break-unless curr
-    c:character <- get *curr, value:offset
-    buffer-append buf, c
-    curr <- next-duplex curr
+    next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
+    break-unless next-sandbox
+    next-start:number <- get *next-sandbox, starting-row-on-screen:offset
+    found?:boolean <- lesser-than click-row, next-start
+    break-if found?
+    sandbox <- copy next-sandbox
     loop
   }
-  result:address:array:character <- buffer-to-array buf
-  reply result
+  # return sandbox if click is in its output region
+  response-starting-row:number <- get *sandbox, response-starting-row-on-screen:offset
+  click-in-response?:boolean <- greater-or-equal click-row, response-starting-row
+  {
+    break-if click-in-response?
+    reply 0/no-click-in-sandbox-output
+  }
+  reply sandbox
 ]
 
-scenario editor-provides-edited-contents [
-  assume-screen 10/width, 5/height
-  1:address:array:character <- new [abc]
-  2:address:editor-data <- new-editor 1:address:array:character, screen:address, 0/left, 10/right
+recipe toggle-expected-response [
+  local-scope
+  sandbox:address:sandbox-data <- next-ingredient
+  expected-response:address:address:array:character <- get-address *sandbox, expected-response:offset
+  {
+    # if expected-response is set, reset
+    break-unless *expected-response
+    *expected-response <- copy 0
+    reply sandbox/same-as-ingredient:0
+  }
+  # if not, current response is the expected response
+  response:address:array:character <- get *sandbox, response:offset
+  *expected-response <- copy response
+  reply sandbox/same-as-ingredient:0
+]
+
+# when rendering a sandbox, color it in red/green if expected response exists
+after +render-sandbox-response [
+  {
+    break-unless sandbox-response
+    expected-response:address:array:character <- get *sandbox, expected-response:offset
+    break-unless expected-response  # fall-through to print in grey
+    response-is-expected?:boolean <- string-equal expected-response, sandbox-response
+    {
+      break-if response-is-expected?:boolean
+      row, screen <- render-string screen, sandbox-response, left, right, 1/red, row
+    }
+    {
+      break-unless response-is-expected?:boolean
+      row, screen <- render-string screen, sandbox-response, left, right, 2/green, row
+    }
+    jump +render-sandbox-end:label
+  }
+]
+
+## click on the code typed into a sandbox to toggle its trace
+
+scenario sandbox-click-on-code-toggles-app-trace [
+  $close-trace
+  assume-screen 40/width, 10/height
+  # basic recipe
+  1:address:array:character <- new [ 
+recipe foo [
+  trace [abc]
+]]
+  # run it
+  2:address:array:character <- new [foo]
   assume-console [
-    left-click 1, 2
-    type [def]
+    press 65532  # F4
+  ]
+  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
+  event-loop screen:address, console:address, 3:address:programming-environment-data
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click on the 'foo' line in the sandbox
+  assume-console [
+    left-click 4, 21
   ]
   run [
-    editor-event-loop screen:address, console:address, 2:address:editor-data
-    3:address:array:character <- editor-contents 2:address:editor-data
-    4:array:character <- copy *3:address:array:character
+    event-loop screen:address, console:address, 3:address:programming-environment-data
   ]
-  memory-should-contain [
-    4:string <- [abdefc]
+  # trace now printed
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc                .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  screen-should-contain-in-color 245/grey, [
+    .                                        .
+    .                    ┊                   .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                  x.
+    .                    ┊                   .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc                .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click again on the same region
+  assume-console [
+    left-click 4, 25
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # trace hidden again
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .]                   ┊foo                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+]
+
+scenario sandbox-shows-app-trace-and-result [
+  $close-trace
+  assume-screen 40/width, 10/height
+  # basic recipe
+  1:address:array:character <- new [ 
+recipe foo [
+  trace [abc]
+  add 2, 2
+]]
+  # run it
+  2:address:array:character <- new [foo]
+  assume-console [
+    press 65532  # F4
   ]
+  3:address:programming-environment-data <- new-programming-environment screen:address, 1:address:array:character, 2:address:array:character
+  event-loop screen:address, console:address, 3:address:programming-environment-data
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .  add 2, 2          ┊foo                .
+    .]                   ┊4                  .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+  # click on the 'foo' line in the sandbox
+  assume-console [
+    left-click 4, 21
+  ]
+  run [
+    event-loop screen:address, console:address, 3:address:programming-environment-data
+  ]
+  # trace now printed
+  screen-should-contain [
+    .                     run (F4)           .
+    .                    ┊                   .
+    .recipe foo [        ┊━━━━━━━━━━━━━━━━━━━.
+    .  trace [abc]       ┊                  x.
+    .  add 2, 2          ┊foo                .
+    .]                   ┊abc                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                  .
+    .                    ┊━━━━━━━━━━━━━━━━━━━.
+    .                    ┊                   .
+  ]
+]
+
+# clicks on sandbox code toggle its display-trace? flag
+after +global-touch [
+  # right side of screen? check if it's inside the code of any sandbox
+  {
+    sandbox-left-margin:number <- get *current-sandbox, left:offset
+    click-column:number <- get *t, column:offset
+    on-sandbox-side?:boolean <- greater-or-equal click-column, sandbox-left-margin
+    break-unless on-sandbox-side?
+    first-sandbox:address:sandbox-data <- get *env, sandbox:offset
+    break-unless first-sandbox
+    first-sandbox-begins:number <- get *first-sandbox, starting-row-on-screen:offset
+    click-row:number <- get *t, row:offset
+    below-sandbox-editor?:boolean <- greater-or-equal click-row, first-sandbox-begins
+    break-unless below-sandbox-editor?
+    # identify the sandbox whose code is being clicked on
+    sandbox:address:sandbox-data <- find-click-in-sandbox-code env, click-row
+    break-unless sandbox
+    # toggle its display-trace? property
+    x:address:boolean <- get-address *sandbox, display-trace?:offset
+    *x <- not *x
+    screen <- render-sandbox-side screen, env, 1/clear
+    # no change in cursor
+    show-screen screen
+    loop +next-event:label
+  }
+]
+
+recipe find-click-in-sandbox-code [
+  local-scope
+  env:address:programming-environment-data <- next-ingredient
+  click-row:number <- next-ingredient
+  # assert click-row >= sandbox.starting-row-on-screen
+  sandbox:address:sandbox-data <- get *env, sandbox:offset
+  start:number <- get *sandbox, starting-row-on-screen:offset
+  clicked-on-sandboxes?:boolean <- greater-or-equal click-row, start
+  assert clicked-on-sandboxes?, [extract-sandbox called on click to sandbox editor]
+  # while click-row < sandbox.next-sandbox.starting-row-on-screen
+  {
+    next-sandbox:address:sandbox-data <- get *sandbox, next-sandbox:offset
+    break-unless next-sandbox
+    next-start:number <- get *next-sandbox, starting-row-on-screen:offset
+    found?:boolean <- lesser-than click-row, next-start
+    break-if found?
+    sandbox <- copy next-sandbox
+    loop
+  }
+  # return sandbox if click is in its code region
+  response-starting-row:number <- get *sandbox, response-starting-row-on-screen:offset
+  click-above-response?:boolean <- lesser-than click-row, response-starting-row
+  start:number <- get *sandbox, starting-row-on-screen:offset
+  click-below-menu?:boolean <- greater-than click-row, start
+  click-on-sandbox-code?:boolean <- and click-above-response?, click-below-menu?
+  {
+    break-if click-on-sandbox-code?
+    reply 0/no-click-in-sandbox-output
+  }
+  reply sandbox
+]
+
+# when rendering a sandbox, dump its trace before response/warning if display-trace? property is set
+after +render-sandbox-results [
+  {
+    display-trace?:boolean <- get *sandbox, display-trace?:offset
+    break-unless display-trace?
+    sandbox-trace:address:array:character <- get *sandbox, trace:offset
+    break-unless sandbox-trace  # nothing to print; move on
+#?     $print [display trace from ], row, 10/newline #? 1
+    row, screen <- render-string, screen, sandbox-trace, left, right, 245/grey, row
+    row <- subtract row, 1  # trim the trailing newline that's always present
+  }
 ]
 
 ## handling malformed programs
@@ -3501,7 +4065,7 @@ container sandbox-data [
   }
   bg-color:number, bg-color-found?:boolean <- next-ingredient
   {
-    break-if bg-color-found?:boolean
+    break-if bg-color-found?
     bg-color <- copy 0/black
   }
   move-cursor screen, row, x
diff --git a/html/factorial.mu.html b/html/factorial.mu.html
index 8cdc3ef6..31ac9c26 100644
--- a/html/factorial.mu.html
+++ b/html/factorial.mu.html
@@ -13,13 +13,13 @@
 pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
 body { font-family: monospace; color: #eeeeee; background-color: #080808; }
 * { font-size: 1.05em; }
+.muControl { color: #c0a020; }
 .muRecipe { color: #ff8700; }
-.Delimiter { color: #a04060; }
 .muScenario { color: #00af00; }
 .Comment { color: #9090ff; }
 .Constant { color: #00a0a0; }
 .Special { color: #ff6060; }
-.muControl { color: #c0a020; }
+.Delimiter { color: #a04060; }
 -->
 
 
diff --git a/html/fork.mu.html b/html/fork.mu.html
index 3f47e9a2..69dc4e30 100644
--- a/html/fork.mu.html
+++ b/html/fork.mu.html
@@ -13,11 +13,11 @@
 pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
 body { font-family: monospace; color: #eeeeee; background-color: #080808; }
 * { font-size: 1.05em; }
+.muControl { color: #c0a020; }
 .muRecipe { color: #ff8700; }
-.Constant { color: #00a0a0; }
 .Comment { color: #9090ff; }
+.Constant { color: #00a0a0; }
 .Delimiter { color: #a04060; }
-.muControl { color: #c0a020; }
 -->
 
 
@@ -32,7 +32,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; }
 # example program: running multiple routines
 
 recipe main [
-  start-running thread2:recipe
+  start-running thread2:recipe
   {
     $print 34
     loop
diff --git a/html/global.mu.html b/html/global.mu.html
index 812e3a35..6e2c0491 100644
--- a/html/global.mu.html
+++ b/html/global.mu.html
@@ -32,7 +32,7 @@ body { font-family: monospace; color: #eeeeee; background-color: #080808; }
 
 recipe main [
   # allocate 5 locations for globals
-  global-space:address:array:location <- new location:type, 5
+  global-space:address:array:location <- new location:type, 5
   # read to globals by using /space:global
   1:number/space:global <- copy 3
   foo
diff --git a/html/tangle.mu.html b/html/tangle.mu.html
index f4e8be60..e6df50f0 100644
--- a/html/tangle.mu.html
+++ b/html/tangle.mu.html
@@ -13,12 +13,12 @@
 pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
 body { font-family: monospace; color: #eeeeee; background-color: #080808; }
 * { font-size: 1.05em; }
+.muControl { color: #c0a020; }
 .muRecipe { color: #ff8700; }
-.Delimiter { color: #a04060; }
 .Comment { color: #9090ff; }
 .Constant { color: #00a0a0; }
 .Special { color: #ff6060; }
-.muControl { color: #c0a020; }
+.Delimiter { color: #a04060; }
 -->
 
 
-- 
cgit 1.4.1-2-gfad0