about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-05-30 11:38:16 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-05-30 12:02:29 -0700
commit3d68a0b026e6551e962124f3a1831120dbd553b7 (patch)
tree6a19bebe955a00eded4a51435fa1e6d584c3c983
parent76acf2b00e5fb265f1b64b2194367d2848afe400 (diff)
downloadmu-3d68a0b026e6551e962124f3a1831120dbd553b7.tar.gz
1514 - cleaner way to show backspaces in fake keyboard
In the process we now support unicode in all mu strings!
-rw-r--r--042new.cc41
-rw-r--r--072scenario_screen.cc4
-rw-r--r--075scenario_keyboard.cc25
-rw-r--r--repl.mu66
4 files changed, 76 insertions, 60 deletions
diff --git a/042new.cc b/042new.cc
index d476a5c5..facb9420 100644
--- a/042new.cc
+++ b/042new.cc
@@ -159,7 +159,7 @@ recipe main [
 +new: routine allocated memory from 1000 to 1002
 +new: routine allocated memory from 1002 to 1004
 
-//:: Next, extend 'new' to handle a string literal argument.
+//:: Next, extend 'new' to handle a unicode string literal argument.
 
 :(scenario new_string)
 recipe main [
@@ -169,6 +169,16 @@ recipe main [
 # number code for 'e'
 +mem: storing 101 in location 2
 
+:(scenario new_string_handles_unicode)
+recipe main [
+  1:address:array:character <- new [a«c]
+  2:number <- length 1:address:array:character/deref
+  3:character <- index 1:address:array:character/deref, 1:literal
+]
++mem: storing 3 in location 2
+# unicode for '«'
++mem: storing 171 in location 3
+
 :(before "End NEW Transform Special-cases")
   if (inst.ingredients.at(0).properties.at(0).second.at(0) == "literal-string") {
     // skip transform
@@ -180,7 +190,7 @@ recipe main [
 if (isa_literal(current_instruction().ingredients.at(0))
     && current_instruction().ingredients.at(0).properties.at(0).second.at(0) == "literal-string") {
   // allocate an array just large enough for it
-  long long int string_length = SIZE(current_instruction().ingredients.at(0).name);
+  long long int string_length = unicode_length(current_instruction().ingredients.at(0).name);
 //?   cout << "string_length is " << string_length << '\n'; //? 1
   ensure_space(string_length+1);  // don't forget the extra location for array size
   products.resize(1);
@@ -188,8 +198,16 @@ if (isa_literal(current_instruction().ingredients.at(0))
   // initialize string
 //?   cout << "new string literal: " << current_instruction().ingredients.at(0).name << '\n'; //? 1
   Memory[Current_routine->alloc++] = string_length;
+  long long int curr = 0;
+  const string& contents = current_instruction().ingredients.at(0).name;
+  const char* raw_contents = contents.c_str();
   for (long long int i = 0; i < string_length; ++i) {
-    Memory[Current_routine->alloc++] = current_instruction().ingredients.at(0).name.at(i);
+    uint32_t curr_character;
+    assert(curr < SIZE(contents));
+    tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
+    Memory[Current_routine->alloc] = curr_character;
+    curr += tb_utf8_char_length(raw_contents[curr]);
+    ++Current_routine->alloc;
   }
   // mu strings are not null-terminated in memory
   break;
@@ -204,3 +222,20 @@ recipe main [
 ]
 +new: routine allocated memory from 1000 to 1002
 +new: routine allocated memory from 1002 to 1004
+
+//: helpers
+:(code)
+long long int unicode_length(const string& s) {
+  const char* in = s.c_str();
+  long long int result = 0;
+  long long int curr = 0;
+  while (curr < SIZE(s)) {  // carefully bounds-check on the string
+    // before accessing its raw pointer
+    ++result;
+    curr += tb_utf8_char_length(in[curr]);
+  }
+  return result;
+}
+
+:(before "End Includes")
+#include"termbox/termbox.h"  // for unicode primitives
diff --git a/072scenario_screen.cc b/072scenario_screen.cc
index 7c5de630..29810906 100644
--- a/072scenario_screen.cc
+++ b/072scenario_screen.cc
@@ -1,7 +1,8 @@
 //: Clean syntax to manipulate and check the screen in scenarios.
 //: Instructions 'assume-screen' and 'screen-should-contain' implicitly create
 //: a variable called 'screen' that is accessible inside other 'run'
-//: instructions in the scenario.
+//: instructions in the scenario. 'screen-should-contain' can check unicode
+//: characters in the fake screen
 
 :(scenarios run_mu_scenario)
 :(scenario screen_in_scenario)
@@ -21,7 +22,6 @@ scenario screen-in-scenario [
 ]
 
 :(scenario screen_in_scenario_unicode)
-# screen-should-contain can check unicode characters in the fake screen
 scenario screen-in-scenario-unicode-color [
   assume-screen 5:literal/width, 3:literal/height
   run [
diff --git a/075scenario_keyboard.cc b/075scenario_keyboard.cc
index 363b3afd..bb0d478b 100644
--- a/075scenario_keyboard.cc
+++ b/075scenario_keyboard.cc
@@ -1,7 +1,8 @@
 //: Clean syntax to manipulate and check the keyboard in scenarios.
 //: Instruction 'assume-keyboard' implicitly creates a variable called
 //: 'keyboard' that is accessible inside other 'run' instructions in the
-//: scenario.
+//: scenario. Like with the fake screen, 'assume-keyboard' transparently
+//: supports unicode.
 
 :(scenarios run_mu_scenario)
 :(scenario keyboard_in_scenario)
@@ -58,3 +59,25 @@ if (curr.name == "assume-keyboard") {
   curr.products.push_back(reagent("keyboard:address"));
   curr.products.at(0).set_value(KEYBOARD);
 }
+
+//: Since we don't yet have a clean way to represent characters like backspace
+//: in literal strings we can't easily pretend they were typed into the fake
+//: keyboard. So we'll use special unicode characters in the literal and then
+//: manually replace them with backspace.
+:(before "End Primitive Recipe Declarations")
+REPLACE_IN_KEYBOARD,
+:(before "End Primitive Recipe Numbers")
+Recipe_number["replace-in-keyboard"] = REPLACE_IN_KEYBOARD;
+:(before "End Primitive Recipe Implementations")
+case REPLACE_IN_KEYBOARD: {
+  long long int size = Memory[KEYBOARD];
+  assert(scalar(ingredients.at(0)));
+  assert(scalar(ingredients.at(1)));
+  for (long long int curr = KEYBOARD+1; curr <= KEYBOARD+size; ++curr) {
+    if (Memory[curr] == ingredients.at(0).at(0)) {
+//?       cerr << "replacing\n"; //? 1
+      Memory[curr] = ingredients.at(1).at(0);
+    }
+  }
+  break;
+}
diff --git a/repl.mu b/repl.mu
index 1e95e7bf..c6165750 100644
--- a/repl.mu
+++ b/repl.mu
@@ -303,16 +303,9 @@ scenario read-instruction-color-comment [
 
 scenario read-instruction-cancel-comment-on-backspace [
   assume-screen 30:literal/width, 5:literal/height
-  assume-keyboard [#a<<z
+  assume-keyboard [#a««z
 ]
-  # setup: replace '<'s with backspace key since we can't represent backspace in strings
-  run [
-    buf:address:array:character <- get keyboard:address:keyboard/deref, data:offset
-    first:address:character <- index-address buf:address:array:character/deref, 2:literal
-    first:address:character/deref <- copy 8:literal/backspace
-    second:address:character <- index-address buf:address:array:character/deref, 3:literal
-    second:address:character/deref <- copy 8:literal/backspace
-  ]
+  replace-in-keyboard 171:literal/«, 8:literal/backspace
   run [
     read-instruction keyboard:address, screen:address
   ]
@@ -328,18 +321,9 @@ scenario read-instruction-cancel-comment-on-backspace [
 
 scenario read-instruction-cancel-comment-on-backspace2 [
   assume-screen 30:literal/width, 5:literal/height
-  assume-keyboard [#ab<<<z
+  assume-keyboard [#ab«««z
 ]
-  # setup: replace '<'s with backspace key since we can't represent backspace in strings
-  run [
-    buf:address:array:character <- get keyboard:address:keyboard/deref, data:offset
-    first:address:character <- index-address buf:address:array:character/deref, 3:literal
-    first:address:character/deref <- copy 8:literal/backspace
-    second:address:character <- index-address buf:address:array:character/deref, 4:literal
-    second:address:character/deref <- copy 8:literal/backspace
-    third:address:character <- index-address buf:address:array:character/deref, 5:literal
-    third:address:character/deref <- copy 8:literal/backspace
-  ]
+  replace-in-keyboard 171:literal/«, 8:literal/backspace
   run [
     read-instruction keyboard:address, screen:address
   ]
@@ -355,14 +339,9 @@ scenario read-instruction-cancel-comment-on-backspace2 [
 
 scenario read-instruction-cancel-comment-on-backspace3 [
   assume-screen 30:literal/width, 5:literal/height
-  assume-keyboard [#a<z
+  assume-keyboard [#a«z
 ]
-  # setup: replace '<'s with backspace key since we can't represent backspace in strings
-  run [
-    buf:address:array:character <- get keyboard:address:keyboard/deref, data:offset
-    first:address:character <- index-address buf:address:array:character/deref, 2:literal
-    first:address:character/deref <- copy 8:literal/backspace
-  ]
+  replace-in-keyboard 171:literal/«, 8:literal/backspace
   run [
     read-instruction keyboard:address, screen:address
   ]
@@ -525,16 +504,9 @@ scenario read-instruction-color-string-inside-string [
 scenario read-instruction-cancel-string-on-backspace [
   assume-screen 30:literal/width, 5:literal/height
   # need to escape the '[' once for 'scenario' and once for 'assume-keyboard'
-  assume-keyboard [\\\[a<<z
+  assume-keyboard [\\\[a««z
 ]
-  # setup: replace '<'s with backspace key since we can't represent backspace in strings
-  run [
-    buf:address:array:character <- get keyboard:address:keyboard/deref, data:offset
-    first-backspace:address:character <- index-address buf:address:array:character/deref, 2:literal
-    first-backspace:address:character/deref <- copy 8:literal/backspace
-    second-backspace:address:character <- index-address buf:address:array:character/deref, 3:literal
-    second-backspace:address:character/deref <- copy 8:literal/backspace
-  ]
+  replace-in-keyboard 171:literal/«, 8:literal/backspace
   run [
     read-instruction keyboard:address, screen:address
   ]
@@ -550,18 +522,9 @@ scenario read-instruction-cancel-string-on-backspace [
 
 scenario read-instruction-cancel-string-inside-string-on-backspace [
   assume-screen 30:literal/width, 5:literal/height
-  assume-keyboard [[a[b]<<<b]
+  assume-keyboard [\[a\[b\]«««b\]
 ]
-  # setup: replace '<'s with backspace key since we can't represent backspace in strings
-  run [
-    buf:address:array:character <- get keyboard:address:keyboard/deref, data:offset
-    first-backspace:address:character <- index-address buf:address:array:character/deref, 5:literal
-    first-backspace:address:character/deref <- copy 8:literal/backspace
-    second-backspace:address:character <- index-address buf:address:array:character/deref, 6:literal
-    second-backspace:address:character/deref <- copy 8:literal/backspace
-    third-backspace:address:character <- index-address buf:address:array:character/deref, 7:literal
-    third-backspace:address:character/deref <- copy 8:literal/backspace
-  ]
+  replace-in-keyboard 171:literal/«, 8:literal/backspace
   run [
     read-instruction keyboard:address, screen:address
   ]
@@ -578,14 +541,9 @@ scenario read-instruction-cancel-string-inside-string-on-backspace [
 scenario read-instruction-backspace-back-into-string [
   assume-screen 30:literal/width, 5:literal/height
   # need to escape the '[' once for 'scenario' and once for 'assume-keyboard'
-  assume-keyboard [[a]<b
+  assume-keyboard [[a]«b
 ]
-  # setup: replace '<'s with backspace key since we can't represent backspace in strings
-  run [
-    buf:address:array:character <- get keyboard:address:keyboard/deref, data:offset
-    first-backspace:address:character <- index-address buf:address:array:character/deref, 3:literal
-    first-backspace:address:character/deref <- copy 8:literal/backspace
-  ]
+  replace-in-keyboard 171:literal/«, 8:literal/backspace
   run [
     read-instruction keyboard:address, screen:address
   ]