Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file ocaml.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498(*
* 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.
*)openCommon(*****************************************************************************)(* 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 *)typet=|Unit|Bool|Float|Char|String|Int|Tupleoftlist|Dictof(string*[`RW|`RO]*t)list|Sumof(string*tlist)list|Varofstring|Polyofstring|Arrowoft*t|Applyofstring*t(* special cases of Apply *)|Optionoft|Listoft(* 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 (* ??? *)
*)|TTODOofstring(* with tarzan *)(* OCaml values (a restricted form of expressions) *)typev=|VUnit|VBoolofbool|VFloatoffloat|VIntofint(* was int64 *)|VCharofchar|VStringofstring|VTupleofvlist|VDictof(string*v)list|VSumofstring*vlist|VVarof(string*int64)|VArrowofstring(* special cases *)|VNone|VSomeofv|VListofvlist|VRefofv(*
| VEnum of v list (* ??? *)
| VRec of (string * int64) * v
| VExt of (string * int64) * v
*)|VTODOofstring(* with tarzan *)(*****************************************************************************)(* Helpers *)(*****************************************************************************)(* the generated code can use that if he wants *)let(_htype:(string,t)Hashtbl.t)=Hashtbl.create101let(add_new_type:string->t->unit)=funst->Hashtbl.add_htypestlet(get_type:string->t)=funs->Hashtbl.find_htypes(* for generated code that want to transform and in and out of a v or t *)letvof_unit()=VUnitletvof_intx=VInt((*Int64.of_int*)x)letvof_floatx=VFloat((*Int64.of_int*)x)letvof_stringx=VStringxletvof_boolb=VBoolbletvof_listofax=VList(List.mapofax)letvof_optionofax=matchxwith|None->VNone|Somex->VSome(ofax)letvof_refofax=matchxwith|{contents=x}->VRef(ofax)letvof_either_of_a_of_b=function|Leftv1->letv1=_of_av1inVSum(("Left",[v1]))|Rightv1->letv1=_of_bv1inVSum(("Right",[v1]))letvof_either3_of_a_of_b_of_c=function|Left3v1->letv1=_of_av1inVSum(("Left3",[v1]))|Middle3v1->letv1=_of_bv1inVSum(("Middle3",[v1]))|Right3v1->letv1=_of_cv1inVSum(("Right3",[v1]))letint_ofv=function|VIntx->x|_->failwith"ofv: was expecting a VInt"letfloat_ofv=function|VFloatx->x|_->failwith"ofv: was expecting a VFloat"letstring_ofv=function|VStringx->x|_->failwith"ofv: was expecting a VString"letunit_ofv=function|VUnit->()|_->failwith"ofv: was expecting a VUnit"letlist_ofva__of_sexpsexp=matchsexpwith|VListlst->letrev_lst=List.rev_mapa__of_sexplstinList.revrev_lst|_->failwith"list_ofv: VLlist needed"letoption_ofva__of_sexpsexp=matchsexpwith|VNone->None|VSomex->Some(a__of_sexpx)|_->failwith"option_ofv: VNone or VSome needed"(*****************************************************************************)(* Format pretty printers *)(*****************************************************************************)letadd_sepxs=xs+>List.map(funx->Rightx)+>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.
*)letstring_of_vv=Common2.format_to_string(fun()->letppf=Format.printfinletrecauxv=matchvwith|VUnit->ppf"()"|VBoolv1->ifv1thenppf"true"elseppf"false"|VFloatv1->ppf"%f"v1|VCharv1->ppf"'%c'"v1|VStringv1->ppf"\"%s\""v1|VInti->ppf"%d"i|VTuplexs->ppf"(@[";xs+>add_sep+>List.iter(function|Left_->ppf",@ ";|Rightv->auxv);ppf"@])";|VDictxs->ppf"{@[";xs+>List.iter(fun(s,v)->(* less: could open a box there too? *)ppf"@,%s="s;auxv;ppf";@ ";);ppf"@]}";|VSum((s,xs))->(matchxswith|[]->ppf"%s"s|y::ys->ppf"@[<hov 2>%s(@,"s;xs+>add_sep+>List.iter(function|Left_->ppf",@ ";|Rightv->auxv);ppf"@])";)|VVar(s,i64)->ppf"%s_%d"s(Int64.to_inti64)|VArrowv1->failwith"Arrow TODO"|VNone->ppf"None";|VSomev->ppf"Some(@[";auxv;ppf"@])";|VRefv->ppf"Ref(@[";auxv;ppf"@])";|VListxs->ppf"[@[<hov>";xs+>add_sep+>List.iter(function|Left_->ppf";@ ";|Rightv->auxv);ppf"@]]";|VTODOv1->ppf"VTODO"inauxv)(*****************************************************************************)(* Mapper Visitor *)(*****************************************************************************)letmap_of_unitx=()letmap_of_boolx=xletmap_of_floatx=xletmap_of_charx=xletmap_of_string(s:string)=sletmap_of_refarefx=x(* dont go into ref *)letmap_of_optionv_of_av=matchvwith|None->None|Somex->Some(v_of_ax)letmap_of_listof_axs=List.mapof_axsletmap_of_intx=xletmap_of_int64x=xletmap_of_either_of_a_of_b=function|Leftv1->letv1=_of_av1inLeft((v1))|Rightv1->letv1=_of_bv1inRight((v1))letmap_of_either3_of_a_of_b_of_c=function|Left3v1->letv1=_of_av1inLeft3((v1))|Middle3v1->letv1=_of_bv1inMiddle3((v1))|Right3v1->letv1=_of_cv1inRight3((v1))(* this is subtle ... *)letrec(map_v:f:(k:(v->v)->v->v)->v->v)=fun~fx->letrecmap_vv=(* generated by ocamltarzan with: camlp4o -o /tmp/yyy.ml -I pa/ pa_type_conv.cmo pa_map.cmo pr_o.cmo /tmp/xxx.ml *)letreckx=matchxwith|VUnit->VUnit|VBoolv1->letv1=map_of_boolv1inVBool((v1))|VFloatv1->letv1=map_of_floatv1inVFloat((v1))|VCharv1->letv1=map_of_charv1inVChar((v1))|VStringv1->letv1=map_of_stringv1inVString((v1))|VIntv1->letv1=map_of_intv1inVInt((v1))|VTuplev1->letv1=map_of_listmap_vv1inVTuple((v1))|VDictv1->letv1=map_of_list(fun(v1,v2)->letv1=map_of_stringv1andv2=map_vv2in(v1,v2))v1inVDict((v1))|VSum((v1,v2))->letv1=map_of_stringv1andv2=map_of_listmap_vv2inVSum((v1,v2))|VVarv1->letv1=(matchv1with|(v1,v2)->letv1=map_of_stringv1andv2=map_of_int64v2in(v1,v2))inVVar((v1))|VArrowv1->letv1=map_of_stringv1inVArrow((v1))|VNone->VNone|VSomev1->letv1=map_vv1inVSome((v1))|VRefv1->letv1=map_vv1inVRef((v1))|VListv1->letv1=map_of_listmap_vv1inVList((v1))|VTODOv1->letv1=map_of_stringv1inVTODO((v1))inf~kvinmap_vx(*****************************************************************************)(* Iterator Visitor *)(*****************************************************************************)letv_unitx=()letv_boolx=()letv_intx=()letv_string(s:string)=()letv_refarefx=()(* dont go into ref *)letv_optionv_of_av=matchvwith|None->()|Somex->v_of_axletv_listof_axs=List.iterof_axsletv_eitherof_aof_bx=matchxwith|Lefta->of_aa|Rightb->of_bbletv_either3of_aof_bof_cx=matchxwith|Left3a->of_aa|Middle3b->of_bb|Right3c->of_cc