//: 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 these functions in other transforms :(before "End initialize_transform_rewrite_literal_string_to_text()") recipes_taking_literal_strings.insert("assume-console"); :(scenarios run_mu_scenario) :(scenario keyboard_in_scenario) scenario keyboard-in-scenario [ assume-console [ type [abc] ] run [ 1:char, 2:bool <- read-key console 3:char, 4:bool <- read-key console 5:char, 6:bool <- read-key console 7:char, 8:bool, 9:bool <- read-key console ] memory-should-contain [ 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 ] ] :(before "End Scenario Globals") extern const int CONSOLE = Next_predefined_global_for_scenarios++; //: 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 = /*space for refcount and length*/2 + num_events*size_of_event(); int event_data_address = allocate(size); // store length put(Memory, event_data_address+/*skip refcount*/1, num_events); int curr_address = event_data_address + /*skip refcount and length*/2; for (int i = 0; i < SIZE(r.steps); ++i) { const instruction& inst = r.steps.at(i); if (inst.name == "left-click") { trace(9999, "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(9999, "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 un
proc p*(f = (proc(): string = "hi")) =
echo f()