Add poc files
This commit is contained in:
parent
30da2412e3
commit
fa600b98f7
220 changed files with 45679 additions and 0 deletions
498
commons/ocaml.ml
Normal file
498
commons/ocaml.ml
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
(*
|
||||
* Yoann Padioleau
|
||||
*
|
||||
* Copyright (C) 2009-2012 Facebook
|
||||
*
|
||||
* Most of the code in this file was inspired by code by Gazagnaire.
|
||||
* Here is the original copyright:
|
||||
*
|
||||
* Copyright (c) 2009 Thomas Gazagnaire <thomas@gazagnaire.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*)
|
||||
open Common
|
||||
|
||||
(*****************************************************************************)
|
||||
(* Purpose *)
|
||||
(*****************************************************************************)
|
||||
(*
|
||||
* OCaml hacks to support reflection.
|
||||
*
|
||||
* OCaml does not support reflection, and it's a good thing: we love
|
||||
* strong type-checking that forbids too clever hacks like 'eval', or
|
||||
* run-time reflection; it's too much power for you, you will misuse
|
||||
* it. At the same time it's sometimes useful. So at least we could make
|
||||
* it possible to still reflect on the type definitions or values in
|
||||
* OCaml source code. We can do it by processing ML source code and
|
||||
* emitting ML source code containing under the form of regular ML
|
||||
* value or functions meta-information about information in other
|
||||
* source code files. It's a little bit a poor's man reflection mechanism,
|
||||
* because it's more manual, but it's for the best. Metaprogramming had
|
||||
* to be painful, because it is dangerous!
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* TODO
|
||||
*
|
||||
* In some sense we reimplement what is in the OCaml compiler, which
|
||||
* contains the full AST of OCaml source code. But the OCaml compiler
|
||||
* and its AST are too big, too scary for many tasks that would be satisfied
|
||||
* by a restricted but simpler AST.
|
||||
*
|
||||
* Camlp4 is obviously also a solution to this problem, but it has a
|
||||
* learning curve, and it's a slightly different world than the pure
|
||||
* regular OCaml world. So this module, and ocamltarzan together can
|
||||
* reduce the problem by taking the best of camlp4, while still
|
||||
* avoiding it.
|
||||
*
|
||||
*
|
||||
*
|
||||
* The support is partial. We support only the OCaml constructions
|
||||
* we found the most useful for programming stuff like
|
||||
* stub generators.
|
||||
*
|
||||
* less? not all OCaml so call it miniml.ml ? or reflection.ml ?
|
||||
*
|
||||
*
|
||||
* Notes: 2 worlds
|
||||
* - the type level world,
|
||||
* - the data level world
|
||||
*
|
||||
* Then there is whether the code is generated on the fly, or output somewhere
|
||||
* to be compiled and linked again (so 2 steps process, more manual, but
|
||||
* arguably less complicated magic)
|
||||
*
|
||||
* different level of (meta)programming:
|
||||
*
|
||||
* - programming in OCaml on OCaml values (classic)
|
||||
* - programming in OCaml on Sexp.t value of value
|
||||
* - programming in OCaml on Sexp.t value of type description
|
||||
* - programming in OCaml on OCaml.v value of value
|
||||
* - programming in OCaml on OCaml.t value of type description
|
||||
*
|
||||
* Depending on what you have to do, some levels are more suited than other.
|
||||
* For instance to do a show, to pretty print value, then sexp is good,
|
||||
* because really you just want to write code that handle 2 cases,
|
||||
* atoms and list. That's really what pretty printing is all about. You
|
||||
* could write a pretty printer for Ocaml.v, but it will need to handle
|
||||
* 10 cases. Now if you want to write a code generator for python, or an ORM,
|
||||
* then Ocaml.v is better than sexp, because in sexp you lost some valuable
|
||||
* information (that you may have to reverse engineer, like whether
|
||||
* a Sexp.List corresponds to a field, or a sum, or wether something is
|
||||
* null or an empty list, or wether it's an int or float, etc).
|
||||
*
|
||||
* Another way to do (meta)programming is:
|
||||
* - programming in Camlp4 on OCaml ast
|
||||
* - writing camlmix code to generate code.
|
||||
*
|
||||
* notes:
|
||||
* - sexp value or sexp of type description, not as precise, but easier to
|
||||
* write really generic code that do not need to have more information
|
||||
* about the sexp nodes (such as wether it's a field, a constuctor, etc)
|
||||
* - miniml value or type, not as precise that the regular type,
|
||||
* but more precise than sexp, and allow write some generic code.
|
||||
* - ocaml value (not type as you cant program at type level),
|
||||
* precise type checking, but can be tedious to write generic
|
||||
* code like generic visitors or pickler/unpicklers
|
||||
*
|
||||
* This file is working with ocamltarzan/pa/pa_type.ml (and so indirectly
|
||||
* it is working with camlp4).
|
||||
*
|
||||
* Note that can even generate sexp_of_x for miniML :) really
|
||||
* reflexive tower here
|
||||
*
|
||||
* Note that even if this module helps a programmer to avoid
|
||||
* using directly camlp4 to auto generate some code, it can
|
||||
* not solve all the tasks.
|
||||
*
|
||||
* history:
|
||||
* - Thought about it when wanting to do the ast_php.ml to be
|
||||
* transformed into a .adsl declaration to be able to generate
|
||||
* corresponding python classes using astgen.py.
|
||||
* - Thought about a miniMLType and miniMLValue, and then realize
|
||||
* that that was maybe what code in the ocaml-orm-sqlite
|
||||
* was doing (type-of et value-of), except I wanted the
|
||||
* ocamltarzan style of meta-programming instead of the camlp4 one.
|
||||
*
|
||||
*
|
||||
* Alternatives:
|
||||
* - camlp4
|
||||
* obviously camlp4 has access to the full AST of OCaml, but
|
||||
* that is one pb, that's too much. We often want only to do
|
||||
* analysis on the type
|
||||
* - type-conv
|
||||
* good, but force to use camlp4. Can use the generic sexplib
|
||||
* and then work on the generated sexp, but as explained below,
|
||||
* is will be on the value.
|
||||
* - use lib-sexp (just the sexp library part, not the camlp4 support part)
|
||||
* but not enough info. Even if usually
|
||||
* can reverse engineer the sexp to rediscover the type,
|
||||
* you will reverse engineer a value; what you want
|
||||
* is the sexp representation of the type! not a value of this type.
|
||||
* Also lib-sexp autogenerated code can be hard to understand, especially
|
||||
* if the type definition is complex. A good side effect of ocaml.ml
|
||||
* is that it provides an intermediate step :) So even if you
|
||||
* could pretty print value from your def to sexp directly, you could
|
||||
* also use transform your value into a Ocaml.v, then use
|
||||
* the somehow more readable function that translate a v into a sexp,
|
||||
* and same when wanting to read a value from a sexp, by using
|
||||
* again Ocaml.v as an intermediate. It's nevertheless obviously
|
||||
* less efficient.
|
||||
*
|
||||
* - zephyr, or thrift ?
|
||||
* - F# ?
|
||||
* - Lisp/Scheme ?
|
||||
* - .Net interoperability
|
||||
*
|
||||
*)
|
||||
|
||||
(*****************************************************************************)
|
||||
(* Types *)
|
||||
(*****************************************************************************)
|
||||
|
||||
(* src:
|
||||
* - orm-sqlite/value/value.ml
|
||||
* (itself a fork of http://xenbits.xen.org/xapi/xen-api-libs.hg?file/7a17b2ab5cfc/rpc-light/rpc.ml)
|
||||
* - orm-sqlite/type-of/type.ml
|
||||
*
|
||||
* update: Gazagnaire made a paper about that.
|
||||
*
|
||||
* modifications:
|
||||
* - slightly renamed the types and rearrange order of constructors. Could
|
||||
* have use nested modules to allow to reuse Int in different contexts,
|
||||
* but I actually prefer to prefix the values with the V, so when debugging
|
||||
* stuff, it's clearer that what you are looking are values, not types
|
||||
* (even if the ocaml toplevel would prefix the value with a V. or T.,
|
||||
* but sexp would not)
|
||||
* - Changed Int of int option
|
||||
* - Introduced List, Apply, Poly
|
||||
* - debugging support (using sexp :) )
|
||||
*)
|
||||
|
||||
(* OCaml type definitions *)
|
||||
type t =
|
||||
| Unit
|
||||
| Bool | Float | Char | String | Int
|
||||
|
||||
| Tuple of t list
|
||||
| Dict of (string * [`RW|`RO] * t) list
|
||||
| Sum of (string * t list) list
|
||||
|
||||
| Var of string
|
||||
| Poly of string
|
||||
| Arrow of t * t
|
||||
|
||||
| Apply of string * t
|
||||
|
||||
(* special cases of Apply *)
|
||||
| Option of t
|
||||
| List of t
|
||||
|
||||
(* todo? split in another type, because here it's the left part,
|
||||
* whereas before is the right part of a type definition. Also
|
||||
* have not the polymorphic args to some defs like ('a, 'b) Hashbtbl
|
||||
* | Rec of string * t
|
||||
* | Ext of string * t
|
||||
*
|
||||
* | Enum of t (* ??? *)
|
||||
*)
|
||||
|
||||
| TTODO of string
|
||||
(* with tarzan *)
|
||||
|
||||
(* OCaml values (a restricted form of expressions) *)
|
||||
type v =
|
||||
| VUnit
|
||||
| VBool of bool | VFloat of float | VInt of int (* was int64 *)
|
||||
| VChar of char | VString of string
|
||||
|
||||
| VTuple of v list
|
||||
| VDict of (string * v) list
|
||||
| VSum of string * v list
|
||||
|
||||
| VVar of (string * int64)
|
||||
| VArrow of string
|
||||
|
||||
(* special cases *)
|
||||
| VNone | VSome of v
|
||||
| VList of v list
|
||||
| VRef of v
|
||||
|
||||
(*
|
||||
| VEnum of v list (* ??? *)
|
||||
| VRec of (string * int64) * v
|
||||
| VExt of (string * int64) * v
|
||||
*)
|
||||
|
||||
| VTODO of string
|
||||
(* with tarzan *)
|
||||
|
||||
(*****************************************************************************)
|
||||
(* Helpers *)
|
||||
(*****************************************************************************)
|
||||
|
||||
(* the generated code can use that if he wants *)
|
||||
let (_htype: (string, t) Hashtbl.t) =
|
||||
Hashtbl.create 101
|
||||
let (add_new_type: string -> t -> unit) = fun s t ->
|
||||
Hashtbl.add _htype s t
|
||||
let (get_type: string -> t) = fun s ->
|
||||
Hashtbl.find _htype s
|
||||
|
||||
|
||||
|
||||
(* for generated code that want to transform and in and out of a v or t *)
|
||||
let vof_unit () =
|
||||
VUnit
|
||||
let vof_int x =
|
||||
VInt ((*Int64.of_int*) x)
|
||||
let vof_float x =
|
||||
VFloat ((*Int64.of_int*) x)
|
||||
let vof_string x =
|
||||
VString x
|
||||
let vof_bool b =
|
||||
VBool b
|
||||
let vof_list ofa x =
|
||||
VList (List.map ofa x)
|
||||
let vof_option ofa x =
|
||||
match x with
|
||||
| None -> VNone
|
||||
| Some x -> VSome (ofa x)
|
||||
let vof_ref ofa x =
|
||||
match x with
|
||||
| {contents = x } -> VRef (ofa x)
|
||||
let vof_either _of_a _of_b =
|
||||
function
|
||||
| Left v1 -> let v1 = _of_a v1 in VSum (("Left", [ v1 ]))
|
||||
| Right v1 -> let v1 = _of_b v1 in VSum (("Right", [ v1 ]))
|
||||
|
||||
let vof_either3 _of_a _of_b _of_c =
|
||||
function
|
||||
| Left3 v1 -> let v1 = _of_a v1 in VSum (("Left3", [ v1 ]))
|
||||
| Middle3 v1 -> let v1 = _of_b v1 in VSum (("Middle3", [ v1 ]))
|
||||
| Right3 v1 -> let v1 = _of_c v1 in VSum (("Right3", [ v1 ]))
|
||||
|
||||
|
||||
|
||||
let int_ofv = function
|
||||
| VInt x -> x
|
||||
| _ -> failwith "ofv: was expecting a VInt"
|
||||
let float_ofv = function
|
||||
| VFloat x -> x
|
||||
| _ -> failwith "ofv: was expecting a VFloat"
|
||||
let string_ofv = function
|
||||
| VString x -> x
|
||||
| _ -> failwith "ofv: was expecting a VString"
|
||||
let unit_ofv = function
|
||||
| VUnit -> ()
|
||||
| _ -> failwith "ofv: was expecting a VUnit"
|
||||
|
||||
let list_ofv a__of_sexp sexp = match sexp with
|
||||
| VList lst ->
|
||||
let rev_lst = List.rev_map a__of_sexp lst in
|
||||
List.rev rev_lst
|
||||
| _ -> failwith "list_ofv: VLlist needed"
|
||||
|
||||
let option_ofv a__of_sexp sexp = match sexp with
|
||||
| VNone -> None
|
||||
| VSome x -> Some (a__of_sexp x)
|
||||
| _ -> failwith "option_ofv: VNone or VSome needed"
|
||||
|
||||
(*****************************************************************************)
|
||||
(* Format pretty printers *)
|
||||
(*****************************************************************************)
|
||||
let add_sep xs =
|
||||
xs +> List.map (fun x -> Right x) +> Common2.join_gen (Left ())
|
||||
|
||||
(*
|
||||
* OCaml value pretty printer. A similar functionnality is provided by
|
||||
* the OCaml toplevel interpreter ('/usr/bin/ocaml') but
|
||||
* sometimes it is useful to print values from a regular command
|
||||
* line program. You don't always want to run the ocaml interpreter (or
|
||||
* customized interpreter built by ocamlmktop), and type an expression
|
||||
* in to get the printed value.
|
||||
*
|
||||
* The v_of_xxx generated code by ocamltarzan is
|
||||
* the first part to make this possible. The function below
|
||||
* is the second part.
|
||||
*
|
||||
* The '@[', '@,', etc are Format printf tags. See the doc of the Format
|
||||
* module in the OCaml manual to understand their meaning. Mainly,
|
||||
* @[ and @] open and close a pretty print box, and '@ ' and '@,'
|
||||
* are to give breaking hints to the pretty printer.
|
||||
*
|
||||
* The output can be copy pasted in ML code directly, which can be
|
||||
* useful when you want to pattern match over complex ocaml value.
|
||||
*)
|
||||
|
||||
let string_of_v v =
|
||||
Common2.format_to_string (fun () ->
|
||||
let ppf = Format.printf in
|
||||
let rec aux v =
|
||||
match v with
|
||||
| VUnit -> ppf "()"
|
||||
| VBool v1 ->
|
||||
if v1
|
||||
then ppf "true"
|
||||
else ppf "false"
|
||||
| VFloat v1 -> ppf "%f" v1
|
||||
| VChar v1 -> ppf "'%c'" v1
|
||||
| VString v1 -> ppf "\"%s\"" v1
|
||||
| VInt i -> ppf "%d" i
|
||||
| VTuple xs ->
|
||||
ppf "(@[";
|
||||
xs +> add_sep +> List.iter (function
|
||||
| Left _ -> ppf ",@ ";
|
||||
| Right v -> aux v
|
||||
);
|
||||
ppf "@])";
|
||||
| VDict xs ->
|
||||
ppf "{@[";
|
||||
xs +> List.iter (fun (s, v) ->
|
||||
(* less: could open a box there too? *)
|
||||
ppf "@,%s=" s;
|
||||
aux v;
|
||||
ppf ";@ ";
|
||||
);
|
||||
ppf "@]}";
|
||||
|
||||
| VSum ((s, xs)) ->
|
||||
(match xs with
|
||||
| [] -> ppf "%s" s
|
||||
| y::ys ->
|
||||
ppf "@[<hov 2>%s(@," s;
|
||||
xs +> add_sep +> List.iter (function
|
||||
| Left _ -> ppf ",@ ";
|
||||
| Right v -> aux v
|
||||
);
|
||||
ppf "@])";
|
||||
)
|
||||
|
||||
| VVar (s, i64) -> ppf "%s_%d" s (Int64.to_int i64)
|
||||
| VArrow v1 -> failwith "Arrow TODO"
|
||||
| VNone -> ppf "None";
|
||||
| VSome v -> ppf "Some(@["; aux v; ppf "@])";
|
||||
| VRef v -> ppf "Ref(@["; aux v; ppf "@])";
|
||||
| VList xs ->
|
||||
ppf "[@[<hov>";
|
||||
xs +> add_sep +> List.iter (function
|
||||
| Left _ -> ppf ";@ ";
|
||||
| Right v -> aux v
|
||||
);
|
||||
ppf "@]]";
|
||||
| VTODO v1 -> ppf "VTODO"
|
||||
in
|
||||
aux v
|
||||
)
|
||||
|
||||
(*****************************************************************************)
|
||||
(* Mapper Visitor *)
|
||||
(*****************************************************************************)
|
||||
|
||||
let map_of_unit x = ()
|
||||
let map_of_bool x = x
|
||||
let map_of_float x = x
|
||||
let map_of_char x = x
|
||||
let map_of_string (s:string) = s
|
||||
|
||||
let map_of_ref aref x = x (* dont go into ref *)
|
||||
let map_of_option v_of_a v =
|
||||
match v with
|
||||
| None -> None
|
||||
| Some x -> Some (v_of_a x)
|
||||
let map_of_list of_a xs =
|
||||
List.map of_a xs
|
||||
let map_of_int x = x
|
||||
let map_of_int64 x = x
|
||||
|
||||
let map_of_either _of_a _of_b =
|
||||
function
|
||||
| Left v1 -> let v1 = _of_a v1 in Left ((v1))
|
||||
| Right v1 -> let v1 = _of_b v1 in Right ((v1))
|
||||
|
||||
let map_of_either3 _of_a _of_b _of_c =
|
||||
function
|
||||
| Left3 v1 -> let v1 = _of_a v1 in Left3 ((v1))
|
||||
| Middle3 v1 -> let v1 = _of_b v1 in Middle3 ((v1))
|
||||
| Right3 v1 -> let v1 = _of_c v1 in Right3 ((v1))
|
||||
|
||||
|
||||
(* this is subtle ... *)
|
||||
let rec (map_v: f:( k:(v -> v) -> v -> v) -> v -> v) =
|
||||
fun ~f x ->
|
||||
|
||||
let rec map_v v =
|
||||
(* generated by ocamltarzan with: camlp4o -o /tmp/yyy.ml -I pa/ pa_type_conv.cmo pa_map.cmo pr_o.cmo /tmp/xxx.ml *)
|
||||
let rec k x =
|
||||
match x with
|
||||
| VUnit -> VUnit
|
||||
| VBool v1 -> let v1 = map_of_bool v1 in VBool ((v1))
|
||||
| VFloat v1 -> let v1 = map_of_float v1 in VFloat ((v1))
|
||||
| VChar v1 -> let v1 = map_of_char v1 in VChar ((v1))
|
||||
| VString v1 -> let v1 = map_of_string v1 in VString ((v1))
|
||||
| VInt v1 -> let v1 = map_of_int v1 in VInt ((v1))
|
||||
| VTuple v1 -> let v1 = map_of_list map_v v1 in VTuple ((v1))
|
||||
| VDict v1 ->
|
||||
let v1 =
|
||||
map_of_list
|
||||
(fun (v1, v2) ->
|
||||
let v1 = map_of_string v1 and v2 = map_v v2 in (v1, v2))
|
||||
v1
|
||||
in VDict ((v1))
|
||||
| VSum ((v1, v2)) ->
|
||||
let v1 = map_of_string v1
|
||||
and v2 = map_of_list map_v v2
|
||||
in VSum ((v1, v2))
|
||||
| VVar v1 ->
|
||||
let v1 =
|
||||
(match v1 with
|
||||
| (v1, v2) ->
|
||||
let v1 = map_of_string v1 and v2 = map_of_int64 v2 in (v1, v2))
|
||||
in VVar ((v1))
|
||||
| VArrow v1 -> let v1 = map_of_string v1 in VArrow ((v1))
|
||||
| VNone -> VNone
|
||||
| VSome v1 -> let v1 = map_v v1 in VSome ((v1))
|
||||
| VRef v1 -> let v1 = map_v v1 in VRef ((v1))
|
||||
| VList v1 -> let v1 = map_of_list map_v v1 in VList ((v1))
|
||||
| VTODO v1 -> let v1 = map_of_string v1 in VTODO ((v1))
|
||||
in
|
||||
f ~k v
|
||||
in
|
||||
map_v x
|
||||
|
||||
(*****************************************************************************)
|
||||
(* Iterator Visitor *)
|
||||
(*****************************************************************************)
|
||||
|
||||
let v_unit x = ()
|
||||
let v_bool x = ()
|
||||
let v_int x = ()
|
||||
let v_string (s:string) = ()
|
||||
let v_ref aref x = () (* dont go into ref *)
|
||||
let v_option v_of_a v =
|
||||
match v with
|
||||
| None -> ()
|
||||
| Some x -> v_of_a x
|
||||
let v_list of_a xs =
|
||||
List.iter of_a xs
|
||||
|
||||
let v_either of_a of_b x =
|
||||
match x with
|
||||
| Left a -> of_a a
|
||||
| Right b -> of_b b
|
||||
|
||||
let v_either3 of_a of_b of_c x =
|
||||
match x with
|
||||
| Left3 a -> of_a a
|
||||
| Middle3 b -> of_b b
|
||||
| Right3 c -> of_c c
|
||||
Loading…
Add table
Add a link
Reference in a new issue