summaryrefslogtreecommitdiff
path: root/lib/tipper.ml
diff options
context:
space:
mode:
authorFederico Igne <undyamon@disroot.org>2024-01-28 23:41:50 +0100
committerFederico Igne <undyamon@disroot.org>2024-01-28 23:41:50 +0100
commit633fb26ed21f7208517aa29dbaab9f0cf3bb2047 (patch)
tree2b6ae4402b61a03886f527355a9b480900598bdf /lib/tipper.ml
parent05e1cc51b2fb0824580925b55319377305105c44 (diff)
downloadsandy-633fb26ed21f7208517aa29dbaab9f0cf3bb2047.tar.gz
sandy-633fb26ed21f7208517aa29dbaab9f0cf3bb2047.zip
feat: add generic tree zipper data structure
Diffstat (limited to 'lib/tipper.ml')
-rw-r--r--lib/tipper.ml45
1 files changed, 45 insertions, 0 deletions
diff --git a/lib/tipper.ml b/lib/tipper.ml
new file mode 100644
index 0000000..bf09bb9
--- /dev/null
+++ b/lib/tipper.ml
@@ -0,0 +1,45 @@
1(* Module [Tipper]: functional tree zippers *)
2
3open Base
4
5type !+'a tipper = {
6 elt : 'a;
7 parent : 'a tipper option;
8 children : 'a tipper Zipper.t;
9}
10
11type !+'a t = 'a tipper
12
13let create a = { elt = a; parent = None; children = Zipper.empty }
14let focus t = t.elt
15let is_root t = Option.(t.parent |> is_none)
16let is_leaf t = Zipper.(t.children |> is_empty)
17let is_far_left t = Zipper.(t.children |> is_far_left)
18let is_far_right t = Zipper.(t.children |> is_far_right ~by_one:true)
19let left t = { t with children = Zipper.left t.children }
20let far_left t = { t with children = Zipper.far_left t.children }
21let right t = { t with children = Zipper.right ~by_one:true t.children }
22let far_right t = { t with children = Zipper.far_right ~by_one:true t.children }
23let set_focus a t = { t with elt = a }
24let push t1 t2 = { t2 with children = Zipper.push t1 t2.children }
25
26let pop t =
27 let s, cs = Zipper.pop t.children in
28 (Sequence.hd s, { t with children = cs })
29
30let up t = Option.(map ~f:(push t) t.parent |> value ~default:t)
31
32let rec up_while ~f t =
33 if (not (is_root t)) && f t then t |> up |> up_while ~f else t
34
35let root t = up_while ~f:(fun n -> not (is_root n)) t
36
37let down t =
38 match pop t with None, _ -> t | Some c, p -> { c with parent = Some p }
39
40let rec fold ~a ~f t =
41 let a = f a t in
42 if is_leaf t then
43 let t = up_while ~f:is_far_right t in
44 if is_root t && is_far_right t then a else fold ~a ~f (t |> right |> down)
45 else fold ~a ~f (t |> far_left |> down)