This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
523
unikernel/duniverse/dune_/otherlibs/dune-rpc/private/conv.ml
Normal file
523
unikernel/duniverse/dune_/otherlibs/dune-rpc/private/conv.ml
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
open Import
|
||||
|
||||
(* Mini clone of Dune_lang.Decoder. Main advantage is that it forbids all the
|
||||
crazy stuff and is automatically bi-directional *)
|
||||
|
||||
(* TODO error handling is complete crap for now.
|
||||
|
||||
This should be unified with [Dune_lang.Decoder] eventually. *)
|
||||
|
||||
type error =
|
||||
| Parse_error of
|
||||
{ message : string
|
||||
; payload : (string * Sexp.t) list
|
||||
}
|
||||
| Version_error of
|
||||
{ since : int * int
|
||||
; until : (int * int) option
|
||||
; message : string
|
||||
; payload : (string * Sexp.t) list
|
||||
}
|
||||
|
||||
let dyn_of_error =
|
||||
let open Dyn in
|
||||
function
|
||||
| Version_error { message; payload; until; since } ->
|
||||
record
|
||||
[ "message", string message
|
||||
; "payload", list (pair string Sexp.to_dyn) payload
|
||||
; "until", option (pair int int) until
|
||||
; "since", (pair int int) since
|
||||
]
|
||||
| Parse_error { message; payload } ->
|
||||
record
|
||||
[ "message", string message; "payload", list (pair string Sexp.to_dyn) payload ]
|
||||
;;
|
||||
|
||||
exception Of_sexp of error
|
||||
|
||||
let raise_of_sexp ?(payload = []) message =
|
||||
raise (Of_sexp (Parse_error { message; payload }))
|
||||
;;
|
||||
|
||||
let raise_version_error ?until ?(payload = []) ~since message =
|
||||
raise (Of_sexp (Version_error { since; until; message; payload }))
|
||||
;;
|
||||
|
||||
let () =
|
||||
Printexc.register_printer (function
|
||||
| Of_sexp (Parse_error { message; payload }) ->
|
||||
Some (message ^ " " ^ Sexp.to_string (Sexp.record payload))
|
||||
| _ -> None)
|
||||
;;
|
||||
|
||||
module Fields = struct
|
||||
type t = Unparsed of Sexp.t String.Map.t
|
||||
|
||||
let check_empty (Unparsed s) =
|
||||
if not (String.Map.is_empty s)
|
||||
then (
|
||||
let payload =
|
||||
[ ( "unparsed"
|
||||
, Sexp.List
|
||||
(String.Map.to_list s
|
||||
|> List.map ~f:(fun (k, v) -> Sexp.List [ Sexp.Atom k; v ])) )
|
||||
]
|
||||
in
|
||||
raise_of_sexp ~payload "unexpected fields")
|
||||
;;
|
||||
|
||||
let empty = Unparsed String.Map.empty
|
||||
|
||||
let merge (Unparsed a) (Unparsed b) =
|
||||
Unparsed
|
||||
(String.Map.union a b ~f:(fun _ _ _ ->
|
||||
(* field names are guaranteed to be different at construction time in
|
||||
[Both] *)
|
||||
assert false))
|
||||
;;
|
||||
|
||||
let of_field name sexp = Unparsed (String.Map.singleton name sexp)
|
||||
|
||||
let of_sexp (x : Sexp.t) =
|
||||
match x with
|
||||
| Atom _ -> raise_of_sexp "Unexpected atom"
|
||||
| List x ->
|
||||
(match
|
||||
String.Map.of_list_map x ~f:(function
|
||||
| List [ Atom s; v ] -> s, v
|
||||
| _ -> raise_of_sexp "unable to read field")
|
||||
with
|
||||
| Error (s, _, _) -> raise_of_sexp "duplicate fields" ~payload:[ "field", Atom s ]
|
||||
| Ok s -> Unparsed s)
|
||||
;;
|
||||
|
||||
let optional (Unparsed t) name =
|
||||
match String.Map.find t name with
|
||||
| None -> None, Unparsed t
|
||||
| Some v -> Some v, Unparsed (String.Map.remove t name)
|
||||
;;
|
||||
|
||||
let required t name =
|
||||
let r, t = optional t name in
|
||||
match r with
|
||||
| Some s -> s, t
|
||||
| None -> raise_of_sexp "missing required field" ~payload:[ "name", Atom name ]
|
||||
;;
|
||||
|
||||
let to_sexp (Unparsed t) : Sexp.t =
|
||||
List (String.Map.to_list t |> List.map ~f:(fun (k, v) -> Sexp.List [ Atom k; v ]))
|
||||
;;
|
||||
end
|
||||
|
||||
type values = Sexp.t
|
||||
type fields = Fields.t
|
||||
|
||||
type version =
|
||||
{ since : int * int
|
||||
; until : (int * int) option
|
||||
}
|
||||
|
||||
type ('a, 'kind) t =
|
||||
| String : (string, values) t
|
||||
| Int : (int, values) t
|
||||
| Float : (float, values) t
|
||||
| Unit : (unit, values) t
|
||||
| Char : (char, values) t
|
||||
| Iso : ('a, 'kind) t * ('a -> 'b) * ('b -> 'a) -> ('b, 'kind) t
|
||||
| Iso_result : ('a, 'kind) t * ('a -> ('b, exn) result) * ('b -> 'a) -> ('b, 'kind) t
|
||||
| Version : ('a, 'kind) t * version -> ('a, 'kind) t
|
||||
| Both :
|
||||
(* Invariant: field names must be different *)
|
||||
('a, fields) t
|
||||
* ('b, fields) t
|
||||
-> ('a * 'b, fields) t
|
||||
| Sexp : (Sexp.t, values) t
|
||||
| List : ('a, values) t -> ('a list, values) t
|
||||
| Field : string * 'a field -> ('a, fields) t
|
||||
| Enum : (string * 'a) list -> ('a, values) t
|
||||
| Sum : 'a econstr list * ('a -> case) -> ('a, values) t
|
||||
| Pair : ('a, values) t * ('b, values) t -> ('a * 'b, values) t
|
||||
| Triple : ('a, values) t * ('b, values) t * ('c, values) t -> ('a * 'b * 'c, values) t
|
||||
| Fdecl : int * ('a, 'k) t Fdecl.t -> ('a, 'k) t
|
||||
| Either :
|
||||
(* Invariant: field names must be different *)
|
||||
('a, fields) t
|
||||
* ('b, fields) t
|
||||
-> (('a, 'b) Either.t, fields) t
|
||||
| Record : ('a, fields) t -> ('a, values) t
|
||||
|
||||
and ('a, 'arg) constr =
|
||||
{ (* TODO allow constructors without an argument *)
|
||||
name : string
|
||||
; arg : ('arg, values) t
|
||||
; inj : 'arg -> 'a
|
||||
}
|
||||
|
||||
and 'a econstr = Constr : ('a, 'arg) constr -> 'a econstr
|
||||
and case = Case : 'arg * ('a, 'arg) constr -> case
|
||||
|
||||
and 'a field =
|
||||
| Required : ('a, values) t -> 'a field
|
||||
| Optional : ('a, values) t -> 'a option field
|
||||
|
||||
and 'k ret =
|
||||
| Values : values ret
|
||||
| Fields : Fields.t -> fields ret
|
||||
|
||||
type 'a value = ('a, values) t
|
||||
|
||||
let case a c = Case (a, c)
|
||||
let constr name arg inj = { name; arg; inj }
|
||||
let econstr c = Constr c
|
||||
let both x y = Both (x, y)
|
||||
let list x = List x
|
||||
let sum x y = Sum (x, y)
|
||||
let pair x y = Pair (x, y)
|
||||
let triple x y z = Triple (x, y, z)
|
||||
|
||||
let discard_values ((a, x) : _ * values ret) =
|
||||
match (x : values ret) with
|
||||
| Values -> a
|
||||
;;
|
||||
|
||||
let string = String
|
||||
let int = Int
|
||||
let float = Float
|
||||
let unit = Unit
|
||||
|
||||
let option x =
|
||||
let none = constr "None" unit (fun () -> None) in
|
||||
let some = constr "Some" x (fun x -> Some x) in
|
||||
sum
|
||||
[ econstr none; econstr some ]
|
||||
(function
|
||||
| None -> case () none
|
||||
| Some s -> case s some)
|
||||
;;
|
||||
|
||||
let char = Char
|
||||
|
||||
let sexp_for_digest t =
|
||||
let rec iter : type a b. int list -> (a, b) t -> Sexp.t =
|
||||
fun ids -> function
|
||||
| String -> Atom "String"
|
||||
| Int -> Atom "Int"
|
||||
| Float -> Atom "Float"
|
||||
| Unit -> Atom "Unit"
|
||||
| Char -> Atom "Char"
|
||||
| Iso (t, _, _) -> List [ Atom "Iso"; iter ids t ]
|
||||
| Iso_result (t, _, _) -> List [ Atom "Iso_result"; iter ids t ]
|
||||
| Version (t, { since = a, b; until }) ->
|
||||
let items : Sexp.t list =
|
||||
[ Atom "Version"
|
||||
; iter ids t
|
||||
; List [ Atom "since"; Atom (Int.to_string a); Atom (Int.to_string b) ]
|
||||
]
|
||||
in
|
||||
let items =
|
||||
match until with
|
||||
| None -> items
|
||||
| Some (a, b) ->
|
||||
items
|
||||
@ [ List [ Atom "until"; Atom (Int.to_string a); Atom (Int.to_string b) ] ]
|
||||
in
|
||||
List items
|
||||
| Both (a, b) -> List [ Atom "Both"; iter ids a; iter ids b ]
|
||||
| Sexp -> Atom "Sexp"
|
||||
| List t -> List [ Atom "List"; iter ids t ]
|
||||
| Field (name, field) ->
|
||||
let field : Sexp.t =
|
||||
match field with
|
||||
| Required t -> List [ Atom "Required"; iter ids t ]
|
||||
| Optional t -> List [ Atom "Optional"; iter ids t ]
|
||||
in
|
||||
List [ Atom "Field"; Atom name; field ]
|
||||
| Enum cases ->
|
||||
List (Atom "Enum" :: List.map cases ~f:(fun (name, _) : Sexp.t -> Atom name))
|
||||
| Sum (constrs, _) ->
|
||||
List
|
||||
(Atom "Sum"
|
||||
:: List.map constrs ~f:(fun (Constr { name; arg; inj = _ }) : Sexp.t ->
|
||||
List [ Atom name; iter ids arg ]))
|
||||
| Pair (a, b) -> List [ Atom "Pair"; iter ids a; iter ids b ]
|
||||
| Triple (a, b, c) -> List [ Atom "Triple"; iter ids a; iter ids b; iter ids c ]
|
||||
| Fdecl (id, fdecl) ->
|
||||
(* Although the id is represented as an auto-incrementing integer, we
|
||||
find De Bruijn indices to put in the digest so that equivalent
|
||||
structures produce the same digest. *)
|
||||
(match List.findi ids ~f:(Int.equal id) with
|
||||
| Some (_, index) -> List [ Atom "Recurse"; Atom (Int.to_string index) ]
|
||||
| None -> List [ Atom "Fixpoint"; iter (id :: ids) (Fdecl.get fdecl) ])
|
||||
| Either (a, b) -> List [ Atom "Either"; iter ids a; iter ids b ]
|
||||
| Record t -> List [ Atom "Record"; iter ids t ]
|
||||
in
|
||||
iter [] t
|
||||
;;
|
||||
|
||||
let to_sexp : 'a. ('a, values) t -> 'a -> Sexp.t =
|
||||
fun t a ->
|
||||
let rec loop : type a k. (a, k) t -> a -> k =
|
||||
fun t a ->
|
||||
match t with
|
||||
| String -> Atom a
|
||||
| Int -> Atom (Int.to_string a)
|
||||
| Float -> Atom (Float.to_string a)
|
||||
| Unit -> List []
|
||||
| Char -> Atom (String.make 1 a)
|
||||
| Sexp -> a
|
||||
| Version (t, _) -> loop t a
|
||||
| Fdecl (_, t) -> loop (Fdecl.get t) a
|
||||
| List t -> List (List.map a ~f:(loop t))
|
||||
| Pair (x, y) ->
|
||||
let a, b = a in
|
||||
List [ loop x a; loop y b ]
|
||||
| Triple (x, y, z) ->
|
||||
let a, b, c = a in
|
||||
List [ loop x a; loop y b; loop z c ]
|
||||
| Record r ->
|
||||
let fields = loop r a in
|
||||
Fields.to_sexp fields
|
||||
| Field (name, spec) ->
|
||||
(match spec with
|
||||
| Required t -> Fields.of_field name (loop t a)
|
||||
| Optional t ->
|
||||
(match a with
|
||||
| None -> Fields.empty
|
||||
| Some a -> Fields.of_field name (loop t a)))
|
||||
| Iso_result (t, _, from) -> loop t (from a)
|
||||
| Iso (t, _, from) -> loop t (from a)
|
||||
| Both (x, y) ->
|
||||
let x = loop x (fst a) in
|
||||
let y = loop y (snd a) in
|
||||
Fields.merge x y
|
||||
| Either (x, y) ->
|
||||
(match a with
|
||||
| Left a -> loop x a
|
||||
| Right a -> loop y a)
|
||||
| Sum (_, constr) ->
|
||||
let (Case (a, constr)) = constr a in
|
||||
let arg = loop constr.arg a in
|
||||
Sexp.List [ Atom constr.name; arg ]
|
||||
| Enum choices ->
|
||||
(match
|
||||
List.find_map choices ~f:(fun (s, a') ->
|
||||
if Poly.equal a a' then Some s else None)
|
||||
with
|
||||
| Some v -> Atom v
|
||||
| None ->
|
||||
let open Dyn in
|
||||
Code_error.raise
|
||||
"enum does not include this value"
|
||||
[ "valid values", list (fun (x, _) -> string x) choices ])
|
||||
in
|
||||
loop t a
|
||||
;;
|
||||
|
||||
let check_version ~version ~since ~until _ctx =
|
||||
if
|
||||
version < since
|
||||
||
|
||||
match until with
|
||||
| None -> false
|
||||
| Some until -> version > until
|
||||
then raise_version_error ?until ~since "invalid version"
|
||||
;;
|
||||
|
||||
let of_sexp : 'a. ('a, values) t -> version:int * int -> Sexp.t -> 'a =
|
||||
fun t ~version sexp ->
|
||||
let rec loop : type a k. (a, k) t -> k -> a * k ret =
|
||||
fun (type a k) (t : (a, k) t) (ctx : k) : (a * k ret) ->
|
||||
match t with
|
||||
| String ->
|
||||
(match ctx with
|
||||
| Atom s -> s, Values
|
||||
| List _ as list ->
|
||||
raise_of_sexp ~payload:[ "list", list ] "string: expected atom. received list")
|
||||
| Int ->
|
||||
(match ctx with
|
||||
| List _ as list ->
|
||||
raise_of_sexp ~payload:[ "list", list ] "int: expected atom. received list"
|
||||
| Atom s ->
|
||||
(match Int.of_string s with
|
||||
| None -> raise_of_sexp "unable to read int"
|
||||
| Some i -> i, Values))
|
||||
| Float ->
|
||||
(match ctx with
|
||||
| List _ as list ->
|
||||
raise_of_sexp ~payload:[ "list", list ] "float: expected atom. received list"
|
||||
| Atom s ->
|
||||
(match Float.of_string_opt s with
|
||||
| None -> raise_of_sexp "unable to read float"
|
||||
| Some i -> i, Values))
|
||||
| Unit ->
|
||||
(match ctx with
|
||||
| List [] -> (), Values
|
||||
| _ -> raise_of_sexp "expected empty list")
|
||||
| Char ->
|
||||
(match ctx with
|
||||
| Atom s ->
|
||||
if String.length s = 1
|
||||
then s.[0], Values
|
||||
else raise_of_sexp "expected only a single character"
|
||||
| List _ -> raise_of_sexp "expected a string of length 1")
|
||||
| Sexp -> ctx, Values
|
||||
| Version (t, { since; until }) ->
|
||||
check_version ~version ~since ~until ctx;
|
||||
loop t ctx
|
||||
| Fdecl (_, t) -> loop (Fdecl.get t) ctx
|
||||
| List t ->
|
||||
(match ctx with
|
||||
| List xs -> List.map xs ~f:(fun x -> discard_values (loop t x)), Values
|
||||
| Atom _ -> raise_of_sexp "expected list")
|
||||
| Pair (x, y) ->
|
||||
(match ctx with
|
||||
| List [ a; b ] ->
|
||||
let a, Values = loop x a in
|
||||
let b, Values = loop y b in
|
||||
(a, b), Values
|
||||
| _ -> raise_of_sexp "expected field entry")
|
||||
| Triple (x, y, z) ->
|
||||
(match ctx with
|
||||
| List [ a; b; c ] ->
|
||||
let a, Values = loop x a in
|
||||
let b, Values = loop y b in
|
||||
let c, Values = loop z c in
|
||||
(a, b, c), Values
|
||||
| _ -> raise_of_sexp "expected field entry")
|
||||
| Record (r : (a, fields) t) ->
|
||||
let (fields : Fields.t) = Fields.of_sexp ctx in
|
||||
let a, Fields f = loop r fields in
|
||||
Fields.check_empty f;
|
||||
a, Values
|
||||
| Field (name, spec) ->
|
||||
(match spec with
|
||||
| Required v ->
|
||||
let field, rest = Fields.required ctx name in
|
||||
let t, Values = loop v field in
|
||||
t, Fields rest
|
||||
| Optional v ->
|
||||
let field, rest = Fields.optional ctx name in
|
||||
let t =
|
||||
match field with
|
||||
| None -> None
|
||||
| Some f ->
|
||||
let a, Values = loop v f in
|
||||
Some a
|
||||
in
|
||||
t, Fields rest)
|
||||
| Either (x, y) ->
|
||||
(try
|
||||
(* TODO share computation somehow *)
|
||||
let a, x = loop x ctx in
|
||||
Left a, x
|
||||
with
|
||||
| Of_sexp _ ->
|
||||
let a, y = loop y ctx in
|
||||
Right a, y)
|
||||
| Iso (t, f, _) ->
|
||||
let a, k = loop t ctx in
|
||||
f a, k
|
||||
| Iso_result (t, f, _) ->
|
||||
let a, k = loop t ctx in
|
||||
(match f a with
|
||||
| Error exn -> raise exn
|
||||
| Ok a -> a, k)
|
||||
| Both (x, y) ->
|
||||
let a, Fields k = loop x ctx in
|
||||
let b, k = loop y k in
|
||||
(a, b), k
|
||||
| Sum (constrs, _) ->
|
||||
(match ctx with
|
||||
| List [ Atom head; args ] ->
|
||||
(match
|
||||
List.find_map constrs ~f:(fun (Constr c) ->
|
||||
if head = c.name
|
||||
then
|
||||
Some
|
||||
(let a, k = loop c.arg args in
|
||||
c.inj a, k)
|
||||
else None)
|
||||
with
|
||||
| None -> raise_of_sexp "invalid constructor name"
|
||||
| Some p -> p)
|
||||
| _ -> raise_of_sexp "expected constructor")
|
||||
| Enum choices ->
|
||||
(match ctx with
|
||||
| List _ -> raise_of_sexp "expected list"
|
||||
| Atom a ->
|
||||
(match List.assoc choices a with
|
||||
| None -> raise_of_sexp "unable to read enum"
|
||||
| Some s -> s, Values))
|
||||
in
|
||||
discard_values (loop t sexp)
|
||||
;;
|
||||
|
||||
let of_sexp conv ~version sexp =
|
||||
match of_sexp conv ~version sexp with
|
||||
| s -> Ok s
|
||||
| exception Of_sexp e -> Error e
|
||||
;;
|
||||
|
||||
let record r = Record r
|
||||
let either x y = Either (x, y)
|
||||
let iso a t f = Iso (a, t, f)
|
||||
let iso_result a t f = Iso_result (a, t, f)
|
||||
let version ?until t ~since = Version (t, { until; since })
|
||||
let field name spec = Field (name, spec)
|
||||
let enum choices = Enum choices
|
||||
|
||||
let three a b c =
|
||||
iso (Both (a, Both (b, c))) (fun (x, (y, z)) -> x, y, z) (fun (x, y, z) -> x, (y, z))
|
||||
;;
|
||||
|
||||
let four a b c d =
|
||||
iso
|
||||
(both (both a b) (both c d))
|
||||
(fun ((w, x), (y, z)) -> w, x, y, z)
|
||||
(fun (w, x, y, z) -> (w, x), (y, z))
|
||||
;;
|
||||
|
||||
let five a b c d e =
|
||||
iso
|
||||
(both (both a b) (three c d e))
|
||||
(fun ((a, b), (c, d, e)) -> a, b, c, d, e)
|
||||
(fun (a, b, c, d, e) -> (a, b), (c, d, e))
|
||||
;;
|
||||
|
||||
let six a b c d e f =
|
||||
iso
|
||||
(both (three a b c) (three d e f))
|
||||
(fun ((a, b, c), (d, e, f)) -> a, b, c, d, e, f)
|
||||
(fun (a, b, c, d, e, f) -> (a, b, c), (d, e, f))
|
||||
;;
|
||||
|
||||
let seven a b c d e f g =
|
||||
iso
|
||||
(both (three a b c) (four d e f g))
|
||||
(fun ((a, b, c), (d, e, f, g)) -> a, b, c, d, e, f, g)
|
||||
(fun (a, b, c, d, e, f, g) -> (a, b, c), (d, e, f, g))
|
||||
;;
|
||||
|
||||
let eight a b c d e f g h =
|
||||
iso
|
||||
(both (four a b c d) (four e f g h))
|
||||
(fun ((a, b, c, d), (e, f, g, h)) -> a, b, c, d, e, f, g, h)
|
||||
(fun (a, b, c, d, e, f, g, h) -> (a, b, c, d), (e, f, g, h))
|
||||
;;
|
||||
|
||||
let sexp = Sexp
|
||||
let required x = Required x
|
||||
let optional x = Optional x
|
||||
let fdecl_id = ref 0
|
||||
|
||||
let fixpoint f =
|
||||
let fdecl = Fdecl.create Dyn.opaque in
|
||||
let id = !fdecl_id in
|
||||
incr fdecl_id;
|
||||
let result = Fdecl (id, fdecl) in
|
||||
Fdecl.set fdecl (f result);
|
||||
result
|
||||
;;
|
||||
|
||||
let error e = raise (Of_sexp e)
|
||||
Loading…
Add table
Add a link
Reference in a new issue