Source file cursor.ml
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
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
(** Cursor (zipper) based tree operations *)
open Utils.Open
open Result
open Node
type modified =
| Modified
| Unmodified of indexed * hashed
type trail =
| Top
| Left of
trail
* node
* modified
| Right of
node
* trail
* modified
| Budded of
trail
* modified
| Extended of
trail
* Segment.t
* modified
type Error.t += Cursor_invariant of string
let () = Error.register_printer @@ function
| Cursor_invariant s -> Some ("Cursor: " ^ s)
| _ -> None
let error_cursor_invariant s = Error (Cursor_invariant s)
let trail_shape_invariant = function
| Extended (Extended _, _, _) -> error_cursor_invariant "Extended: cannot have Extended"
| Extended (_, seg, _) when Segment.is_empty seg -> error_cursor_invariant "Extended: invalid empty segment"
| _ -> Ok ()
let trail_modified_invariant = function
| Top -> Ok ()
| Left (_, n, Unmodified (ir, hit)) ->
begin match ir with
| Not_Indexed -> Ok ()
| Indexed _ when indexed n -> Ok ()
| Indexed _ -> error_cursor_invariant "Left: invalid Indexed"
end >>= fun () ->
begin match hit with
| Hashed _ when hashed n -> Ok ()
| Hashed _ -> error_cursor_invariant "Left: invalid Hashed"
| Not_Hashed -> Ok ()
end
| Left (_, _, Modified) -> Ok ()
| Right (n, _, Unmodified (ir, hit)) ->
begin match ir with
| Not_Indexed -> Ok ()
| Indexed _ when indexed n -> Ok ()
| Indexed _ -> error_cursor_invariant "Right: invalid Indexed"
end >>= fun () ->
begin match hit with
| Hashed _ when hashed n -> Ok ()
| Hashed _ -> error_cursor_invariant "Right: invalid Hashed"
| Not_Hashed -> Ok ()
end
| Right (_, _, Modified) -> Ok ()
| Budded (_, Unmodified (ir, _hit)) ->
begin match ir with
| Indexed _ | Not_Indexed -> Ok ()
end
| Budded (_, Modified) -> Ok ()
| Extended (_, _, Unmodified (ir, _hit)) ->
begin match ir with
| Indexed _ | Not_Indexed -> Ok ()
end
| Extended (_, _, Modified) -> Ok ()
let trail_index_and_hash_invariant = function
| Top -> Ok ()
| Left (_, _, Unmodified (Indexed _, Not_Hashed))
| Right (_, _, Unmodified (Indexed _, Not_Hashed))
| Budded (_, Unmodified (Indexed _, Not_Hashed))
| Extended (_, _, Unmodified (Indexed _, Not_Hashed)) -> error_cursor_invariant "Trail: Indexed with Not_Hashed"
| _ -> Ok ()
let trail_invariant t =
trail_shape_invariant t >>= fun () ->
trail_modified_invariant t >>= fun () ->
trail_index_and_hash_invariant t
let check_trail t =
match trail_invariant t with
| Ok _ -> t
| Error s -> Error.raise s
let _Top = Top
let _Left (t, n, mr) = check_trail @@ Left (t, n, mr)
let _Right (n, t, mr) = check_trail @@ Right (n, t, mr)
let _Budded (t, mr) = check_trail @@ Budded (t, mr)
let _Extended (t, s, mr) = check_trail @@ Extended (t, s, mr)
type cursor =
Cursor of trail
* node
* Context.t
type t = cursor
let context (Cursor (_, _, context)) = context
let cursor_invariant (Cursor (trail, n, c)) =
match trail with
| Top ->
begin match view c n with
| Bud _ -> Ok ()
| v ->
Log.fatal "@[<v2>Cursor: Top has no Bud:@ %a@]" Node.pp (View v);
Error "Cursor: Top has no Bud"
end
| Left (_, n', Unmodified (ir, hit)) ->
begin match ir with
| Not_Indexed -> Ok ()
| Indexed _ when indexed n && indexed n' -> Ok ()
| Indexed _ -> Error "Cursor: invalid Indexed"
end >>= fun () ->
begin match hit with
| Hashed _ when hashed n -> Ok ()
| Hashed _ -> Error "Cursor: invalid Hashed"
| Not_Hashed -> Ok ()
end
| Left (_, _, Modified) -> Ok ()
| Right (n', _, Unmodified (ir, hit)) ->
begin match ir with
| Not_Indexed -> Ok ()
| Indexed _ when indexed n && indexed n' -> Ok ()
| Indexed _ -> Error "Cursor: invalid Indexed"
end >>= fun () ->
begin match hit with
| Hashed _ when hashed n -> Ok ()
| Hashed _ -> Error "Cursor: invalid Hashed"
| Not_Hashed -> Ok ()
end
| Right (_, _, Modified) -> Ok ()
| Budded (_, Unmodified (ir, _hit)) ->
begin match ir with
| Indexed _ when indexed n -> Ok ()
| Indexed _ -> Error "Budded: invalid Indexed"
| Not_Indexed -> Ok ()
end
| Budded (_, Modified) -> Ok ()
| Extended (_, _, Unmodified (ir, hit)) ->
begin match ir with
| Indexed _ when indexed n -> Ok ()
| Indexed _ -> Error "Extended: invalid Indexed"
| Not_Indexed -> Ok ()
end >>= fun () ->
begin match hit with
| Hashed _ when hashed n -> Ok ()
| Hashed _ -> Error "Extended: invalid Hashed"
| Not_Hashed -> Ok ()
end
| Extended (_, _, Modified) -> Ok ()
let check_cursor c =
match cursor_invariant c with
| Ok _ -> c
| Error s -> failwith s
let _Cursor (t, n, c) =
check_cursor @@ Cursor (t, n, c)
let segs_of_trail trail =
let rec aux (xs, xss) = function
| Top ->
assert (Segment.is_empty xs); xss
| Budded (tr, _) -> aux (Segment.empty, xs::xss) tr
| Left (tr, _, _) -> aux (Segment.(cons Left xs), xss) tr
| Right (_, tr, _) -> aux (Segment.(cons Right xs), xss) tr
| Extended (tr, seg, _) -> aux (Segment.append seg xs, xss) tr
in
aux (Segment.empty, []) trail
let segs_of_cursor (Cursor (trail, _, _)) = segs_of_trail trail
let local_seg_of_trail trail =
let rec aux xs = function
| Top -> xs
| Budded (_, _) -> xs
| Left (tr, _, _) -> aux (Segment.cons Left xs) tr
| Right (_, tr, _) -> aux (Segment.cons Right xs) tr
| Extended (tr, seg, _) -> aux (Segment.append seg xs) tr
in
aux Segment.empty trail
let local_seg_of_cursor (Cursor (trail, _, _)) = local_seg_of_trail trail
let dot_of_cursor_ref = ref (fun _ -> assert false)
let attach trail node context =
match trail with
| Top -> _Cursor (_Top, node, context)
| Left (prev_trail, right, _) ->
_Cursor (_Left (prev_trail, right, Modified), node, context)
| Right (left, prev_trail, _) ->
_Cursor (_Right (left, prev_trail, Modified), node, context)
| Budded (prev_trail, _) ->
_Cursor (_Budded (prev_trail, Modified), node, context)
| Extended (prev_trail, segment, _) ->
_Cursor (_Extended (prev_trail, segment, Modified), node, context)
type access_result =
| Empty_bud
| Collide of cursor * view
| Middle_of_extender of cursor * Segment.t * Segment.t * Segment.t
| Reached of cursor * view
type Error.t +=
| Access of access_result
| Move of string
let () = Error.register_printer (function
| Access a ->
Some (match a with
| Empty_bud -> "Nothing beneath this bud"
| Collide _ -> "Reached to a leaf or bud before finishing"
| Middle_of_extender (_, _, _, seg) when Segment.is_empty seg ->
"Finished at the middle of an Extender"
| Middle_of_extender (_, _, _, _) ->
"Diverged in the middle of an Extender"
| Reached (_, Bud _) -> "Reached to a Bud"
| Reached (_, Leaf _) -> "Reached to a Leaf"
| Reached (_, Internal _) -> "Reached to an Internal"
| Reached (_, Extender _) -> "Reached to an Extender")
| Move s -> Some s
| _ -> None)
let error_access a = Error (Access a)
let view_cursor (Cursor (trail, n, context)) =
let v = view context n in
(_Cursor (trail, View v, context), v)
let index (Cursor (_, n, _)) = Node.index n
let go_below_bud (Cursor (trail, n, context)) =
match view context n with
| Bud (None, _, _) -> Ok None
| Bud (Some below, indexed, hashed) ->
Ok (Some (_Cursor (
_Budded (trail, Unmodified (indexed, hashed)), below, context)))
| _ -> Error (Move "Attempted to navigate below a bud, but got a different kind of node.")
let go_side side (Cursor (trail, n, context)) =
match view context n with
| Internal (l, r, indexed, hashed) ->
Ok (match side with
| Segment.Right ->
_Cursor (_Right (l, trail,
Unmodified (indexed, hashed)),
r, context)
| Segment.Left ->
_Cursor (_Left (trail, r,
Unmodified (indexed, hashed)),
l, context))
| _ -> Error (Move "Attempted to navigate right or left of a non internal node")
let go_down_extender (Cursor (trail, n, context)) =
match view context n with
| Extender (segment, below, indexed, hashed) ->
Ok (_Cursor (_Extended (trail, segment,
Unmodified (indexed, hashed)),
below, context))
| _ -> Error (Move "Attempted to go down an extender but did not find an extender")
let go_up (Cursor (trail, node, context)) = match trail with
| Top -> Error (Move "cannot go above top")
| Left (prev_trail, right,
Unmodified (indexed, hashed)) ->
let new_node =
View (_Internal (node, right, indexed, hashed))
in Ok (_Cursor (prev_trail, new_node, context))
| Right (left, prev_trail,
Unmodified (indexed, hashed)) ->
let new_node =
View (_Internal (left, node, indexed, hashed))
in Ok (_Cursor (prev_trail, new_node, context))
| Budded (prev_trail,
Unmodified (indexed, hashed)) ->
let new_node =
View (_Bud (Some node, indexed, hashed))
in Ok (_Cursor (prev_trail, new_node, context))
| Extended (prev_trail, segment,
Unmodified (indexed, hashed)) ->
let new_node =
View (_Extender (segment, node, indexed, hashed))
in Ok (_Cursor (prev_trail, new_node, context))
| Left (prev_trail, right, Modified) ->
let internal = new_internal node right in
Ok (attach prev_trail internal context)
| Right (left, prev_trail, Modified) ->
let internal = new_internal left node in
Ok (attach prev_trail internal context)
| Budded (prev_trail, Modified) ->
let bud = new_bud @@ Some node in
Ok (attach prev_trail bud context)
| Extended (prev_trail, segment, Modified) ->
let extender = new_extender segment node in
Ok (attach prev_trail extender context)
let rec go_top (Cursor (trail, _, _) as c) =
match trail with
| Top -> Ok c
| _ -> go_up c >>= go_top
let rec go_up_to_a_bud c =
let c, v = view_cursor c in
match v with
| Bud _ -> Ok c
| _ -> go_up c >>= go_up_to_a_bud
let rec go_up_to_bud c =
go_up c >>= fun c ->
let c, v = view_cursor c in
match v with
| Bud _ -> Ok c
| _ -> go_up_to_bud c
let parent c =
let c, v = view_cursor c in
match v with
| Bud _ -> go_up_to_bud c
| _ -> Error (Move "parent: cursor must be at a bud")
let unify_extenders prev_trail node context = match node with
| Disk (_, Is_Extender) -> Error (Move "unify_exenders: Disk is not allowed")
| View (Extender (seg, n, _, _)) ->
begin match prev_trail with
| Extended (prev_trail', seg', _mr) ->
Ok (attach prev_trail' (new_extender (Segment.append seg' seg) n) context)
| _ -> Ok (attach prev_trail node context)
end
| _ -> Ok (attach prev_trail node context)
let rec remove_up trail context = match trail with
| Top -> Error (Move "cannot remove top")
| Budded (prev_trail, _) ->
Ok (attach prev_trail (new_bud None) context)
| Extended (prev_trail, _, _) -> remove_up prev_trail context
| Left (prev_trail, right, _) ->
let right = View (view context right) in
let n = new_extender Segment.(of_sides [Right]) right in
unify_extenders prev_trail n context
| Right (left, prev_trail, _) ->
let left = View (view context left) in
let n = new_extender Segment.(of_sides [Left]) left in
unify_extenders prev_trail n context
let diverge (Cursor (trail, extender, _context)) (common_prefix, remaining_extender, remaining_segment) =
match extender with
| View (Extender (_seg, n, _ir, _hit)) ->
begin match Segment.cut remaining_segment, Segment.cut remaining_extender with
| None, _ -> error_cursor_invariant "diverge: remaining_segment is empty"
| _, None -> error_cursor_invariant "diverge: remaining_extender is empty"
| Some (side, seg), Some (side', seg') ->
assert (side <> side');
let trail =
if Segment.is_empty common_prefix then trail
else _Extended (trail, common_prefix, Modified)
in
let n' = new_extender seg' n in
match side with
| Segment.Left ->
if Segment.is_empty seg then
Ok (_Left (trail, n', Modified))
else
Ok (_Extended (_Left (trail, n', Modified), seg, Modified))
| Segment.Right ->
if Segment.is_empty seg then
Ok (_Right (n', trail, Modified))
else
Ok (_Extended (_Right (n', trail, Modified), seg, Modified))
end
| _ -> error_cursor_invariant "diverge: not an Extender"
let access_gen' cur segment =
let rec aux (Cursor (trail, n, context)) segment =
let v = view context n in
let cur = _Cursor (trail, View v, context) in
match Segment.cut segment with
| None -> Ok (Reached (cur, v))
| Some (dir, segment_rest) ->
match v with
| Leaf _ | Bud _ -> Ok (Collide (cur, v))
| Internal (l, r, indexed, hashed) -> begin
match dir with
| Left ->
let new_trail = _Left (trail, r, Unmodified (indexed, hashed)) in
aux (_Cursor (new_trail, l, context)) segment_rest
| Right ->
let new_trail = _Right (l, trail, Unmodified (indexed, hashed)) in
aux (_Cursor (new_trail, r, context)) segment_rest
end
| Extender (extender, node_below, indexed, hashed) ->
let (shared, remaining_extender, remaining_segment) =
Segment.common_prefix extender segment in
if Segment.is_empty remaining_extender then
let new_trail =
_Extended (trail, extender, Unmodified (indexed, hashed)) in
aux (_Cursor (new_trail, node_below, context)) remaining_segment
else
Ok (Middle_of_extender (cur, shared, remaining_extender, remaining_segment))
in
aux cur segment
let access_gen cur segment =
go_below_bud cur >>= function
| None -> Ok Empty_bud
| Some cur -> access_gen' cur segment
let subtree cur seg =
access_gen cur seg >>= function
| Reached (cur, Bud _) -> Ok cur
| res -> error_access res
let get cur seg =
access_gen cur seg >>= function
| Reached (c, (Bud _ as v)) -> go_up_to_bud c >>= fun c -> Ok (c, `Bud v)
| Reached (c, (Leaf _ as v)) -> go_up_to_bud c >>= fun c -> Ok (c, `Leaf v)
| res -> error_access res
let get_value cur seg =
access_gen cur seg >>= function
| Reached (c, Leaf (v, _, _)) ->
go_up_to_bud c >>= fun c -> Ok (c, v)
| Reached _ as res -> error_access res
| res -> error_access res
let empty context =
_Cursor (_Top, new_bud None, context)
let delete cur seg =
access_gen cur seg >>= function
| Reached (Cursor (trail, _, context), (Bud _ | Leaf _)) ->
remove_up trail context
>>= go_up_to_a_bud
| res -> error_access res
let alter (Cursor (trail, _, context) as cur) segment alteration =
access_gen cur segment >>= function
| Empty_bud ->
alteration None >>= fun n ->
let n = new_extender segment n in
let n = new_bud (Some n) in
Ok (attach trail n context)
| (Middle_of_extender (_, _, _, seg) as res) when Segment.is_empty seg -> error_access res
| (Reached (c, _) | Middle_of_extender (c, _, _, _) as res) ->
let segsopt = match res with
| Reached _ -> None
| Middle_of_extender (_c, shared, rest_extender, rest_segment) ->
Some (shared, rest_extender, rest_segment)
| _ -> assert false
in
begin match segsopt with
| None ->
let Cursor (trail, n, context) = c in
let v = view context n in
Ok (trail, Some v)
| Some segs -> diverge c segs >>| fun trail -> (trail, None)
end >>= fun (trail, vo) ->
alteration vo >>= fun n ->
let no_mod = match vo, n with
| Some v, View v' when v == v' -> true
| Some (Leaf (v, i, h)), View (Leaf (v', i', h')) when v = v' ->
begin match (i, i'), (h, h') with
| (Indexed _, _ | Not_Indexed, Not_Indexed),
(Hashed _, _ | Not_Hashed, Not_Hashed) -> true
| _ -> false
end
| _ -> false
in
let c =
if no_mod then c else attach trail n context
in
go_up c >>= go_up_to_a_bud
| res -> error_access res
let update cur segment value =
access_gen cur segment >>= function
| Reached (Cursor (trail, _, context), Leaf _) ->
go_up_to_a_bud (attach trail (View (_Leaf (value, Not_Indexed, Not_Hashed))) context)
| res -> error_access res
type Error.t +=
| Write of string
let () = Error.register_printer @@ function
| Write s -> Some ("Write: " ^ s)
| _ -> None
let upsert cur segment value =
alter cur segment (fun x ->
let y = Ok (new_leaf value) in
match x with
| None -> y
| Some (Leaf _) -> y
| Some _ -> Error (Write "a non Leaf node already presents for this path"))
let insert cur segment value =
alter cur segment (function
| None -> Ok (View (_Leaf (value, Not_Indexed, Not_Hashed)))
| Some _ -> Error (Write "a node already presents for this path"))
let create_subtree cur segment =
alter cur segment (function
| None -> Ok (new_bud None)
| Some _ -> Error (Write "a node already presents for this path"))
let subtree_or_create cur segment =
let cur =
match create_subtree cur segment with
| Ok cur -> cur
| Error _ -> cur
in
subtree cur segment
let traverse acc cs f = match cs with
| [] -> acc, []
| c::cs ->
let c, v = view_cursor c in
match f acc c with
| `Exit, acc -> acc, []
| `Up, acc -> acc, cs
| `Continue, acc ->
match v with
| Leaf _ | Bud (None, _, _) -> acc, cs
| Bud (Some _, _, _) ->
acc, from_Some (from_Ok (go_below_bud c)) :: cs
| Internal (_, _, _, _) ->
let c1 = from_Ok @@ go_side Left c in
let c2 = from_Ok @@ go_side Right c in
acc, c1 :: c2 :: cs
| Extender (_, _, _, _) ->
acc, from_Ok (go_down_extender c) ::cs
let fold ~init c f =
let rec aux acc cs = match traverse acc cs f with
| acc, [] -> acc
| acc, cs -> aux acc cs
in
aux init [c]
let stat (Cursor (_,_,{ stat ; _ })) = stat
let view = view_cursor
let remove_empty_bud c =
match view c with
| Cursor (Top, _, _), _ -> Ok c
| c, Bud (None, _, _) ->
let rec find c =
match go_up c with
| Error _ -> c
| Ok (Cursor (Top, _, _)) -> c
| Ok c' ->
let c', v = view c' in
match v with
| Leaf _ -> assert false
| Bud (None, _, _) -> assert false
| Bud (Some _, _, _) | Extender _ -> find c'
| Internal _ -> c
in
let Cursor (trail, _, context) = find c in
remove_up trail context
| _ -> Ok c
let may_forget c =
let Cursor (trail, n, context) = c in
match Node.may_forget n with
| None -> None
| Some n -> Some (_Cursor (trail, n, context))