123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296(* This file is part of 'travesty'.
Copyright (c) 2018, 2019 by Matt Windsor
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE. *)openBase(** [S_non_monadic] contains the core operations of a zipper, without any
parametrisation over a particular failure monad. *)moduletypeS_non_monadic=sig(** The opaque type of zippers. *)type'at[@@derivingsexp](** {3 Construction and destruction} *)valmake:left:'alist->right:'alist->'at(** [make ~left ~right] constructs a zipper with left list [left] and
right list [right].
These lists go directly into the zipper itself, so [left], if
non-empty, should be in the reverse order to how it should appear when
fully rewound. *)valof_list:'alist->'at(** [of_list xs] converts a list [xs] to a fully-rewound zipper.
It is equivalent to [make] with an empty [left]. *)valto_list:'at->'alist(** [to_list zipper] returns the list of _all_ items in the zipper,
including those in the left list.
All items appear in the same order that they would take in the right
list if the zipper was fully rewound. In other words, the left list
appears first (in reverse order), followed by the right list (in
forwards order).
To get only the items in the right list, use [right_list]; to get only
the items in the left list (reversed), use [left_list]. *)(** {3 Querying the left and right lists} *)valleft_list:'at->'alist(** [left_list zipper] gets the raw left list of the zipper: all of the
already-processed items in reverse order. *)valright_list:'at->'alist(** [right_list zipper] gets the right list of the zipper: all of the
not-yet-processed items in forwards order. *)valto_two_lists:'at->'alist*'alist(** [to_two_lists zipper] is [(left_list zipper, right_list zipper)]. *)valleft_length:'at->int(** [left_length zipper] gets the length of [zipper]'s left list. *)valright_length:'at->int(** [right_length zipper] gets the length of [zipper]'s right list. *)(** {3 Predicates} *)valis_at_start:'at->bool(** [is_at_start zipper] tests whether [zipper]'s left list is empty. *)valis_at_end:'at->bool(** [is_at_end zipper] tests whether [zipper]'s right list is empty. *)(** {3 Pushing} *)valpush:'at->value:'a->'at(** [push zipper ~value] pushes [value] into [zipper] at the cursor. The
current cursor becomes the second item in the right list, and so on. *)valpush_left:'at->value:'a->'at(** [push_left zipper ~value] pushes [value] into [zipper] just to the
left of the cursor. *)(** {3 Peeking and popping} *)valpeek_opt:?steps:int->'at->'aoption(** [peek_opt ?steps zipper] retrieves the cursor value without popping it
from the zipper. If the cursor is empty, [None] is returned.
If [steps] is given, it shifts the effective cursor [steps] places
forwards. *)valpop:'at->('a*'at)Or_error.t(** [pop zipper] returns an error if [zipper] has no cursor, or
[Ok (a, zipper')] where [a] is [zipper]'s cursor and [zipper'] is the
new zipper formed by removing [a]. *)valpop_opt:'at->('a*'at)option(** [pop_opt zipper] behaves as {{!pop} pop}, but returns [None] if
[zipper] has no cursor and [Some (a, zipper')] otherwise. *)valmap_head:'at->f:('a->'aoption)->'at(** [map_head zipper ~f] maps [f] across the cursor of [zipper], if it
exists, and replaces the cursor with the result (or drops it if [f]
returns [None]). *)(** {3 Movement} *)valstep:?steps:int->'at->'atOr_error.t(** [step ?steps zipper ~on_empty] takes one or more steps across
[zipper]. The number of steps defaults to 1 (forwards), but can be
given by [steps]; negative numbers step backwards through the zipper.
If the number of steps exceeds the bounds of the zipper, an error is
returned. *)end(** [S_monadic] contains the core operations of a zipper, parametrised over
a particular failure monad. *)moduletypeS_monadic=sigtype'atmoduleM:Monad.Svalpop_m:'at->on_empty:('at->('a*'at)M.t)->('a*'at)M.t(** [pop_m zipper ~on_empty] behaves like {{!pop} pop}, but executes a
custom monadic action [on_empty], instead of returning an error, when
the cursor is empty. *)valpeek_m:?steps:int->'at->on_empty:('at->'aM.t)->'aM.t(** [peek_m ?steps zipper ~on_empty] behaves like {{!peek_opt} peek_opt},
but executes a custom monadic action [on_empty], instead of returning
[None], when the cursor is empty. *)valstep_m:?steps:int->'at->on_empty:('at->'atM.t)->'atM.t(** [step_m ?steps zipper ~on_empty] behaves like {{!step} step}, but
executes a custom monadic action [on_empty], instead of returning an
error, when the cursor is empty. *)valmap_m_head:'at->f:('a->'aoptionM.t)->on_empty:('at->'atM.t)->'atM.t(** [map_m_head ?steps zipper ~on_empty] behaves like
{{!map_head} map_head}, but executes a custom monadic action
[on_empty], instead of leaving the zipper unchanged, when the cursor
is empty. *)end(** [S] contains [S_non_monadic]; a functor for generating [S_monadic] over
a custom monad; and specialisations of it over common monads. *)moduletypeS=sigincludeS_non_monadic(** [On_monad] provides various zipper operations parametrised by a monad. *)moduleOn_monad(M:Monad.S):S_monadicwithtype'at:='atandmoduleM:=M(** [On_ident] is [On_monad] specialised to the identity monad. *)moduleOn_ident:moduletypeofOn_monad(Monad.Ident)(** [On_error] is [On_monad] specialised to the error monad. *)moduleOn_error:moduletypeofOn_monad(Or_error)(** [On_option] is [On_monad] specialised to the option monad. *)moduleOn_option:moduletypeofOn_monad(Option)end(** The type of instructions returned by functions used with [fold_until_m]
and [fold_until]. *)type('mark,'a,'acc,'final)fold_outcome=[`Stopof'final(** Stop folding, immediately return *)|`Dropof'acc(** Drop the cursor and continue *)|`Swapof'a*'acc(** Replace cursor with a new value *)|`Markof'mark*'a*'acc(** Replace, and mark, the cursor *)](** [S_marked_non_monadic] extends [S_non_monadic] to add functions for
manipulating marks. *)moduletypeS_marked_non_monadic=sigincludeS_non_monadic(** The type of marks. *)typemarkvalmark:'at->mark:mark->'atOr_error.t(** [mark zipper ~mark] marks the cursor with [mark], and returns the
marked-up zipper.
If the cursor is empty, an error is returned. *)valrecall:'at->mark:mark->'atOr_error.t(** [recall zipper ~mark] rewinds [zipper] until the cursor is on an
element previously marked with [mark].
If [recall] runs out of left-list to rewind before finding [mark], an
error is returned. *)valfold_until:'at->f:('acc->'a->'at->(mark,'a,'acc,'final)fold_outcome)->init:'acc->finish:('acc->'at->'final)->'final(** [fold_until zipper ~f ~init ~finish] behaves conceptually like
{{!List.fold_until} List.fold_until}, but folds [f] through the
remaining elements of a zipper.
[f] receives the current accumulator, current cursor, and zipper with
cursor popped at each stage. It can't directly modify the zipper
mid-fold, but can influence the value of the final zipper provided to
the [finish] continuation by using the various legs of
{{!fold_outcome} fold_outcome}. *)valdelete_to_mark:'at->mark:mark->'atOr_error.t(** [delete_to_mark zipper ~mark] deletes every item in the left-list up
to, and including, the element previously marked with [mark].
If [delete_to_mark] runs out of left-list to rewind before finding
[mark], an error is returned. *)end(** [S_marked_monadic] extends [S_monadic] to add functions for manipulating
marks. *)moduletypeS_marked_monadic=sigincludeS_monadic(** The type of marks. *)typemarkvalmark_m:'at->mark:mark->on_empty:('at->'atM.t)->'atM.t(** [mark_m zipper ~mark ~on_empty] behaves like {{!mark} mark}, but
executes a custom monadic action [on_empty], instead of returning an
error, when the cursor is empty. *)valrecall_m:'at->mark:mark->on_empty:('at->'atM.t)->'atM.t(** [recall_m zipper ~mark ~on_empty] behaves like {{!recall} recall}, but
executes a custom monadic action [on_empty], instead of returning an
error, when the mark can't be found. *)valdelete_to_mark_m:'at->mark:mark->on_empty:('at->'atM.t)->'atM.t(** [delete_to_mark_m zipper ~mark ~on_empty] behaves like
{{!delete_to_mark} delete_to_mark}, but executes a custom monadic
action [on_empty], instead of returning an error, when the mark can't
be found. *)valfold_m_until:'at->f:('acc->'a->'at->(mark,'a,'acc,'final)fold_outcomeM.t)->init:'acc->finish:('acc->'at->'finalM.t)->'finalM.t(** [fold_m_until zipper ~f ~init ~finish] behaves like
{{!fold_until} fold_until}, except that [f] and [finish], and
therefore the function itself, return results inside a monad context. *)end(** [S_marked] extends [S] to add functions for manipulating marks. *)moduletypeS_marked=sigincludeS_marked_non_monadic(** [On_monad] provides various marked zipper operations parametrised by a
monad. *)moduleOn_monad(M:Monad.S):S_marked_monadicwithtype'at:='atandtypemark:=markandmoduleM:=M(** [On_ident] is [On_monad] specialised to the identity monad. *)moduleOn_ident:moduletypeofOn_monad(Monad.Ident)(** [On_error] is [On_monad] specialised to the error monad. *)moduleOn_error:moduletypeofOn_monad(Or_error)(** [On_option] is [On_monad] specialised to the option monad. *)moduleOn_option:moduletypeofOn_monad(Option)end(** [Basic_mark] is the interface that mark types must implement. *)moduletypeBasic_mark=sigtypet[@@derivingsexp,compare]end