-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: factor out signatures to avoid duplication
- Loading branch information
1 parent
b2375f9
commit 8f0175a
Showing
9 changed files
with
285 additions
and
399 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
module type S = | ||
sig | ||
(** {1 Types} *) | ||
|
||
(** The abstract type of modifiers, parametrized by the type of hook labels. See {!val:hook} for hook labels. | ||
Construct terms using builders in {!module:Language} and execute them using {!val:Modifier.S.modify}. *) | ||
type 'hook t | ||
|
||
(** Checking equality. *) | ||
val equal : ('hook -> 'hook -> bool) -> 'hook t -> 'hook t -> bool | ||
|
||
(** {1 Modifier Builders} *) | ||
|
||
(** {2 Basics} *) | ||
|
||
(** [any] keeps the content of the current tree. It is an error if the tree is empty (no name to match). | ||
To avoid the emptiness checking, use the identity modifier {!val:seq}[ []]. | ||
This is equivalent to {!val:only}[ []]. *) | ||
val any : 'hook t | ||
|
||
(** [only path] keeps the subtree rooted at [path]. It is an error if the subtree was empty. *) | ||
val only : Trie.path -> 'hook t | ||
|
||
(** [in_ path m] runs the modifier [m] on the subtree rooted at [path]. Bindings outside the subtree are kept intact. For example, [in_ ["x"] ]{!val:any} will keep [y] (if existing), while {!val:only}[ ["x"]] will drop [y]. *) | ||
val in_ : Trie.path -> 'hook t -> 'hook t | ||
|
||
(** {2 Negation} *) | ||
|
||
(** [none] drops everything. It is an error if the tree was already empty (nothing to drop). | ||
To avid the emptiness checking, use the empty modifier {!val:union}[ []]. *) | ||
val none : 'hook t | ||
|
||
(** [except p] drops the subtree rooted at [p]. It is an error if there was nothing in the subtree. This is equivalent to {!val:in_}[ p ]{!val:none}. *) | ||
val except : Trie.path -> 'hook t | ||
|
||
(** {2 Renaming} *) | ||
|
||
(** [renaming path path'] relocates the subtree rooted at [path] to [path']. The existing bindings at [path'] (if any) will be dropped. | ||
It is an error if the subtree was empty (nothing to move). *) | ||
val renaming : Trie.path -> Trie.path -> 'hook t | ||
|
||
(** {2 Sequencing} *) | ||
|
||
(** [seq [m0; m1; m2; ...; mn]] runs the modifiers [m0], [m1], [m2], ..., [mn] in order. | ||
In particular, [seq []] is the identity modifier. *) | ||
val seq : 'hook t list -> 'hook t | ||
|
||
(** {2 Union} *) | ||
|
||
(** [union [m0; m1; m2; ...; mn]] calculates the union of the results of individual modifiers [m0], [m1], [m2], ..., [mn]. | ||
In particular, [union []] is the empty modifier. | ||
The {!field:Modifier.shadow} effect will be performed to resolve name conflicts, | ||
with an intention for results of a modifier to shadow those of previous ones. *) | ||
val union : 'hook t list -> 'hook t | ||
|
||
(** {2 Custom Hooks} *) | ||
|
||
(** [hook h] applies the hook labelled [h] to the entire trie | ||
by performing the {!field:Modifier.hook} effect. *) | ||
val hook : 'hook -> 'hook t | ||
|
||
(** {2 Ugly Printing} *) | ||
|
||
(** [dump dump_hook m] dumps the internal representation of [m] for debugging, | ||
where [dump_hook] is the ugly printer for hook labels (see {!val:hook}). *) | ||
val dump : (Format.formatter -> 'hook -> unit) -> Format.formatter -> 'hook t -> unit [@@ocaml.toplevel_printer] | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,8 @@ | ||
(* See Yuujinchou.mli for documentation. *) | ||
open ModifierSigs | ||
|
||
module type Param = | ||
sig | ||
type data | ||
type tag | ||
type hook | ||
type context | ||
end | ||
module type Param = Param | ||
module type Handler = Handler | ||
module type S = ModifierSigs.S with module Language := Language | ||
|
||
module type Handler = | ||
sig | ||
module P : Param | ||
val not_found : P.context option -> Trie.bwd_path -> unit | ||
val shadow : P.context option -> Trie.bwd_path -> P.data * P.tag -> P.data * P.tag -> P.data * P.tag | ||
val hook : P.context option -> Trie.bwd_path -> P.hook -> (P.data, P.tag) Trie.t -> (P.data, P.tag) Trie.t | ||
end | ||
|
||
module type S = | ||
sig | ||
module P : Param | ||
open P | ||
|
||
val modify : ?context:context -> ?prefix:Trie.bwd_path -> hook Language.t -> (data, tag) Trie.t -> (data, tag) Trie.t | ||
|
||
module Handle (H : Handler with module P := P) : | ||
sig | ||
val run : (unit -> 'a) -> 'a | ||
val try_with : (unit -> 'a) -> 'a | ||
end | ||
|
||
module Perform : Handler with module P := P | ||
end | ||
|
||
module Make (P : Param) : S with module P = P | ||
module Make (P : Param) : S with module P := P |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
|
||
(** The parameters of an engine. *) | ||
module type Param = | ||
sig | ||
(** The type of data held by the bindings. The difference between data and tags is that the data will survive the efficient retagging. See {!val:Trie.retag}. *) | ||
type data | ||
|
||
(** The type of tags attached to the bindings. The difference between data and tags is that tags can be efficiently reset. See {!val:Trie.retag}. *) | ||
type tag | ||
|
||
(** The type of modifier hook labels. This is for extending the modifier language. *) | ||
type hook | ||
|
||
(** The type of contexts passed to each call of {!val:Modifier.S.modify} for the effect handler to distinguish different function calls. *) | ||
type context | ||
end | ||
|
||
module type Handler = | ||
sig | ||
module P : Param | ||
|
||
val not_found : P.context option -> Trie.bwd_path -> unit | ||
(** [not_found ctx prefix] is called when the engine expects at least one binding within the subtree at [prefix] but could not find any, where [ctx] is the context passed to {!val:S.modify}. Modifiers such as {!val:Language.any}, {!val:Language.only}, {!val:Language.none}, and a few other modifiers expect at least one matching binding. For example, the modifier {!val:Language.except}[ ["x"; "y"]] expects that there was already something under the subtree at [x.y]. If there were actually no names with the prefix [x.y], then the modifier will trigger this effect with [prefix] being [Emp #< "x" #< "y"]. *) | ||
|
||
val shadow : P.context option -> Trie.bwd_path -> P.data * P.tag -> P.data * P.tag -> P.data * P.tag | ||
(** [shadow ctx path x y] is called when item [y] is being assigned to [path] but [x] is already bound at [path], where [ctx] is the context passed to {!val:S.modify}. Modifiers such as {!val:Language.renaming} and {!val:Language.union} could lead to bindings having the same name, and when that happens, this function is called to resolve the conflicting bindings. To implement silent shadowing, one can simply return item [y]. One can also employ a more sophisticated strategy to implement type-directed disambiguation. *) | ||
|
||
|
||
val hook : P.context option -> Trie.bwd_path -> P.hook -> (P.data, P.tag) Trie.t -> (P.data, P.tag) Trie.t | ||
(** [hook prefix id input] is called when processing the modifiers created by {!val:Language.hook}, where [ctx] is the context passed to {!val:S.modify}. When the engine encounters the modifier {!val:Language.hook}[ id] while handling the subtree [input] at [prefix], it will call [hook prefix id input] and replace the existing subtree [input] with the return value. *) | ||
|
||
end | ||
|
||
module type S = | ||
sig | ||
module Language : LanguageSigs.S | ||
|
||
module P : Param | ||
open P | ||
(** @closed *) | ||
|
||
val modify : ?context:context -> ?prefix:Trie.bwd_path -> hook Language.t -> (data, tag) Trie.t -> (data, tag) Trie.t | ||
(** [modify modifier trie] runs the [modifier] on the [trie] and return the transformed trie. | ||
@param context The context sent to the effect handlers. If unspecified, effects come with {!constructor:None} as their context. | ||
@param prefix The prefix prepended to any path or prefix sent to the effect handlers. The default is the empty path ([Emp]). *) | ||
|
||
module Handle (H : Handler with module P := P) : | ||
sig | ||
val run : (unit -> 'a) -> 'a | ||
(** [run f h] initializes the engine and runs the thunk [f], using [h] to handle modifier effects. See {!type:handler}. *) | ||
|
||
val try_with : (unit -> 'a) -> 'a | ||
(** [try_with f h] runs the thunk [f], using [h] to handle the intercepted modifier effects. See {!type:handler}. | ||
Currently, [try_with] is an alias of {!val:run}, but [try_with] is intended to use within {!val:run} to intercept effects, | ||
while {!val:run} is intended to be at the outermost layer to handle effects. That is, the following is the expected program structure: | ||
{[ | ||
run @@ fun () -> | ||
(* code *) | ||
try_with f | ||
(* more code *) | ||
]} | ||
*) | ||
end | ||
|
||
module Perform : Handler with module P := P | ||
(** A handler that reperforms the effects. It can also be used to manually trigger the effects; | ||
for example, [Perform.not_found (Emp #< "a" #< "b")] will perform the [not_found] effect | ||
to be handled by the outer handler. *) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,8 @@ | ||
(* See Yuujinchou.mli for documentation. *) | ||
open ScopeSigs | ||
|
||
module type Param = Modifier.Param | ||
module type Handler = Modifier.Handler | ||
module type Param = Param | ||
module type Handler = Handler | ||
module type S = S with module Language := Language | ||
|
||
module type S = | ||
sig | ||
module P : Param | ||
open P | ||
|
||
exception Locked | ||
|
||
val resolve : Trie.path -> (data * tag) option | ||
val include_singleton : ?context_visible:context -> ?context_export:context -> Trie.path * (data * tag) -> unit | ||
val include_subtree : ?context_visible:context -> ?context_export:context -> Trie.path * (data, tag) Trie.t -> unit | ||
val import_subtree : ?context:context -> Trie.path * (data, tag) Trie.t -> unit | ||
val modify_visible : ?context:context -> hook Language.t -> unit | ||
val modify_export : ?context:context -> hook Language.t -> unit | ||
val modify : ?context:context -> ?prefix:Trie.bwd_path -> hook Language.t -> (data, tag) Trie.t -> (data, tag) Trie.t | ||
val export_visible : ?context:context -> hook Language.t -> unit | ||
val get_export : unit -> (data, tag) Trie.t | ||
|
||
val section : ?context_visible:context -> ?context_export:context -> Trie.path -> (unit -> 'a) -> 'a | ||
|
||
module Handle (H : Handler with module P := P) : | ||
sig | ||
val run : ?export_prefix:Trie.bwd_path -> ?init_visible:(data, tag) Trie.t -> (unit -> 'a) -> 'a | ||
val try_with : (unit -> 'a) -> 'a | ||
end | ||
|
||
module Perform : Handler with module P := P | ||
end | ||
|
||
module Make (P : Param) : S with module P = P | ||
module Make (P : Param) : S with module P := P |
Oops, something went wrong.