summaryrefslogtreecommitdiff
path: root/lib/zipper.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/zipper.ml')
-rw-r--r--lib/zipper.ml131
1 files changed, 131 insertions, 0 deletions
diff --git a/lib/zipper.ml b/lib/zipper.ml
new file mode 100644
index 0000000..2322a58
--- /dev/null
+++ b/lib/zipper.ml
@@ -0,0 +1,131 @@
1(* Module [Zipper]: functional zippers *)
2
3open Base
4
5type !+'a zipper = {
6 pos : int;
7 popped : 'a Sequence.t;
8 before : 'a Sequence.t;
9 after : 'a Sequence.t;
10}
11
12type !+'a t = 'a zipper
13
14let empty =
15 {
16 pos = 0;
17 popped = Sequence.empty;
18 before = Sequence.empty;
19 after = Sequence.empty;
20 }
21
22let before z = z.before
23let after z = z.after
24let focus z = after z |> Sequence.next |> Option.map ~f:fst
25let focus_or z ~default = Option.value ~default (focus z)
26let history z = z.popped
27let is_far_left z = before z |> Sequence.is_empty
28let is_far_right z = after z |> Sequence.is_empty
29let is_empty z = is_far_left z && is_far_right z
30let left_length z = z.pos
31let right_length z = after z |> Sequence.length
32let length z = left_length z + right_length z
33
34let left z =
35 match Sequence.next z.before with
36 | None -> z
37 | Some (h, t) ->
38 {
39 z with
40 pos = z.pos - 1;
41 before = t;
42 after = Sequence.shift_right z.after h;
43 }
44
45let rec left_while f z =
46 if (not (is_far_left z)) && Option.(focus z |> map ~f |> value ~default:false)
47 then left z |> left_while f
48 else z
49
50let rec far_left z = if is_far_left z then z else z |> left |> far_left
51
52let right z =
53 match Sequence.next z.after with
54 | None -> z
55 | Some (h, t) ->
56 {
57 z with
58 pos = z.pos + 1;
59 before = Sequence.shift_right z.before h;
60 after = t;
61 }
62
63let rec right_while f z =
64 if
65 (not (is_far_right z)) && Option.(focus z |> map ~f |> value ~default:false)
66 then right z |> right_while f
67 else z
68
69let rec far_right z = if is_far_right z then z else z |> right |> far_right
70
71let goto n z =
72 let n = n - z.pos in
73 let step = if n < 0 then left else right in
74 Fn.apply_n_times ~n:(abs n) step z
75
76let pop_after z = { z with after = Sequence.drop_eagerly z.after 1 }
77let pop_before z = if is_far_left z then z else z |> left |> pop_after
78let pop = pop_before
79let push_after x z = { z with after = Sequence.shift_right z.after x }
80
81let push_before x z =
82 { z with pos = z.pos + 1; before = Sequence.shift_right z.before x }
83
84let push = push_before
85
86let split z =
87 ( { z with after = Sequence.empty },
88 { z with pos = 0; before = Sequence.empty } )
89
90let iter_before f z = Sequence.iter ~f z.before
91let iter_after f z = Sequence.iter ~f z.after
92
93let iter f z =
94 iter_before f z;
95 iter_after f z
96
97let for_all f z = Sequence.(for_all ~f z.before && for_all ~f z.after)
98let exists f z = Sequence.(exists ~f z.before || exists ~f z.after)
99let find_before f z = Sequence.find ~f z.before
100let find_after f z = Sequence.find ~f z.after
101let map_before f z = { z with before = Sequence.map ~f z.before }
102let map_after f z = { z with after = Sequence.map ~f z.after }
103
104let map_focus f z =
105 match Sequence.next z.after with
106 | None -> z
107 | Some (h, t) -> { z with after = Sequence.shift_right t (f h) }
108
109let map f z =
110 { z with before = Sequence.map ~f z.before; after = Sequence.map ~f z.after }
111
112let mapi_before f z = { z with before = Sequence.mapi ~f z.before }
113let mapi_after f z = { z with after = Sequence.mapi ~f z.after }
114
115let mapi f z =
116 {
117 z with
118 before = Sequence.mapi ~f z.before;
119 after = Sequence.mapi ~f z.after;
120 }
121
122let filter_before f z = { z with before = Sequence.filter ~f z.before }
123let filter_after f z = { z with after = Sequence.filter ~f z.after }
124let filter p z = z |> filter_before p |> filter_after p
125let context_before n z = { z with before = Sequence.take z.before n }
126let context_after n z = { z with after = Sequence.take z.after n }
127let context ~b ?(a = b) z = z |> context_before b |> context_after a
128let clear_history z = { z with popped = Sequence.empty }
129let of_seq s = { empty with after = s }
130let to_seq z = z |> far_left |> after
131let window ~from ~len z = goto from z |> context_after len |> after