2015-06-22 06:23:57 +00:00
|
|
|
//: 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.
|
2015-06-15 20:58:08 +00:00
|
|
|
|
|
|
|
:(scenarios run_mu_scenario)
|
2015-06-22 06:23:57 +00:00
|
|
|
:(scenario keyboard_in_scenario)
|
|
|
|
scenario keyboard-in-scenario [
|
|
|
|
assume-console [
|
2015-06-15 20:58:08 +00:00
|
|
|
type [abc]
|
|
|
|
]
|
|
|
|
run [
|
2015-10-28 20:08:26 +00:00
|
|
|
1:character, console:address:console, 2:boolean <- read-key console:address:console
|
|
|
|
3:character, console:address:console, 4:boolean <- read-key console:address:console
|
|
|
|
5:character, console:address:console, 6:boolean <- read-key console:address:console
|
|
|
|
7:character, console:address:console, 8:boolean, 9:boolean <- read-key console:address:console
|
2015-06-15 20:58:08 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
2015-06-22 06:23:57 +00:00
|
|
|
1 <- 97 # 'a'
|
|
|
|
2 <- 1
|
|
|
|
3 <- 98 # 'b'
|
|
|
|
4 <- 1
|
|
|
|
5 <- 99 # 'c'
|
|
|
|
6 <- 1
|
|
|
|
7 <- 0 # unset
|
|
|
|
8 <- 1
|
|
|
|
9 <- 1 # end of test events
|
2015-06-15 20:58:08 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
:(before "End Scenario Globals")
|
2015-06-22 06:23:57 +00:00
|
|
|
const long long int CONSOLE = Next_predefined_global_for_scenarios++;
|
2015-06-28 06:13:57 +00:00
|
|
|
:(before "End Special Scenario Variable Names(r)")
|
|
|
|
Name[r]["console"] = CONSOLE;
|
2015-06-22 06:23:57 +00:00
|
|
|
|
|
|
|
//: allow naming just for 'console'
|
2015-06-15 20:58:08 +00:00
|
|
|
:(before "End is_special_name Cases")
|
2015-06-22 06:23:57 +00:00
|
|
|
if (s == "console") return true;
|
2015-06-15 20:58:08 +00:00
|
|
|
|
|
|
|
:(before "End Primitive Recipe Declarations")
|
2015-06-22 06:23:57 +00:00
|
|
|
ASSUME_CONSOLE,
|
2015-06-15 20:58:08 +00:00
|
|
|
:(before "End Primitive Recipe Numbers")
|
2015-07-04 16:40:50 +00:00
|
|
|
Recipe_ordinal["assume-console"] = ASSUME_CONSOLE;
|
2015-10-02 00:30:14 +00:00
|
|
|
:(before "End Primitive Recipe Checks")
|
|
|
|
case ASSUME_CONSOLE: {
|
|
|
|
break;
|
|
|
|
}
|
2015-06-15 20:58:08 +00:00
|
|
|
:(before "End Primitive Recipe Implementations")
|
2015-06-22 06:23:57 +00:00
|
|
|
case ASSUME_CONSOLE: {
|
2015-06-15 20:58:08 +00:00
|
|
|
// create a temporary recipe just for parsing; it won't contain valid instructions
|
|
|
|
istringstream in("[" + current_instruction().ingredients.at(0).name + "]");
|
2015-10-29 01:19:41 +00:00
|
|
|
recipe r;
|
|
|
|
slurp_body(in, r);
|
2015-06-15 20:58:08 +00:00
|
|
|
long long int num_events = count_events(r);
|
2015-10-05 23:43:21 +00:00
|
|
|
// initialize the events like in new-fake-console
|
2015-06-15 20:58:08 +00:00
|
|
|
long long int size = num_events*size_of_event() + /*space for length*/1;
|
|
|
|
ensure_space(size);
|
|
|
|
long long int event_data_address = Current_routine->alloc;
|
|
|
|
Memory[event_data_address] = num_events;
|
|
|
|
++Current_routine->alloc;
|
|
|
|
for (long long int i = 0; i < SIZE(r.steps); ++i) {
|
|
|
|
const instruction& curr = r.steps.at(i);
|
|
|
|
if (curr.name == "left-click") {
|
2015-06-23 07:18:40 +00:00
|
|
|
Memory[Current_routine->alloc] = /*tag for 'touch-event' variant of 'event' exclusive-container*/2;
|
2015-06-15 20:58:08 +00:00
|
|
|
Memory[Current_routine->alloc+1+/*offset of 'type' in 'mouse-event'*/0] = TB_KEY_MOUSE_LEFT;
|
|
|
|
Memory[Current_routine->alloc+1+/*offset of 'row' in 'mouse-event'*/1] = to_integer(curr.ingredients.at(0).name);
|
|
|
|
Memory[Current_routine->alloc+1+/*offset of 'column' in 'mouse-event'*/2] = to_integer(curr.ingredients.at(1).name);
|
|
|
|
Current_routine->alloc += size_of_event();
|
|
|
|
}
|
2015-06-22 06:23:57 +00:00
|
|
|
else if (curr.name == "press") {
|
2015-08-29 23:20:01 +00:00
|
|
|
string key = curr.ingredients.at(0).name;
|
|
|
|
if (is_integer(key))
|
|
|
|
Memory[Current_routine->alloc+1] = to_integer(key);
|
|
|
|
else if (Key.find(key) != Key.end())
|
|
|
|
Memory[Current_routine->alloc+1] = Key[key];
|
|
|
|
else
|
2015-10-07 05:15:45 +00:00
|
|
|
raise_error << "assume-console: can't press " << key << '\n' << end();
|
2015-08-29 23:20:01 +00:00
|
|
|
if (Memory[Current_routine->alloc+1] < 256)
|
|
|
|
// these keys are in ascii
|
|
|
|
Memory[Current_routine->alloc] = /*tag for 'text' variant of 'event' exclusive-container*/0;
|
|
|
|
else {
|
|
|
|
// distinguish from unicode
|
|
|
|
Memory[Current_routine->alloc] = /*tag for 'keycode' variant of 'event' exclusive-container*/1;
|
|
|
|
}
|
2015-06-22 06:23:57 +00:00
|
|
|
Current_routine->alloc += size_of_event();
|
|
|
|
}
|
2015-06-15 20:58:08 +00:00
|
|
|
// End Event Handlers
|
|
|
|
else {
|
|
|
|
// keyboard input
|
|
|
|
assert(curr.name == "type");
|
|
|
|
const string& contents = curr.ingredients.at(0).name;
|
|
|
|
const char* raw_contents = contents.c_str();
|
|
|
|
long long int num_keyboard_events = unicode_length(contents);
|
|
|
|
long long int curr = 0;
|
|
|
|
for (long long int i = 0; i < num_keyboard_events; ++i) {
|
2015-06-23 06:32:21 +00:00
|
|
|
Memory[Current_routine->alloc] = /*tag for 'text' variant of 'event' exclusive-container*/0;
|
2015-06-15 20:58:08 +00:00
|
|
|
uint32_t curr_character;
|
|
|
|
assert(curr < SIZE(contents));
|
|
|
|
tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
|
|
|
|
Memory[Current_routine->alloc+/*skip exclusive container tag*/1] = curr_character;
|
|
|
|
curr += tb_utf8_char_length(raw_contents[curr]);
|
|
|
|
Current_routine->alloc += size_of_event();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(Current_routine->alloc == event_data_address+size);
|
2015-10-26 04:42:18 +00:00
|
|
|
// wrap the array of events in a console object
|
|
|
|
ensure_space(size_of_console());
|
2015-06-22 06:23:57 +00:00
|
|
|
Memory[CONSOLE] = Current_routine->alloc;
|
2015-10-26 04:42:18 +00:00
|
|
|
Current_routine->alloc += size_of_console();
|
2015-06-22 06:23:57 +00:00
|
|
|
Memory[Memory[CONSOLE]+/*offset of 'data' in container 'events'*/1] = event_data_address;
|
2015-06-15 20:58:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-29 23:20:01 +00:00
|
|
|
:(before "End Globals")
|
|
|
|
map<string, long long 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;
|
2015-08-30 00:57:13 +00:00
|
|
|
Key["enter"] = TB_KEY_NEWLINE; // ignore CR/LF distinction; there is only 'enter'
|
2015-08-29 23:20:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-22 06:23:57 +00:00
|
|
|
:(scenario events_in_scenario)
|
|
|
|
scenario events-in-scenario [
|
|
|
|
assume-console [
|
2015-06-17 08:22:25 +00:00
|
|
|
type [abc]
|
2015-06-22 06:23:57 +00:00
|
|
|
left-click 0, 1
|
2015-08-29 23:20:01 +00:00
|
|
|
press up-arrow
|
2015-06-22 06:23:57 +00:00
|
|
|
type [d]
|
2015-06-17 08:22:25 +00:00
|
|
|
]
|
|
|
|
run [
|
2015-06-22 06:23:57 +00:00
|
|
|
# 3 keyboard events; each event occupies 4 locations
|
2015-10-28 20:08:26 +00:00
|
|
|
1:event <- read-event console:address:console
|
|
|
|
5:event <- read-event console:address:console
|
|
|
|
9:event <- read-event console:address:console
|
2015-06-22 06:23:57 +00:00
|
|
|
# mouse click
|
2015-10-28 20:08:26 +00:00
|
|
|
13:event <- read-event console:address:console
|
2015-06-22 06:23:57 +00:00
|
|
|
# non-character keycode
|
2015-10-28 20:08:26 +00:00
|
|
|
17:event <- read-event console:address:console
|
2015-06-22 06:23:57 +00:00
|
|
|
# final keyboard event
|
2015-10-28 20:08:26 +00:00
|
|
|
21:event <- read-event console:address:console
|
2015-06-22 06:23:57 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
1 <- 0 # 'text'
|
|
|
|
2 <- 97 # 'a'
|
|
|
|
3 <- 0 # unused
|
|
|
|
4 <- 0 # unused
|
|
|
|
5 <- 0 # 'text'
|
|
|
|
6 <- 98 # 'b'
|
|
|
|
7 <- 0 # unused
|
|
|
|
8 <- 0 # unused
|
|
|
|
9 <- 0 # 'text'
|
|
|
|
10 <- 99 # 'c'
|
|
|
|
11 <- 0 # unused
|
|
|
|
12 <- 0 # unused
|
|
|
|
13 <- 2 # 'mouse'
|
|
|
|
14 <- 65513 # mouse click
|
|
|
|
15 <- 0 # row
|
|
|
|
16 <- 1 # column
|
2015-06-23 18:48:28 +00:00
|
|
|
17 <- 1 # 'keycode'
|
2015-08-29 23:20:01 +00:00
|
|
|
18 <- 65517 # up arrow
|
2015-06-22 06:23:57 +00:00
|
|
|
19 <- 0 # unused
|
|
|
|
20 <- 0 # unused
|
|
|
|
21 <- 0 # 'text'
|
|
|
|
22 <- 100 # 'd'
|
|
|
|
23 <- 0 # unused
|
|
|
|
24 <- 0 # unused
|
|
|
|
25 <- 0
|
2015-06-17 08:22:25 +00:00
|
|
|
]
|
|
|
|
]
|
2015-06-23 18:51:17 +00:00
|
|
|
|
2015-06-23 20:04:46 +00:00
|
|
|
//: 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")
|
2015-07-04 16:40:50 +00:00
|
|
|
Recipe_ordinal["replace-in-console"] = REPLACE_IN_CONSOLE;
|
2015-10-02 00:30:14 +00:00
|
|
|
:(before "End Primitive Recipe Checks")
|
|
|
|
case REPLACE_IN_CONSOLE: {
|
|
|
|
break;
|
|
|
|
}
|
2015-06-23 20:04:46 +00:00
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case REPLACE_IN_CONSOLE: {
|
|
|
|
assert(scalar(ingredients.at(0)));
|
2015-07-25 07:02:20 +00:00
|
|
|
if (!Memory[CONSOLE]) {
|
2015-10-07 05:15:45 +00:00
|
|
|
raise_error << "console not initialized\n" << end();
|
2015-07-25 07:02:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-06-23 20:04:46 +00:00
|
|
|
long long int console_data = Memory[Memory[CONSOLE]+1];
|
|
|
|
long long int size = Memory[console_data]; // array size
|
|
|
|
for (long long int i = 0, curr = console_data+1; i < size; ++i, curr+=size_of_event()) {
|
|
|
|
if (Memory[curr] != /*text*/0) continue;
|
|
|
|
if (Memory[curr+1] != ingredients.at(0).at(0)) continue;
|
|
|
|
for (long long int n = 0; n < size_of_event(); ++n)
|
|
|
|
Memory[curr+n] = ingredients.at(1).at(n);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-23 18:51:17 +00:00
|
|
|
:(code)
|
|
|
|
long long int count_events(const recipe& r) {
|
|
|
|
long long int result = 0;
|
|
|
|
for (long long 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
long long int size_of_event() {
|
|
|
|
// memoize result if already computed
|
|
|
|
static long long int result = 0;
|
|
|
|
if (result) return result;
|
2015-10-26 04:42:18 +00:00
|
|
|
type_tree* type = new type_tree(Type_ordinal["event"]);
|
2015-06-23 18:51:17 +00:00
|
|
|
result = size_of(type);
|
2015-10-26 04:42:18 +00:00
|
|
|
delete type;
|
2015-06-23 18:51:17 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-10-26 04:42:18 +00:00
|
|
|
long long int size_of_console() {
|
2015-06-23 18:51:17 +00:00
|
|
|
// memoize result if already computed
|
|
|
|
static long long int result = 0;
|
|
|
|
if (result) return result;
|
2015-07-04 16:40:50 +00:00
|
|
|
assert(Type_ordinal["console"]);
|
2015-10-26 04:42:18 +00:00
|
|
|
type_tree* type = new type_tree(Type_ordinal["console"]);
|
2015-06-23 18:51:17 +00:00
|
|
|
result = size_of(type);
|
2015-10-26 04:42:18 +00:00
|
|
|
delete type;
|
2015-06-23 18:51:17 +00:00
|
|
|
return result;
|
|
|
|
}
|