about summary refs log tree commit diff stats
path: root/archive/2.vm/085scenario_console.cc
diff options
context:
space:
mode:
Diffstat (limited to 'archive/2.vm/085scenario_console.cc')
-rw-r--r--archive/2.vm/085scenario_console.cc317
1 files changed, 317 insertions, 0 deletions
diff --git a/archive/2.vm/085scenario_console.cc b/archive/2.vm/085scenario_console.cc
new file mode 100644
index 00000000..75c2a289
--- /dev/null
+++ b/archive/2.vm/085scenario_console.cc
@@ -0,0 +1,317 @@
+//: Clean syntax to manipulate and check the console in scenarios.
+//: Instruction 'assume-console' implicitly creates a variable called
+//: 'console' that is accessible inside other 'run' instructions in the
+//: scenario. Like with the fake screen, 'assume-console' transparently
+//: supports unicode.
+
+//: first make sure we don't mangle this instruction in other transforms
+:(before "End initialize_transform_rewrite_literal_string_to_text()")
+recipes_taking_literal_strings.insert("assume-console");
+
+:(code)
+void test_keyboard_in_scenario() {
+  run_mu_scenario(
+      "scenario keyboard-in-scenario [\n"
+      "  assume-console [\n"
+      "    type [abc]\n"
+      "  ]\n"
+      "  run [\n"
+      "    1:char, 2:bool <- read-key console\n"
+      "    3:char, 4:bool <- read-key console\n"
+      "    5:char, 6:bool <- read-key console\n"
+      "    7:char, 8:bool, 9:bool <- read-key console\n"
+      "  ]\n"
+      "  memory-should-contain [\n"
+      "    1 <- 97\n"  // 'a'
+      "    2 <- 1\n"
+      "    3 <- 98\n"  // 'b'
+      "    4 <- 1\n"
+      "    5 <- 99\n"  // 'c'
+      "    6 <- 1\n"
+      "    7 <- 0\n"  // unset
+      "    8 <- 1\n"
+      "    9 <- 1\n"  // end of test events
+      "  ]\n"
+      "]\n"
+  );
+}
+
+:(before "End Scenario Globals")
+extern const int CONSOLE = next_predefined_global_for_scenarios(/*size_of(address:console)*/2);
+//: give 'console' a fixed location in scenarios
+:(before "End Special Scenario Variable Names(r)")
+Name[r]["console"] = CONSOLE;
+//: make 'console' always a raw location in scenarios
+:(before "End is_special_name Special-cases")
+if (s == "console") return true;
+
+:(before "End Primitive Recipe Declarations")
+ASSUME_CONSOLE,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "assume-console", ASSUME_CONSOLE);
+:(before "End Primitive Recipe Checks")
+case ASSUME_CONSOLE: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case ASSUME_CONSOLE: {
+  // create a temporary recipe just for parsing; it won't contain valid instructions
+  istringstream in("[" + current_instruction().ingredients.at(0).name + "]");
+  recipe r;
+  slurp_body(in, r);
+  int num_events = count_events(r);
+  // initialize the events like in new-fake-console
+  int size = /*length*/1 + num_events*size_of_event();
+  int event_data_address = allocate(size);
+  // store length
+  put(Memory, event_data_address+/*skip alloc id*/1, num_events);
+  int curr_address = event_data_address + /*skip alloc id*/1 + /*skip length*/1;
+  for (int i = 0;  i < SIZE(r.steps);  ++i) {
+    const instruction& inst = r.steps.at(i);
+    if (inst.name == "left-click") {
+      trace(Callstack_depth+1, "mem") << "storing 'left-click' event starting at " << Current_routine->alloc << end();
+      put(Memory, curr_address, /*tag for 'touch-event' variant of 'event' exclusive-container*/2);
+      put(Memory, curr_address+/*skip tag*/1+/*offset of 'type' in 'mouse-event'*/0, TB_KEY_MOUSE_LEFT);
+      put(Memory, curr_address+/*skip tag*/1+/*offset of 'row' in 'mouse-event'*/1, to_integer(inst.ingredients.at(0).name));
+      put(Memory, curr_address+/*skip tag*/1+/*offset of 'column' in 'mouse-event'*/2, to_integer(inst.ingredients.at(1).name));
+      curr_address += size_of_event();
+    }
+    else if (inst.name == "press") {
+      trace(Callstack_depth+1, "mem") << "storing 'press' event starting at " << curr_address << end();
+      string key = inst.ingredients.at(0).name;
+      if (is_integer(key))
+        put(Memory, curr_address+1, to_integer(key));
+      else if (contains_key(Key, key))
+        put(Memory, curr_address+1, Key[key]);
+      else
+        raise << "assume-console: can't press '" << key << "'\n" << end();
+      if (get_or_insert(Memory, curr_address+1) < 256)
+        // these keys are in ascii
+        put(Memory, curr_address, /*tag for 'text' variant of 'event' exclusive-container*/0);
+      else {
+        // distinguish from unicode
+        put(Memory, curr_address, /*tag for 'keycode' variant of 'event' exclusive-container*/1);
+      }
+      curr_address += size_of_event();
+    }
+    // End Event Handlers
+    else {
+      // keyboard input
+      assert(inst.name == "type");
+      trace(Callstack_depth+1, "mem") << "storing 'type' event starting at " << curr_address << end();
+      const string& contents = inst.ingredients.at(0).name;
+      const char* raw_contents = contents.c_str();
+      int num_keyboard_events = unicode_length(contents);
+      int curr = 0;
+      for (int i = 0;  i < num_keyboard_events;  ++i) {
+        trace(Callstack_depth+1, "mem") << "storing 'text' tag at " << curr_address << end();
+        put(Memory, curr_address, /*tag for 'text' variant of 'event' exclusive-container*/0);
+        uint32_t curr_character;
+        assert(curr < SIZE(contents));
+        tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
+        trace(Callstack_depth+1, "mem") << "storing character " << curr_character << " at " << curr_address+/*skip exclusive container tag*/1 << end();
+        put(Memory, curr_address+/*skip exclusive container tag*/1, curr_character);
+        curr += tb_utf8_char_length(raw_contents[curr]);
+        curr_address += size_of_event();
+      }
+    }
+  }
+  assert(curr_address == event_data_address+/*skip alloc id*/1+size);
+  // wrap the array of events in a console object
+  int console_address = allocate(size_of_console());
+  trace(Callstack_depth+1, "mem") << "storing console in " << console_address << end();
+  put(Memory, CONSOLE+/*skip alloc id*/1, console_address);
+  trace(Callstack_depth+1, "mem") << "storing console data in " << console_address+/*offset of 'data' in container 'events'*/1 << end();
+  put(Memory, console_address+/*skip alloc id*/1+/*offset of 'data' in container 'events'*/1+/*skip alloc id of 'data'*/1, event_data_address);
+  break;
+}
+
+:(before "End Globals")
+map<string, int> Key;
+:(before "End One-time Setup")
+initialize_key_names();
+:(code)
+void initialize_key_names() {
+  Key["F1"] = TB_KEY_F1;
+  Key["F2"] = TB_KEY_F2;
+  Key["F3"] = TB_KEY_F3;
+  Key["F4"] = TB_KEY_F4;
+  Key["F5"] = TB_KEY_F5;
+  Key["F6"] = TB_KEY_F6;
+  Key["F7"] = TB_KEY_F7;
+  Key["F8"] = TB_KEY_F8;
+  Key["F9"] = TB_KEY_F9;
+  Key["F10"] = TB_KEY_F10;
+  Key["F11"] = TB_KEY_F11;
+  Key["F12"] = TB_KEY_F12;
+  Key["insert"] = TB_KEY_INSERT;
+  Key["delete"] = TB_KEY_DELETE;
+  Key["home"] = TB_KEY_HOME;
+  Key["end"] = TB_KEY_END;
+  Key["page-up"] = TB_KEY_PGUP;
+  Key["page-down"] = TB_KEY_PGDN;
+  Key["up-arrow"] = TB_KEY_ARROW_UP;
+  Key["down-arrow"] = TB_KEY_ARROW_DOWN;
+  Key["left-arrow"] = TB_KEY_ARROW_LEFT;
+  Key["right-arrow"] = TB_KEY_ARROW_RIGHT;
+  Key["ctrl-a"] = TB_KEY_CTRL_A;
+  Key["ctrl-b"] = TB_KEY_CTRL_B;
+  Key["ctrl-c"] = TB_KEY_CTRL_C;
+  Key["ctrl-d"] = TB_KEY_CTRL_D;
+  Key["ctrl-e"] = TB_KEY_CTRL_E;
+  Key["ctrl-f"] = TB_KEY_CTRL_F;
+  Key["ctrl-g"] = TB_KEY_CTRL_G;
+  Key["backspace"] = TB_KEY_BACKSPACE;
+  Key["ctrl-h"] = TB_KEY_CTRL_H;
+  Key["tab"] = TB_KEY_TAB;
+  Key["ctrl-i"] = TB_KEY_CTRL_I;
+  Key["ctrl-j"] = TB_KEY_CTRL_J;
+  Key["enter"] = TB_KEY_NEWLINE;  // ignore CR/LF distinction; there is only 'enter'
+  Key["ctrl-k"] = TB_KEY_CTRL_K;
+  Key["ctrl-l"] = TB_KEY_CTRL_L;
+  Key["ctrl-m"] = TB_KEY_CTRL_M;
+  Key["ctrl-n"] = TB_KEY_CTRL_N;
+  Key["ctrl-o"] = TB_KEY_CTRL_O;
+  Key["ctrl-p"] = TB_KEY_CTRL_P;
+  Key["ctrl-q"] = TB_KEY_CTRL_Q;
+  Key["ctrl-r"] = TB_KEY_CTRL_R;
+  Key["ctrl-s"] = TB_KEY_CTRL_S;
+  Key["ctrl-t"] = TB_KEY_CTRL_T;
+  Key["ctrl-u"] = TB_KEY_CTRL_U;
+  Key["ctrl-v"] = TB_KEY_CTRL_V;
+  Key["ctrl-w"] = TB_KEY_CTRL_W;
+  Key["ctrl-x"] = TB_KEY_CTRL_X;
+  Key["ctrl-y"] = TB_KEY_CTRL_Y;
+  Key["ctrl-z"] = TB_KEY_CTRL_Z;
+  Key["escape"] = TB_KEY_ESC;
+  Key["ctrl-slash"] = TB_KEY_CTRL_SLASH;
+}
+
+:(after "Begin check_or_set_invalid_types(r)")
+if (is_scenario(caller))
+  initialize_special_name(r);
+:(code)
+bool is_scenario(const recipe& caller) {
+  return starts_with(caller.name, "scenario_");
+}
+void initialize_special_name(reagent& r) {
+  if (r.type) return;
+  // no need for screen
+  if (r.name == "console") r.type = new_type_tree("address:console");
+  // End Initialize Type Of Special Name In Scenario(r)
+}
+
+void test_events_in_scenario() {
+  run_mu_scenario(
+      "scenario events-in-scenario [\n"
+      "  assume-console [\n"
+      "    type [abc]\n"
+      "    left-click 0, 1\n"
+      "    press up-arrow\n"
+      "    type [d]\n"
+      "  ]\n"
+      "  run [\n"
+           // 3 keyboard events; each event occupies 4 locations
+      "    1:event <- read-event console\n"
+      "    5:event <- read-event console\n"
+      "    9:event <- read-event console\n"
+           // mouse click
+      "    13:event <- read-event console\n"
+           // non-character keycode
+      "    17:event <- read-event console\n"
+           // final keyboard event
+      "    21:event <- read-event console\n"
+      "  ]\n"
+      "  memory-should-contain [\n"
+      "    1 <- 0\n"  // 'text'
+      "    2 <- 97\n"  // 'a'
+      "    3 <- 0\n"  // unused
+      "    4 <- 0\n"  // unused
+      "    5 <- 0\n"  // 'text'
+      "    6 <- 98\n"  // 'b'
+      "    7 <- 0\n"  // unused
+      "    8 <- 0\n"  // unused
+      "    9 <- 0\n"  // 'text'
+      "    10 <- 99\n"  // 'c'
+      "    11 <- 0\n"  // unused
+      "    12 <- 0\n"  // unused
+      "    13 <- 2\n"  // 'mouse'
+      "    14 <- 65513\n"  // mouse click
+      "    15 <- 0\n"  // row
+      "    16 <- 1\n"  // column
+      "    17 <- 1\n"  // 'keycode'
+      "    18 <- 65517\n"  // up arrow
+      "    19 <- 0\n"  // unused
+      "    20 <- 0\n"  // unused
+      "    21 <- 0\n"  // 'text'
+      "    22 <- 100\n"  // 'd'
+      "    23 <- 0\n"  // unused
+      "    24 <- 0\n"  // unused
+      "    25 <- 0\n"
+      "  ]\n"
+      "]\n"
+  );
+}
+
+//: Deal with special keys and unmatched brackets by allowing each test to
+//: independently choose the unicode symbol to denote them.
+:(before "End Primitive Recipe Declarations")
+REPLACE_IN_CONSOLE,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "replace-in-console", REPLACE_IN_CONSOLE);
+:(before "End Primitive Recipe Checks")
+case REPLACE_IN_CONSOLE: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case REPLACE_IN_CONSOLE: {
+  assert(scalar(ingredients.at(0)));
+  if (!get_or_insert(Memory, CONSOLE)) {
+    raise << "console not initialized\n" << end();
+    break;
+  }
+  int console_address = get_or_insert(Memory, CONSOLE);
+  int console_data = get_or_insert(Memory, console_address+1);
+  int length = get_or_insert(Memory, console_data);  // array length
+  for (int i = 0, curr = console_data+1;  i < length;  ++i, curr+=size_of_event()) {
+    if (get_or_insert(Memory, curr) != /*text*/0) continue;
+    if (get_or_insert(Memory, curr+1) != ingredients.at(0).at(0)) continue;
+    for (int n = 0;  n < size_of_event();  ++n)
+      put(Memory, curr+n, ingredients.at(1).at(n));
+  }
+  break;
+}
+
+:(code)
+int count_events(const recipe& r) {
+  int result = 0;
+  for (int i = 0;  i < SIZE(r.steps);  ++i) {
+    const instruction& curr = r.steps.at(i);
+    if (curr.name == "type")
+      result += unicode_length(curr.ingredients.at(0).name);
+    else
+      ++result;
+  }
+  return result;
+}
+
+int size_of_event() {
+  // memoize result if already computed
+  static int result = 0;
+  if (result) return result;
+  type_tree* type = new type_tree("event");
+  result = size_of(type);
+  delete type;
+  return result;
+}
+
+int size_of_console() {
+  // memoize result if already computed
+  static int result = 0;
+  if (result) return result;
+  assert(get(Type_ordinal, "console"));
+  type_tree* type = new type_tree("console");
+  result = size_of(type);
+  delete type;
+  return result;
+}