2015-07-07 19:32:31 +00:00
|
|
|
//: Helper for various programming environments: run arbitrary mu code and
|
|
|
|
//: return some result in string form.
|
|
|
|
|
|
|
|
:(scenario run_interactive_code)
|
|
|
|
recipe main [
|
2015-07-08 21:47:12 +00:00
|
|
|
2:address:array:character <- new [1:number <- copy 34:literal]
|
|
|
|
run-interactive 2:address:array:character
|
2015-07-07 19:32:31 +00:00
|
|
|
]
|
|
|
|
+mem: storing 34 in location 1
|
|
|
|
|
|
|
|
:(scenario run_interactive_empty)
|
|
|
|
recipe main [
|
|
|
|
1:address:array:character <- run-interactive 0:literal
|
|
|
|
]
|
|
|
|
# result is null
|
|
|
|
+mem: storing 0 in location 1
|
2015-06-01 06:44:52 +00:00
|
|
|
|
2015-07-17 19:51:32 +00:00
|
|
|
//: run code in 'interactive mode', i.e. with warnings off and return:
|
|
|
|
//: stringified output in case we want to print it to screen
|
|
|
|
//: any warnings encountered
|
|
|
|
//: simulated screen any prints went to
|
2015-06-01 06:44:52 +00:00
|
|
|
:(before "End Primitive Recipe Declarations")
|
|
|
|
RUN_INTERACTIVE,
|
|
|
|
:(before "End Primitive Recipe Numbers")
|
2015-07-04 16:40:50 +00:00
|
|
|
Recipe_ordinal["run-interactive"] = RUN_INTERACTIVE;
|
2015-06-01 06:44:52 +00:00
|
|
|
//? cerr << "run-interactive: " << RUN_INTERACTIVE << '\n'; //? 1
|
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case RUN_INTERACTIVE: {
|
|
|
|
assert(scalar(ingredients.at(0)));
|
2015-07-17 19:51:32 +00:00
|
|
|
products.resize(3);
|
2015-07-08 19:54:06 +00:00
|
|
|
bool new_code_pushed_to_stack = run_interactive(ingredients.at(0).at(0));
|
2015-07-07 19:32:31 +00:00
|
|
|
if (!new_code_pushed_to_stack) {
|
2015-07-08 19:54:06 +00:00
|
|
|
products.at(0).push_back(0);
|
2015-07-08 23:16:11 +00:00
|
|
|
products.at(1).push_back(warnings_from_trace());
|
2015-07-17 19:51:32 +00:00
|
|
|
products.at(2).push_back(0);
|
2015-07-16 01:05:28 +00:00
|
|
|
clean_up_interactive();
|
2015-07-08 19:54:06 +00:00
|
|
|
break; // done with this instruction
|
2015-07-07 19:32:31 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
continue; // not done with caller; don't increment current_step_index()
|
|
|
|
}
|
2015-06-01 06:44:52 +00:00
|
|
|
}
|
|
|
|
|
2015-07-08 21:47:12 +00:00
|
|
|
:(before "End Globals")
|
|
|
|
bool Running_interactive = false;
|
2015-07-17 19:51:32 +00:00
|
|
|
long long int Old_screen = 0; // we can support one iteration of screen inside screen
|
2015-07-08 21:47:12 +00:00
|
|
|
:(before "End Setup")
|
|
|
|
Running_interactive = false;
|
2015-07-17 19:51:32 +00:00
|
|
|
Old_screen = 0;
|
2015-06-01 06:44:52 +00:00
|
|
|
:(code)
|
2015-07-16 05:08:21 +00:00
|
|
|
// reads a string, tries to call it as code (treating it as a test), saving
|
|
|
|
// all warnings.
|
2015-07-08 19:54:06 +00:00
|
|
|
// returns true if successfully called (no errors found during load and transform)
|
|
|
|
bool run_interactive(long long int address) {
|
2015-07-04 16:40:50 +00:00
|
|
|
if (Recipe_ordinal.find("interactive") == Recipe_ordinal.end())
|
|
|
|
Recipe_ordinal["interactive"] = Next_recipe_ordinal++;
|
2015-07-17 19:51:32 +00:00
|
|
|
Old_screen = Memory[SCREEN];
|
2015-07-17 21:57:44 +00:00
|
|
|
//? cerr << "save screen: " << Old_screen << '\n'; //? 2
|
2015-07-16 05:08:21 +00:00
|
|
|
// try to sandbox the run as best you can
|
|
|
|
// todo: test this
|
|
|
|
if (!Current_scenario) {
|
|
|
|
// not already sandboxed
|
|
|
|
for (long long int i = 1; i < Reserved_for_tests; ++i)
|
|
|
|
Memory.erase(i);
|
|
|
|
Name[Recipe_ordinal["interactive"]].clear();
|
|
|
|
}
|
2015-07-17 19:51:32 +00:00
|
|
|
//? cerr << "screen was at " << Name[Recipe_ordinal["interactive"]]["screen"] << '\n'; //? 1
|
|
|
|
Name[Recipe_ordinal["interactive"]]["screen"] = SCREEN;
|
|
|
|
//? cerr << "screen now at " << Name[Recipe_ordinal["interactive"]]["screen"] << '\n'; //? 1
|
2015-07-19 16:57:40 +00:00
|
|
|
string command = trim(strip_comments(read_mu_string(address)));
|
2015-07-07 19:32:31 +00:00
|
|
|
if (command.empty()) return false;
|
2015-07-04 16:40:50 +00:00
|
|
|
Recipe.erase(Recipe_ordinal["interactive"]);
|
2015-07-08 18:43:59 +00:00
|
|
|
Hide_warnings = true;
|
2015-07-11 07:55:01 +00:00
|
|
|
if (!Trace_stream) {
|
|
|
|
Trace_file = ""; // if there wasn't already a stream we don't want to save it
|
|
|
|
Trace_stream = new trace_stream;
|
2015-07-14 22:51:15 +00:00
|
|
|
Trace_stream->collect_layer = "warn";
|
2015-07-11 07:55:01 +00:00
|
|
|
}
|
2015-06-01 06:44:52 +00:00
|
|
|
// call run(string) but without the scheduling
|
2015-07-17 19:51:32 +00:00
|
|
|
// we won't create a local scope so that we can get to the new screen after
|
|
|
|
// we return from 'interactive'.
|
|
|
|
load(string("recipe interactive [\n") +
|
|
|
|
"screen:address <- new-fake-screen 5, 5\n" +
|
|
|
|
command + "\n" +
|
|
|
|
"]\n");
|
2015-06-01 06:44:52 +00:00
|
|
|
transform_all();
|
2015-07-16 01:05:28 +00:00
|
|
|
if (trace_count("warn") > 0) return false;
|
2015-07-08 21:47:12 +00:00
|
|
|
Running_interactive = true;
|
2015-07-04 16:40:50 +00:00
|
|
|
Current_routine->calls.push_front(call(Recipe_ordinal["interactive"]));
|
2015-07-07 19:32:31 +00:00
|
|
|
return true;
|
2015-06-01 06:44:52 +00:00
|
|
|
}
|
2015-06-02 07:48:32 +00:00
|
|
|
|
2015-07-08 21:47:12 +00:00
|
|
|
:(scenario "run_interactive_returns_stringified_result")
|
|
|
|
recipe main [
|
|
|
|
# try to interactively add 2 and 2
|
|
|
|
1:address:array:character <- new [add 2:literal, 2:literal]
|
|
|
|
2:address:array:character <- run-interactive 1:address:array:character
|
|
|
|
10:array:character <- copy 2:address:array:character/deref
|
|
|
|
]
|
|
|
|
# first letter in the output should be '4' in unicode
|
|
|
|
+mem: storing 52 in location 11
|
|
|
|
|
2015-07-08 22:25:11 +00:00
|
|
|
:(scenario "run_interactive_returns_string")
|
|
|
|
recipe main [
|
|
|
|
# try to interactively add 2 and 2
|
|
|
|
1:address:array:character <- new [
|
|
|
|
100:address:array:character <- new [a]
|
|
|
|
101:address:array:character <- new [b]
|
|
|
|
102:address:array:character <- string-append 100:address:array:character, 101:address:array:character
|
|
|
|
]
|
|
|
|
2:address:array:character <- run-interactive 1:address:array:character
|
|
|
|
10:array:character <- copy 2:address:array:character/deref
|
|
|
|
]
|
|
|
|
# output contains "ab"
|
|
|
|
+mem: storing 97 in location 11
|
|
|
|
+mem: storing 98 in location 12
|
|
|
|
|
2015-07-08 23:16:11 +00:00
|
|
|
:(scenario "run_interactive_returns_warnings")
|
|
|
|
recipe main [
|
|
|
|
# run a command that generates a warning
|
|
|
|
1:address:array:character <- new [get 1234:number, foo:offset]
|
|
|
|
2:address:array:character, 3:address:array:character <- run-interactive 1:address:array:character
|
|
|
|
10:array:character <- copy 3:address:array:character/deref
|
|
|
|
]
|
|
|
|
# warning should be "unknown element foo in container number"
|
|
|
|
+mem: storing 117 in location 11
|
|
|
|
+mem: storing 110 in location 12
|
|
|
|
+mem: storing 107 in location 13
|
|
|
|
+mem: storing 110 in location 14
|
|
|
|
|
2015-07-08 21:47:12 +00:00
|
|
|
:(before "End Globals")
|
|
|
|
string Most_recent_results;
|
2015-07-08 21:53:37 +00:00
|
|
|
:(before "End Setup")
|
|
|
|
Most_recent_results = "";
|
2015-07-08 21:47:12 +00:00
|
|
|
:(before "End of Instruction")
|
2015-07-08 22:25:11 +00:00
|
|
|
if (Running_interactive) {
|
2015-07-08 21:53:37 +00:00
|
|
|
record_products(current_instruction(), products);
|
|
|
|
}
|
2015-07-08 21:47:12 +00:00
|
|
|
:(code)
|
|
|
|
void record_products(const instruction& instruction, const vector<vector<double> >& products) {
|
|
|
|
ostringstream out;
|
|
|
|
for (long long int i = 0; i < SIZE(products); ++i) {
|
2015-07-08 22:25:11 +00:00
|
|
|
// string
|
|
|
|
if (i < SIZE(instruction.products)) {
|
|
|
|
if (is_string(instruction.products.at(i))) {
|
|
|
|
assert(scalar(products.at(i)));
|
2015-07-19 16:57:40 +00:00
|
|
|
out << read_mu_string(products.at(i).at(0)) << '\n';
|
2015-07-08 22:25:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// End Record Product Special-cases
|
|
|
|
}
|
2015-07-16 01:05:28 +00:00
|
|
|
for (long long int j = 0; j < SIZE(products.at(i)); ++j)
|
|
|
|
out << products.at(i).at(j) << ' ';
|
2015-07-08 21:47:12 +00:00
|
|
|
out << '\n';
|
|
|
|
}
|
|
|
|
Most_recent_results = out.str();
|
|
|
|
}
|
|
|
|
:(before "Complete Call Fallthrough")
|
|
|
|
if (current_instruction().operation == RUN_INTERACTIVE && !current_instruction().products.empty()) {
|
2015-07-17 19:51:32 +00:00
|
|
|
assert(SIZE(current_instruction().products) <= 3);
|
2015-07-08 21:47:12 +00:00
|
|
|
// 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;
|
2015-07-19 16:57:40 +00:00
|
|
|
result.push_back(new_mu_string(Most_recent_results));
|
2015-07-08 21:47:12 +00:00
|
|
|
write_memory(current_instruction().products.at(0), result);
|
2015-07-17 19:51:32 +00:00
|
|
|
if (SIZE(current_instruction().products) >= 2) {
|
2015-07-08 23:16:11 +00:00
|
|
|
vector<double> warnings;
|
|
|
|
warnings.push_back(warnings_from_trace());
|
|
|
|
write_memory(current_instruction().products.at(1), warnings);
|
|
|
|
}
|
2015-07-17 19:51:32 +00:00
|
|
|
if (SIZE(current_instruction().products) >= 3) {
|
|
|
|
vector<double> screen;
|
2015-07-17 21:57:44 +00:00
|
|
|
//? cerr << "returning screen " << Memory[SCREEN] << " to " << current_instruction().products.at(2).to_string() << " value " << current_instruction().products.at(2).value << '\n'; //? 1
|
2015-07-17 19:51:32 +00:00
|
|
|
screen.push_back(Memory[SCREEN]);
|
|
|
|
write_memory(current_instruction().products.at(2), screen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//: clean up reply after we've popped it off the call-stack
|
|
|
|
//: however, we need what was on the stack to decide whether to clean up
|
|
|
|
:(after "Starting Reply")
|
|
|
|
bool must_clean_up_interactive = (current_recipe_name() == "interactive");
|
|
|
|
:(after "Falling Through End Of Recipe")
|
|
|
|
bool must_clean_up_interactive = (current_recipe_name() == "interactive");
|
|
|
|
:(before "End Reply")
|
|
|
|
if (must_clean_up_interactive) clean_up_interactive();
|
|
|
|
:(before "Complete Call Fallthrough")
|
|
|
|
if (must_clean_up_interactive) clean_up_interactive();
|
|
|
|
:(code)
|
|
|
|
void clean_up_interactive() {
|
|
|
|
Hide_warnings = false;
|
|
|
|
Running_interactive = false;
|
|
|
|
// hack: assume collect_layer isn't set anywhere else
|
|
|
|
if (Trace_stream->collect_layer == "warn") {
|
|
|
|
delete Trace_stream;
|
|
|
|
Trace_stream = NULL;
|
|
|
|
}
|
2015-07-17 21:57:44 +00:00
|
|
|
//? cerr << "restore screen: " << Memory[SCREEN] << " to " << Old_screen << '\n'; //? 1
|
2015-07-17 19:51:32 +00:00
|
|
|
Memory[SCREEN] = Old_screen;
|
|
|
|
Old_screen = 0;
|
2015-07-08 20:26:02 +00:00
|
|
|
}
|
|
|
|
|
2015-07-08 21:47:12 +00:00
|
|
|
:(code)
|
2015-06-06 18:10:46 +00:00
|
|
|
string strip_comments(string in) {
|
|
|
|
ostringstream result;
|
|
|
|
for (long long int i = 0; i < SIZE(in); ++i) {
|
|
|
|
if (in.at(i) != '#') {
|
|
|
|
result << in.at(i);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
while (i < SIZE(in) && in.at(i) != '\n')
|
|
|
|
++i;
|
|
|
|
if (i < SIZE(in) && in.at(i) == '\n') ++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.str();
|
|
|
|
}
|
|
|
|
|
2015-07-19 16:57:40 +00:00
|
|
|
string read_mu_string(long long int address) {
|
2015-07-08 22:25:11 +00:00
|
|
|
long long int size = Memory[address];
|
|
|
|
if (size == 0) return "";
|
|
|
|
ostringstream tmp;
|
|
|
|
for (long long int curr = address+1; curr <= address+size; ++curr) {
|
|
|
|
// todo: unicode
|
|
|
|
tmp << (char)(int)Memory[curr];
|
|
|
|
}
|
|
|
|
return tmp.str();
|
|
|
|
}
|
|
|
|
|
2015-07-07 19:32:31 +00:00
|
|
|
long long int stringified_value_of_location(long long int address) {
|
2015-06-06 17:38:51 +00:00
|
|
|
// convert to string
|
|
|
|
ostringstream out;
|
2015-07-07 19:32:31 +00:00
|
|
|
out << Memory[address];
|
2015-07-19 16:57:40 +00:00
|
|
|
return new_mu_string(out.str());
|
2015-06-06 17:38:51 +00:00
|
|
|
}
|
|
|
|
|
2015-07-08 22:25:11 +00:00
|
|
|
bool is_string(const reagent& x) {
|
|
|
|
return x.types.size() == 3
|
|
|
|
&& x.types.at(0) == Type_ordinal["address"]
|
|
|
|
&& x.types.at(1) == Type_ordinal["array"]
|
|
|
|
&& x.types.at(2) == Type_ordinal["character"];
|
|
|
|
}
|
|
|
|
|
2015-07-08 23:16:11 +00:00
|
|
|
long long int warnings_from_trace() {
|
|
|
|
if (!Trace_stream) return 0;
|
|
|
|
if (trace_count("warn") <= 0) return 0;
|
|
|
|
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;
|
|
|
|
out << p->contents;
|
|
|
|
if (*--p->contents.end() != '\n') out << '\n';
|
|
|
|
}
|
|
|
|
assert(!out.str().empty());
|
2015-07-19 16:57:40 +00:00
|
|
|
return new_mu_string(out.str());
|
2015-07-08 23:16:11 +00:00
|
|
|
}
|
2015-07-09 06:10:02 +00:00
|
|
|
|
|
|
|
//: simpler version of run-interactive: doesn't do any running, just loads
|
|
|
|
//: recipes and reports warnings.
|
2015-07-09 07:47:36 +00:00
|
|
|
:(before "End Globals")
|
|
|
|
bool Loading_interactive = false;
|
|
|
|
:(before "End Setup")
|
|
|
|
Loading_interactive = false;
|
2015-07-09 06:10:02 +00:00
|
|
|
:(before "End Primitive Recipe Declarations")
|
|
|
|
RELOAD,
|
|
|
|
:(before "End Primitive Recipe Numbers")
|
|
|
|
Recipe_ordinal["reload"] = RELOAD;
|
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case RELOAD: {
|
|
|
|
assert(scalar(ingredients.at(0)));
|
2015-07-09 07:47:36 +00:00
|
|
|
Loading_interactive = true;
|
2015-07-09 06:10:02 +00:00
|
|
|
Hide_warnings = true;
|
2015-07-19 16:57:40 +00:00
|
|
|
load(read_mu_string(ingredients.at(0).at(0)));
|
2015-07-09 06:10:02 +00:00
|
|
|
transform_all();
|
|
|
|
Hide_warnings = false;
|
2015-07-09 07:47:36 +00:00
|
|
|
Loading_interactive = false;
|
2015-07-09 06:10:02 +00:00
|
|
|
products.resize(1);
|
|
|
|
products.at(0).push_back(warnings_from_trace());
|
|
|
|
break;
|
|
|
|
}
|