summaryrefslogtreecommitdiff
path: root/lib/zipper.ml
blob: 202f6d42bfd71f986ffbeb9cf80fec62b08460e5 (plain) (blame)
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
(* Module [Zipper]: functional zippers *)

open Base

type !+'a zipper = { pos : int; before : 'a Sequence.t; after : 'a Sequence.t }
type !+'a t = 'a zipper

let empty = { pos = 0; before = Sequence.empty; after = Sequence.empty }
let before z = z.before
let after z = z.after
let focus z = after z |> Sequence.next |> Option.map ~f:fst
let focus_or ~default z = Option.value ~default (focus z)
let is_far_left z = before z |> Sequence.is_empty
let is_far_right z = after z |> Sequence.is_empty
let is_empty z = is_far_left z && is_far_right z
let left_length z = z.pos
let right_length z = after z |> Sequence.length
let length z = left_length z + right_length z

let left z =
  match Sequence.next z.before with
  | None -> z
  | Some (h, t) ->
      { pos = z.pos - 1; before = t; after = Sequence.shift_right z.after h }

let rec left_while f z =
  if (not (is_far_left z)) && Option.(focus z |> map ~f |> value ~default:false)
  then left z |> left_while f
  else z

let rec far_left z = if is_far_left z then z else z |> left |> far_left

let right z =
  match Sequence.next z.after with
  | None -> z
  | Some (h, t) ->
      { pos = z.pos + 1; before = Sequence.shift_right z.before h; after = t }

let rec right_while f z =
  if
    (not (is_far_right z)) && Option.(focus z |> map ~f |> value ~default:false)
  then right z |> right_while f
  else z

let rec far_right z = if is_far_right z then z else z |> right |> far_right

let goto n z =
  let n = n - z.pos in
  let step = if n < 0 then left else right in
  Fn.apply_n_times ~n:(abs n) step z

let pop ?(n = 1) z = { z with after = Sequence.drop_eagerly z.after n }

let pop_before ?(n = 1) =
  let rec aux m z =
    if m < n && not (is_far_left z) then aux (m + 1) (left z) else pop ~n:m z
  in
  aux 0

let pop_after ?(n = 1) z =
  if right_length z < 2 then z else right z |> pop ~n |> left

let push x z = { z with after = Sequence.shift_right z.after x }
let push_after x z = right z |> push x |> left

let push_before x z =
  { z with pos = z.pos + 1; before = Sequence.shift_right z.before x }

let split z =
  ( { z with after = Sequence.empty },
    { z with pos = 0; before = Sequence.empty } )

let join z1 ~z2 =
  let z1 = far_right z1 and z2 = far_left z2 in
  { z1 with after = z2.after }

let iter_left f z = Sequence.iter ~f z.before
let iter_right f z = Sequence.iter ~f z.after

let iter f z =
  iter_left f z;
  iter_right f z

let for_all f z = Sequence.(for_all ~f z.before && for_all ~f z.after)
let exists f z = Sequence.(exists ~f z.before || exists ~f z.after)
let find_left f z = Sequence.find ~f z.before
let find_right f z = Sequence.find ~f z.after
let apply_focus f z = Option.map ~f (focus z)
let apply_focus_or ~default f z = Option.value ~default (apply_focus f z)
let map_left f z = { z with before = Sequence.map ~f z.before }
let map_right f z = { z with after = Sequence.map ~f z.after }

let map_focus f z =
  match Sequence.next z.after with
  | None -> z
  | Some (h, t) -> { z with after = Sequence.shift_right t (f h) }

let map_focus_or ~default f z =
  match Sequence.next z.after with
  | None -> { z with after = Sequence.singleton default }
  | Some (h, t) -> { z with after = Sequence.shift_right t (f h) }

let map f z =
  { z with before = Sequence.map ~f z.before; after = Sequence.map ~f z.after }

let mapi_left f z = { z with before = Sequence.mapi ~f z.before }
let mapi_right f z = { z with after = Sequence.mapi ~f z.after }

let mapi f z =
  {
    z with
    before = Sequence.mapi ~f z.before;
    after = Sequence.mapi ~f z.after;
  }

let filter_left f z = { z with before = Sequence.filter ~f z.before }
let filter_right f z = { z with after = Sequence.filter ~f z.after }
let filter p z = z |> filter_left p |> filter_right p
let context_left n z = { z with before = Sequence.take z.before n }
let context_right n z = { z with after = Sequence.take z.after n }
let context ~l ?(r = l) z = z |> context_left l |> context_right r
let swap_focus a = map_focus (Fn.const a)
let of_seq s = { empty with after = s }
let to_seq z = z |> far_left |> after
let window ~from ~len z = goto from z |> context_right len |> after