diff options
Diffstat (limited to 'lib/zipper.ml')
-rw-r--r-- | lib/zipper.ml | 131 |
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 | |||
3 | open Base | ||
4 | |||
5 | type !+'a zipper = { | ||
6 | pos : int; | ||
7 | popped : 'a Sequence.t; | ||
8 | before : 'a Sequence.t; | ||
9 | after : 'a Sequence.t; | ||
10 | } | ||
11 | |||
12 | type !+'a t = 'a zipper | ||
13 | |||
14 | let empty = | ||
15 | { | ||
16 | pos = 0; | ||
17 | popped = Sequence.empty; | ||
18 | before = Sequence.empty; | ||
19 | after = Sequence.empty; | ||
20 | } | ||
21 | |||
22 | let before z = z.before | ||
23 | let after z = z.after | ||
24 | let focus z = after z |> Sequence.next |> Option.map ~f:fst | ||
25 | let focus_or z ~default = Option.value ~default (focus z) | ||
26 | let history z = z.popped | ||
27 | let is_far_left z = before z |> Sequence.is_empty | ||
28 | let is_far_right z = after z |> Sequence.is_empty | ||
29 | let is_empty z = is_far_left z && is_far_right z | ||
30 | let left_length z = z.pos | ||
31 | let right_length z = after z |> Sequence.length | ||
32 | let length z = left_length z + right_length z | ||
33 | |||
34 | let 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 | |||
45 | let 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 | |||
50 | let rec far_left z = if is_far_left z then z else z |> left |> far_left | ||
51 | |||
52 | let 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 | |||
63 | let 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 | |||
69 | let rec far_right z = if is_far_right z then z else z |> right |> far_right | ||
70 | |||
71 | let 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 | |||
76 | let pop_after z = { z with after = Sequence.drop_eagerly z.after 1 } | ||
77 | let pop_before z = if is_far_left z then z else z |> left |> pop_after | ||
78 | let pop = pop_before | ||
79 | let push_after x z = { z with after = Sequence.shift_right z.after x } | ||
80 | |||
81 | let push_before x z = | ||
82 | { z with pos = z.pos + 1; before = Sequence.shift_right z.before x } | ||
83 | |||
84 | let push = push_before | ||
85 | |||
86 | let split z = | ||
87 | ( { z with after = Sequence.empty }, | ||
88 | { z with pos = 0; before = Sequence.empty } ) | ||
89 | |||
90 | let iter_before f z = Sequence.iter ~f z.before | ||
91 | let iter_after f z = Sequence.iter ~f z.after | ||
92 | |||
93 | let iter f z = | ||
94 | iter_before f z; | ||
95 | iter_after f z | ||
96 | |||
97 | let for_all f z = Sequence.(for_all ~f z.before && for_all ~f z.after) | ||
98 | let exists f z = Sequence.(exists ~f z.before || exists ~f z.after) | ||
99 | let find_before f z = Sequence.find ~f z.before | ||
100 | let find_after f z = Sequence.find ~f z.after | ||
101 | let map_before f z = { z with before = Sequence.map ~f z.before } | ||
102 | let map_after f z = { z with after = Sequence.map ~f z.after } | ||
103 | |||
104 | let 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 | |||
109 | let map f z = | ||
110 | { z with before = Sequence.map ~f z.before; after = Sequence.map ~f z.after } | ||
111 | |||
112 | let mapi_before f z = { z with before = Sequence.mapi ~f z.before } | ||
113 | let mapi_after f z = { z with after = Sequence.mapi ~f z.after } | ||
114 | |||
115 | let mapi f z = | ||
116 | { | ||
117 | z with | ||
118 | before = Sequence.mapi ~f z.before; | ||
119 | after = Sequence.mapi ~f z.after; | ||
120 | } | ||
121 | |||
122 | let filter_before f z = { z with before = Sequence.filter ~f z.before } | ||
123 | let filter_after f z = { z with after = Sequence.filter ~f z.after } | ||
124 | let filter p z = z |> filter_before p |> filter_after p | ||
125 | let context_before n z = { z with before = Sequence.take z.before n } | ||
126 | let context_after n z = { z with after = Sequence.take z.after n } | ||
127 | let context ~b ?(a = b) z = z |> context_before b |> context_after a | ||
128 | let clear_history z = { z with popped = Sequence.empty } | ||
129 | let of_seq s = { empty with after = s } | ||
130 | let to_seq z = z |> far_left |> after | ||
131 | let window ~from ~len z = goto from z |> context_after len |> after | ||