From a0deaa1cb1920339b0e10cb53c8806c35ed94445 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 19 Sep 2020 09:02:40 -0700 Subject: 6801 - snapshot: RPN structured editor There's some worrisome memory corruption here between the call to max-stack-depth and the callee picking up its args. All this code is incredibly ugly as I start to wrestle with the challenges of structured editors. I keep wanting to keep business logic separate from rendering, but there are feedback loops from wanting to know where to render the cursor. And I haven't even started trying to avoid full-screen renders yet. That'll complect things even more. For now the data path for every iteration of the render loop is: process key compute max depth needed (or any other global information needed for rendering) render --- apps/tile/gap-buffer.mu | 256 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 219 insertions(+), 37 deletions(-) (limited to 'apps/tile/gap-buffer.mu') diff --git a/apps/tile/gap-buffer.mu b/apps/tile/gap-buffer.mu index 9d6b7161..1503690b 100644 --- a/apps/tile/gap-buffer.mu +++ b/apps/tile/gap-buffer.mu @@ -11,6 +11,22 @@ fn initialize-gap-buffer _self: (addr gap-buffer) { initialize-grapheme-stack right, 0x10 } +# just for tests +fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) { + initialize-gap-buffer self + var stream-storage: (stream byte 0x10) + var stream/ecx: (addr stream byte) <- address stream-storage + write stream, s + { + var done?/eax: boolean <- stream-empty? stream + compare done?, 0 # false + break-if-!= + var g/eax: grapheme <- read-grapheme stream + add-grapheme-at-gap self, g + loop + } +} + fn render-gap-buffer screen: (addr screen), _gap: (addr gap-buffer) { var gap/esi: (addr gap-buffer) <- copy _gap var left/eax: (addr grapheme-stack) <- get gap, left @@ -30,49 +46,26 @@ fn gap-buffer-length _gap: (addr gap-buffer) -> result/eax: int { result <- add left-length } -# dump stack to screen from bottom to top -# don't move the cursor or anything -fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { - var self/esi: (addr grapheme-stack) <- copy _self - var data-ah/edi: (addr handle array grapheme) <- get self, data - var _data/eax: (addr array grapheme) <- lookup *data-ah - var data/edi: (addr array grapheme) <- copy _data - var top-addr/ecx: (addr int) <- get self, top - var i/eax: int <- copy 0 - { - compare i, *top-addr - break-if->= - var g/edx: (addr grapheme) <- index data, i - print-grapheme screen, *g - i <- increment - loop - } +fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme { + var self/esi: (addr gap-buffer) <- copy _self + var left/eax: (addr grapheme-stack) <- get self, left + push-grapheme-stack left, g } -# dump stack to screen from top to bottom -# don't move the cursor or anything -fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { - var self/esi: (addr grapheme-stack) <- copy _self - var data-ah/edi: (addr handle array grapheme) <- get self, data - var _data/eax: (addr array grapheme) <- lookup *data-ah - var data/edi: (addr array grapheme) <- copy _data - var top-addr/ecx: (addr int) <- get self, top - var i/eax: int <- copy *top-addr - i <- decrement +fn gap-to-start self: (addr gap-buffer) { { - compare i, 0 - break-if-< - var g/edx: (addr grapheme) <- index data, i - print-grapheme screen, *g - i <- decrement - loop + var curr/eax: grapheme <- gap-left self + compare curr, -1 + loop-if-!= } } -fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme { - var self/esi: (addr gap-buffer) <- copy _self - var left/eax: (addr grapheme-stack) <- get self, left - push-grapheme-stack left, g +fn gap-to-end self: (addr gap-buffer) { + { + var curr/eax: grapheme <- gap-right self + compare curr, -1 + loop-if-!= + } } fn gap-right _self: (addr gap-buffer) -> result/eax: grapheme { @@ -112,6 +105,79 @@ $gap-left:body: { } } +fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> result/eax: boolean { +$gap-buffer-equal?:body: { + var self/esi: (addr gap-buffer) <- copy _self + # complication: graphemes may be multiple bytes + # so don't rely on length + # instead turn the expected result into a stream and arrange to read from it in order + var stream-storage: (stream byte 0x10) + var expected-stream/ecx: (addr stream byte) <- address stream-storage + write expected-stream, s + # compare left + var left/edx: (addr grapheme-stack) <- get self, left + result <- prefix-match? left, expected-stream + compare result, 0 # false + break-if-= $gap-buffer-equal?:body + # compare right + var right/edx: (addr grapheme-stack) <- get self, right + result <- suffix-match? right, expected-stream + compare result, 0 # false + break-if-= $gap-buffer-equal?:body + # ensure there's nothing left over + result <- stream-empty? expected-stream +} +} + +fn test-gap-buffer-equal-from-end? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + var c/eax: grapheme <- copy 0x61 # 'a' + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + # gap is at end (right is empty) + var _result/eax: boolean <- gap-buffer-equal? g, "aaa" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-end?" +} + +fn test-gap-buffer-equal-from-middle? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + var c/eax: grapheme <- copy 0x61 # 'a' + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + var dummy/eax: grapheme <- gap-left g + # gap is in the middle + var _result/eax: boolean <- gap-buffer-equal? g, "aaa" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-middle?" +} + +fn test-gap-buffer-equal-from-start? { + var _g: gap-buffer + var g/esi: (addr gap-buffer) <- address _g + initialize-gap-buffer g + # + var c/eax: grapheme <- copy 0x61 # 'a' + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + add-grapheme-at-gap g, c + var dummy/eax: grapheme <- gap-left g + dummy <- gap-left g + dummy <- gap-left g + # gap is at the start + var _result/eax: boolean <- gap-buffer-equal? g, "aaa" + var result/eax: int <- copy _result + check-ints-equal result, 1, "F - test-gap-buffer-equal-from-start?" +} + type grapheme-stack { data: (handle array grapheme) top: int @@ -125,6 +191,20 @@ fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int { copy-to *top, 0 } +fn grapheme-stack-empty? _self: (addr grapheme-stack) -> result/eax: boolean { +$grapheme-stack-empty?:body: { + var self/esi: (addr grapheme-stack) <- copy _self + var top/eax: (addr int) <- get self, top + compare *top, 0 + { + break-if-= + result <- copy 1 # false + break $grapheme-stack-empty?:body + } + result <- copy 0 # false +} +} + fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme { var self/esi: (addr grapheme-stack) <- copy _self var top-addr/ecx: (addr int) <- get self, top @@ -155,3 +235,105 @@ $pop-grapheme-stack:body: { val <- copy *result-addr } } + +# dump stack to screen from bottom to top +# don't move the cursor or anything +fn render-stack-from-bottom _self: (addr grapheme-stack), screen: (addr screen) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy 0 + { + compare i, *top-addr + break-if->= + var g/edx: (addr grapheme) <- index data, i + print-grapheme screen, *g + i <- increment + loop + } +} + +# dump stack to screen from top to bottom +# don't move the cursor or anything +fn render-stack-from-top _self: (addr grapheme-stack), screen: (addr screen) { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/eax: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + var g/edx: (addr grapheme) <- index data, i + print-grapheme screen, *g + i <- decrement + loop + } +} + +# compare from bottom +# beware: modifies 'stream', which must be disposed of after a false result +fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> result/eax: boolean { +$prefix-match?:body: { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/ecx: (addr int) <- get self, top + var i/ebx: int <- copy 0 + { + compare i, *top-addr + break-if->= + # if curr != expected, return false + { + var curr-a/edx: (addr grapheme) <- index data, i + var expected/eax: grapheme <- read-grapheme s + { + compare expected, *curr-a + break-if-= + result <- copy 0 # false + break $prefix-match?:body + } + } + i <- increment + loop + } + result <- copy 1 # true +} +} + +# compare from bottom +# beware: modifies 'stream', which must be disposed of after a false result +fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> result/eax: boolean { +$suffix-match?:body: { + var self/esi: (addr grapheme-stack) <- copy _self + var data-ah/edi: (addr handle array grapheme) <- get self, data + var _data/eax: (addr array grapheme) <- lookup *data-ah + var data/edi: (addr array grapheme) <- copy _data + var top-addr/eax: (addr int) <- get self, top + var i/ebx: int <- copy *top-addr + i <- decrement + { + compare i, 0 + break-if-< + { + var curr-a/edx: (addr grapheme) <- index data, i + var expected/eax: grapheme <- read-grapheme s + # if curr != expected, return false + { + compare expected, *curr-a + break-if-= + result <- copy 0 # false + break $suffix-match?:body + } + } + i <- decrement + loop + } + result <- copy 1 # true +} +} -- cgit 1.4.1-2-gfad0 2ef151ad86e2003ad0e2bfb09f3d4c2d'>^
6808ff7d ^
1f7e3c05 ^
6808ff7d ^




a17f9186 ^
6808ff7d ^

a17f9186 ^
6808ff7d ^

9cfd925a ^
1ead3562 ^
15f79a66 ^
6808ff7d ^
15f79a66 ^




acc4792d ^
ff8d96ae ^
69e418d7 ^
1ead3562 ^
69e418d7 ^
991d76f3 ^



69e418d7 ^



ff8d96ae ^


1ead3562 ^
455fbac6 ^
ff8d96ae ^
8a3d101e ^
ff8d96ae ^
bb33c5e8 ^
c4e143d6 ^





ff8d96ae ^

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99




                                                                              
                                                
          

                                               
                                                               


                                               

                                                    
















                                                    
                                      
                                 




                         

                                                         

                        


                               
                         
                                
                                        




                                            
   

                        
 

                                          
                                                 
          
                                                              
 




                      
                                                                                
 
                                           
          
                                                        



                                                                                                                        



                              


                                                                            
          
                                                                 
 
                                       
 
                                                                     





                                                                               

                                                                
// So far instructions can only contain linear lists of properties. Now we add
// support for more complex trees of properties in dilated reagents. This will
// come in handy later for expressing complex types, like "a dictionary from
// (address to array of charaters) to (list of numbers)".

:(scenario dilated_reagent_with_nested_brackets)
def main [
  {1: number, foo: (bar (baz quux))} <- copy 34
]
+parse:   product: {1: "number", "foo": ("bar" ("baz" "quux"))}

:(before "End Parsing Reagent Property(value)")
value = parse_string_tree(value);
:(before "End Parsing Reagent Type Property(value)")
value = parse_string_tree(value);

:(code)
string_tree* parse_string_tree(string_tree* s) {
  assert(!s->left && !s->right);
  if (s->value.at(0) != '(') return s;
  string_tree* result = parse_string_tree(s->value);
  delete s;
  return result;
}

string_tree* parse_string_tree(const string& s) {
  istringstream in(s);
  in >> std::noskipws;
  return parse_string_tree(in);
}

string_tree* parse_string_tree(istream& in) {
  skip_whitespace_but_not_newline(in);
  if (!has_data(in)) return NULL;
  if (in.peek() == ')') {
    in.get();
    return NULL;
  }
  if (in.peek() != '(') {
    string_tree* result = new string_tree(next_word(in));
    return result;
  }
  in.get();  // skip '('
  string_tree* result = NULL;
  string_tree** curr = &result;
  while (in.peek() != ')') {
    assert(has_data(in));
    *curr = new string_tree("");
    skip_whitespace_but_not_newline(in);
    if (in.peek() == '(')
      (*curr)->left = parse_string_tree(in);
    else
      (*curr)->value = next_word(in);
    curr = &(*curr)->right;
  }
  in.get();  // skip ')'
  return result;
}

:(scenario dilated_reagent_with_type_tree)
% Hide_errors = true;  // 'map' isn't defined yet
def main [
  {1: (foo (address array character) (bar number))} <- copy 34
]
# just to avoid errors
container foo [
]
container bar [
]
+parse:   product: {1: ("foo" ("address" "array" "character") ("bar" "number"))}

:(scenario dilated_reagent_in_static_array)
def main [
  {1: (array (address shared number) 3)} <- create-array
  5:address:shared:number <- new number:type
  {1: (array (address shared number) 3)} <- put-index {1: (array (address shared number) 3)}, 0, 5:address:shared:number
  *5:address:shared:number <- copy 34
  6:number <- copy *5:address:shared:number
]
+run: creating array of size 4
+mem: storing 34 in location 6

//: an exception is 'new', which takes a type tree as its ingredient *value*

:(scenario dilated_reagent_with_new)
def main [
  x:address:shared:address:number <- new {(address number): type}
]
+new: size of ("address" "number") is 1

:(before "End Post-processing(expected_product) When Checking 'new'")
{
  string_tree* tmp_type_names = parse_string_tree(expected_product.type->name);
  delete expected_product.type;
  expected_product.type = new_type_tree(tmp_type_names);
  delete tmp_type_names;
}
:(before "End Post-processing(type_name) When Converting 'new'")
type_name = parse_string_tree(type_name);