about summary refs log tree commit diff stats
path: root/088file.mu
Commit message (Expand)AuthorAgeFilesLines
* 4262 - literal 'null'Kartik Agaram2018-06-171-5/+5
* 4261 - start using literals for 'true' and 'false'Kartik Agaram2018-06-171-7/+7
* 4134 - 'input' = 'ingredient'Kartik K. Agaram2017-12-031-9/+9
* 3828 - make buffers shape-shifting (generic)Kartik K. Agaram2017-04-181-2/+2
* 3705 - switch to tested file-system primitivesKartik K. Agaram2016-12-111-8/+57
* 3704Kartik K. Agaram2016-12-111-4/+8
* 3693Kartik K. Agaram2016-11-251-1/+5
* 3656Kartik K. Agaram2016-11-101-2/+2
* 3608 - concurrent writes to fake file systemKartik K. Agaram2016-10-291-1/+5
* 3574Kartik K. Agaram2016-10-231-16/+16
* 3573 - client socket testsKartik K. Agaram2016-10-231-2/+8
* 3507Kartik K. Agaram2016-10-161-4/+4
* 3504Kartik K. Agaram2016-10-151-21/+21
* 3390Kartik K. Agaram2016-09-171-4/+4
* 3389Kartik K. Agaram2016-09-171-14/+14
* 3388Kartik K. Agaram2016-09-171-9/+9
* 3385Kartik K. Agaram2016-09-171-13/+13
* 3379Kartik K. Agaram2016-09-171-13/+13
* 3341Kartik K. Agaram2016-09-121-4/+4
* 3338Kartik K. Agaram2016-09-121-2/+2
* 3337 - first use of type abbreviations: textKartik K. Agaram2016-09-121-4/+4
* 3256Kartik K. Agaram2016-08-261-0/+12
* 3254Kartik K. Agaram2016-08-261-0/+1
* 3253 - writing to fake files in scenariosKartik K. Agaram2016-08-251-3/+45
* 3243Kartik K. Agaram2016-08-221-2/+2
* 3238Kartik K. Agaram2016-08-211-2/+2
* 3237Kartik K. Agaram2016-08-211-2/+2
* 3231 - reading from fake files in scenariosKartik K. Agaram2016-08-201-4/+43
* 3229 - fake file systems using 'assume-filesystem'Kartik K. Agaram2016-08-201-1/+6
* 3225 - testable interface for writing filesKartik K. Agaram2016-08-181-3/+24
* 3224Kartik K. Agaram2016-08-181-5/+0
* 3203 - testable interface for reading a fileKartik K. Agaram2016-08-161-0/+31
ght .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
//: 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;
}