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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
|
type gap-buffer {
left: grapheme-stack
right: grapheme-stack
}
fn initialize-gap-buffer _self: (addr gap-buffer) {
var self/esi: (addr gap-buffer) <- copy _self
var left/eax: (addr grapheme-stack) <- get self, left
initialize-grapheme-stack left, 0x10 # max-word-size
var right/eax: (addr grapheme-stack) <- get self, right
initialize-grapheme-stack right, 0x10 # max-word-size
}
# 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) # max-word-size
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 emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
var self/esi: (addr gap-buffer) <- copy _self
clear-stream out
var left/eax: (addr grapheme-stack) <- get self, left
emit-stack-from-bottom left, out
var right/eax: (addr grapheme-stack) <- get self, right
emit-stack-from-top right, out
}
# dump stack from bottom to top
fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
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
write-grapheme out, *g
i <- increment
loop
}
}
# dump stack from top to bottom
fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
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
write-grapheme out, *g
i <- decrement
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
render-stack-from-bottom left, screen
var right/eax: (addr grapheme-stack) <- get gap, right
render-stack-from-top right, screen
}
fn gap-buffer-length _gap: (addr gap-buffer) -> result/eax: int {
var gap/esi: (addr gap-buffer) <- copy _gap
var left/eax: (addr grapheme-stack) <- get gap, left
var tmp/eax: (addr int) <- get left, top
var left-length/ecx: int <- copy *tmp
var right/esi: (addr grapheme-stack) <- get gap, right
tmp <- get right, top
result <- copy *tmp
result <- add left-length
}
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-start self: (addr gap-buffer) {
{
var curr/eax: grapheme <- gap-left self
compare curr, -1
loop-if-!=
}
}
fn gap-to-end self: (addr gap-buffer) {
{
var curr/eax: grapheme <- gap-right self
compare curr, -1
loop-if-!=
}
}
fn gap-at-start? _self: (addr gap-buffer) -> result/eax: boolean {
var self/esi: (addr gap-buffer) <- copy _self
var left/eax: (addr grapheme-stack) <- get self, left
result <- grapheme-stack-empty? left
}
fn gap-at-end? _self: (addr gap-buffer) -> result/eax: boolean {
var self/esi: (addr gap-buffer) <- copy _self
var right/eax: (addr grapheme-stack) <- get self, right
result <- grapheme-stack-empty? right
}
fn gap-right _self: (addr gap-buffer) -> result/eax: grapheme {
$gap-right:body: {
var self/esi: (addr gap-buffer) <- copy _self
var g/edx: grapheme <- copy 0
{
var right/ecx: (addr grapheme-stack) <- get self, right
result <- pop-grapheme-stack right
compare result, -1
break-if-= $gap-right:body
g <- copy result
}
{
var left/ecx: (addr grapheme-stack) <- get self, left
push-grapheme-stack left, g
}
}
}
fn gap-left _self: (addr gap-buffer) -> result/eax: grapheme {
$gap-left:body: {
var self/esi: (addr gap-buffer) <- copy _self
var g/edx: grapheme <- copy 0
{
var left/ecx: (addr grapheme-stack) <- get self, left
result <- pop-grapheme-stack left
compare result, -1
break-if-= $gap-left:body
g <- copy result
}
{
var right/ecx: (addr grapheme-stack) <- get self, right
push-grapheme-stack right, g
}
}
}
fn gap-index _self: (addr gap-buffer) -> result/eax: int {
var self/eax: (addr gap-buffer) <- copy _self
var left/eax: (addr grapheme-stack) <- get self, left
var top-addr/eax: (addr int) <- get left, top
result <- copy *top-addr
}
fn delete-before-gap _self: (addr gap-buffer) {
var self/eax: (addr gap-buffer) <- copy _self
var left/eax: (addr grapheme-stack) <- get self, left
var dummy/eax: grapheme <- pop-grapheme-stack left
}
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) # max-word-size
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
}
fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int {
var self/esi: (addr grapheme-stack) <- copy _self
var d/edi: (addr handle array grapheme) <- get self, data
populate d, n
var top/eax: (addr int) <- get self, top
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
var data-ah/edx: (addr handle array grapheme) <- get self, data
var data/eax: (addr array grapheme) <- lookup *data-ah
var top/edx: int <- copy *top-addr
var dest-addr/edx: (addr grapheme) <- index data, top
var val/eax: grapheme <- copy _val
copy-to *dest-addr, val
add-to *top-addr, 1
}
fn pop-grapheme-stack _self: (addr grapheme-stack) -> val/eax: grapheme {
$pop-grapheme-stack:body: {
var self/esi: (addr grapheme-stack) <- copy _self
var top-addr/ecx: (addr int) <- get self, top
{
compare *top-addr, 0
break-if->
val <- copy -1
break $pop-grapheme-stack:body
}
subtract-from *top-addr, 1
var data-ah/edx: (addr handle array grapheme) <- get self, data
var data/eax: (addr array grapheme) <- lookup *data-ah
var top/edx: int <- copy *top-addr
var result-addr/eax: (addr grapheme) <- index data, top
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
}
}
|