···11+module Param = struct
22+ type 'a t =
33+ { name : string option
44+ ; description : string list
55+ ; typedef : 'a Rpc.Types.def
66+ ; version : Rpc.Version.t option
77+ }
88+99+ type boxed = Boxed : 'a t -> boxed
1010+1111+ let mk ?name ?description ?version typedef =
1212+ let description =
1313+ match description with
1414+ | Some d -> d
1515+ | None -> typedef.Rpc.Types.description
1616+ in
1717+ { name; description; version; typedef }
1818+end
1919+2020+module Error = struct
2121+ type 'a t =
2222+ { def : 'a Rpc.Types.def
2323+ ; raiser : 'a -> exn
2424+ ; matcher : exn -> 'a option
2525+ }
2626+2727+ module type ERROR = sig
2828+ type t
2929+3030+ val t : t Rpc.Types.def
3131+ val internal_error_of : exn -> t option
3232+ end
3333+3434+ module Make (T : ERROR) = struct
3535+ exception Exn of T.t
3636+3737+ let () =
3838+ let printer = function
3939+ | Exn x ->
4040+ Some
4141+ (Printf.sprintf
4242+ "IDL Error: %s"
4343+ (Rpcmarshal.marshal T.t.Rpc.Types.ty x |> Rpc.to_string))
4444+ | _ -> None
4545+ in
4646+ Printexc.register_printer printer
4747+4848+4949+ let error =
5050+ { def = T.t
5151+ ; raiser =
5252+ (function
5353+ | e -> Exn e)
5454+ ; matcher =
5555+ (function
5656+ | Exn e -> Some e
5757+ | e -> T.internal_error_of e)
5858+ }
5959+ end
6060+end
6161+6262+module Interface = struct
6363+ type description =
6464+ { name : string
6565+ ; namespace : string option
6666+ ; description : string list
6767+ ; version : Rpc.Version.t
6868+ }
6969+end
7070+7171+module type RPC = sig
7272+ type implementation
7373+ type 'a res
7474+ type ('a, 'b) comp
7575+ type _ fn
7676+7777+ val implement : Interface.description -> implementation
7878+ val ( @-> ) : 'a Param.t -> 'b fn -> ('a -> 'b) fn
7979+ val returning : 'a Param.t -> 'b Error.t -> ('a, 'b) comp fn
8080+ val declare : string -> string list -> 'a fn -> 'a res
8181+ val declare_notification : string -> string list -> 'a fn -> 'a res
8282+end
8383+8484+module type MONAD = sig
8585+ type 'a t
8686+8787+ val return : 'a -> 'a t
8888+ val bind : 'a t -> ('a -> 'b t) -> 'b t
8989+ val fail : exn -> 'a t
9090+end
9191+9292+exception MarshalError of string
9393+exception UnknownMethod of string
9494+exception UnboundImplementation of string list
9595+exception NoDescription
9696+9797+let get_wire_name description name =
9898+ match description with
9999+ | None -> name
100100+ | Some d ->
101101+ (match d.Interface.namespace with
102102+ | Some ns -> Printf.sprintf "%s.%s" ns name
103103+ | None -> name)
104104+105105+106106+let get_arg call has_named name is_opt =
107107+ match has_named, name, call.Rpc.params with
108108+ | true, Some n, Rpc.Dict named :: unnamed ->
109109+ (match List.partition (fun (x, _) -> x = n) named with
110110+ | (_, arg) :: dups, others when is_opt ->
111111+ Ok
112112+ (Rpc.Enum [ arg ], { call with Rpc.params = Rpc.Dict (dups @ others) :: unnamed })
113113+ | (_, arg) :: dups, others ->
114114+ Ok (arg, { call with Rpc.params = Rpc.Dict (dups @ others) :: unnamed })
115115+ | [], _others when is_opt -> Ok (Rpc.Enum [], call)
116116+ | _, _ -> Error (`Msg (Printf.sprintf "Expecting named argument '%s'" n)))
117117+ | true, None, Rpc.Dict named :: unnamed ->
118118+ (match unnamed with
119119+ | head :: tail -> Ok (head, { call with Rpc.params = Rpc.Dict named :: tail })
120120+ | _ -> Error (`Msg "Incorrect number of arguments"))
121121+ | true, _, _ ->
122122+ Error
123123+ (`Msg
124124+ "Marshalling error: Expecting dict as first argument when named parameters exist")
125125+ | false, None, head :: tail -> Ok (head, { call with Rpc.params = tail })
126126+ | false, None, [] -> Error (`Msg "Incorrect number of arguments")
127127+ | false, Some _, _ -> failwith "Can't happen by construction"
128128+129129+130130+module Make (M : MONAD) = struct
131131+ module type RPCTRANSFORMER = sig
132132+ type 'a box
133133+ type ('a, 'b) resultb = ('a, 'b) result box
134134+ type rpcfn = Rpc.call -> Rpc.response M.t
135135+136136+ val lift : ('a -> 'b M.t) -> 'a -> 'b box
137137+ val bind : 'a box -> ('a -> 'b M.t) -> 'b box
138138+ val return : 'a -> 'a box
139139+ val get : 'a box -> 'a M.t
140140+ val ( !@ ) : 'a box -> 'a M.t
141141+ val put : 'a M.t -> 'a box
142142+ val ( ~@ ) : 'a M.t -> 'a box
143143+ end
144144+145145+ module T = struct
146146+ type 'a box = { box : 'a M.t }
147147+ type ('a, 'b) resultb = ('a, 'b) result box
148148+ type rpcfn = Rpc.call -> Rpc.response M.t
149149+150150+ let lift f x = { box = f x }
151151+ let bind { box = x } f = { box = M.bind x f }
152152+ let return x = { box = M.return x }
153153+ let get { box = x } = x
154154+ let ( !@ ) = get
155155+ let put x = { box = x }
156156+ let ( ~@ ) = put
157157+ end
158158+159159+ type client_implementation = unit
160160+ type server_implementation = (string, T.rpcfn option) Hashtbl.t
161161+162162+ module ErrM : sig
163163+ val return : 'a -> ('a, 'b) T.resultb
164164+ val return_err : 'b -> ('a, 'b) T.resultb
165165+166166+ val checked_bind
167167+ : ('a, 'b) T.resultb
168168+ -> ('a -> ('c, 'd) T.resultb)
169169+ -> ('b -> ('c, 'd) T.resultb)
170170+ -> ('c, 'd) T.resultb
171171+172172+ val bind : ('a, 'b) T.resultb -> ('a -> ('c, 'b) T.resultb) -> ('c, 'b) T.resultb
173173+ val ( >>= ) : ('a, 'b) T.resultb -> ('a -> ('c, 'b) T.resultb) -> ('c, 'b) T.resultb
174174+ end = struct
175175+ let return x = T.put (M.return (Ok x))
176176+ let return_err e = T.put (M.return (Error e))
177177+178178+ let checked_bind x f f1 =
179179+ T.bind
180180+ x
181181+ T.(
182182+ function
183183+ | Ok x -> !@(f x)
184184+ | Error x -> !@(f1 x))
185185+186186+187187+ let bind x f = checked_bind x f return_err
188188+ let ( >>= ) x f = bind x f
189189+ end
190190+191191+ module GenClient () = struct
192192+ type implementation = client_implementation
193193+ type 'a res = T.rpcfn -> 'a
194194+ type ('a, 'b) comp = ('a, 'b) T.resultb
195195+196196+ type _ fn =
197197+ | Function : 'a Param.t * 'b fn -> ('a -> 'b) fn
198198+ | Returning : ('a Param.t * 'b Error.t) -> ('a, 'b) comp fn
199199+200200+ let description = ref None
201201+ let strict = ref false
202202+ let make_strict () = strict := true
203203+204204+ let implement x =
205205+ description := Some x;
206206+ ()
207207+208208+209209+ let returning a err = Returning (a, err)
210210+ let ( @-> ) t f = Function (t, f)
211211+212212+ let declare_ is_notification name _ ty (rpc : T.rpcfn) =
213213+ let rec inner : type b. (string * Rpc.t) list option * Rpc.t list -> b fn -> b =
214214+ fun (named, unnamed) -> function
215215+ | Function (t, f) ->
216216+ let cur_named =
217217+ match named with
218218+ | Some l -> l
219219+ | None -> []
220220+ in
221221+ fun v ->
222222+ (match t.Param.name with
223223+ | Some n ->
224224+ (match t.Param.typedef.Rpc.Types.ty, v with
225225+ | Rpc.Types.Option ty, Some v' ->
226226+ let marshalled = Rpcmarshal.marshal ty v' in
227227+ inner (Some ((n, marshalled) :: cur_named), unnamed) f
228228+ | Rpc.Types.Option _ty, None -> inner (Some cur_named, unnamed) f
229229+ | ty, v ->
230230+ let marshalled = Rpcmarshal.marshal ty v in
231231+ inner (Some ((n, marshalled) :: cur_named), unnamed) f)
232232+ | None ->
233233+ let marshalled = Rpcmarshal.marshal t.Param.typedef.Rpc.Types.ty v in
234234+ inner (named, marshalled :: unnamed) f)
235235+ | Returning (t, e) ->
236236+ let wire_name = get_wire_name !description name in
237237+ let args =
238238+ match named with
239239+ | None -> List.rev unnamed
240240+ | Some l -> Rpc.Dict l :: List.rev unnamed
241241+ in
242242+ let call' = Rpc.call wire_name args in
243243+ let call = { call' with is_notification } in
244244+ let rpc = T.put (rpc call) in
245245+ let res =
246246+ T.bind rpc (fun r ->
247247+ if r.Rpc.success
248248+ then (
249249+ match
250250+ Rpcmarshal.unmarshal t.Param.typedef.Rpc.Types.ty r.Rpc.contents
251251+ with
252252+ | Ok x -> M.return (Ok x)
253253+ | Error (`Msg x) -> M.fail (MarshalError x))
254254+ else (
255255+ match Rpcmarshal.unmarshal e.Error.def.Rpc.Types.ty r.Rpc.contents with
256256+ | Ok x ->
257257+ if !strict then M.fail (e.Error.raiser x) else M.return (Error x)
258258+ | Error (`Msg x) -> M.fail (MarshalError x)))
259259+ in
260260+ res
261261+ in
262262+ inner (None, []) ty
263263+264264+265265+ let declare_notification name a ty (rpc : T.rpcfn) = declare_ true name a ty rpc
266266+ let declare name a ty (rpc : T.rpcfn) = declare_ false name a ty rpc
267267+ end
268268+269269+ let server hashtbl =
270270+ let impl = Hashtbl.create (Hashtbl.length hashtbl) in
271271+ let unbound_impls =
272272+ Hashtbl.fold
273273+ (fun key fn acc ->
274274+ match fn with
275275+ | None -> key :: acc
276276+ | Some fn ->
277277+ Hashtbl.add impl key fn;
278278+ acc)
279279+ hashtbl
280280+ []
281281+ in
282282+ if unbound_impls <> [] then raise (UnboundImplementation unbound_impls);
283283+ fun call ->
284284+ let fn =
285285+ try Hashtbl.find impl call.Rpc.name with
286286+ | Not_found -> raise (UnknownMethod call.Rpc.name)
287287+ in
288288+ fn call
289289+290290+291291+ let combine hashtbls =
292292+ let result = Hashtbl.create 16 in
293293+ List.iter (Hashtbl.iter (fun k v -> Hashtbl.add result k v)) hashtbls;
294294+ result
295295+296296+297297+ module GenServer () = struct
298298+ type implementation = server_implementation
299299+ type ('a, 'b) comp = ('a, 'b) T.resultb
300300+ type 'a res = 'a -> unit
301301+302302+ type _ fn =
303303+ | Function : 'a Param.t * 'b fn -> ('a -> 'b) fn
304304+ | Returning : ('a Param.t * 'b Error.t) -> ('a, 'b) comp fn
305305+306306+ let funcs = Hashtbl.create 20
307307+ let description = ref None
308308+309309+ let implement x =
310310+ description := Some x;
311311+ funcs
312312+313313+314314+ let returning a b = Returning (a, b)
315315+ let ( @-> ) t f = Function (t, f)
316316+317317+ let rec has_named_args : type a. a fn -> bool = function
318318+ | Function (t, f) ->
319319+ (match t.Param.name with
320320+ | Some _ -> true
321321+ | None -> has_named_args f)
322322+ | Returning (_, _) -> false
323323+324324+325325+ let declare_ : bool -> string -> string list -> 'a fn -> 'a res =
326326+ fun is_notification name _ ty ->
327327+ let ( >>= ) = M.bind in
328328+ (* We do not know the wire name yet as the description may still be unset *)
329329+ Hashtbl.add funcs name None;
330330+ fun impl ->
331331+ ((* Sanity check: ensure the description has been set before we declare
332332+ any RPCs. Here we raise an exception immediately and let everything fail. *)
333333+ match !description with
334334+ | Some _ -> ()
335335+ | None -> raise NoDescription);
336336+ let rpcfn =
337337+ let has_named = has_named_args ty in
338338+ let rec inner : type a. a fn -> a -> T.rpcfn =
339339+ fun f impl call ->
340340+ match f with
341341+ | Function (t, f) ->
342342+ let is_opt =
343343+ match t.Param.typedef.Rpc.Types.ty with
344344+ | Rpc.Types.Option _ -> true
345345+ | _ -> false
346346+ in
347347+ (match get_arg call has_named t.Param.name is_opt with
348348+ | Ok (x, y) -> M.return (x, y)
349349+ | Error (`Msg m) -> M.fail (MarshalError m))
350350+ >>= fun (arg_rpc, call') ->
351351+ let z = Rpcmarshal.unmarshal t.Param.typedef.Rpc.Types.ty arg_rpc in
352352+ (match z with
353353+ | Ok arg -> inner f (impl arg) call'
354354+ | Error (`Msg m) -> M.fail (MarshalError m))
355355+ | Returning (t, e) ->
356356+ T.bind impl (function
357357+ | Ok x ->
358358+ let res =
359359+ Rpc.success (Rpcmarshal.marshal t.Param.typedef.Rpc.Types.ty x)
360360+ in
361361+ M.return { res with is_notification }
362362+ | Error y ->
363363+ let res =
364364+ Rpc.failure (Rpcmarshal.marshal e.Error.def.Rpc.Types.ty y)
365365+ in
366366+ M.return { res with is_notification })
367367+ |> T.get
368368+ in
369369+ inner ty impl
370370+ in
371371+ Hashtbl.remove funcs name;
372372+ (* The wire name might be different from the name *)
373373+ let wire_name = get_wire_name !description name in
374374+ Hashtbl.add funcs wire_name (Some rpcfn)
375375+376376+377377+ let declare_notification name a ty = declare_ true name a ty
378378+ let declare name a ty = declare_ false name a ty
379379+ end
380380+end
381381+382382+module ExnM = struct
383383+ type 'a t =
384384+ | V of 'a
385385+ | E of exn
386386+387387+ let return x = V x
388388+389389+ let lift f x =
390390+ match f x with
391391+ | y -> V y
392392+ | exception e -> E e
393393+394394+395395+ let bind x (f : 'a -> 'b t) : 'b t =
396396+ match x with
397397+ | V x -> f x
398398+ | E e -> E e
399399+400400+401401+ let ( >>= ) = bind
402402+ let fail e = E e
403403+404404+ let run = function
405405+ | V x -> x
406406+ | E e -> raise e
407407+end
408408+409409+module IdM = struct
410410+ type 'a t = T of 'a
411411+412412+ let return x = T x
413413+ let lift f x = T (f x)
414414+ let bind (T x) f = f x
415415+ let ( >>= ) = bind
416416+ let fail e = raise e
417417+ let run (T x) = x
418418+end
419419+420420+(* A default error variant as an example. In real code, this is more easily expressed by using the PPX:
421421+ type default_error = InternalError of string [@@deriving rpcty]
422422+*)
423423+module DefaultError = struct
424424+ type t = InternalError of string
425425+426426+ exception InternalErrorExn of string
427427+428428+ let internalerror : (string, t) Rpc.Types.tag =
429429+ let open Rpc.Types in
430430+ { tname = "InternalError"
431431+ ; tdescription = [ "Internal Error" ]
432432+ ; tversion = Some (1, 0, 0)
433433+ ; tcontents = Basic String
434434+ ; tpreview =
435435+ (function
436436+ | InternalError s -> Some s)
437437+ ; treview = (fun s -> InternalError s)
438438+ }
439439+440440+441441+ (* And then we can create the 'variant' type *)
442442+ let t : t Rpc.Types.variant =
443443+ let open Rpc.Types in
444444+ { vname = "t"
445445+ ; variants = [ BoxedTag internalerror ]
446446+ ; vversion = Some (1, 0, 0)
447447+ ; vdefault = Some (InternalError "Unknown error tag!")
448448+ ; vconstructor =
449449+ (fun s t ->
450450+ match s with
451451+ | "InternalError" -> begin
452452+ match t.tget (Basic String) with
453453+ | Ok s -> Ok (internalerror.treview s)
454454+ | Error y -> Error y
455455+ end
456456+ | s -> Error (`Msg (Printf.sprintf "Unknown tag '%s'" s)))
457457+ }
458458+459459+460460+ let def =
461461+ let open Rpc.Types in
462462+ { name = "default_error"
463463+ ; description = [ "Errors declared as part of the interface" ]
464464+ ; ty = Variant t
465465+ }
466466+467467+468468+ let err =
469469+ let open Error in
470470+ { def
471471+ ; raiser =
472472+ (function
473473+ | InternalError s -> raise (InternalErrorExn s))
474474+ ; matcher =
475475+ (function
476476+ | InternalErrorExn s -> Some (InternalError s)
477477+ | _ -> None)
478478+ }
479479+end
480480+481481+module Exn = struct
482482+ type rpcfn = Rpc.call -> Rpc.response
483483+ type client_implementation = unit
484484+ type server_implementation = (string, rpcfn option) Hashtbl.t
485485+486486+ module GenClient (R : sig
487487+ val rpc : rpcfn
488488+ end) =
489489+ struct
490490+ type implementation = client_implementation
491491+ type ('a, 'b) comp = 'a
492492+ type 'a res = 'a
493493+494494+ type _ fn =
495495+ | Function : 'a Param.t * 'b fn -> ('a -> 'b) fn
496496+ | Returning : ('a Param.t * 'b Error.t) -> ('a, _) comp fn
497497+498498+ let description = ref None
499499+500500+ let implement x =
501501+ description := Some x;
502502+ ()
503503+504504+505505+ let returning a err = Returning (a, err)
506506+ let ( @-> ) t f = Function (t, f)
507507+508508+ let declare_ is_notification name _ ty =
509509+ let rec inner : type b. (string * Rpc.t) list option * Rpc.t list -> b fn -> b =
510510+ fun (named, unnamed) -> function
511511+ | Function (t, f) ->
512512+ let cur_named =
513513+ match named with
514514+ | Some l -> l
515515+ | None -> []
516516+ in
517517+ fun v ->
518518+ (match t.Param.name with
519519+ | Some n ->
520520+ (match t.Param.typedef.Rpc.Types.ty, v with
521521+ | Rpc.Types.Option ty, Some v' ->
522522+ let marshalled = Rpcmarshal.marshal ty v' in
523523+ inner (Some ((n, marshalled) :: cur_named), unnamed) f
524524+ | Rpc.Types.Option _ty, None -> inner (Some cur_named, unnamed) f
525525+ | ty, v ->
526526+ let marshalled = Rpcmarshal.marshal ty v in
527527+ inner (Some ((n, marshalled) :: cur_named), unnamed) f)
528528+ | None ->
529529+ let marshalled = Rpcmarshal.marshal t.Param.typedef.Rpc.Types.ty v in
530530+ inner (named, marshalled :: unnamed) f)
531531+ | Returning (t, e) ->
532532+ let wire_name = get_wire_name !description name in
533533+ let args =
534534+ match named with
535535+ | None -> List.rev unnamed
536536+ | Some l -> Rpc.Dict l :: List.rev unnamed
537537+ in
538538+ let call' = Rpc.call wire_name args in
539539+ let call = { call' with is_notification } in
540540+ let r = R.rpc call in
541541+ if r.Rpc.success
542542+ then (
543543+ match Rpcmarshal.unmarshal t.Param.typedef.Rpc.Types.ty r.Rpc.contents with
544544+ | Ok x -> x
545545+ | Error (`Msg x) -> raise (MarshalError x))
546546+ else (
547547+ match Rpcmarshal.unmarshal e.Error.def.Rpc.Types.ty r.Rpc.contents with
548548+ | Ok x -> raise (e.Error.raiser x)
549549+ | Error (`Msg x) -> raise (MarshalError x))
550550+ in
551551+ inner (None, []) ty
552552+553553+554554+ let declare name a ty = declare_ false name a ty
555555+ let declare_notification name a ty = declare_ true name a ty
556556+ end
557557+558558+ let server hashtbl =
559559+ let impl = Hashtbl.create (Hashtbl.length hashtbl) in
560560+ let unbound_impls =
561561+ Hashtbl.fold
562562+ (fun key fn acc ->
563563+ match fn with
564564+ | None -> key :: acc
565565+ | Some fn ->
566566+ Hashtbl.add impl key fn;
567567+ acc)
568568+ hashtbl
569569+ []
570570+ in
571571+ if unbound_impls <> [] then raise (UnboundImplementation unbound_impls);
572572+ fun call ->
573573+ let fn =
574574+ try Hashtbl.find impl call.Rpc.name with
575575+ | Not_found -> raise (UnknownMethod call.Rpc.name)
576576+ in
577577+ fn call
578578+579579+580580+ let combine hashtbls =
581581+ let result = Hashtbl.create 16 in
582582+ List.iter (Hashtbl.iter (fun k v -> Hashtbl.add result k v)) hashtbls;
583583+ result
584584+585585+586586+ module GenServer () = struct
587587+ type implementation = server_implementation
588588+ type ('a, 'b) comp = 'a
589589+ type 'a res = 'a -> unit
590590+591591+ type _ fn =
592592+ | Function : 'a Param.t * 'b fn -> ('a -> 'b) fn
593593+ | Returning : ('a Param.t * 'b Error.t) -> ('a, _) comp fn
594594+595595+ let funcs = Hashtbl.create 20
596596+ let description = ref None
597597+598598+ let implement x =
599599+ description := Some x;
600600+ funcs
601601+602602+603603+ let returning a b = Returning (a, b)
604604+ let ( @-> ) t f = Function (t, f)
605605+606606+ type boxed_error = BoxedError : 'a Error.t -> boxed_error
607607+608608+ let rec get_error_ty : type a. a fn -> boxed_error = function
609609+ | Function (_, f) -> get_error_ty f
610610+ | Returning (_, e) -> BoxedError e
611611+612612+613613+ let rec has_named_args : type a. a fn -> bool = function
614614+ | Function (t, f) ->
615615+ (match t.Param.name with
616616+ | Some _ -> true
617617+ | None -> has_named_args f)
618618+ | Returning (_, _) -> false
619619+620620+621621+ let declare_ : bool -> string -> string list -> 'a fn -> 'a res =
622622+ fun is_notification name _ ty ->
623623+ (* We do not know the wire name yet as the description may still be unset *)
624624+ Hashtbl.add funcs name None;
625625+ fun impl ->
626626+ ((* Sanity check: ensure the description has been set before we declare
627627+ any RPCs *)
628628+ match !description with
629629+ | Some _ -> ()
630630+ | None -> raise NoDescription);
631631+ let rpcfn =
632632+ let has_named = has_named_args ty in
633633+ let rec inner : type a. a fn -> a -> Rpc.call -> Rpc.response =
634634+ fun f impl call ->
635635+ try
636636+ match f with
637637+ | Function (t, f) ->
638638+ let is_opt =
639639+ match t.Param.typedef.Rpc.Types.ty with
640640+ | Rpc.Types.Option _ -> true
641641+ | _ -> false
642642+ in
643643+ let arg_rpc, call' =
644644+ match get_arg call has_named t.Param.name is_opt with
645645+ | Ok (x, y) -> x, y
646646+ | Error (`Msg m) -> raise (MarshalError m)
647647+ in
648648+ let z = Rpcmarshal.unmarshal t.Param.typedef.Rpc.Types.ty arg_rpc in
649649+ let arg =
650650+ match z with
651651+ | Ok arg -> arg
652652+ | Error (`Msg m) -> raise (MarshalError m)
653653+ in
654654+ inner f (impl arg) call'
655655+ | Returning (t, _) ->
656656+ let call =
657657+ Rpc.success (Rpcmarshal.marshal t.Param.typedef.Rpc.Types.ty impl)
658658+ in
659659+ { call with is_notification }
660660+ with
661661+ | e ->
662662+ let (BoxedError error_ty) = get_error_ty f in
663663+ (match error_ty.Error.matcher e with
664664+ | Some y ->
665665+ Rpc.failure (Rpcmarshal.marshal error_ty.Error.def.Rpc.Types.ty y)
666666+ | None -> raise e)
667667+ in
668668+ inner ty impl
669669+ in
670670+ Hashtbl.remove funcs name;
671671+ (* The wire name might be different from the name *)
672672+ let wire_name = get_wire_name !description name in
673673+ Hashtbl.add funcs wire_name (Some rpcfn)
674674+675675+676676+ let declare name a ty = declare_ true name a ty
677677+ let declare_notification name a ty = declare_ false name a ty
678678+ end
679679+end
+375
idl/rpc.ml
···11+(*
22+ * Copyright (c) 2006-2009 Citrix Systems Inc.
33+ * Copyright (c) 2006-2014 Thomas Gazagnaire <thomas@gazagnaire.org>
44+ *
55+ * Permission to use, copy, modify, and distribute this software for any
66+ * purpose with or without fee is hereby granted, provided that the above
77+ * copyright notice and this permission notice appear in all copies.
88+ *
99+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+ *)
1717+1818+let debug = ref false
1919+let set_debug x = debug := x
2020+let get_debug () = !debug
2121+2222+type msg = [ `Msg of string ]
2323+2424+type t =
2525+ | Int of int64
2626+ | Int32 of int32
2727+ | Bool of bool
2828+ | Float of float
2929+ | String of string
3030+ | DateTime of string
3131+ | Enum of t list
3232+ | Dict of (string * t) list
3333+ | Base64 of string
3434+ | Null
3535+3636+module Version = struct
3737+ type t = int * int * int
3838+3939+ let compare (x, y, z) (x', y', z') =
4040+ let cmp a b fn () =
4141+ let c = compare a b in
4242+ if c <> 0 then c else fn ()
4343+ in
4444+ cmp x x' (cmp y y' (cmp z z' (fun () -> 0))) ()
4545+end
4646+4747+module Types = struct
4848+ type _ basic =
4949+ | Int : int basic
5050+ | Int32 : int32 basic
5151+ | Int64 : int64 basic
5252+ | Bool : bool basic
5353+ | Float : float basic
5454+ | String : string basic
5555+ | Char : char basic
5656+5757+ type _ typ =
5858+ | Basic : 'a basic -> 'a typ
5959+ | DateTime : string typ
6060+ | Base64 : string typ
6161+ | Array : 'a typ -> 'a array typ
6262+ | List : 'a typ -> 'a list typ
6363+ | Dict : 'a basic * 'b typ -> ('a * 'b) list typ
6464+ | Unit : unit typ
6565+ | Option : 'a typ -> 'a option typ
6666+ | Tuple : 'a typ * 'b typ -> ('a * 'b) typ
6767+ | Tuple3 : 'a typ * 'b typ * 'c typ -> ('a * 'b * 'c) typ
6868+ | Tuple4 : 'a typ * 'b typ * 'c typ * 'd typ -> ('a * 'b * 'c * 'd) typ
6969+ | Struct : 'a structure -> 'a typ
7070+ | Variant : 'a variant -> 'a typ
7171+ | Abstract : 'a abstract -> 'a typ
7272+7373+ (* A type definition has a name and description *)
7474+ and 'a def =
7575+ { name : string
7676+ ; description : string list
7777+ ; ty : 'a typ
7878+ }
7979+8080+ and boxed_def = BoxedDef : 'a def -> boxed_def
8181+8282+ and ('a, 's) field =
8383+ { fname : string
8484+ ; fdescription : string list
8585+ ; fversion : Version.t option
8686+ ; field : 'a typ
8787+ ; fdefault : 'a option
8888+ ; fget : 's -> 'a
8989+ ; (* Lenses *)
9090+ fset : 'a -> 's -> 's
9191+ }
9292+9393+ and 'a boxed_field = BoxedField : ('a, 's) field -> 's boxed_field
9494+9595+ and field_getter =
9696+ { field_get : 'a. string -> 'a typ -> ('a, msg) result }
9797+9898+ and 'a structure =
9999+ { sname : string
100100+ ; fields : 'a boxed_field list
101101+ ; version : Version.t option
102102+ ; constructor : field_getter -> ('a, msg) result
103103+ }
104104+105105+ and ('a, 's) tag =
106106+ { tname : string
107107+ ; tdescription : string list
108108+ ; tversion : Version.t option
109109+ ; tcontents : 'a typ
110110+ ; tpreview : 's -> 'a option
111111+ ; treview : 'a -> 's
112112+ }
113113+114114+ and 'a boxed_tag = BoxedTag : ('a, 's) tag -> 's boxed_tag
115115+116116+ and tag_getter = { tget : 'a. 'a typ -> ('a, msg) result }
117117+118118+ and 'a variant =
119119+ { vname : string
120120+ ; variants : 'a boxed_tag list
121121+ ; vdefault : 'a option
122122+ ; vversion : Version.t option
123123+ ; vconstructor : string -> tag_getter -> ('a, msg) result
124124+ }
125125+126126+ and 'a abstract =
127127+ { aname : string
128128+ ; test_data : 'a list
129129+ ; rpc_of : 'a -> t
130130+ ; of_rpc : t -> ('a, msg) result
131131+ }
132132+133133+ let int = { name = "int"; ty = Basic Int; description = [ "Native integer" ] }
134134+ let int32 = { name = "int32"; ty = Basic Int32; description = [ "32-bit integer" ] }
135135+ let int64 = { name = "int64"; ty = Basic Int64; description = [ "64-bit integer" ] }
136136+ let bool = { name = "bool"; ty = Basic Bool; description = [ "Boolean" ] }
137137+138138+ let float =
139139+ { name = "float"; ty = Basic Float; description = [ "Floating-point number" ] }
140140+141141+142142+ let string = { name = "string"; ty = Basic String; description = [ "String" ] }
143143+ let char = { name = "char"; ty = Basic Char; description = [ "Char" ] }
144144+ let unit = { name = "unit"; ty = Unit; description = [ "Unit" ] }
145145+146146+ let default_types =
147147+ [ BoxedDef int
148148+ ; BoxedDef int32
149149+ ; BoxedDef int64
150150+ ; BoxedDef bool
151151+ ; BoxedDef float
152152+ ; BoxedDef string
153153+ ; BoxedDef char
154154+ ; BoxedDef unit
155155+ ]
156156+end
157157+158158+exception Runtime_error of string * t
159159+exception Runtime_exception of string * string
160160+161161+let map_strings sep fn l = String.concat sep (List.map fn l)
162162+163163+let rec to_string t =
164164+ let open Printf in
165165+ match t with
166166+ | Int i -> sprintf "I(%Li)" i
167167+ | Int32 i -> sprintf "I32(%li)" i
168168+ | Bool b -> sprintf "B(%b)" b
169169+ | Float f -> sprintf "F(%g)" f
170170+ | String s -> sprintf "S(%s)" s
171171+ | DateTime s -> sprintf "D(%s)" s
172172+ | Enum ts -> sprintf "[%s]" (map_strings ";" to_string ts)
173173+ | Dict ts ->
174174+ sprintf "{%s}" (map_strings ";" (fun (s, t) -> sprintf "%s:%s" s (to_string t)) ts)
175175+ | Base64 s -> sprintf "B64(%s)" s
176176+ | Null -> "N"
177177+178178+179179+let rpc_of_t x = x
180180+let rpc_of_int64 i = Int i
181181+let rpc_of_int32 i = Int (Int64.of_int32 i)
182182+let rpc_of_int i = Int (Int64.of_int i)
183183+let rpc_of_bool b = Bool b
184184+let rpc_of_float f = Float f
185185+let rpc_of_string s = String s
186186+let rpc_of_dateTime s = DateTime s
187187+let rpc_of_base64 s = Base64 s
188188+let rpc_of_unit () = Null
189189+let rpc_of_char x = Int (Int64.of_int (Char.code x))
190190+191191+let int64_of_rpc = function
192192+ | Int i -> i
193193+ | String s -> Int64.of_string s
194194+ | x -> failwith (Printf.sprintf "Expected int64, got '%s'" (to_string x))
195195+196196+197197+let int32_of_rpc = function
198198+ | Int i -> Int64.to_int32 i
199199+ | String s -> Int32.of_string s
200200+ | x -> failwith (Printf.sprintf "Expected int32, got '%s'" (to_string x))
201201+202202+203203+let int_of_rpc = function
204204+ | Int i -> Int64.to_int i
205205+ | String s -> int_of_string s
206206+ | x -> failwith (Printf.sprintf "Expected int, got '%s'" (to_string x))
207207+208208+209209+let bool_of_rpc = function
210210+ | Bool b -> b
211211+ | x -> failwith (Printf.sprintf "Expected bool, got '%s'" (to_string x))
212212+213213+214214+let float_of_rpc = function
215215+ | Float f -> f
216216+ | Int i -> Int64.to_float i
217217+ | Int32 i -> Int32.to_float i
218218+ | String s -> float_of_string s
219219+ | x -> failwith (Printf.sprintf "Expected float, got '%s'" (to_string x))
220220+221221+222222+let string_of_rpc = function
223223+ | String s -> s
224224+ | x -> failwith (Printf.sprintf "Expected string, got '%s'" (to_string x))
225225+226226+227227+let dateTime_of_rpc = function
228228+ | DateTime s -> s
229229+ | x -> failwith (Printf.sprintf "Expected DateTime, got '%s'" (to_string x))
230230+231231+232232+let base64_of_rpc = function
233233+ | _ -> failwith "Base64 Unhandled"
234234+235235+236236+let unit_of_rpc = function
237237+ | Null -> ()
238238+ | x -> failwith (Printf.sprintf "Expected unit, got '%s'" (to_string x))
239239+240240+241241+let char_of_rpc x =
242242+ let x = int_of_rpc x in
243243+ if x < 0 || x > 255
244244+ then failwith (Printf.sprintf "Char out of range (%d)" x)
245245+ else Char.chr x
246246+247247+248248+let t_of_rpc t = t
249249+250250+let lowerfn = function
251251+ | String s -> String (String.lowercase_ascii s)
252252+ | Enum (String s :: ss) -> Enum (String (String.lowercase_ascii s) :: ss)
253253+ | x -> x
254254+255255+256256+module ResultUnmarshallers = struct
257257+ let error_msg m = Error (`Msg m)
258258+ let ok x = Ok x
259259+260260+ let int64_of_rpc = function
261261+ | Int i -> ok i
262262+ | String s ->
263263+ (try ok (Int64.of_string s) with
264264+ | _ -> error_msg (Printf.sprintf "Expected int64, got string '%s'" s))
265265+ | x -> error_msg (Printf.sprintf "Expected int64, got '%s'" (to_string x))
266266+267267+268268+ let int32_of_rpc = function
269269+ | Int i -> ok (Int64.to_int32 i)
270270+ | String s ->
271271+ (try ok (Int32.of_string s) with
272272+ | _ -> error_msg (Printf.sprintf "Expected int32, got string '%s'" s))
273273+ | x -> error_msg (Printf.sprintf "Expected int32, got '%s'" (to_string x))
274274+275275+276276+ let int_of_rpc = function
277277+ | Int i -> ok (Int64.to_int i)
278278+ | String s ->
279279+ (try ok (int_of_string s) with
280280+ | _ -> error_msg (Printf.sprintf "Expected int, got string '%s'" s))
281281+ | x -> error_msg (Printf.sprintf "Expected int, got '%s'" (to_string x))
282282+283283+284284+ let bool_of_rpc = function
285285+ | Bool b -> ok b
286286+ | x -> error_msg (Printf.sprintf "Expected bool, got '%s'" (to_string x))
287287+288288+289289+ let float_of_rpc = function
290290+ | Float f -> ok f
291291+ | Int i -> ok (Int64.to_float i)
292292+ | Int32 i -> ok (Int32.to_float i)
293293+ | String s ->
294294+ (try ok (float_of_string s) with
295295+ | _ -> error_msg (Printf.sprintf "Expected float, got string '%s'" s))
296296+ | x -> error_msg (Printf.sprintf "Expected float, got '%s'" (to_string x))
297297+298298+299299+ let string_of_rpc = function
300300+ | String s -> ok s
301301+ | x -> error_msg (Printf.sprintf "Expected string, got '%s'" (to_string x))
302302+303303+304304+ let dateTime_of_rpc = function
305305+ | DateTime s -> ok s
306306+ | x -> error_msg (Printf.sprintf "Expected DateTime, got '%s'" (to_string x))
307307+308308+309309+ let base64_of_rpc = function
310310+ | _ -> error_msg "Base64 Unhandled"
311311+312312+313313+ let unit_of_rpc = function
314314+ | Null -> ok ()
315315+ | x -> error_msg (Printf.sprintf "Expected unit, got '%s'" (to_string x))
316316+317317+318318+ let char_of_rpc x =
319319+ match (int_of_rpc x) with
320320+ | Ok x ->
321321+ if x < 0 || x > 255
322322+ then error_msg (Printf.sprintf "Char out of range (%d)" x)
323323+ else ok (Char.chr x)
324324+ | Error y -> Error y
325325+326326+ let t_of_rpc t = ok t
327327+end
328328+329329+let struct_extend rpc default_rpc =
330330+ match rpc, default_rpc with
331331+ | Dict real, Dict default_fields ->
332332+ Dict
333333+ (List.fold_left
334334+ (fun real (f, default) ->
335335+ if List.mem_assoc f real then real else (f, default) :: real)
336336+ real
337337+ default_fields)
338338+ | _, _ -> rpc
339339+340340+341341+type callback = string list -> t -> unit
342342+343343+type call =
344344+ { name : string
345345+ ; params : t list
346346+ ; is_notification : bool
347347+ }
348348+349349+let call name params = { name; params; is_notification = false }
350350+let notification name params = { name; params; is_notification = true }
351351+352352+let string_of_call call =
353353+ Printf.sprintf
354354+ "-> %s(%s)"
355355+ call.name
356356+ (String.concat "," (List.map to_string call.params))
357357+358358+359359+type response =
360360+ { success : bool
361361+ ; contents : t
362362+ ; is_notification : bool
363363+ }
364364+365365+let string_of_response response =
366366+ Printf.sprintf
367367+ "<- %s(%s)"
368368+ (if response.success then "success" else "failure")
369369+ (to_string response.contents)
370370+371371+372372+(* is_notification is to be set as true only if the call was a notification *)
373373+374374+let success v = { success = true; contents = v; is_notification = false }
375375+let failure v = { success = false; contents = v; is_notification = false }
+220
idl/rpc.mli
···11+(*
22+ * Copyright (c) 2006-2009 Citrix Systems Inc.
33+ * Copyright (c) 2006-2014 Thomas Gazagnaire <thomas@gazagnaire.org>
44+ *
55+ * Permission to use, copy, modify, and distribute this software for any
66+ * purpose with or without fee is hereby granted, provided that the above
77+ * copyright notice and this permission notice appear in all copies.
88+ *
99+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1010+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1111+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1212+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1313+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1414+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1515+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1616+ *)
1717+1818+(** {2 Value} *)
1919+type msg = [ `Msg of string ]
2020+2121+type t =
2222+ | Int of int64
2323+ | Int32 of int32
2424+ | Bool of bool
2525+ | Float of float
2626+ | String of string
2727+ | DateTime of string
2828+ | Enum of t list
2929+ | Dict of (string * t) list
3030+ | Base64 of string
3131+ | Null
3232+3333+val to_string : t -> string
3434+3535+module Version : sig
3636+ type t = int * int * int
3737+3838+ val compare : t -> t -> int
3939+end
4040+4141+(** {2 Type declarations} *)
4242+module Types : sig
4343+ type _ basic =
4444+ | Int : int basic
4545+ | Int32 : int32 basic
4646+ | Int64 : int64 basic
4747+ | Bool : bool basic
4848+ | Float : float basic
4949+ | String : string basic
5050+ | Char : char basic
5151+5252+ type _ typ =
5353+ | Basic : 'a basic -> 'a typ
5454+ | DateTime : string typ
5555+ | Base64 : string typ
5656+ | Array : 'a typ -> 'a array typ
5757+ | List : 'a typ -> 'a list typ
5858+ | Dict : 'a basic * 'b typ -> ('a * 'b) list typ
5959+ | Unit : unit typ
6060+ | Option : 'a typ -> 'a option typ
6161+ | Tuple : 'a typ * 'b typ -> ('a * 'b) typ
6262+ | Tuple3 : 'a typ * 'b typ * 'c typ -> ('a * 'b * 'c) typ
6363+ | Tuple4 : 'a typ * 'b typ * 'c typ * 'd typ -> ('a * 'b * 'c * 'd) typ
6464+ | Struct : 'a structure -> 'a typ
6565+ | Variant : 'a variant -> 'a typ
6666+ | Abstract : 'a abstract -> 'a typ
6767+6868+ and 'a def =
6969+ { name : string
7070+ ; description : string list
7171+ ; ty : 'a typ
7272+ }
7373+7474+ and boxed_def = BoxedDef : 'a def -> boxed_def
7575+7676+ and ('a, 's) field =
7777+ { fname : string
7878+ ; fdescription : string list
7979+ ; fversion : Version.t option
8080+ ; field : 'a typ
8181+ ; fdefault : 'a option
8282+ ; fget : 's -> 'a
8383+ ; fset : 'a -> 's -> 's
8484+ }
8585+8686+ and 'a boxed_field = BoxedField : ('a, 's) field -> 's boxed_field
8787+8888+ and field_getter =
8989+ { field_get : 'a. string -> 'a typ -> ('a, msg) result }
9090+9191+ and 'a structure =
9292+ { sname : string
9393+ ; fields : 'a boxed_field list
9494+ ; version : Version.t option
9595+ ; constructor : field_getter -> ('a, msg) result
9696+ }
9797+9898+ and ('a, 's) tag =
9999+ { tname : string
100100+ ; tdescription : string list
101101+ ; tversion : Version.t option
102102+ ; tcontents : 'a typ
103103+ ; tpreview : 's -> 'a option
104104+ ; (* Prism *)
105105+ treview : 'a -> 's
106106+ }
107107+108108+ and 'a boxed_tag = BoxedTag : ('a, 's) tag -> 's boxed_tag
109109+110110+ and tag_getter = { tget : 'a. 'a typ -> ('a, msg) result }
111111+112112+ and 'a variant =
113113+ { vname : string
114114+ ; variants : 'a boxed_tag list
115115+ ; vdefault : 'a option
116116+ ; vversion : Version.t option
117117+ ; vconstructor : string -> tag_getter -> ('a, msg) result
118118+ }
119119+120120+ and 'a abstract =
121121+ { aname : string
122122+ ; test_data : 'a list
123123+ ; rpc_of : 'a -> t
124124+ ; of_rpc : t -> ('a, msg) result
125125+ }
126126+127127+ val int : int def
128128+ val int32 : int32 def
129129+ val int64 : int64 def
130130+ val bool : bool def
131131+ val float : float def
132132+ val string : string def
133133+ val char : char def
134134+ val unit : unit def
135135+ val default_types : boxed_def list
136136+end
137137+138138+(** {2 Basic constructors} *)
139139+140140+val rpc_of_int64 : int64 -> t
141141+val rpc_of_int32 : int32 -> t
142142+val rpc_of_int : int -> t
143143+val rpc_of_bool : bool -> t
144144+val rpc_of_float : float -> t
145145+val rpc_of_string : string -> t
146146+val rpc_of_dateTime : string -> t
147147+val rpc_of_base64 : string -> t
148148+val rpc_of_t : t -> t
149149+val rpc_of_unit : unit -> t
150150+val rpc_of_char : char -> t
151151+val int64_of_rpc : t -> int64
152152+val int32_of_rpc : t -> int32
153153+val int_of_rpc : t -> int
154154+val bool_of_rpc : t -> bool
155155+val float_of_rpc : t -> float
156156+val string_of_rpc : t -> string
157157+val dateTime_of_rpc : t -> string
158158+val base64_of_rpc : t -> string
159159+val t_of_rpc : t -> t
160160+val char_of_rpc : t -> char
161161+val unit_of_rpc : t -> unit
162162+163163+module ResultUnmarshallers : sig
164164+ val int64_of_rpc : t -> (int64, msg) result
165165+ val int32_of_rpc : t -> (int32, msg) result
166166+ val int_of_rpc : t -> (int, msg) result
167167+ val bool_of_rpc : t -> (bool, msg) result
168168+ val float_of_rpc : t -> (float, msg) result
169169+ val string_of_rpc : t -> (string, msg) result
170170+ val dateTime_of_rpc : t -> (string, msg) result
171171+ val base64_of_rpc : t -> (string, msg) result
172172+ val t_of_rpc : t -> (t, msg) result
173173+ val unit_of_rpc : t -> (unit, msg) result
174174+ val char_of_rpc : t -> (char, msg) result
175175+end
176176+177177+(** {2 Calls} *)
178178+179179+type callback = string list -> t -> unit
180180+181181+type call =
182182+ { name : string
183183+ ; params : t list
184184+ ; is_notification : bool
185185+ }
186186+187187+val call : string -> t list -> call
188188+val notification : string -> t list -> call
189189+val string_of_call : call -> string
190190+191191+(** {2 Responses} *)
192192+193193+type response =
194194+ { success : bool
195195+ ; contents : t
196196+ ; is_notification : bool
197197+ }
198198+199199+val string_of_response : response -> string
200200+val success : t -> response
201201+val failure : t -> response
202202+203203+(** {2 Run-time errors} *)
204204+205205+exception Runtime_error of string * t
206206+exception Runtime_exception of string * string
207207+208208+(** {2 Debug options} *)
209209+val set_debug : bool -> unit
210210+211211+val get_debug : unit -> bool
212212+213213+(** Helper *)
214214+val lowerfn : t -> t
215215+216216+(** [struct_extend rpc1 rpc2] first checks that [rpc1] and [rpc2] are both
217217+ * dictionaries. If this is the case then [struct_extend] will create a new
218218+ * [Rpc.t] which contains all key-value pairs from [rpc1], as well as all
219219+ * key-value pairs from [rpc2] for which the key does not exist in [rpc1]. *)
220220+val struct_extend : t -> t -> t
+267
idl/rpcmarshal.ml
···11+(* Basic type definitions *)
22+open Rpc.Types
33+44+type err = [ `Msg of string ]
55+66+let tailrec_map f l = List.rev_map f l |> List.rev
77+88+let (>>|) x f = match x with | Ok x -> Ok (f x) | Error y -> Error y
99+let (>>=) x f = match x with | Ok x -> f x | Error y -> Error y
1010+let return x = Ok x
1111+let ok x = Ok x
1212+1313+let rec unmarshal : type a. a typ -> Rpc.t -> (a, err) result =
1414+ fun t v ->
1515+ let open Rpc in
1616+ let open Rpc.ResultUnmarshallers in
1717+ let list_helper typ l =
1818+ List.fold_left
1919+ (fun acc v ->
2020+ match acc, unmarshal typ v with
2121+ | Ok a, Ok v -> Ok (v :: a)
2222+ | _, Error (`Msg s) ->
2323+ Error
2424+ (`Msg
2525+ (Printf.sprintf
2626+ "Failed to unmarshal array: %s (when unmarshalling: %s)"
2727+ s
2828+ (Rpc.to_string v)))
2929+ | x, _ -> x)
3030+ (Ok [])
3131+ l
3232+ >>| List.rev
3333+ in
3434+ match t with
3535+ | Basic Int -> int_of_rpc v
3636+ | Basic Int32 -> int32_of_rpc v
3737+ | Basic Int64 -> int64_of_rpc v
3838+ | Basic Bool -> bool_of_rpc v
3939+ | Basic Float -> float_of_rpc v
4040+ | Basic String -> string_of_rpc v
4141+ | Basic Char -> int_of_rpc v >>| Char.chr
4242+ | DateTime -> dateTime_of_rpc v
4343+ | Base64 -> base64_of_rpc v
4444+ | Array typ ->
4545+ (match v with
4646+ | Enum xs -> list_helper typ xs >>| Array.of_list
4747+ | _ -> Error (`Msg "Expecting Array"))
4848+ | List (Tuple (Basic String, typ)) ->
4949+ (match v with
5050+ | Dict xs ->
5151+ let keys = tailrec_map fst xs in
5252+ let vs = tailrec_map snd xs in
5353+ list_helper typ vs >>= fun vs -> return (List.combine keys vs)
5454+ | _ -> Error (`Msg "Unhandled"))
5555+ | Dict (basic, typ) ->
5656+ (match v with
5757+ | Dict xs ->
5858+ (match basic with
5959+ | String ->
6060+ let keys = tailrec_map fst xs in
6161+ let vs = tailrec_map snd xs in
6262+ list_helper typ vs >>= fun vs -> return (List.combine keys vs)
6363+ | _ -> Error (`Msg "Expecting something other than a Dict type"))
6464+ | _ -> Error (`Msg "Unhandled"))
6565+ | List typ ->
6666+ (match v with
6767+ | Enum xs -> list_helper typ xs
6868+ | _ -> Error (`Msg "Expecting array"))
6969+ | Unit -> unit_of_rpc v
7070+ | Option t ->
7171+ (match v with
7272+ | Enum [ x ] -> unmarshal t x >>= fun x -> return (Some x)
7373+ | Enum [] -> return None
7474+ | y ->
7575+ Error (`Msg
7676+ (Printf.sprintf "Expecting an Enum value, got '%s'" (Rpc.to_string y))))
7777+ | Tuple (t1, t2) ->
7878+ (match v, t2 with
7979+ | Rpc.Enum list, Tuple (_, _) ->
8080+ unmarshal t1 (List.hd list)
8181+ >>= fun v1 -> unmarshal t2 (Rpc.Enum (List.tl list)) >>= fun v2 -> Ok (v1, v2)
8282+ | Rpc.Enum [ x; y ], _ ->
8383+ unmarshal t1 x >>= fun v1 -> unmarshal t2 y >>= fun v2 -> Ok (v1, v2)
8484+ | Rpc.Enum _, _ -> Error (`Msg "Too many items in a tuple!")
8585+ | _, _ -> Error (`Msg "Expecting Rpc.Enum when unmarshalling a tuple"))
8686+ | Tuple3 (t1, t2, t3) ->
8787+ (match v with
8888+ | Rpc.Enum [ x; y; z ] ->
8989+ unmarshal t1 x
9090+ >>= fun v1 ->
9191+ unmarshal t2 y >>= fun v2 -> unmarshal t3 z >>= fun v3 -> Ok (v1, v2, v3)
9292+ | Rpc.Enum _ ->
9393+ Error (`Msg "Expecting precisely 3 items when unmarshalling a Tuple3")
9494+ | _ -> Error (`Msg "Expecting Rpc.Enum when unmarshalling a tuple3"))
9595+ | Tuple4 (t1, t2, t3, t4) ->
9696+ (match v with
9797+ | Rpc.Enum [ x; y; z; a ] ->
9898+ unmarshal t1 x
9999+ >>= fun v1 ->
100100+ unmarshal t2 y
101101+ >>= fun v2 ->
102102+ unmarshal t3 z >>= fun v3 -> unmarshal t4 a >>= fun v4 -> Ok (v1, v2, v3, v4)
103103+ | Rpc.Enum _ ->
104104+ Error (`Msg
105105+ "Expecting precisely 4 items in an Enum when unmarshalling a Tuple4")
106106+ | _ -> Error (`Msg "Expecting Rpc.Enum when unmarshalling a tuple4"))
107107+ | Struct { constructor; sname; _ } ->
108108+ (match v with
109109+ | Rpc.Dict keys' ->
110110+ let keys = List.map (fun (s, v) -> String.lowercase_ascii s, v) keys' in
111111+ constructor
112112+ { field_get =
113113+ (let x : type a. string -> a typ -> (a, Rpc.msg) result =
114114+ fun s ty ->
115115+ let s = String.lowercase_ascii s in
116116+ match ty with
117117+ | Option x ->
118118+ (try List.assoc s keys |> unmarshal x >>= fun o -> return (Some o) with
119119+ | _ -> return None)
120120+ | y ->
121121+ (try List.assoc s keys |> unmarshal y with
122122+ | Not_found ->
123123+ Error (`Msg
124124+ (Printf.sprintf
125125+ "No value found for key: '%s' when unmarshalling '%s'"
126126+ s
127127+ sname)))
128128+ in
129129+ x)
130130+ }
131131+ | _ -> Error (`Msg (Printf.sprintf "Expecting Rpc.Dict when unmarshalling a '%s'" sname)))
132132+ | Variant { vconstructor; _ } ->
133133+ (match v with
134134+ | Rpc.String name -> ok (name, Rpc.Null)
135135+ | Rpc.Enum [ Rpc.String name; contents ] -> ok (name, contents)
136136+ | _ -> Error (`Msg "Expecting String or Enum when unmarshalling a variant"))
137137+ >>= fun (name, contents) ->
138138+ let constr = { tget = (fun typ -> unmarshal typ contents) } in
139139+ vconstructor name constr
140140+ | Abstract { of_rpc; _ } -> of_rpc v
141141+142142+143143+let rec marshal : type a. a typ -> a -> Rpc.t =
144144+ fun t v ->
145145+ let open Rpc in
146146+ let rpc_of_basic : type a. a basic -> a -> Rpc.t =
147147+ fun t v ->
148148+ match t with
149149+ | Int -> rpc_of_int v
150150+ | Int32 -> rpc_of_int32 v
151151+ | Int64 -> rpc_of_int64 v
152152+ | Bool -> rpc_of_bool v
153153+ | Float -> rpc_of_float v
154154+ | String -> rpc_of_string v
155155+ | Char -> rpc_of_int (Char.code v)
156156+ in
157157+ match t with
158158+ | Basic t -> rpc_of_basic t v
159159+ | DateTime -> rpc_of_dateTime v
160160+ | Base64 -> rpc_of_base64 v
161161+ | Array typ -> Enum (tailrec_map (marshal typ) (Array.to_list v))
162162+ | List (Tuple (Basic String, typ)) ->
163163+ Dict (tailrec_map (fun (x, y) -> x, marshal typ y) v)
164164+ | List typ -> Enum (tailrec_map (marshal typ) v)
165165+ | Dict (String, typ) -> Rpc.Dict (tailrec_map (fun (k, v) -> k, marshal typ v) v)
166166+ | Dict (basic, typ) ->
167167+ Rpc.Enum
168168+ (tailrec_map (fun (k, v) -> Rpc.Enum [ rpc_of_basic basic k; marshal typ v ]) v)
169169+ | Unit -> rpc_of_unit v
170170+ | Option ty ->
171171+ Rpc.Enum
172172+ (match v with
173173+ | Some x -> [ marshal ty x ]
174174+ | None -> [])
175175+ | Tuple (x, (Tuple (_, _) as y)) ->
176176+ (match marshal y (snd v) with
177177+ | Rpc.Enum xs -> Rpc.Enum (marshal x (fst v) :: xs)
178178+ | _ -> failwith "Marshalling a tuple should always give an Enum")
179179+ | Tuple (x, y) -> Rpc.Enum [ marshal x (fst v); marshal y (snd v) ]
180180+ | Tuple3 (x, y, z) ->
181181+ let vx, vy, vz = v in
182182+ Rpc.Enum [ marshal x vx; marshal y vy; marshal z vz ]
183183+ | Tuple4 (x, y, z, a) ->
184184+ let vx, vy, vz, va = v in
185185+ Rpc.Enum [ marshal x vx; marshal y vy; marshal z vz; marshal a va ]
186186+ | Struct { fields; _ } ->
187187+ let fields =
188188+ List.fold_left
189189+ (fun acc f ->
190190+ match f with
191191+ | BoxedField f ->
192192+ let value = marshal f.field (f.fget v) in
193193+ (match f.field, value with
194194+ | Option _, Rpc.Enum [] -> acc
195195+ | Option _, Rpc.Enum [ x ] -> (f.fname, x) :: acc
196196+ | _, _ -> (f.fname, value) :: acc))
197197+ []
198198+ fields
199199+ in
200200+ Rpc.Dict fields
201201+ | Variant { variants; _ } ->
202202+ List.fold_left
203203+ (fun acc t ->
204204+ match t with
205205+ | BoxedTag t ->
206206+ (match t.tpreview v with
207207+ | Some x ->
208208+ (match marshal t.tcontents x with
209209+ | Rpc.Null -> Rpc.String t.tname
210210+ | y -> Rpc.Enum [ Rpc.String t.tname; y ])
211211+ | None -> acc))
212212+ Rpc.Null
213213+ variants
214214+ | Abstract { rpc_of; _ } -> rpc_of v
215215+216216+217217+let ocaml_of_basic : type a. a basic -> string = function
218218+ | Int64 -> "int64"
219219+ | Int32 -> "int32"
220220+ | Int -> "int"
221221+ | String -> "string"
222222+ | Float -> "float"
223223+ | Bool -> "bool"
224224+ | Char -> "char"
225225+226226+227227+let rec ocaml_of_t : type a. a typ -> string = function
228228+ | Basic b -> ocaml_of_basic b
229229+ | DateTime -> "string"
230230+ | Base64 -> "base64"
231231+ | Array t -> ocaml_of_t t ^ " list"
232232+ | List t -> ocaml_of_t t ^ " list"
233233+ | Dict (b, t) -> Printf.sprintf "(%s * %s) list" (ocaml_of_basic b) (ocaml_of_t t)
234234+ | Unit -> "unit"
235235+ | Option t -> ocaml_of_t t ^ " option"
236236+ | Tuple (a, b) -> Printf.sprintf "(%s * %s)" (ocaml_of_t a) (ocaml_of_t b)
237237+ | Tuple3 (a, b, c) ->
238238+ Printf.sprintf "(%s * %s * %s)" (ocaml_of_t a) (ocaml_of_t b) (ocaml_of_t c)
239239+ | Tuple4 (a, b, c, d) ->
240240+ Printf.sprintf
241241+ "(%s * %s * %s * %s)"
242242+ (ocaml_of_t a)
243243+ (ocaml_of_t b)
244244+ (ocaml_of_t c)
245245+ (ocaml_of_t d)
246246+ | Struct { fields; _ } ->
247247+ let fields =
248248+ List.map
249249+ (function
250250+ | BoxedField f -> Printf.sprintf "%s: %s;" f.fname (ocaml_of_t f.field))
251251+ fields
252252+ in
253253+ Printf.sprintf "{ %s }" (String.concat " " fields)
254254+ | Variant { variants; _ } ->
255255+ let tags =
256256+ List.map
257257+ (function
258258+ | BoxedTag t ->
259259+ Printf.sprintf
260260+ "| %s (%s) (** %s *)"
261261+ t.tname
262262+ (ocaml_of_t t.tcontents)
263263+ (String.concat " " t.tdescription))
264264+ variants
265265+ in
266266+ String.concat " " tags
267267+ | Abstract _ -> "<abstract>"
+94
idl/toplevel_api.ml
···11+(** IDL for talking to the toplevel webworker *)
22+33+open Rpc
44+open Idl
55+66+(** An area to be highlighted *)
77+type highlight =
88+ { line1 : int
99+ ; line2 : int
1010+ ; col1 : int
1111+ ; col2 : int
1212+ }
1313+[@@deriving rpcty]
1414+1515+(** Represents the result of executing a toplevel phrase *)
1616+type exec_result =
1717+ { stdout : string option
1818+ ; stderr : string option
1919+ ; sharp_ppf : string option
2020+ ; caml_ppf : string option
2121+ ; highlight : highlight option
2222+ }
2323+[@@deriving rpcty]
2424+2525+(** The result returned by a 'complete' call. *)
2626+type completion_result =
2727+ { n : int
2828+ (** The position in the input string from where the completions may be
2929+ inserted *)
3030+ ; completions : string list (** The list of possible completions *)
3131+ }
3232+[@@deriving rpcty]
3333+3434+(** For now we are only using a simple error type *)
3535+type err = InternalError of string [@@deriving rpcty]
3636+3737+module E = Idl.Error.Make (struct
3838+ type t = err
3939+4040+ let t = err
4141+4242+ let internal_error_of e = Some (InternalError (Printexc.to_string e))
4343+end)
4444+4545+let err = E.error
4646+4747+module Make (R : RPC) = struct
4848+ open R
4949+5050+ let description =
5151+ Interface.
5252+ { name = "Toplevel"
5353+ ; namespace = None
5454+ ; description =
5555+ [ "Functions for manipulating the toplevel worker thread" ]
5656+ ; version = 1, 0, 0
5757+ }
5858+5959+ let implementation = implement description
6060+6161+ let unit_p = Param.mk Types.unit
6262+6363+ let phrase_p = Param.mk Types.string
6464+6565+ let exec_result_p = Param.mk exec_result
6666+6767+ let completion_p = Param.mk completion_result
6868+6969+ let setup =
7070+ declare
7171+ "setup"
7272+ [ "Initialise the toplevel. Return value is the initial blurb "
7373+ ; "printed when starting a toplevel."
7474+ ]
7575+ (unit_p @-> returning exec_result_p err)
7676+7777+ let exec =
7878+ declare
7979+ "exec"
8080+ [ "Execute a phrase using the toplevel. The toplevel must have been"
8181+ ; "Initialised first."
8282+ ]
8383+ (phrase_p @-> returning exec_result_p err)
8484+8585+ let complete =
8686+ declare
8787+ "complete"
8888+ [ "Find completions of the incomplete phrase. Completion occurs at the"
8989+ ; "end of the phrase passed in. If completion is required at a point"
9090+ ; "other than the end of a string, then take the substring before calling"
9191+ ; "this API."
9292+ ]
9393+ (phrase_p @-> returning completion_p err)
9494+end
+409
idl/toplevel_api_gen.ml
···11+[@@@ocaml.ppx.context
22+ {
33+ tool_name = "ppx_driver";
44+ include_dirs = [];
55+ load_path = [];
66+ open_modules = [];
77+ for_package = None;
88+ debug = false;
99+ use_threads = false;
1010+ use_vmthreads = false;
1111+ recursive_types = false;
1212+ principal = false;
1313+ transparent_modules = false;
1414+ unboxed_types = false;
1515+ unsafe_string = false;
1616+ cookies = [("library-name", "js_top_worker_rpc_dummy")]
1717+ }]
1818+[@@@ocaml.text " IDL for talking to the toplevel webworker "]
1919+open Rpc
2020+open Idl
2121+type highlight = {
2222+ line1: int ;
2323+ line2: int ;
2424+ col1: int ;
2525+ col2: int }[@@ocaml.doc " An area to be highlighted "][@@deriving rpcty]
2626+include
2727+ struct
2828+ let _ = fun (_ : highlight) -> ()
2929+ let rec (highlight_line1 : (_, highlight) Rpc.Types.field) =
3030+ {
3131+ Rpc.Types.fname = "line1";
3232+ Rpc.Types.field = (let open Rpc.Types in Basic Int);
3333+ Rpc.Types.fdefault = None;
3434+ Rpc.Types.fdescription = [];
3535+ Rpc.Types.fversion = None;
3636+ Rpc.Types.fget = (fun _r -> _r.line1);
3737+ Rpc.Types.fset = (fun v -> fun _s -> { _s with line1 = v })
3838+ }
3939+ and (highlight_line2 : (_, highlight) Rpc.Types.field) =
4040+ {
4141+ Rpc.Types.fname = "line2";
4242+ Rpc.Types.field = (let open Rpc.Types in Basic Int);
4343+ Rpc.Types.fdefault = None;
4444+ Rpc.Types.fdescription = [];
4545+ Rpc.Types.fversion = None;
4646+ Rpc.Types.fget = (fun _r -> _r.line2);
4747+ Rpc.Types.fset = (fun v -> fun _s -> { _s with line2 = v })
4848+ }
4949+ and (highlight_col1 : (_, highlight) Rpc.Types.field) =
5050+ {
5151+ Rpc.Types.fname = "col1";
5252+ Rpc.Types.field = (let open Rpc.Types in Basic Int);
5353+ Rpc.Types.fdefault = None;
5454+ Rpc.Types.fdescription = [];
5555+ Rpc.Types.fversion = None;
5656+ Rpc.Types.fget = (fun _r -> _r.col1);
5757+ Rpc.Types.fset = (fun v -> fun _s -> { _s with col1 = v })
5858+ }
5959+ and (highlight_col2 : (_, highlight) Rpc.Types.field) =
6060+ {
6161+ Rpc.Types.fname = "col2";
6262+ Rpc.Types.field = (let open Rpc.Types in Basic Int);
6363+ Rpc.Types.fdefault = None;
6464+ Rpc.Types.fdescription = [];
6565+ Rpc.Types.fversion = None;
6666+ Rpc.Types.fget = (fun _r -> _r.col2);
6767+ Rpc.Types.fset = (fun v -> fun _s -> { _s with col2 = v })
6868+ }
6969+ and typ_of_highlight =
7070+ Rpc.Types.Struct
7171+ ({
7272+ Rpc.Types.fields =
7373+ [Rpc.Types.BoxedField highlight_line1;
7474+ Rpc.Types.BoxedField highlight_line2;
7575+ Rpc.Types.BoxedField highlight_col1;
7676+ Rpc.Types.BoxedField highlight_col2];
7777+ Rpc.Types.sname = "highlight";
7878+ Rpc.Types.version = None;
7979+ Rpc.Types.constructor =
8080+ (fun getter ->
8181+ let open Rresult.R in
8282+ (getter.Rpc.Types.field_get "col2"
8383+ (let open Rpc.Types in Basic Int))
8484+ >>=
8585+ (fun highlight_col2 ->
8686+ (getter.Rpc.Types.field_get "col1"
8787+ (let open Rpc.Types in Basic Int))
8888+ >>=
8989+ (fun highlight_col1 ->
9090+ (getter.Rpc.Types.field_get "line2"
9191+ (let open Rpc.Types in Basic Int))
9292+ >>=
9393+ (fun highlight_line2 ->
9494+ (getter.Rpc.Types.field_get "line1"
9595+ (let open Rpc.Types in Basic Int))
9696+ >>=
9797+ (fun highlight_line1 ->
9898+ return
9999+ {
100100+ line1 = highlight_line1;
101101+ line2 = highlight_line2;
102102+ col1 = highlight_col1;
103103+ col2 = highlight_col2
104104+ })))))
105105+ } : highlight Rpc.Types.structure)
106106+ and highlight =
107107+ {
108108+ Rpc.Types.name = "highlight";
109109+ Rpc.Types.description = ["An area to be highlighted"];
110110+ Rpc.Types.ty = typ_of_highlight
111111+ }
112112+ let _ = highlight_line1
113113+ and _ = highlight_line2
114114+ and _ = highlight_col1
115115+ and _ = highlight_col2
116116+ and _ = typ_of_highlight
117117+ and _ = highlight
118118+ end[@@ocaml.doc "@inline"][@@merlin.hide ]
119119+type exec_result =
120120+ {
121121+ stdout: string option ;
122122+ stderr: string option ;
123123+ sharp_ppf: string option ;
124124+ caml_ppf: string option ;
125125+ highlight: highlight option }[@@ocaml.doc
126126+ " Represents the result of executing a toplevel phrase "]
127127+[@@deriving rpcty]
128128+include
129129+ struct
130130+ let _ = fun (_ : exec_result) -> ()
131131+ let rec (exec_result_stdout : (_, exec_result) Rpc.Types.field) =
132132+ {
133133+ Rpc.Types.fname = "stdout";
134134+ Rpc.Types.field =
135135+ (Rpc.Types.Option (let open Rpc.Types in Basic String));
136136+ Rpc.Types.fdefault = None;
137137+ Rpc.Types.fdescription = [];
138138+ Rpc.Types.fversion = None;
139139+ Rpc.Types.fget = (fun _r -> _r.stdout);
140140+ Rpc.Types.fset = (fun v -> fun _s -> { _s with stdout = v })
141141+ }
142142+ and (exec_result_stderr : (_, exec_result) Rpc.Types.field) =
143143+ {
144144+ Rpc.Types.fname = "stderr";
145145+ Rpc.Types.field =
146146+ (Rpc.Types.Option (let open Rpc.Types in Basic String));
147147+ Rpc.Types.fdefault = None;
148148+ Rpc.Types.fdescription = [];
149149+ Rpc.Types.fversion = None;
150150+ Rpc.Types.fget = (fun _r -> _r.stderr);
151151+ Rpc.Types.fset = (fun v -> fun _s -> { _s with stderr = v })
152152+ }
153153+ and (exec_result_sharp_ppf : (_, exec_result) Rpc.Types.field) =
154154+ {
155155+ Rpc.Types.fname = "sharp_ppf";
156156+ Rpc.Types.field =
157157+ (Rpc.Types.Option (let open Rpc.Types in Basic String));
158158+ Rpc.Types.fdefault = None;
159159+ Rpc.Types.fdescription = [];
160160+ Rpc.Types.fversion = None;
161161+ Rpc.Types.fget = (fun _r -> _r.sharp_ppf);
162162+ Rpc.Types.fset = (fun v -> fun _s -> { _s with sharp_ppf = v })
163163+ }
164164+ and (exec_result_caml_ppf : (_, exec_result) Rpc.Types.field) =
165165+ {
166166+ Rpc.Types.fname = "caml_ppf";
167167+ Rpc.Types.field =
168168+ (Rpc.Types.Option (let open Rpc.Types in Basic String));
169169+ Rpc.Types.fdefault = None;
170170+ Rpc.Types.fdescription = [];
171171+ Rpc.Types.fversion = None;
172172+ Rpc.Types.fget = (fun _r -> _r.caml_ppf);
173173+ Rpc.Types.fset = (fun v -> fun _s -> { _s with caml_ppf = v })
174174+ }
175175+ and (exec_result_highlight : (_, exec_result) Rpc.Types.field) =
176176+ {
177177+ Rpc.Types.fname = "highlight";
178178+ Rpc.Types.field = (Rpc.Types.Option typ_of_highlight);
179179+ Rpc.Types.fdefault = None;
180180+ Rpc.Types.fdescription = [];
181181+ Rpc.Types.fversion = None;
182182+ Rpc.Types.fget = (fun _r -> _r.highlight);
183183+ Rpc.Types.fset = (fun v -> fun _s -> { _s with highlight = v })
184184+ }
185185+ and typ_of_exec_result =
186186+ Rpc.Types.Struct
187187+ ({
188188+ Rpc.Types.fields =
189189+ [Rpc.Types.BoxedField exec_result_stdout;
190190+ Rpc.Types.BoxedField exec_result_stderr;
191191+ Rpc.Types.BoxedField exec_result_sharp_ppf;
192192+ Rpc.Types.BoxedField exec_result_caml_ppf;
193193+ Rpc.Types.BoxedField exec_result_highlight];
194194+ Rpc.Types.sname = "exec_result";
195195+ Rpc.Types.version = None;
196196+ Rpc.Types.constructor =
197197+ (fun getter ->
198198+ let open Rresult.R in
199199+ (getter.Rpc.Types.field_get "highlight"
200200+ (Rpc.Types.Option typ_of_highlight))
201201+ >>=
202202+ (fun exec_result_highlight ->
203203+ (getter.Rpc.Types.field_get "caml_ppf"
204204+ (Rpc.Types.Option
205205+ (let open Rpc.Types in Basic String)))
206206+ >>=
207207+ (fun exec_result_caml_ppf ->
208208+ (getter.Rpc.Types.field_get "sharp_ppf"
209209+ (Rpc.Types.Option
210210+ (let open Rpc.Types in Basic String)))
211211+ >>=
212212+ (fun exec_result_sharp_ppf ->
213213+ (getter.Rpc.Types.field_get "stderr"
214214+ (Rpc.Types.Option
215215+ (let open Rpc.Types in Basic String)))
216216+ >>=
217217+ (fun exec_result_stderr ->
218218+ (getter.Rpc.Types.field_get "stdout"
219219+ (Rpc.Types.Option
220220+ (let open Rpc.Types in
221221+ Basic String)))
222222+ >>=
223223+ (fun exec_result_stdout ->
224224+ return
225225+ {
226226+ stdout = exec_result_stdout;
227227+ stderr = exec_result_stderr;
228228+ sharp_ppf =
229229+ exec_result_sharp_ppf;
230230+ caml_ppf =
231231+ exec_result_caml_ppf;
232232+ highlight =
233233+ exec_result_highlight
234234+ }))))))
235235+ } : exec_result Rpc.Types.structure)
236236+ and exec_result =
237237+ {
238238+ Rpc.Types.name = "exec_result";
239239+ Rpc.Types.description =
240240+ ["Represents the result of executing a toplevel phrase"];
241241+ Rpc.Types.ty = typ_of_exec_result
242242+ }
243243+ let _ = exec_result_stdout
244244+ and _ = exec_result_stderr
245245+ and _ = exec_result_sharp_ppf
246246+ and _ = exec_result_caml_ppf
247247+ and _ = exec_result_highlight
248248+ and _ = typ_of_exec_result
249249+ and _ = exec_result
250250+ end[@@ocaml.doc "@inline"][@@merlin.hide ]
251251+type completion_result =
252252+ {
253253+ n: int
254254+ [@ocaml.doc
255255+ " The position in the input string from where the completions may be\n inserted "];
256256+ completions: string list [@ocaml.doc " The list of possible completions "]}
257257+[@@ocaml.doc " The result returned by a 'complete' call. "][@@deriving rpcty]
258258+include
259259+ struct
260260+ let _ = fun (_ : completion_result) -> ()
261261+ let rec (completion_result_n : (_, completion_result) Rpc.Types.field) =
262262+ {
263263+ Rpc.Types.fname = "n";
264264+ Rpc.Types.field = (let open Rpc.Types in Basic Int);
265265+ Rpc.Types.fdefault = None;
266266+ Rpc.Types.fdescription =
267267+ ["The position in the input string from where the completions may be";
268268+ "inserted"];
269269+ Rpc.Types.fversion = None;
270270+ Rpc.Types.fget = (fun _r -> _r.n);
271271+ Rpc.Types.fset = (fun v -> fun _s -> { _s with n = v })
272272+ }
273273+ and (completion_result_completions :
274274+ (_, completion_result) Rpc.Types.field) =
275275+ {
276276+ Rpc.Types.fname = "completions";
277277+ Rpc.Types.field =
278278+ (Rpc.Types.List (let open Rpc.Types in Basic String));
279279+ Rpc.Types.fdefault = None;
280280+ Rpc.Types.fdescription = ["The list of possible completions"];
281281+ Rpc.Types.fversion = None;
282282+ Rpc.Types.fget = (fun _r -> _r.completions);
283283+ Rpc.Types.fset = (fun v -> fun _s -> { _s with completions = v })
284284+ }
285285+ and typ_of_completion_result =
286286+ Rpc.Types.Struct
287287+ ({
288288+ Rpc.Types.fields =
289289+ [Rpc.Types.BoxedField completion_result_n;
290290+ Rpc.Types.BoxedField completion_result_completions];
291291+ Rpc.Types.sname = "completion_result";
292292+ Rpc.Types.version = None;
293293+ Rpc.Types.constructor =
294294+ (fun getter ->
295295+ let open Rresult.R in
296296+ (getter.Rpc.Types.field_get "completions"
297297+ (Rpc.Types.List (let open Rpc.Types in Basic String)))
298298+ >>=
299299+ (fun completion_result_completions ->
300300+ (getter.Rpc.Types.field_get "n"
301301+ (let open Rpc.Types in Basic Int))
302302+ >>=
303303+ (fun completion_result_n ->
304304+ return
305305+ {
306306+ n = completion_result_n;
307307+ completions = completion_result_completions
308308+ })))
309309+ } : completion_result Rpc.Types.structure)
310310+ and completion_result =
311311+ {
312312+ Rpc.Types.name = "completion_result";
313313+ Rpc.Types.description = ["The result returned by a 'complete' call."];
314314+ Rpc.Types.ty = typ_of_completion_result
315315+ }
316316+ let _ = completion_result_n
317317+ and _ = completion_result_completions
318318+ and _ = typ_of_completion_result
319319+ and _ = completion_result
320320+ end[@@ocaml.doc "@inline"][@@merlin.hide ]
321321+type err =
322322+ | InternalError of string [@@ocaml.doc
323323+ " For now we are only using a simple error type "]
324324+[@@deriving rpcty]
325325+include
326326+ struct
327327+ let _ = fun (_ : err) -> ()
328328+ let rec typ_of_err =
329329+ Rpc.Types.Variant
330330+ ({
331331+ Rpc.Types.vname = "err";
332332+ Rpc.Types.variants =
333333+ [BoxedTag
334334+ {
335335+ Rpc.Types.tname = "InternalError";
336336+ Rpc.Types.tcontents =
337337+ ((let open Rpc.Types in Basic String));
338338+ Rpc.Types.tversion = None;
339339+ Rpc.Types.tdescription = [];
340340+ Rpc.Types.tpreview =
341341+ ((function | InternalError a0 -> Some a0));
342342+ Rpc.Types.treview = ((function | a0 -> InternalError a0))
343343+ }];
344344+ Rpc.Types.vdefault = None;
345345+ Rpc.Types.vversion = None;
346346+ Rpc.Types.vconstructor =
347347+ (fun s' ->
348348+ fun t ->
349349+ let s = String.lowercase_ascii s' in
350350+ match s with
351351+ | "internalerror" ->
352352+ Rresult.R.bind
353353+ (t.tget (let open Rpc.Types in Basic String))
354354+ (function | a0 -> Rresult.R.ok (InternalError a0))
355355+ | _ ->
356356+ Rresult.R.error_msg
357357+ (Printf.sprintf "Unknown tag '%s'" s))
358358+ } : err Rpc.Types.variant)
359359+ and err =
360360+ {
361361+ Rpc.Types.name = "err";
362362+ Rpc.Types.description =
363363+ ["For now we are only using a simple error type"];
364364+ Rpc.Types.ty = typ_of_err
365365+ }
366366+ let _ = typ_of_err
367367+ and _ = err
368368+ end[@@ocaml.doc "@inline"][@@merlin.hide ]
369369+module E =
370370+ (Idl.Error.Make)(struct
371371+ type t = err
372372+ let t = err
373373+ let internal_error_of e =
374374+ Some (InternalError (Printexc.to_string e))
375375+ end)
376376+let err = E.error
377377+module Make(R:RPC) =
378378+ struct
379379+ open R
380380+ let description =
381381+ let open Interface in
382382+ {
383383+ name = "Toplevel";
384384+ namespace = None;
385385+ description =
386386+ ["Functions for manipulating the toplevel worker thread"];
387387+ version = (1, 0, 0)
388388+ }
389389+ let implementation = implement description
390390+ let unit_p = Param.mk Types.unit
391391+ let phrase_p = Param.mk Types.string
392392+ let exec_result_p = Param.mk exec_result
393393+ let completion_p = Param.mk completion_result
394394+ let setup =
395395+ declare "setup"
396396+ ["Initialise the toplevel. Return value is the initial blurb ";
397397+ "printed when starting a toplevel."]
398398+ (unit_p @-> (returning exec_result_p err))
399399+ let exec =
400400+ declare "exec"
401401+ ["Execute a phrase using the toplevel. The toplevel must have been";
402402+ "Initialised first."] (phrase_p @-> (returning exec_result_p err))
403403+ let complete =
404404+ declare "complete"
405405+ ["Find completions of the incomplete phrase. Completion occurs at the";
406406+ "end of the phrase passed in. If completion is required at a point";
407407+ "other than the end of a string, then take the substring before calling";
408408+ "this API."] (phrase_p @-> (returning completion_p err))
409409+ end
···11+(*
22+ * uTop.ml
33+ * -------
44+ * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+[@@@warning "-27"]
1111+1212+1313+module String_set = Set.Make(String)
1414+1515+let version = "2.7.0"
1616+1717+(* +-----------------------------------------------------------------+
1818+ | Keywords |
1919+ +-----------------------------------------------------------------+ *)
2020+2121+let default_keywords = [
2222+ "and"; "as"; "assert"; "begin"; "class"; "constraint"; "do";
2323+ "done"; "downto"; "else"; "end"; "exception"; "external";
2424+ "for"; "fun"; "function"; "functor"; "if"; "in"; "include";
2525+ "inherit"; "initializer"; "lazy"; "let"; "match"; "method"; "module";
2626+ "mutable"; "new"; "object"; "of"; "open"; "private"; "rec"; "sig";
2727+ "struct"; "then"; "to"; "try"; "type"; "val"; "virtual";
2828+ "when"; "while"; "with"; "try_lwt"; "finally"; "for_lwt"; "lwt";
2929+]
3030+3131+let keywords = ref (String_set.of_list default_keywords)
3232+let add_keyword kwd = keywords := String_set.add kwd !keywords
3333+3434+(* +-----------------------------------------------------------------+
3535+ | Error reporting |
3636+ +-----------------------------------------------------------------+ *)
3737+3838+let get_message func x =
3939+ let buffer = Buffer.create 1024 in
4040+ let pp = Format.formatter_of_buffer buffer in
4141+ func pp x;
4242+ Format.pp_print_flush pp ();
4343+ Buffer.contents buffer
4444+4545+let get_ocaml_error_message exn =
4646+ let buffer = Buffer.create 1024 in
4747+ let pp = Format.formatter_of_buffer buffer in
4848+ Errors.report_error pp exn;
4949+ Format.pp_print_flush pp ();
5050+ let str = Buffer.contents buffer in
5151+ try
5252+ Scanf.sscanf
5353+ str
5454+ "Characters %d-%d:\n%[\000-\255]"
5555+ (fun start stop msg -> ((start, stop), msg))
5656+ with _ ->
5757+ ((0, 0), str)
5858+5959+let collect_formatters buf pps f =
6060+ (* First flush all formatters. *)
6161+ List.iter (fun pp -> Format.pp_print_flush pp ()) pps;
6262+ (* Save all formatter functions. *)
6363+ let save = List.map (fun pp -> Format.pp_get_formatter_out_functions pp ()) pps in
6464+ let restore () =
6565+ List.iter2
6666+ (fun pp out_functions ->
6767+ Format.pp_print_flush pp ();
6868+ Format.pp_set_formatter_out_functions pp out_functions)
6969+ pps save
7070+ in
7171+ (* Output functions. *)
7272+ let out_functions =
7373+ let ppb = Format.formatter_of_buffer buf in
7474+ Format.pp_get_formatter_out_functions ppb ()
7575+ in
7676+ (* Replace formatter functions. *)
7777+ List.iter
7878+ (fun pp ->
7979+ Format.pp_set_formatter_out_functions pp out_functions)
8080+ pps;
8181+ try
8282+ let x = f () in
8383+ restore ();
8484+ x
8585+ with exn ->
8686+ restore ();
8787+ raise exn
8888+8989+let discard_formatters pps f =
9090+ (* First flush all formatters. *)
9191+ List.iter (fun pp -> Format.pp_print_flush pp ()) pps;
9292+ (* Save all formatter functions. *)
9393+ let save = List.map (fun pp -> Format.pp_get_formatter_out_functions pp ()) pps in
9494+ let restore () =
9595+ List.iter2
9696+ (fun pp out_functions ->
9797+ Format.pp_print_flush pp ();
9898+ Format.pp_set_formatter_out_functions pp out_functions)
9999+ pps save
100100+ in
101101+ (* Output functions. *)
102102+ let out_functions = {
103103+ Format.out_string = (fun _ _ _ -> ()); out_flush = ignore;
104104+ out_newline = ignore; out_spaces = ignore
105105+#if OCAML_VERSION >= (4, 06, 0)
106106+ ; out_indent = ignore
107107+#endif
108108+ } in
109109+ (* Replace formatter functions. *)
110110+ List.iter (fun pp -> Format.pp_set_formatter_out_functions pp out_functions) pps;
111111+ try
112112+ let x = f () in
113113+ restore ();
114114+ x
115115+ with exn ->
116116+ restore ();
117117+ raise exn
118118+119119+(* +-----------------------------------------------------------------+
120120+ | Parsing |
121121+ +-----------------------------------------------------------------+ *)
122122+123123+type location = int * int
124124+125125+type 'a result =
126126+ | Value of 'a
127127+ | Error of location list * string
128128+129129+exception Need_more
130130+131131+let input_name = "//toplevel//"
132132+133133+let lexbuf_of_string eof str =
134134+ let pos = ref 0 in
135135+ let lexbuf =
136136+ Lexing.from_function
137137+ (fun buf len ->
138138+ if !pos = String.length str then begin
139139+ eof := true;
140140+ 0
141141+ end else begin
142142+ let len = min len (String.length str - !pos) in
143143+ String.blit str !pos buf 0 len;
144144+ pos := !pos + len;
145145+ len
146146+ end)
147147+ in
148148+ Location.init lexbuf input_name;
149149+ lexbuf
150150+151151+let mkloc loc =
152152+ (loc.Location.loc_start.Lexing.pos_cnum,
153153+ loc.Location.loc_end.Lexing.pos_cnum)
154154+155155+let parse_default parse str eos_is_error =
156156+ let eof = ref false in
157157+ let lexbuf = lexbuf_of_string eof str in
158158+ try
159159+ (* Try to parse the phrase. *)
160160+ let phrase = parse lexbuf in
161161+ Value phrase
162162+ with
163163+ | _ when !eof && not eos_is_error ->
164164+ (* This is not an error, we just need more input. *)
165165+ raise Need_more
166166+ | End_of_file ->
167167+ (* If the string is empty, do not report an error. *)
168168+ raise Need_more
169169+ | Lexer.Error (error, loc) ->
170170+#if OCAML_VERSION >= (4, 08, 0)
171171+ (match Location.error_of_exn (Lexer.Error (error, loc)) with
172172+ | Some (`Ok error)->
173173+ Error ([mkloc loc], get_message Location.print_report error)
174174+ | _-> raise Need_more)
175175+#else
176176+ Error ([mkloc loc], get_message Lexer.report_error error)
177177+#endif
178178+ | Syntaxerr.Error error -> begin
179179+ match error with
180180+ | Syntaxerr.Unclosed (opening_loc, opening, closing_loc, closing) ->
181181+ Error ([mkloc opening_loc; mkloc closing_loc],
182182+ Printf.sprintf "Syntax error: '%s' expected, the highlighted '%s' might be unmatched" closing opening)
183183+ | Syntaxerr.Applicative_path loc ->
184184+ Error ([mkloc loc],
185185+ "Syntax error: applicative paths of the form F(X).t are not supported when the option -no-app-funct is set.")
186186+ | Syntaxerr.Other loc ->
187187+ Error ([mkloc loc],
188188+ "Syntax error")
189189+ | Syntaxerr.Expecting (loc, nonterm) ->
190190+ Error ([mkloc loc],
191191+ Printf.sprintf "Syntax error: %s expected." nonterm)
192192+ | Syntaxerr.Variable_in_scope (loc, var) ->
193193+ Error ([mkloc loc],
194194+ Printf.sprintf "In this scoped type, variable '%s is reserved for the local type %s." var var)
195195+ | Syntaxerr.Not_expecting (loc, nonterm) ->
196196+ Error ([mkloc loc],
197197+ Printf.sprintf "Syntax error: %s not expected" nonterm)
198198+ | Syntaxerr.Ill_formed_ast (loc, s) ->
199199+ Error ([mkloc loc],
200200+ Printf.sprintf "Error: broken invariant in parsetree: %s" s)
201201+#if OCAML_VERSION >= (4, 03, 0)
202202+ | Syntaxerr.Invalid_package_type (loc, s) ->
203203+ Error ([mkloc loc],
204204+ Printf.sprintf "Invalid package type: %s" s)
205205+#endif
206206+ end
207207+ | Syntaxerr.Escape_error | Parsing.Parse_error ->
208208+ Error ([mkloc (Location.curr lexbuf)],
209209+ "Syntax error")
210210+ | exn ->
211211+ Error ([], "Unknown parsing error (please report it to the utop project): " ^ Printexc.to_string exn)
212212+213213+let parse_toplevel_phrase_default = parse_default Parse.toplevel_phrase
214214+let parse_toplevel_phrase = ref parse_toplevel_phrase_default
215215+216216+(* +-----------------------------------------------------------------+
217217+ | Safety checking |
218218+ +-----------------------------------------------------------------+ *)
219219+220220+let null = Format.make_formatter (fun str ofs len -> ()) ignore
221221+222222+let rec last head tail =
223223+ match tail with
224224+ | [] ->
225225+ head
226226+ | head :: tail ->
227227+ last head tail
228228+229229+let with_loc loc str = {
230230+ Location.txt = str;
231231+ Location.loc = loc;
232232+}
233233+234234+#if OCAML_VERSION >= (4, 03, 0)
235235+let nolabel = Asttypes.Nolabel
236236+#else
237237+let nolabel = ""
238238+#endif
239239+240240+(* Check that the given phrase can be evaluated without typing/compile
241241+ errors. *)
242242+let check_phrase phrase =
243243+ let open Parsetree in
244244+ match phrase with
245245+ | Ptop_dir _ ->
246246+ None
247247+ | Ptop_def [] ->
248248+ None
249249+ | Ptop_def (item :: items) ->
250250+ let loc = {
251251+ Location.loc_start = item.pstr_loc.Location.loc_start;
252252+ Location.loc_end = (last item items).pstr_loc.Location.loc_end;
253253+ Location.loc_ghost = false;
254254+ } in
255255+ (* Backup. *)
256256+ let snap = Btype.snapshot () in
257257+ let env = !Toploop.toplevel_env in
258258+ (* Construct "let _ () = let module _ = struct <items> end in ()" in order to test
259259+ the typing and compilation of [items] without evaluating them. *)
260260+ let unit = with_loc loc (Longident.Lident "()") in
261261+ let top_def =
262262+ let open Ast_helper in
263263+ with_default_loc loc
264264+ (fun () ->
265265+ Str.eval
266266+ (Exp.fun_ nolabel None (Pat.construct unit None)
267267+ (Exp.letmodule (with_loc loc
268268+ #if OCAML_VERSION >= (4, 10, 0)
269269+ (Some "_")
270270+ #else
271271+ "_"
272272+ #endif
273273+ )
274274+ (Mod.structure (item :: items))
275275+ (Exp.construct unit None))))
276276+ in
277277+ let check_phrase = Ptop_def [top_def] in
278278+ try
279279+ let _ =
280280+ discard_formatters [Format.err_formatter] (fun () ->
281281+ Env.reset_cache_toplevel ();
282282+ Toploop.execute_phrase false null check_phrase)
283283+ in
284284+ (* The phrase is safe. *)
285285+ Toploop.toplevel_env := env;
286286+ Btype.backtrack snap;
287287+ None
288288+ with exn ->
289289+ (* The phrase contains errors. *)
290290+ let loc, msg = get_ocaml_error_message exn in
291291+ Toploop.toplevel_env := env;
292292+ Btype.backtrack snap;
293293+ Some ([loc], msg)
294294+295295+296296+297297+(*let try_finally ~always work=
298298+#if OCAML_VERSION >= (4, 08, 0)
299299+ Misc.try_finally ~always work
300300+#else
301301+ Misc.try_finally work always
302302+#endif
303303+304304+let use_output command =
305305+ let fn = Filename.temp_file "ocaml" "_toploop.ml" in
306306+ try_finally ~always:(fun () ->
307307+ try Sys.remove fn with Sys_error _ -> ())
308308+ (fun () ->
309309+ match
310310+ Printf.ksprintf Sys.command "%s > %s"
311311+ command
312312+ (Filename.quote fn)
313313+ with
314314+ | 0 ->
315315+ ignore (Toploop.use_file Format.std_formatter fn : bool)
316316+ | n ->
317317+ Format.printf "Command exited with code %d.@." n)
318318+319319+let () =
320320+ let name = "use_output" in
321321+ if not (Hashtbl.mem Toploop.directive_table name) then
322322+ Hashtbl.add
323323+ Toploop.directive_table
324324+ name
325325+ (Toploop.Directive_string use_output)
326326+*)
327327+328328+ (* +-----------------------------------------------------------------+
329329+ | Compiler-libs re-exports |
330330+ +-----------------------------------------------------------------+ *)
331331+332332+#if OCAML_VERSION >= (4, 08, 0)
333333+let get_load_path ()= Load_path.get_paths ()
334334+let set_load_path path= Load_path.init path
335335+#else
336336+let get_load_path ()= !Config.load_path
337337+let set_load_path path= Config.load_path := path
338338+#endif
339339+
+105
lib/uTop.mli
···11+(*
22+ * uTop.mli
33+ * --------
44+ * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+(** UTop configuration. *)
1111+1212+val version : string
1313+(** Version of utop. *)
1414+1515+val keywords : Set.Make(String).t ref
1616+(** The set of OCaml keywords. *)
1717+1818+val add_keyword : string -> unit
1919+(** Add a new OCaml keyword. *)
2020+2121+(** Type of a string-location. It is composed of a start and stop offsets (in
2222+ bytes). *)
2323+type location = int * int
2424+2525+(** Result of a function processing a programx. *)
2626+type 'a result =
2727+ | Value of 'a (** The function succeeded and returned this value. *)
2828+ | Error of location list * string
2929+ (** The function failed. Arguments are a list of locations to highlight in
3030+ the source and an error message. *)
3131+3232+(** Exception raised by a parser when it need more data. *)
3333+exception Need_more
3434+3535+val parse_toplevel_phrase
3636+ : (string -> bool -> Parsetree.toplevel_phrase result) ref
3737+(** [parse_toplevel_phrase] is the function used to parse a phrase typed in the
3838+ toplevel.
3939+4040+ Its arguments are:
4141+4242+ - [input]: the string to parse
4343+ - [eos_is_error]
4444+4545+ If [eos_is_error] is [true] and the parser reach the end of input, then
4646+ {!Parse_failure} should be returned.
4747+4848+ If [eos_is_error] is [false] and the parser reach the end of input, the
4949+ exception {!Need_more} must be thrown.
5050+5151+ Except for {!Need_more}, the function must not raise any exception. *)
5252+5353+val parse_toplevel_phrase_default
5454+ : string
5555+ -> bool
5656+ -> Parsetree.toplevel_phrase result
5757+(** The default parser for toplevel phrases. It uses the standard ocaml parser. *)
5858+5959+val parse_default : (Lexing.lexbuf -> 'a) -> string -> bool -> 'a result
6060+(** The default parser. It uses the standard ocaml parser. *)
6161+6262+val input_name : string
6363+(** The name you must use in location to let ocaml know that it is from the
6464+ toplevel. *)
6565+6666+val lexbuf_of_string : bool ref -> string -> Lexing.lexbuf
6767+(** [lexbuf_of_string eof str] is the same as [Lexing.from_string
6868+ str]
6969+ except that if the lexer reach the end of [str] then [eof] is set to [true]. *)
7070+7171+(** {6 Helpers} *)
7272+7373+val get_message : (Format.formatter -> 'a -> unit) -> 'a -> string
7474+(** [get_message printer x] applies [printer] on [x] and returns everything it
7575+ prints as a string. *)
7676+7777+val get_ocaml_error_message : exn -> location * string
7878+(** [get_ocaml_error_message exn] returns the location and error message for the
7979+ exception [exn] which must be an exception from the compiler. *)
8080+8181+val check_phrase : Parsetree.toplevel_phrase -> (location list * string) option
8282+(** [check_phrase phrase] checks that [phrase] can be executed without typing or
8383+ compilation errors. It returns [None] if [phrase] is OK and an error message
8484+ otherwise.
8585+8686+ If the result is [None] it is guaranteed that [Toploop.execute_phrase] won't
8787+ raise any exception. *)
8888+8989+val collect_formatters : Buffer.t -> Format.formatter list -> (unit -> 'a) -> 'a
9090+(** [collect_formatters buf pps f] executes [f] and redirect everything it
9191+ prints on [pps] to [buf]. *)
9292+9393+val discard_formatters : Format.formatter list -> (unit -> 'a) -> 'a
9494+(** [discard_formatters pps f] executes [f], dropping everything it prints on
9595+ [pps]. *)
9696+9797+(** {6 compiler-libs reexports} *)
9898+9999+val get_load_path : unit -> string list
100100+101101+val set_load_path : string list -> unit
102102+(** [get_load_path] and [set_load_path] manage the include directories.
103103+104104+ The internal variable contains the list of directories added by
105105+ findlib-required packages and [#directory] directives. *)
+1081
lib/uTop_complete.ml
···11+(*
22+ * uTop_complete.ml
33+ * ----------------
44+ * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+[@@@warning "-9-27-32"]
1111+1212+open Types
1313+open UTop_token
1414+1515+module String_set = Set.Make(String)
1616+module String_map = Map.Make(String)
1717+1818+let lookup_assoc word words = List.filter (fun (word', _) -> Astring.String.is_prefix ~affix:word word') words
1919+let lookup word words = List.filter (fun word' -> Astring.String.is_prefix word' ~affix:word) words
2020+2121+let set_of_list = List.fold_left (fun set x -> String_set.add x set) String_set.empty
2222+2323+(* +-----------------------------------------------------------------+
2424+ | Utils |
2525+ +-----------------------------------------------------------------+ *)
2626+2727+(* Transform a non-empty list of strings into a long-identifier. *)
2828+let longident_of_list = function
2929+ | [] ->
3030+ invalid_arg "UTop_complete.longident_of_list"
3131+ | component :: rest ->
3232+ let rec loop acc = function
3333+ | [] -> acc
3434+ | component :: rest -> loop (Longident.Ldot(acc, component)) rest
3535+ in
3636+ loop (Longident.Lident component) rest
3737+3838+(* Check whether an identifier is a valid one. *)
3939+let is_valid_identifier id =
4040+ id <> "" &&
4141+ (match id.[0] with
4242+ | 'A' .. 'Z' | 'a' .. 'z' | '_' -> true
4343+ | _ -> false)
4444+4545+let add id set = if is_valid_identifier id then String_set.add id set else set
4646+4747+let lookup_env f x env =
4848+ try
4949+ Some (f x env)
5050+ with Not_found | Env.Error _ ->
5151+ None
5252+5353+(* +-----------------------------------------------------------------+
5454+ | Parsing |
5555+ +-----------------------------------------------------------------+ *)
5656+5757+(* The following functions takes a list of tokens in reverse order. *)
5858+5959+type value_or_field = Value | Field
6060+ (* Either a value, or a record field. *)
6161+6262+(* Parse something of the form [M1.M2. ... .Mn.id] or
6363+ [field.M1.M2. ... .Mn.id] *)
6464+let parse_longident tokens =
6565+ let rec loop acc tokens =
6666+ match tokens with
6767+ | (Symbol ".", _) :: (Uident id, _) :: tokens ->
6868+ loop (id :: acc) tokens
6969+ | (Symbol ".", _) :: (Lident id, _) :: tokens ->
7070+ (Field,
7171+ match acc with
7272+ | [] -> None
7373+ | l -> Some (longident_of_list l))
7474+ | _ ->
7575+ (Value,
7676+ match acc with
7777+ | [] -> None
7878+ | l -> Some (longident_of_list l))
7979+ in
8080+ match tokens with
8181+ | ((Comment (_, false) | String (_, false) | Quotation (_, false)), _) :: _ ->
8282+ (* An unterminated command, string, or quotation. *)
8383+ None
8484+ | ((Uident id | Lident id), { idx1 = start }) :: tokens ->
8585+ (* An identifier. *)
8686+ let kind, path = loop [] tokens in
8787+ Some (kind, path, start, id)
8888+ | (Blanks, { idx2 = stop }) :: tokens ->
8989+ (* Some blanks at the end. *)
9090+ let kind, path = loop [] tokens in
9191+ Some (kind, path, stop, "")
9292+ | (_, { idx2 = stop }) :: _ ->
9393+ (* Otherwise complete after the last token. *)
9494+ let kind, path = loop [] tokens in
9595+ Some (kind, path, stop, "")
9696+ | [] ->
9797+ None
9898+9999+(* Parse something of the form [M1.M2. ... .Mn.id#m1#m2# ... #mp#m] *)
100100+let parse_method tokens =
101101+ (* Collect [M1.M2. ... .Mn.id] and returns the corresponding
102102+ longidentifier. *)
103103+ let rec loop_uidents acc tokens =
104104+ match tokens with
105105+ | (Symbol ".", _) :: (Uident id, _) :: tokens ->
106106+ loop_uidents (id :: acc) tokens
107107+ | _ ->
108108+ longident_of_list acc
109109+ in
110110+ (* Collect [m1#m2# ... #mp] *)
111111+ let rec loop_methods acc tokens =
112112+ match tokens with
113113+ | (Lident meth, _) :: (Symbol "#", _) :: tokens ->
114114+ loop_methods (meth :: acc) tokens
115115+ | (Lident id, _) :: tokens ->
116116+ Some (loop_uidents [id] tokens, acc)
117117+ | _ ->
118118+ None
119119+ in
120120+ match tokens with
121121+ | (Lident meth, { idx1 = start }) :: (Symbol "#", _) :: tokens -> begin
122122+ match loop_methods [] tokens with
123123+ | None -> None
124124+ | Some (path, meths) -> Some (path, meths, start, meth)
125125+ end
126126+ | (Symbol "#", { idx2 = stop }) :: tokens
127127+ | (Blanks, { idx2 = stop }) :: (Symbol "#", _) :: tokens -> begin
128128+ match loop_methods [] tokens with
129129+ | None -> None
130130+ | Some (path, meths) -> Some (path, meths, stop, "")
131131+ end
132132+ | _ ->
133133+ None
134134+135135+type label_kind = Required | Optional
136136+ (* Kind of labels: required or optional. *)
137137+138138+type fun_or_new = Fun | New
139139+ (* Either a function application, either an object creation. *)
140140+141141+(* Parse something of the form [M1.M2. ... .Mn.id#m1#m2# ... #mp expr1 ... exprq ~label]
142142+ or [new M1.M2. ... .Mn.id expr1 ... exprq ~label] *)
143143+let parse_label tokens =
144144+ (* Collect [M1.M2. ... .Mn] *)
145145+ let rec loop_uidents acc_uidents acc_methods tokens =
146146+ match tokens with
147147+ | (Lident "new", _) :: _ ->
148148+ Some (New, longident_of_list acc_uidents, acc_methods)
149149+ | ((Lident id | Uident id), _) :: _ when String_set.mem id !UTop.keywords ->
150150+ Some (Fun, longident_of_list acc_uidents, acc_methods)
151151+ | (Symbol ".", _) :: (Uident id, _) :: tokens ->
152152+ loop_uidents (id :: acc_uidents) acc_methods tokens
153153+ | (Symbol ("~" | "?" | ":" | "." | "#" | "!" | "`"), _) :: tokens ->
154154+ search tokens
155155+ | (Symbol ")", _) :: tokens ->
156156+ skip tokens "(" []
157157+ | (Symbol "}", _) :: tokens ->
158158+ skip tokens "{" []
159159+ | (Symbol "]", _) :: tokens ->
160160+ skip tokens "[" []
161161+ | (Symbol _, _) :: _ ->
162162+ Some (Fun, longident_of_list acc_uidents, acc_methods)
163163+ | [] ->
164164+ Some (Fun, longident_of_list acc_uidents, acc_methods)
165165+ | _ ->
166166+ search tokens
167167+ and loop_methods acc tokens =
168168+ match tokens with
169169+ | ((Lident id | Uident id), _) :: _ when String_set.mem id !UTop.keywords ->
170170+ None
171171+ | (Symbol ("~" | "?" | ":" | "." | "#" | "!" | "`"), _) :: tokens ->
172172+ search tokens
173173+ | (Symbol ")", _) :: tokens ->
174174+ skip tokens "(" []
175175+ | (Symbol "}", _) :: tokens ->
176176+ skip tokens "{" []
177177+ | (Symbol "]", _) :: tokens ->
178178+ skip tokens "[" []
179179+ | (Symbol _, _) :: _ ->
180180+ None
181181+ | (Lident id, _) :: (Symbol "#", _) :: tokens ->
182182+ loop_methods (id :: acc) tokens
183183+ | (Lident id, _) :: tokens ->
184184+ loop_uidents [id] acc tokens
185185+ | [] ->
186186+ None
187187+ | _ ->
188188+ search tokens
189189+ and search tokens =
190190+ match tokens with
191191+ | ((Lident id | Uident id), _) :: _ when String_set.mem id !UTop.keywords ->
192192+ None
193193+ | (Symbol ("~" | "?" | ":" | "." | "#" | "!" | "`"), _) :: tokens ->
194194+ search tokens
195195+ | (Symbol ")", _) :: tokens ->
196196+ skip tokens "(" []
197197+ | (Symbol "}", _) :: tokens ->
198198+ skip tokens "{" []
199199+ | (Symbol "]", _) :: tokens ->
200200+ skip tokens "[" []
201201+ | (Symbol _, _) :: _ ->
202202+ None
203203+ | (Lident id, _) :: (Symbol "#", _) :: tokens ->
204204+ loop_methods [id] tokens
205205+ | (Lident id, _) :: tokens ->
206206+ loop_uidents [id] [] tokens
207207+ | _ :: tokens ->
208208+ search tokens
209209+ | [] ->
210210+ None
211211+ and skip tokens top stack =
212212+ match tokens with
213213+ | (Symbol symbol, _) :: tokens when symbol = top -> begin
214214+ match stack with
215215+ | [] -> search tokens
216216+ | top :: stack -> skip tokens top stack
217217+ end
218218+ | (Symbol ")", _) :: tokens ->
219219+ skip tokens "(" (top :: stack)
220220+ | (Symbol "}", _) :: tokens ->
221221+ skip tokens "{" (top :: stack)
222222+ | (Symbol "]", _) :: tokens ->
223223+ skip tokens "[" (top :: stack)
224224+ | _ :: tokens ->
225225+ skip tokens top stack
226226+ | [] ->
227227+ None
228228+ in
229229+ match tokens with
230230+ | (Lident label, { idx1 = start }) :: (Symbol "~", _) :: tokens -> begin
231231+ match search tokens with
232232+ | None -> None
233233+ | Some (kind, id, meths) -> Some (kind, id, meths, Required, start, label)
234234+ end
235235+ | (Symbol "~", { idx2 = stop }) :: tokens -> begin
236236+ match search tokens with
237237+ | None -> None
238238+ | Some (kind, id, meths) -> Some (kind, id, meths, Required, stop, "")
239239+ end
240240+ | (Lident label, { idx1 = start }) :: (Symbol "?", _) :: tokens -> begin
241241+ match search tokens with
242242+ | None -> None
243243+ | Some (kind, id, meths) -> Some (kind, id, meths, Optional, start, label)
244244+ end
245245+ | (Symbol "?", { idx2 = stop }) :: tokens -> begin
246246+ match search tokens with
247247+ | None -> None
248248+ | Some (kind, id, meths) -> Some (kind, id, meths, Optional, stop, "")
249249+ end
250250+ | _ ->
251251+ None
252252+253253+(* +-----------------------------------------------------------------+
254254+ | Directive listing |
255255+ +-----------------------------------------------------------------+ *)
256256+257257+let list_directives phrase_terminator =
258258+ String_map.bindings
259259+ (Hashtbl.fold
260260+ (fun dir kind map ->
261261+ let suffix =
262262+ match kind with
263263+ | Toploop.Directive_none _ -> phrase_terminator
264264+ | Toploop.Directive_string _ -> " \""
265265+ | Toploop.Directive_bool _ | Toploop.Directive_int _ | Toploop.Directive_ident _ -> " "
266266+ in
267267+ String_map.add dir suffix map)
268268+ Toploop.directive_table
269269+ String_map.empty)
270270+271271+(* +-----------------------------------------------------------------+
272272+ | File listing |
273273+ +-----------------------------------------------------------------+ *)
274274+275275+type file_kind = Directory | File
276276+277277+let basename name =
278278+ let name' = Filename.basename name in
279279+ if name' = "." && not (Astring.String.is_suffix name ~affix:".") then
280280+ ""
281281+ else
282282+ name'
283283+284284+let add_files filter acc dir =
285285+ Array.fold_left
286286+ (fun map name ->
287287+ let absolute_name = Filename.concat dir name in
288288+ if try Sys.is_directory absolute_name with Sys_error _ -> false then
289289+ String_map.add (Filename.concat name "") Directory map
290290+ else if filter name then
291291+ String_map.add name File map
292292+ else
293293+ map)
294294+ acc
295295+ (try Sys.readdir dir with Sys_error _ -> [||])
296296+297297+let list_directories dir =
298298+ String_set.elements
299299+ (Array.fold_left
300300+ (fun set name ->
301301+ let absolute_name = Filename.concat dir name in
302302+ if try Sys.is_directory absolute_name with Sys_error _ -> false then
303303+ String_set.add name set
304304+ else
305305+ set)
306306+ String_set.empty
307307+ (try Sys.readdir (if dir = "" then Filename.current_dir_name else dir) with Sys_error _ -> [||]))
308308+309309+let path () = []
310310+311311+(* +-----------------------------------------------------------------+
312312+ | Names listing |
313313+ +-----------------------------------------------------------------+ *)
314314+315315+module Path_map = Map.Make(struct type t = Path.t let compare = compare end)
316316+module Longident_map = Map.Make(struct type t = Longident.t let compare = compare end)
317317+318318+(* All names accessible without a path. *)
319319+let global_names = ref None
320320+let global_names_revised = ref None
321321+322322+(* All names accessible with a path, by path. *)
323323+let local_names_by_path = ref Path_map.empty
324324+325325+(* All names accessible with a path, by long identifier. *)
326326+let local_names_by_longident = ref Longident_map.empty
327327+328328+(* All record fields accessible without a path. *)
329329+let global_fields = ref None
330330+331331+(* All record fields accessible with a path, by path. *)
332332+let local_fields_by_path = ref Path_map.empty
333333+334334+(* All record fields accessible with a path, by long identifier. *)
335335+let local_fields_by_longident = ref Longident_map.empty
336336+337337+(* All visible modules according to Config.load_path. *)
338338+let visible_modules = ref None
339339+340340+let reset () =
341341+ visible_modules := None;
342342+ global_names := None;
343343+ global_names_revised := None;
344344+ local_names_by_path := Path_map.empty;
345345+ local_names_by_longident := Longident_map.empty;
346346+ global_fields := None;
347347+ local_fields_by_path := Path_map.empty;
348348+ local_fields_by_longident := Longident_map.empty
349349+350350+let get_cached var f =
351351+ match !var with
352352+ | Some x ->
353353+ x
354354+ | None ->
355355+ let x = f () in
356356+ var := Some x;
357357+ x
358358+359359+(* List all visible modules. *)
360360+let visible_modules () =
361361+ get_cached visible_modules
362362+ (fun () ->
363363+ List.fold_left
364364+ (fun acc dir ->
365365+ try
366366+ Array.fold_left
367367+ (fun acc fname ->
368368+ if Filename.check_suffix fname ".cmi" then
369369+ String_set.add (String.capitalize_ascii (Filename.chop_suffix fname ".cmi")) acc
370370+ else
371371+ acc)
372372+ acc
373373+ (Sys.readdir (if dir = "" then Filename.current_dir_name else dir))
374374+ with Sys_error _ ->
375375+ acc)
376376+#if OCAML_VERSION >= (4, 08, 0)
377377+ String_set.empty @@ Load_path.get_paths ()
378378+#else
379379+ String_set.empty !Config.load_path
380380+#endif
381381+ )
382382+383383+let field_name { ld_id = id } = Ident.name id
384384+let constructor_name { cd_id = id } = Ident.name id
385385+386386+let add_fields_of_type decl acc =
387387+ match decl.type_kind with
388388+#if OCAML_VERSION >= (4, 13, 0)
389389+ | Type_variant (constructors,_) ->
390390+#else
391391+ | Type_variant constructors ->
392392+#endif
393393+ acc
394394+ | Type_record (fields, _) ->
395395+ List.fold_left (fun acc field -> add (field_name field) acc) acc fields
396396+ | Type_abstract ->
397397+ acc
398398+ | Type_open ->
399399+ acc
400400+401401+let add_names_of_type decl acc =
402402+ match decl.type_kind with
403403+#if OCAML_VERSION >= (4, 13, 0)
404404+ | Type_variant (constructors,_) ->
405405+#else
406406+ | Type_variant constructors ->
407407+#endif
408408+ List.fold_left (fun acc cstr -> add (constructor_name cstr) acc) acc constructors
409409+ | Type_record (fields, _) ->
410410+ List.fold_left (fun acc field -> add (field_name field) acc) acc fields
411411+ | Type_abstract ->
412412+ acc
413413+ | Type_open ->
414414+ acc
415415+416416+#if OCAML_VERSION >= (4, 08, 0)
417417+let path_of_mty_alias = function
418418+ | Mty_alias path -> path
419419+ | _ -> assert false
420420+#elif OCAML_VERSION >= (4, 04, 0)
421421+let path_of_mty_alias = function
422422+ | Mty_alias (_, path) -> path
423423+ | _ -> assert false
424424+#else
425425+let path_of_mty_alias = function
426426+ | Mty_alias path -> path
427427+ | _ -> assert false
428428+#endif
429429+430430+let rec names_of_module_type = function
431431+ | Mty_signature decls ->
432432+ List.fold_left
433433+ (fun acc decl -> match decl with
434434+#if OCAML_VERSION >= (4, 08, 0)
435435+ | Sig_value (id, _, _)
436436+ | Sig_typext (id, _, _, _)
437437+ | Sig_module (id, _, _, _, _)
438438+ | Sig_modtype (id, _, _)
439439+ | Sig_class (id, _, _, _)
440440+ | Sig_class_type (id, _, _, _) ->
441441+#else
442442+ | Sig_value (id, _)
443443+ | Sig_typext (id, _, _)
444444+ | Sig_module (id, _, _)
445445+ | Sig_modtype (id, _)
446446+ | Sig_class (id, _, _)
447447+ | Sig_class_type (id, _, _) ->
448448+#endif
449449+ add (Ident.name id) acc
450450+#if OCAML_VERSION >= (4, 08, 0)
451451+ | Sig_type (id, decl, _, _) ->
452452+#else
453453+ | Sig_type (id, decl, _) ->
454454+#endif
455455+ add_names_of_type decl (add (Ident.name id) acc))
456456+ String_set.empty decls
457457+ | Mty_ident path -> begin
458458+ match lookup_env Env.find_modtype path !Toploop.toplevel_env with
459459+ | Some { mtd_type = None } -> String_set.empty
460460+ | Some { mtd_type = Some module_type } -> names_of_module_type module_type
461461+ | None -> String_set.empty
462462+ end
463463+ | Mty_alias _ as mty_alias -> begin
464464+ let path = path_of_mty_alias mty_alias in
465465+ match lookup_env Env.find_module path !Toploop.toplevel_env with
466466+ | None -> String_set.empty
467467+ | Some { md_type = module_type } -> names_of_module_type module_type
468468+ end
469469+ | _ ->
470470+ String_set.empty
471471+472472+let rec fields_of_module_type = function
473473+ | Mty_signature decls ->
474474+ List.fold_left
475475+ (fun acc decl -> match decl with
476476+ | Sig_value _
477477+ | Sig_typext _
478478+ | Sig_module _
479479+ | Sig_modtype _
480480+ | Sig_class _
481481+ | Sig_class_type _ ->
482482+ acc
483483+#if OCAML_VERSION >= (4, 08, 0)
484484+ | Sig_type (_, decl, _, _) ->
485485+#else
486486+ | Sig_type (_, decl, _) ->
487487+#endif
488488+ add_fields_of_type decl acc)
489489+ String_set.empty decls
490490+ | Mty_ident path -> begin
491491+ match lookup_env Env.find_modtype path !Toploop.toplevel_env with
492492+ | Some { mtd_type = None } -> String_set.empty
493493+ | Some { mtd_type = Some module_type } -> fields_of_module_type module_type
494494+ | None -> String_set.empty
495495+ end
496496+ | Mty_alias _ as mty_alias -> begin
497497+ let path = path_of_mty_alias mty_alias in
498498+ match lookup_env Env.find_module path !Toploop.toplevel_env with
499499+ | None -> String_set.empty
500500+ | Some { md_type = module_type } -> fields_of_module_type module_type
501501+ end
502502+ | _ ->
503503+ String_set.empty
504504+505505+let lookup_module id env =
506506+#if OCAML_VERSION >= (4, 10, 0)
507507+ let path, decl = Env.find_module_by_name id env in
508508+ (path, decl.md_type)
509509+#else
510510+ let path = Env.lookup_module id env ~load:true in
511511+ (path, (Env.find_module path env).md_type)
512512+#endif
513513+514514+let find_module path env = (Env.find_module path env).md_type
515515+516516+let names_of_module longident =
517517+ try
518518+ Longident_map.find longident !local_names_by_longident
519519+ with Not_found ->
520520+ match lookup_env lookup_module longident !Toploop.toplevel_env with
521521+ | Some(path, module_type) ->
522522+ let names = names_of_module_type module_type in
523523+ local_names_by_path := Path_map.add path names !local_names_by_path;
524524+ local_names_by_longident := Longident_map.add longident names !local_names_by_longident;
525525+ names
526526+ | None ->
527527+ local_names_by_longident := Longident_map.add longident String_set.empty !local_names_by_longident;
528528+ String_set.empty
529529+530530+let fields_of_module longident =
531531+ try
532532+ Longident_map.find longident !local_fields_by_longident
533533+ with Not_found ->
534534+ match lookup_env lookup_module longident !Toploop.toplevel_env with
535535+ | Some(path, module_type) ->
536536+ let fields = fields_of_module_type module_type in
537537+ local_fields_by_path := Path_map.add path fields !local_fields_by_path;
538538+ local_fields_by_longident := Longident_map.add longident fields !local_fields_by_longident;
539539+ fields
540540+ | None ->
541541+ local_fields_by_longident := Longident_map.add longident String_set.empty !local_fields_by_longident;
542542+ String_set.empty
543543+544544+let list_global_names () =
545545+ let rec loop acc = function
546546+ | Env.Env_empty -> acc
547547+#if OCAML_VERSION >= (4, 10, 0)
548548+ | Env.Env_value_unbound _-> acc
549549+ | Env.Env_module_unbound _-> acc
550550+#endif
551551+ | Env.Env_value(summary, id, _) ->
552552+ loop (add (Ident.name id) acc) summary
553553+ | Env.Env_type(summary, id, decl) ->
554554+ loop (add_names_of_type decl (add (Ident.name id) acc)) summary
555555+ | Env.Env_extension(summary, id, _) ->
556556+ loop (add (Ident.name id) acc) summary
557557+#if OCAML_VERSION >= (4, 08, 0)
558558+ | Env.Env_module(summary, id, _, _) ->
559559+#else
560560+ | Env.Env_module(summary, id, _) ->
561561+#endif
562562+ loop (add (Ident.name id) acc) summary
563563+ | Env.Env_modtype(summary, id, _) ->
564564+ loop (add (Ident.name id) acc) summary
565565+ | Env.Env_class(summary, id, _) ->
566566+ loop (add (Ident.name id) acc) summary
567567+ | Env.Env_cltype(summary, id, _) ->
568568+ loop (add (Ident.name id) acc) summary
569569+ | Env.Env_functor_arg(summary, id) ->
570570+ loop (add (Ident.name id) acc) summary
571571+#if OCAML_VERSION >= (4, 08, 0)
572572+ | Env.Env_persistent (summary, id) ->
573573+ loop (add (Ident.name id) acc) summary
574574+#endif
575575+#if OCAML_VERSION >= (4, 04, 0)
576576+ | Env.Env_constraints (summary, _) ->
577577+ loop acc summary
578578+#endif
579579+#if OCAML_VERSION >= (4, 10, 0)
580580+ | Env.Env_copy_types summary ->
581581+ loop acc summary
582582+#elif OCAML_VERSION >= (4, 06, 0)
583583+ | Env.Env_copy_types (summary, _) ->
584584+ loop acc summary
585585+#endif
586586+#if OCAML_VERSION >= (4, 08, 0)
587587+ | Env.Env_open(summary, path) ->
588588+#elif OCAML_VERSION >= (4, 07, 0)
589589+ | Env.Env_open(summary, _, path) ->
590590+#else
591591+ | Env.Env_open(summary, path) ->
592592+#endif
593593+ match try Some (Path_map.find path !local_names_by_path) with Not_found -> None with
594594+ | Some names ->
595595+ loop (String_set.union acc names) summary
596596+ | None ->
597597+ match lookup_env find_module path !Toploop.toplevel_env with
598598+ | Some module_type ->
599599+ let names = names_of_module_type module_type in
600600+ local_names_by_path := Path_map.add path names !local_names_by_path;
601601+ loop (String_set.union acc names) summary
602602+ | None ->
603603+ local_names_by_path := Path_map.add path String_set.empty !local_names_by_path;
604604+ loop acc summary
605605+ in
606606+ (* Add names of the environment: *)
607607+ let acc = loop String_set.empty (Env.summary !Toploop.toplevel_env) in
608608+ (* Add accessible modules: *)
609609+ String_set.union acc (visible_modules ())
610610+611611+let global_names () = get_cached global_names list_global_names
612612+613613+let replace x y set =
614614+ if String_set.mem x set then
615615+ String_set.add y (String_set.remove x set)
616616+ else
617617+ set
618618+619619+let list_global_fields () =
620620+ let rec loop acc = function
621621+ | Env.Env_empty -> acc
622622+#if OCAML_VERSION >= (4, 10, 0)
623623+ | Env.Env_value_unbound _-> acc
624624+ | Env.Env_module_unbound _-> acc
625625+#endif
626626+ | Env.Env_value(summary, id, _) ->
627627+ loop (add (Ident.name id) acc) summary
628628+ | Env.Env_type(summary, id, decl) ->
629629+ loop (add_fields_of_type decl (add (Ident.name id) acc)) summary
630630+ | Env.Env_extension(summary, id, _) ->
631631+ loop (add (Ident.name id) acc) summary
632632+#if OCAML_VERSION >= (4, 08, 0)
633633+ | Env.Env_module(summary, id, _, _) ->
634634+#else
635635+ | Env.Env_module(summary, id, _) ->
636636+#endif
637637+ loop (add (Ident.name id) acc) summary
638638+ | Env.Env_functor_arg(summary, id) ->
639639+ loop (add (Ident.name id) acc) summary
640640+ | Env.Env_modtype(summary, id, _) ->
641641+ loop (add (Ident.name id) acc) summary
642642+ | Env.Env_class(summary, id, _) ->
643643+ loop (add (Ident.name id) acc) summary
644644+ | Env.Env_cltype(summary, id, _) ->
645645+ loop (add (Ident.name id) acc) summary
646646+#if OCAML_VERSION >= (4, 08, 0)
647647+ | Env.Env_persistent (summary, id) ->
648648+ loop (add (Ident.name id) acc) summary
649649+#endif
650650+#if OCAML_VERSION >= (4, 04, 0)
651651+ | Env.Env_constraints (summary, _) ->
652652+ loop acc summary
653653+#endif
654654+#if OCAML_VERSION >= (4, 10, 0)
655655+ | Env.Env_copy_types summary ->
656656+ loop acc summary
657657+#elif OCAML_VERSION >= (4, 06, 0)
658658+ | Env.Env_copy_types (summary, _) ->
659659+ loop acc summary
660660+#endif
661661+#if OCAML_VERSION >= (4, 07, 0)
662662+ #if OCAML_VERSION >= (4, 08, 0)
663663+ | Env.Env_open(summary, path) ->
664664+ #else
665665+ | Env.Env_open(summary, _, path) ->
666666+ #endif
667667+#else
668668+ | Env.Env_open(summary, path) ->
669669+#endif
670670+ match try Some (Path_map.find path !local_fields_by_path) with Not_found -> None with
671671+ | Some fields ->
672672+ loop (String_set.union acc fields) summary
673673+ | None ->
674674+ match lookup_env find_module path !Toploop.toplevel_env with
675675+ | Some module_type ->
676676+ let fields = fields_of_module_type module_type in
677677+ local_fields_by_path := Path_map.add path fields !local_fields_by_path;
678678+ loop (String_set.union acc fields) summary
679679+ | None ->
680680+ local_fields_by_path := Path_map.add path String_set.empty !local_fields_by_path;
681681+ loop acc summary
682682+ in
683683+ (* Add fields of the environment: *)
684684+ let acc = loop String_set.empty (Env.summary !Toploop.toplevel_env) in
685685+ (* Add accessible modules: *)
686686+ String_set.union acc (visible_modules ())
687687+688688+let global_fields () = get_cached global_fields list_global_fields
689689+690690+(* +-----------------------------------------------------------------+
691691+ | Listing methods |
692692+ +-----------------------------------------------------------------+ *)
693693+694694+let rec find_method meth type_expr =
695695+ match type_expr.desc with
696696+ | Tlink type_expr ->
697697+ find_method meth type_expr
698698+ | Tobject (type_expr, _) ->
699699+ find_method meth type_expr
700700+ | Tfield (name, _, type_expr, rest) ->
701701+ if name = meth then
702702+ Some type_expr
703703+ else
704704+ find_method meth rest
705705+ | Tpoly (type_expr, _) ->
706706+ find_method meth type_expr
707707+ | Tconstr (path, _, _) -> begin
708708+ match lookup_env Env.find_type path !Toploop.toplevel_env with
709709+ | None
710710+ | Some { type_manifest = None } ->
711711+ None
712712+ | Some { type_manifest = Some type_expr } ->
713713+ find_method meth type_expr
714714+ end
715715+ | _ ->
716716+ None
717717+718718+let rec methods_of_type acc type_expr =
719719+ match type_expr.desc with
720720+ | Tlink type_expr ->
721721+ methods_of_type acc type_expr
722722+ | Tobject (type_expr, _) ->
723723+ methods_of_type acc type_expr
724724+ | Tfield (name, _, _, rest) ->
725725+ methods_of_type (add name acc) rest
726726+ | Tpoly (type_expr, _) ->
727727+ methods_of_type acc type_expr
728728+ | Tconstr (path, _, _) -> begin
729729+ match lookup_env Env.find_type path !Toploop.toplevel_env with
730730+ | None
731731+ | Some { type_manifest = None } ->
732732+ acc
733733+ | Some { type_manifest = Some type_expr } ->
734734+ methods_of_type acc type_expr
735735+ end
736736+ | _ ->
737737+ acc
738738+739739+let rec find_object meths type_expr =
740740+ match meths with
741741+ | [] ->
742742+ Some type_expr
743743+ | meth :: meths ->
744744+ match find_method meth type_expr with
745745+ | Some type_expr ->
746746+ find_object meths type_expr
747747+ | None ->
748748+ None
749749+750750+let methods_of_object longident meths =
751751+ let lookup_value=
752752+#if OCAML_VERSION >= (4, 10, 0)
753753+ Env.find_value_by_name
754754+#else
755755+ Env.lookup_value
756756+#endif
757757+ in
758758+ match lookup_env lookup_value longident !Toploop.toplevel_env with
759759+ | None ->
760760+ []
761761+ | Some (path, { val_type = type_expr }) ->
762762+ match find_object meths type_expr with
763763+ | None ->
764764+ []
765765+ | Some type_expr ->
766766+ String_set.elements (methods_of_type String_set.empty type_expr)
767767+768768+(* +-----------------------------------------------------------------+
769769+ | Listing labels |
770770+ +-----------------------------------------------------------------+ *)
771771+772772+let rec labels_of_type acc type_expr =
773773+ match type_expr.desc with
774774+ | Tlink te ->
775775+ labels_of_type acc te
776776+ | Tpoly (te, _) ->
777777+ labels_of_type acc te
778778+ | Tarrow(label, _, te, _) ->
779779+#if OCAML_VERSION < (4, 03, 0)
780780+ if label = "" then
781781+ labels_of_type acc te
782782+ else if label.[0] = '?' then
783783+ labels_of_type (String_map.add (String.sub label 1 (String.length label - 1)) Optional acc) te
784784+ else
785785+ labels_of_type (String_map.add label Required acc) te
786786+#else
787787+ (match label with
788788+ | Nolabel ->
789789+ labels_of_type acc te
790790+ | Optional label ->
791791+ labels_of_type (String_map.add label Optional acc) te
792792+ | Labelled label ->
793793+ labels_of_type (String_map.add label Required acc) te)
794794+#endif
795795+ | Tconstr(path, _, _) -> begin
796796+ match lookup_env Env.find_type path !Toploop.toplevel_env with
797797+ | None
798798+ | Some { type_manifest = None } ->
799799+ String_map.bindings acc
800800+ | Some { type_manifest = Some type_expr } ->
801801+ labels_of_type acc type_expr
802802+ end
803803+ | _ ->
804804+ String_map.bindings acc
805805+806806+let labels_of_function longident meths =
807807+ let lookup_value=
808808+#if OCAML_VERSION >= (4, 10, 0)
809809+ Env.find_value_by_name
810810+#else
811811+ Env.lookup_value
812812+#endif
813813+ in
814814+ match lookup_env lookup_value longident !Toploop.toplevel_env with
815815+ | None ->
816816+ []
817817+ | Some (path, { val_type = type_expr }) ->
818818+ match find_object meths type_expr with
819819+ | None ->
820820+ []
821821+ | Some type_expr ->
822822+ labels_of_type String_map.empty type_expr
823823+824824+let labels_of_newclass longident =
825825+ let lookup_class=
826826+#if OCAML_VERSION >= (4, 10, 0)
827827+ Env.find_class_by_name
828828+#else
829829+ Env.lookup_class
830830+#endif
831831+ in
832832+ match lookup_env lookup_class longident !Toploop.toplevel_env with
833833+ | None ->
834834+ []
835835+ | Some (path, { cty_new = None }) ->
836836+ []
837837+ | Some (path, { cty_new = Some type_expr }) ->
838838+ labels_of_type String_map.empty type_expr
839839+840840+(* +-----------------------------------------------------------------+
841841+ | Tokens processing |
842842+ +-----------------------------------------------------------------+ *)
843843+844844+(* Filter blanks and comments except for the last token. *)
845845+let filter tokens =
846846+ let rec aux acc = function
847847+ | [] -> acc
848848+ | [((Blanks | Comment (_, true)), loc)] -> (Blanks, loc) :: acc
849849+ | ((Blanks | Comment (_, true)), _) :: rest -> aux acc rest
850850+ | x :: rest -> aux (x :: acc) rest
851851+ in
852852+ List.rev (aux [] tokens)
853853+854854+(* Reverse and filter blanks and comments except for the last
855855+ token. *)
856856+let rec rev_filter acc tokens =
857857+ match tokens with
858858+ | [] -> acc
859859+ | [((Blanks | Comment (_, true)), loc)] -> (Blanks, loc) :: acc
860860+ | ((Blanks | Comment (_, true)), _) :: rest -> rev_filter acc rest
861861+ | x :: rest -> rev_filter (x :: acc) rest
862862+863863+(* Find the current context. *)
864864+let rec find_context tokens = function
865865+ | [] ->
866866+ Some (rev_filter [] tokens)
867867+ | [(Quotation (items, false), _)] ->
868868+ find_context_in_quotation items
869869+ | _ :: rest ->
870870+ find_context tokens rest
871871+872872+and find_context_in_quotation = function
873873+ | [] ->
874874+ None
875875+ | [(Quot_anti { a_closing = None; a_contents = tokens }, _)] ->
876876+ find_context tokens tokens
877877+ | _ :: rest ->
878878+ find_context_in_quotation rest
879879+880880+(* +-----------------------------------------------------------------+
881881+ | Completion |
882882+ +-----------------------------------------------------------------+ *)
883883+884884+#if OCAML_VERSION < (4, 11, 0)
885885+let longident_parse= Longident.parse
886886+#else
887887+let longident_parse str=
888888+ let lexbuf= Lexing.from_string str in
889889+ Parse.longident lexbuf
890890+#endif
891891+892892+let complete ~phrase_terminator ~input =
893893+ let true_name, false_name = ("true", "false") in
894894+ let tokens = UTop_lexer.lex_string input in
895895+ (* Filter blanks and comments. *)
896896+ let tokens = filter tokens in
897897+ match tokens with
898898+899899+ (* Completion on directive names. *)
900900+ | [(Symbol "#", { idx2 = stop })]
901901+ | [(Symbol "#", _); (Blanks, { idx2 = stop })] ->
902902+ (stop, list_directives phrase_terminator)
903903+ | [(Symbol "#", _); ((Lident src | Uident src), { idx1 = start })] ->
904904+ (start, lookup_assoc src (list_directives phrase_terminator))
905905+906906+ (* Complete with ";;" when possible. *)
907907+ | [(Symbol "#", _); ((Lident _ | Uident _), _); (String (_, true), { idx2 = stop })]
908908+ | [(Symbol "#", _); ((Lident _ | Uident _), _); (String (_, true), _); (Blanks, { idx2 = stop })] ->
909909+ (stop, [(phrase_terminator, "")])
910910+ | [(Symbol "#", _); ((Lident _ | Uident _), _); (String (_, true), _); (Symbol sym, { idx1 = start })] ->
911911+ if Astring.String.is_prefix phrase_terminator ~affix:sym then
912912+ (start, [(phrase_terminator, "")])
913913+ else
914914+ (0, [])
915915+916916+ (* Completion on #require. *)
917917+ | [(Symbol "#", _); (Lident "require", _); (String (tlen, false), loc)] ->
918918+ let pkg = String.sub input (loc.ofs1 + tlen) (String.length input - loc.ofs1 - tlen) in
919919+ let pkgs = lookup pkg [] in
920920+ (loc.idx1 + 1, List.map (fun pkg -> (pkg, "\"" ^ phrase_terminator)) (List.sort compare pkgs))
921921+922922+ | [(Symbol "#", _); (Lident "typeof", _); (String (tlen, false), loc)] ->
923923+ let prefix = String.sub input (loc.ofs1 + tlen) (String.length input - loc.ofs1 - tlen) in
924924+ begin match longident_parse prefix with
925925+ | Longident.Ldot (lident, last_prefix) ->
926926+ let set = names_of_module lident in
927927+ let compls = lookup last_prefix (String_set.elements set) in
928928+ let start = loc.idx1 + 1 + (String.length prefix - String.length last_prefix) in
929929+ (start, List.map (fun w -> (w, "")) compls)
930930+ | _ ->
931931+ let set = global_names () in
932932+ let compls = lookup prefix (String_set.elements set) in
933933+ (loc.idx1 + 1, List.map (fun w -> (w, "")) compls)
934934+ end
935935+936936+ (* Completion on #load. *)
937937+ | [(Symbol "#", _); (Lident ("load" | "load_rec"), _); (String (tlen, false), loc)] ->
938938+ let file = String.sub input (loc.ofs1 + tlen) (String.length input - loc.ofs1 - tlen) in
939939+ let filter name = Filename.check_suffix name ".cma" || Filename.check_suffix name ".cmo" in
940940+ let map =
941941+ if Filename.is_relative file then
942942+ let dir = Filename.dirname file in
943943+ List.fold_left
944944+ (fun acc d -> add_files filter acc (Filename.concat d dir))
945945+ String_map.empty
946946+ (Filename.current_dir_name ::
947947+#if OCAML_VERSION >= (4, 08, 0)
948948+ (Load_path.get_paths ())
949949+#else
950950+ !Config.load_path
951951+#endif
952952+ )
953953+954954+ else
955955+ add_files filter String_map.empty (Filename.dirname file)
956956+ in
957957+ let list = String_map.bindings map in
958958+ let name = basename file in
959959+ let result = lookup_assoc name list in
960960+ (loc.idx2 - String.length name,
961961+ List.map (function (w, Directory) -> (w, "") | (w, File) -> (w, "\"" ^ phrase_terminator)) result)
962962+963963+ (* Completion on #use and #mod_use *)
964964+ | [(Symbol "#", _); (Lident "use", _); (String (tlen, false), loc)]
965965+ | [(Symbol "#", _); (Lident "mod_use", _); (String (tlen, false), loc)] ->
966966+ let file = String.sub input (loc.ofs1 + tlen) (String.length input - loc.ofs1 - tlen) in
967967+ let filter name =
968968+ match try Some (String.rindex name '.') with Not_found -> None with
969969+ | None ->
970970+ true
971971+ | Some idx ->
972972+ let ext = String.sub name (idx + 1) (String.length name - (idx + 1)) in
973973+ ext = "ml"
974974+ in
975975+ let map =
976976+ if Filename.is_relative file then
977977+ let dir = Filename.dirname file in
978978+ List.fold_left
979979+ (fun acc d -> add_files filter acc (Filename.concat d dir))
980980+ String_map.empty
981981+ (Filename.current_dir_name ::
982982+#if OCAML_VERSION >= (4, 08, 0)
983983+ (Load_path.get_paths ())
984984+#else
985985+ !Config.load_path
986986+#endif
987987+ )
988988+ else
989989+ add_files filter String_map.empty (Filename.dirname file)
990990+ in
991991+ let list = String_map.bindings map in
992992+ let name = basename file in
993993+ let result = lookup_assoc name list in
994994+ (loc.idx2 - String.length name,
995995+ List.map (function (w, Directory) -> (w, "") | (w, File) -> (w, "\"" ^ phrase_terminator)) result)
996996+997997+ (* Completion on #directory and #cd. *)
998998+ | [(Symbol "#", _); (Lident ("cd" | "directory"), _); (String (tlen, false), loc)] ->
999999+ let file = String.sub input (loc.ofs1 + tlen) (String.length input - loc.ofs1 - tlen) in
10001000+ let list = list_directories (Filename.dirname file) in
10011001+ let name = basename file in
10021002+ let result = lookup name list in
10031003+ (loc.idx2 - String.length name, List.map (function dir -> (dir, "")) result)
10041004+10051005+ (* Generic completion on directives. *)
10061006+ | [(Symbol "#", _); ((Lident dir | Uident dir), _); (Blanks, { idx2 = stop })] ->
10071007+ (stop,
10081008+ match try Some (Hashtbl.find Toploop.directive_table dir) with Not_found -> None with
10091009+ | Some (Toploop.Directive_none _) -> [(phrase_terminator, "")]
10101010+ | Some (Toploop.Directive_string _) -> [(" \"", "")]
10111011+ | Some (Toploop.Directive_bool _) -> [(true_name, phrase_terminator); (false_name, phrase_terminator)]
10121012+ | Some (Toploop.Directive_int _) -> []
10131013+ | Some (Toploop.Directive_ident _) -> List.map (fun w -> (w, "")) (String_set.elements (global_names ()))
10141014+ | None -> [])
10151015+ | (Symbol "#", _) :: ((Lident dir | Uident dir), _) :: tokens -> begin
10161016+ match try Some (Hashtbl.find Toploop.directive_table dir) with Not_found -> None with
10171017+ | Some (Toploop.Directive_none _) ->
10181018+ (0, [])
10191019+ | Some (Toploop.Directive_string _) ->
10201020+ (0, [])
10211021+ | Some (Toploop.Directive_bool _) -> begin
10221022+ match tokens with
10231023+ | [(Lident id, { idx1 = start })] ->
10241024+ (start, lookup_assoc id [(true_name, phrase_terminator); (false_name, phrase_terminator)])
10251025+ | _ ->
10261026+ (0, [])
10271027+ end
10281028+ | Some (Toploop.Directive_int _) ->
10291029+ (0, [])
10301030+ | Some (Toploop.Directive_ident _) -> begin
10311031+ match parse_longident (List.rev tokens) with
10321032+ | Some (Value, None, start, id) ->
10331033+ (start, List.map (fun w -> (w, "")) (lookup id (String_set.elements (global_names ()))))
10341034+ | Some (Value, Some longident, start, id) ->
10351035+ (start, List.map (fun w -> (w, "")) (lookup id (String_set.elements (names_of_module longident))))
10361036+ | _ ->
10371037+ (0, [])
10381038+ end
10391039+ | None ->
10401040+ (0, [])
10411041+ end
10421042+10431043+ (* Completion on identifiers. *)
10441044+ | _ ->
10451045+ match find_context tokens tokens with
10461046+ | None ->
10471047+ (0, [])
10481048+ | Some [] ->
10491049+ (0, List.map (fun w -> (w, "")) (String_set.elements (String_set.union !UTop.keywords (global_names ()))))
10501050+ | Some tokens ->
10511051+ match parse_method tokens with
10521052+ | Some (longident, meths, start, meth) ->
10531053+ (start, List.map (fun w -> (w, "")) (lookup meth (methods_of_object longident meths)))
10541054+ | None ->
10551055+ match parse_label tokens with
10561056+ | Some (Fun, longident, meths, Optional, start, label) ->
10571057+ (start, List.map (fun (w, kind) -> (w, ":")) (lookup_assoc label (List.filter (function (w, Optional) -> true | (w, Required) -> false) (labels_of_function longident meths))))
10581058+ | Some (Fun, longident, meths, Required, start, label) ->
10591059+ (start, List.map (fun (w, kind) -> (w, ":")) (lookup_assoc label (labels_of_function longident meths)))
10601060+ | Some (New, longident, meths, Optional, start, label) ->
10611061+ (start, List.map (fun (w, kind) -> (w, ":")) (lookup_assoc label (List.filter (function (w, Optional) -> true | (w, Required) -> false) (labels_of_newclass longident))))
10621062+ | Some (New, longident, meths, Required, start, label) ->
10631063+ (start, List.map (fun (w, kind) -> (w, ":")) (lookup_assoc label (labels_of_newclass longident)))
10641064+ | None ->
10651065+ match parse_longident tokens with
10661066+ | None ->
10671067+ (0, [])
10681068+ | Some (Value, None, start, id) ->
10691069+ (start, List.map (fun w -> (w, "")) (lookup id (String_set.elements (String_set.union !UTop.keywords (global_names ())))))
10701070+ | Some (Value, Some longident, start, id) ->
10711071+ (start, List.map (fun w -> (w, "")) (lookup id (String_set.elements (names_of_module longident))))
10721072+ | Some (Field, None, start, id) ->
10731073+ (start, List.map (fun w -> (w, "")) (lookup id (String_set.elements (global_fields ()))))
10741074+ | Some (Field, Some longident, start, id) ->
10751075+ (start, List.map (fun w -> (w, "")) (lookup id (String_set.elements (fields_of_module longident))))
10761076+10771077+let complete ~phrase_terminator ~input =
10781078+ try
10791079+ (complete ~phrase_terminator ~input : int * (string * string) list)
10801080+ with Cmi_format.Error _ ->
10811081+ (0, [])
+20
lib/uTop_complete.mli
···11+(*
22+ * uTop_complete.mli
33+ * -----------------
44+ * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+(** OCaml completion. *)
1111+1212+val complete
1313+ : phrase_terminator:string
1414+ -> input:string
1515+ -> int * (string * string) list
1616+(** [complete ~phrase_terminator ~input] returns the start of the completed word
1717+ in [input] and the list of possible completions with their suffixes. *)
1818+1919+val reset : unit -> unit
2020+(** Reset global cache. It must be called before each interactive read line. *)
+11
lib/uTop_lexer.mli
···11+(*
22+ * uTop_lexer.mli
33+ * --------------
44+ * Copyright : (c) 2012, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+val lex_string : string -> (UTop_token.t * UTop_token.location) list
1111+(** [lex_string str] returns all the tokens contained in [str]. *)
+230
lib/uTop_lexer.mll
···11+(*
22+ * uTop_lexer.mll
33+ * --------------
44+ * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+(* Lexer for the OCaml language. *)
1111+1212+{
1313+ open Lexing
1414+ open UTop_token
1515+1616+ let mkloc idx1 idx2 ofs1 ofs2 = {
1717+ idx1 = idx1;
1818+ idx2 = idx2;
1919+ ofs1 = ofs1;
2020+ ofs2 = ofs2;
2121+ }
2222+2323+ (* Only for ascii-only lexemes. *)
2424+ let lexeme_loc idx lexbuf =
2525+ let ofs1 = lexeme_start lexbuf and ofs2 = lexeme_end lexbuf in
2626+ {
2727+ idx1 = idx;
2828+ idx2 = idx + (ofs2 - ofs1);
2929+ ofs1 = ofs1;
3030+ ofs2 = ofs2;
3131+ }
3232+3333+ let _merge_loc l1 l2 = {
3434+ idx1 = l1.idx1;
3535+ idx2 = l2.idx2;
3636+ ofs1 = l1.ofs1;
3737+ ofs2 = l2.ofs2;
3838+ }
3939+4040+}
4141+4242+let uchar = ['\x00' - '\x7f'] | _ [ '\x80' - '\xbf' ]*
4343+4444+let blank = [' ' '\009' '\012']
4545+let lowercase = ['a'-'z' '_']
4646+let uppercase = ['A'-'Z']
4747+let identchar = ['A'-'Z' 'a'-'z' '_' '\'' '0'-'9']
4848+let lident = lowercase identchar*
4949+let uident = uppercase identchar*
5050+let ident = (lowercase|uppercase) identchar*
5151+5252+let hexa_char = ['0'-'9' 'A'-'F' 'a'-'f']
5353+let decimal_literal =
5454+ ['0'-'9'] ['0'-'9' '_']*
5555+let hex_literal =
5656+ '0' ['x' 'X'] hexa_char ['0'-'9' 'A'-'F' 'a'-'f' '_']*
5757+let oct_literal =
5858+ '0' ['o' 'O'] ['0'-'7'] ['0'-'7' '_']*
5959+let bin_literal =
6060+ '0' ['b' 'B'] ['0'-'1'] ['0'-'1' '_']*
6161+let int_literal =
6262+ decimal_literal | hex_literal | oct_literal | bin_literal
6363+let float_literal =
6464+ ['0'-'9'] ['0'-'9' '_']*
6565+ ('.' ['0'-'9' '_']* )?
6666+ (['e' 'E'] ['+' '-']? ['0'-'9'] ['0'-'9' '_']*)?
6767+6868+let symbolchar =
6969+ ['!' '$' '%' '&' '*' '+' '-' '.' '/' ':' '<' '=' '>' '?' '@' '^' '|' '~']
7070+7171+rule tokens idx acc = parse
7272+ | eof
7373+ { (idx, None, List.rev acc) }
7474+ | ('\n' | blank)+
7575+ { let loc = lexeme_loc idx lexbuf in
7676+ tokens loc.idx2 ((Blanks, loc) :: acc) lexbuf }
7777+ | lident
7878+ { let src = lexeme lexbuf in
7979+ let loc = lexeme_loc idx lexbuf in
8080+ let tok =
8181+ match src with
8282+ | ("true" | "false") ->
8383+ Constant src
8484+ | _ ->
8585+ Lident src
8686+ in
8787+ tokens loc.idx2 ((tok, loc) :: acc) lexbuf }
8888+ | uident
8989+ { let src = lexeme lexbuf in
9090+ let loc = lexeme_loc idx lexbuf in
9191+ let tok = Uident src in
9292+ tokens loc.idx2 ((tok, loc) :: acc) lexbuf }
9393+ | int_literal "l"
9494+ | int_literal "L"
9595+ | int_literal "n"
9696+ | int_literal
9797+ | float_literal
9898+ { let loc = lexeme_loc idx lexbuf in
9999+ let tok = Constant (lexeme lexbuf) in
100100+ tokens loc.idx2 ((tok, loc) :: acc) lexbuf }
101101+ | '"'
102102+ { let ofs = lexeme_start lexbuf in
103103+ let item, idx2= cm_string (idx + 1) lexbuf in
104104+ let loc = mkloc idx idx2 ofs (lexeme_end lexbuf) in
105105+ tokens idx2 ((item, loc) :: acc) lexbuf }
106106+ | '{' (lowercase* as tag) '|'
107107+ { let ofs = lexeme_start lexbuf in
108108+ let delim_len = String.length tag + 2 in
109109+ let idx2, terminated = quoted_string (idx + delim_len) tag lexbuf in
110110+ let loc = mkloc idx idx2 ofs (lexeme_end lexbuf) in
111111+ tokens idx2 ((String (delim_len, terminated), loc) :: acc) lexbuf }
112112+ | "'" [^'\'' '\\'] "'"
113113+ | "'\\" ['\\' '"' 'n' 't' 'b' 'r' ' ' '\'' 'x' '0'-'9'] eof
114114+ | "'\\" ['\\' '"' 'n' 't' 'b' 'r' ' ' '\''] "'"
115115+ | "'\\" (['0'-'9'] ['0'-'9'] | 'x' hexa_char) eof
116116+ | "'\\" (['0'-'9'] ['0'-'9'] ['0'-'9'] | 'x' hexa_char hexa_char) eof
117117+ | "'\\" (['0'-'9'] ['0'-'9'] ['0'-'9'] | 'x' hexa_char hexa_char) "'"
118118+ { let loc = lexeme_loc idx lexbuf in
119119+ tokens loc.idx2 ((Char, loc) :: acc) lexbuf }
120120+ | "'\\" uchar
121121+ { let loc = mkloc idx (idx + 3) (lexeme_start lexbuf) (lexeme_end lexbuf) in
122122+ tokens loc.idx2 ((Error, loc) :: acc) lexbuf }
123123+ | "(*)"
124124+ { let loc = lexeme_loc idx lexbuf in
125125+ tokens loc.idx2 ((Comment (Comment_reg, true), loc) :: acc) lexbuf }
126126+ | "(**)"
127127+ { let loc = lexeme_loc idx lexbuf in
128128+ tokens loc.idx2 ((Comment (Comment_doc, true), loc) :: acc) lexbuf }
129129+ | "(**"
130130+ { let ofs = lexeme_start lexbuf in
131131+ let idx2, terminated = comment (idx + 3) 0 lexbuf in
132132+ let loc = mkloc idx idx2 ofs (lexeme_end lexbuf) in
133133+ tokens idx2 ((Comment (Comment_doc, terminated), loc) :: acc) lexbuf }
134134+ | "(*"
135135+ { let ofs = lexeme_start lexbuf in
136136+ let idx2, terminated = comment (idx + 2) 0 lexbuf in
137137+ let loc = mkloc idx idx2 ofs (lexeme_end lexbuf) in
138138+ tokens idx2 ((Comment (Comment_reg, terminated), loc) :: acc) lexbuf }
139139+ | ""
140140+ { symbol idx acc lexbuf }
141141+142142+and symbol idx acc = parse
143143+ | "(" | ")"
144144+ | "[" | "]"
145145+ | "{" | "}"
146146+ | "`"
147147+ | "#"
148148+ | ","
149149+ | ";" | ";;"
150150+ | symbolchar+
151151+ { let loc = lexeme_loc idx lexbuf in
152152+ let tok = Symbol (lexeme lexbuf) in
153153+ tokens loc.idx2 ((tok, loc) :: acc) lexbuf }
154154+ | uchar
155155+ {
156156+ let loc = mkloc idx (idx + 1) (lexeme_start lexbuf) (lexeme_end lexbuf) in
157157+ tokens loc.idx2 ((Error, loc) :: acc) lexbuf
158158+ }
159159+160160+and cm_string idx= parse
161161+ | '"'
162162+ { (String (1, true), idx+1) }
163163+ | "\\\""
164164+ { let idx2, terminated= string (idx + 2) lexbuf in
165165+ (String (1, terminated), idx2)
166166+ }
167167+ | uchar
168168+ {
169169+170170+ let idx2, terminated= string (idx + 1) lexbuf in
171171+ (String (1, terminated), idx2)
172172+ }
173173+ | eof
174174+ { (String (1, false), idx) }
175175+176176+and comment idx depth = parse
177177+ | "(*"
178178+ { comment (idx + 2) (depth + 1) lexbuf }
179179+ | "*)"
180180+ { if depth = 0 then
181181+ (idx + 2, true)
182182+ else
183183+ comment (idx + 2) (depth - 1) lexbuf }
184184+ | '"'
185185+ { let idx, terminated = string (idx + 1) lexbuf in
186186+ if terminated then
187187+ comment idx depth lexbuf
188188+ else
189189+ (idx, false) }
190190+ | uchar
191191+ {
192192+ comment (idx + 1) depth lexbuf
193193+194194+ }
195195+ | eof
196196+ { (idx, false) }
197197+198198+and string idx = parse
199199+ | '"'
200200+ { (idx + 1, true) }
201201+ | "\\\""
202202+ { string (idx + 2) lexbuf }
203203+ | uchar
204204+ {
205205+ string (idx + 1) lexbuf
206206+207207+ }
208208+ | eof
209209+ { (idx, false) }
210210+211211+and quoted_string idx tag = parse
212212+ | '|' (lowercase* as tag2) '}'
213213+ { let idx = idx + 2 + String.length tag2 in
214214+ if tag = tag2 then
215215+ (idx, true)
216216+ else
217217+ quoted_string idx tag lexbuf }
218218+ | eof
219219+ { (idx, false) }
220220+ | uchar
221221+ {
222222+ quoted_string (idx + 1) tag lexbuf
223223+224224+ }
225225+226226+{
227227+ let lex_string str =
228228+ let _, _, items = tokens 0 [] (Lexing.from_string str) in
229229+ items
230230+}
+50
lib/uTop_token.ml
···11+(*
22+ * uTop_token.ml
33+ * -------------
44+ * Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
55+ * Licence : BSD3
66+ *
77+ * This file is a part of utop.
88+ *)
99+1010+(** Tokens.
1111+1212+ The type of tokens is semi-structured: parentheses construct and quotations
1313+ are nested and others tokens are flat list. *)
1414+1515+(** Locations in the source string, which is encoded in UTF-8. *)
1616+type location =
1717+ { idx1 : int (** Start position in unicode characters. *)
1818+ ; idx2 : int (** Stop position in unicode characters. *)
1919+ ; ofs1 : int (** Start position in bytes. *)
2020+ ; ofs2 : int (** Stop position in bytes. *)
2121+ }
2222+2323+type t =
2424+ | Symbol of string
2525+ | Lident of string
2626+ | Uident of string
2727+ | Constant of string
2828+ | Char
2929+ | String of int * bool (** [String (quote_size, terminated)]. *)
3030+ | Comment of comment_kind * bool (** [Comment (kind, terminated)]. *)
3131+ | Blanks
3232+ | Error
3333+ | Quotation of (quotation_item * location) list * bool
3434+ (** [Quotation (items, terminated)]. *)
3535+3636+and comment_kind =
3737+ | Comment_reg (** Regular comment. *)
3838+ | Comment_doc (** Documentation comment. *)
3939+4040+and quotation_item =
4141+ | Quot_data
4242+ | Quot_anti of antiquotation
4343+4444+and antiquotation =
4545+ { a_opening : location (** Location of the opening [$]. *)
4646+ ; a_closing : location option (** Location of the closing [$]. *)
4747+ ; a_name : (location * location) option
4848+ (** Location of the name and colon if any. *)
4949+ ; a_contents : (t * location) list (** Contents of the location. *)
5050+ }
+177
lib/worker.ml
···11+open Js_of_ocaml_toplevel
22+open Js_top_worker_rpc
33+44+(* OCamlorg toplevel in a web worker
55+66+ This communicates with the toplevel code via the API defined in
77+ {!Toplevel_api}. This allows the OCaml execution to not block the "main
88+ thread" keeping the page responsive. *)
99+1010+module Version = struct
1111+ type t = int list
1212+1313+ let split_char ~sep p =
1414+ let len = String.length p in
1515+ let rec split beg cur =
1616+ if cur >= len then
1717+ if cur - beg > 0 then [ String.sub p beg (cur - beg) ] else []
1818+ else if sep p.[cur] then
1919+ String.sub p beg (cur - beg) :: split (cur + 1) (cur + 1)
2020+ else
2121+ split beg (cur + 1)
2222+ in
2323+ split 0 0
2424+2525+ let split v =
2626+ match
2727+ split_char ~sep:(function '+' | '-' | '~' -> true | _ -> false) v
2828+ with
2929+ | [] ->
3030+ assert false
3131+ | x :: _ ->
3232+ List.map
3333+ int_of_string
3434+ (split_char ~sep:(function '.' -> true | _ -> false) x)
3535+3636+ let current = split Sys.ocaml_version
3737+3838+ let compint (a : int) b = compare a b
3939+4040+ let rec compare v v' =
4141+ match v, v' with
4242+ | [ x ], [ y ] ->
4343+ compint x y
4444+ | [], [] ->
4545+ 0
4646+ | [], y :: _ ->
4747+ compint 0 y
4848+ | x :: _, [] ->
4949+ compint x 0
5050+ | x :: xs, y :: ys ->
5151+ (match compint x y with 0 -> compare xs ys | n -> n)
5252+end
5353+5454+let exec' s =
5555+ let res : bool = JsooTop.use Format.std_formatter s in
5656+ if not res then Format.eprintf "error while evaluating %s@." s
5757+5858+let setup () =
5959+ JsooTop.initialize ();
6060+ Sys.interactive := false;
6161+ if Version.compare Version.current [ 4; 07 ] >= 0 then exec' "open Stdlib";
6262+ let header1 = Printf.sprintf " %s version %%s" "OCaml" in
6363+ let header2 =
6464+ Printf.sprintf
6565+ " Compiled with Js_of_ocaml version %s"
6666+ Js_of_ocaml.Sys_js.js_of_ocaml_version
6767+ in
6868+ exec' (Printf.sprintf "Format.printf \"%s@.\" Sys.ocaml_version;;" header1);
6969+ exec' (Printf.sprintf "Format.printf \"%s@.\";;" header2);
7070+ exec' "#enable \"pretty\";;";
7171+ exec' "#disable \"shortvar\";;";
7272+ Toploop.add_directive
7373+ "load_js"
7474+ (Toploop.Directive_string
7575+ (fun name -> Js_of_ocaml.Js.Unsafe.global##load_script_ name))
7676+ Toploop.{ section = ""; doc = "Load a javascript script" };
7777+ Sys.interactive := true;
7878+ ()
7979+8080+let setup_printers () =
8181+ exec' "let _print_unit fmt (_ : 'a) : 'a = Format.pp_print_string fmt \"()\"";
8282+ Topdirs.dir_install_printer
8383+ Format.std_formatter
8484+ Longident.(Lident "_print_unit")
8585+8686+let stdout_buff = Buffer.create 100
8787+8888+let stderr_buff = Buffer.create 100
8989+9090+(* RPC function implementations *)
9191+9292+module M = Idl.IdM (* Server is synchronous *)
9393+9494+module IdlM = Idl.Make (M)
9595+9696+module Server = Toplevel_api_gen.Make (IdlM.GenServer ())
9797+9898+(* These are all required to return the appropriate value for the API within the
9999+ [IdlM.T] monad. The simplest way to do this is to use [IdlM.ErrM.return] for
100100+ the success case and [IdlM.ErrM.return_err] for the failure case *)
101101+102102+let buff_opt b = match Buffer.contents b with "" -> None | s -> Some s
103103+104104+let execute =
105105+ let code_buff = Buffer.create 100 in
106106+ let res_buff = Buffer.create 100 in
107107+ let pp_code = Format.formatter_of_buffer code_buff in
108108+ let pp_result = Format.formatter_of_buffer res_buff in
109109+ let highlighted = ref None in
110110+ let highlight_location loc =
111111+ let _file1, line1, col1 = Location.get_pos_info loc.Location.loc_start in
112112+ let _file2, line2, col2 = Location.get_pos_info loc.Location.loc_end in
113113+ highlighted := Some Toplevel_api_gen.{ line1; col1; line2; col2 }
114114+ in
115115+ fun phrase ->
116116+ Buffer.clear code_buff;
117117+ Buffer.clear res_buff;
118118+ Buffer.clear stderr_buff;
119119+ Buffer.clear stdout_buff;
120120+ JsooTop.execute true ~pp_code ~highlight_location pp_result phrase;
121121+ Format.pp_print_flush pp_code ();
122122+ Format.pp_print_flush pp_result ();
123123+ IdlM.ErrM.return
124124+ Toplevel_api_gen.
125125+ { stdout = buff_opt stdout_buff
126126+ ; stderr = buff_opt stderr_buff
127127+ ; sharp_ppf = buff_opt code_buff
128128+ ; caml_ppf = buff_opt res_buff
129129+ ; highlight = !highlighted
130130+ }
131131+132132+let setup () =
133133+ Js_of_ocaml.Sys_js.set_channel_flusher stdout (Buffer.add_string stdout_buff);
134134+ Js_of_ocaml.Sys_js.set_channel_flusher stderr (Buffer.add_string stderr_buff);
135135+ setup ();
136136+ setup_printers ();
137137+ IdlM.ErrM.return
138138+ Toplevel_api_gen.
139139+ { stdout = buff_opt stdout_buff
140140+ ; stderr = buff_opt stderr_buff
141141+ ; sharp_ppf = None
142142+ ; caml_ppf = None
143143+ ; highlight = None
144144+ }
145145+146146+let complete phrase =
147147+ let contains_double_underscore s =
148148+ let len = String.length s in
149149+ let rec aux i =
150150+ if i > len - 2 then
151151+ false
152152+ else if s.[i] = '_' && s.[i + 1] = '_' then
153153+ true
154154+ else
155155+ aux (i + 1)
156156+ in
157157+ aux 0
158158+ in
159159+ let n, res = UTop_complete.complete ~phrase_terminator:";;" ~input:phrase in
160160+ let res =
161161+ List.filter (fun (l, _) -> not (contains_double_underscore l)) res
162162+ in
163163+ let completions = List.map fst res in
164164+ IdlM.ErrM.return Toplevel_api_gen.{ n; completions }
165165+166166+let server process e =
167167+ let call : Rpc.call = e in
168168+ M.bind (process call) (fun response -> Js_of_ocaml.Worker.post_message (response : Rpc.response));
169169+ ()
170170+171171+let run () =
172172+ (* Here we bind the server stub functions to the implementations *)
173173+ Server.complete complete;
174174+ Server.exec execute;
175175+ Server.setup setup;
176176+ let rpc_fn = IdlM.server Server.implementation in
177177+ Js_of_ocaml.Worker.set_onmessage (server rpc_fn)