123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2017 Anton Lavrik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*)modulerecPiqobj:sigtyperecord=Record.ttypevariant=Variant.ttypeenum=Enum.ttypealias=Alias.ttypelist=List.ttypefield=Field.ttypeoption=Option.ttypeany=Any.ttypetypedef=[`recordofrecord|`variantofvariant|`enumofenum|`aliasofalias|`listoflist]typeobj=[typedef(* built-in types *)|`intofint64(* XXX: use big_int for internal representation? *)|`uintofint64|`floatoffloat|`boolofbool|`stringofstring|`binaryofstring|`anyofany]end=PiqobjandRecord:sigtypet={mutablet:Piqi_impl_piqi.record;mutablefield:Piqobj.fieldlist;mutableunparsed_piq_fields_ref:intoption;}end=RecordandField:sigtypet={mutablet:Piqi_impl_piqi.field;mutableobj:Piqobj.objoption;}end=FieldandVariant:sigtypet={mutablet:Piqi_impl_piqi.variant;mutableoption:Piqobj.option;}end=VariantandEnum:sigtypet={mutablet:Piqi_impl_piqi.enum;mutableoption:Piqobj.option;}end=EnumandOption:sigtypet={mutablet:Piqi_impl_piqi.option;mutableobj:Piqobj.objoption;(* None for named options, i.e. constants *)}end=OptionandList:sigtypet={mutablet:Piqi_impl_piqi.piqi_list;mutableobj:Piqobj.objlist;}end=ListandAlias:sigtypet={mutablet:Piqi_impl_piqi.alias;mutableobj:Piqobj.obj;}end=AliasandAny:sigtypet={(* type of the object, when the type is known (NOTE: object can be
* untyped *)mutabletypename:stringoption;(* internal representation of a typed data object *)mutableobj:Piqobj.objoption;(* external representation in various formats *)mutablepb:stringoption;(* protocol buffers binary *)mutablepiq_ast:Piq_ast.astoption;(* the original embedded json reprsented as a string *)mutablejson_string:stringoption;mutablejson_ast:Piqi_json_type.jsonoption;mutablexml_ast:Piqi_xml_type.xml_elemoption;(* unique reference to self in Piqi_objstore *)mutableref:intoption;}end=AnymoduleC=Piqi_commonmoduleU=C.UopenC.Stdletdefault_any=Any.({obj=None;typename=None;pb=None;piq_ast=None;json_string=None;json_ast=None;xml_ast=None;ref=None;})letrecunalias(obj:Piqobj.obj)=matchobjwith|`aliasx->unaliasx.Alias.obj|_->obj(* store Piqobj.any and return reference of the stored object in Piqi_objstore
*)letput_any(any:Piqobj.any):int=letopenAnyinmatchany.refwith|Someref->ref|None->(* FIXME: memory leak by allocating elements in objstore and never
* freeing them *)letref=Piqi_objstore.putanyinany.ref<-Someref;C.debug"Piqobj.put_any: with ref %d\n"ref;ref(* find Piqobj.any by reference in Piqi_objstore *)letget_any(ref:int):Piqobj.any=Piqi_objstore.getrefletmake_piqi_any_from_obj?typename(obj:Piqobj.obj)=letany=Any.({default_anywithobj=Someobj;typename=typename;})in(* cache the value in objstore and in the piqi_any itself *)letref=put_anyanyinC.debug"Piqobj.make_any_from_obj: creating new any with ref %d\n"ref;letpiqi_any=Piqi_impl_piqi.Any.({(Piqi_impl_piqi.default_any())withref=Someref;typename=typename;})inpiqi_anyletany_of_piqi_any(piqi_any:Piqi_impl_piqi.any):Piqobj.any=matchpiqi_any.Piqi_impl_piqi.Any.refwith|Someref->(* recover internally passed Piqobj.any from an integer reference *)C.debug"Piqobj.any_of_piqi_any: recovering any from existing ref %d\n"ref;get_anyref|None->(* NOTE: this branch is used when the function is called from
* Piqi.resolve_field_default when Piqi is read from Protobuf during
* Piqi.boot *)letany=Any.({default_anywithtypename=piqi_any.Piqi_impl_piqi.Any.typename;pb=piqi_any.Piqi_impl_piqi.Any.protobuf;})in(* cache the value in objstore and in the piqi_any itself *)letref=put_anyanyinC.debug"Piqobj.any_of_piqi_any: creating new any with ref %d\n"ref;piqi_any.Piqi_impl_piqi.Any.ref<-Someref;any(* these functions will be properly set by piqobj_to* modules *)letto_pb(obj:Piqobj.obj):string=assertfalseletto_piq(obj:Piqobj.obj):Piq_ast.ast=assertfalseletto_json(obj:Piqobj.obj):Piqi_json_type.json=assertfalseletto_xml(obj:Piqobj.obj):Piqi_xml_type.xmllist=assertfalseletof_pb(piqtype:Piqi_impl_piqi.piqtype)(x:string):Piqobj.obj=assertfalseletof_piq(piqtype:Piqi_impl_piqi.piqtype)(x:Piq_ast.ast):Piqobj.obj=assertfalseletof_json(piqtype:Piqi_impl_piqi.piqtype)(x:Piqi_json_type.json):Piqobj.obj=assertfalseletof_xml(piqtype:Piqi_impl_piqi.piqtype)(x:Piqi_xml_type.xml_elem):Piqobj.obj=assertfalseletto_pb=refto_pbletto_piq=refto_piqletto_json=refto_jsonletto_xml=refto_xmlletof_pb=refof_pbletof_piq=refof_piqletof_json=refof_jsonletof_xml=refof_xml(* these function will be set by correspondent piqi_json* and piqi_xml* modules;
* they are used for unptyped json
* TODO: find a better module for these functions *)letjson_of_string(x:string):Piqi_json_type.json=assertfalseletxml_of_string(x:string):Piqi_xml_type.xmllist=assertfalseletpiq_of_string(x:string):Piq_ast.ast=assertfalseletstring_of_json(x:Piqi_json_type.json):string=assertfalseletstring_of_xml(x:Piqi_xml_type.xml):string=assertfalseletstring_of_piq(x:Piq_ast.ast):string=assertfalseletjson_of_string=refjson_of_stringletxml_of_string=refxml_of_stringletpiq_of_string=refpiq_of_stringletstring_of_json=refstring_of_jsonletstring_of_xml=refstring_of_xmlletstring_of_piq=refstring_of_piqletof_any(piqtype:Piqi_impl_piqi.piqtype)(any:Piqobj.any):Piqobj.objoption=letopenAnyinifany.pb<>None(* try parsing from Protobuf *)thenletobj=!of_pbpiqtype(C.some_ofany.pb)inSomeobjelseifany.piq_ast<>Nonethenletobj=!of_piqpiqtype(C.some_ofany.piq_ast)inSomeobjelseifany.json_ast<>Nonethenletobj=!of_jsonpiqtype(C.some_ofany.json_ast)inSomeobjelseifany.xml_ast<>Nonethenletobj=!of_xmlpiqtype(C.some_ofany.xml_ast)inSomeobjelseNone(* resolve obj from any given, possibly given its type *)letresolve_obj?(piqtype:Piqi_impl_piqi.piqtypeoption)(any:Piqobj.any):unit=letopenAnyinifany.obj<>Nonethen()(* already resolved *)else(letdo_resolve_objpiqtype=(* XXX: cache typename -- disabling for now, because it breaks
* reversibility -- why add an extra typename?
*
* XXX: do not use fully qualified names for locally defined types? *)(*
if any.typename = None
then any.typename <- Some (C.full_piqi_typename piqtype);
*)letobj=of_anypiqtypeanyinany.obj<-objinmatchpiqtype,any.typenamewith|Somet,_->(* XXX: when both are present, check their correspondence? *)C.debug"Piqobj.resolve_obj using known type\n";do_resolve_objt|None,Sometypename->C.debug"Piqobj.resolve_obj using type %s\n"typename;do_resolve_obj(Piqi_db.find_piqtypetypename)|_->()(* can't resolve, because type is unknown *))letpiq_of_any(any:Piqobj.any):Piq_ast.astoption=letopenAnyinmatchany.piq_astwith|(Some_)asres->res|_->((* resolve obj if it wasn't resolved before *)resolve_objany;matchany.objwith|None->None(* obj could't be resolved *)|Someobj->letast=!to_piqobjinany.piq_ast<-Someast;(* XXX: cache the result *)Someast)letpb_of_any(any:Piqobj.any):stringoption=letopenAnyinmatchany.pbwith|(Some_)asres->res|_->((* resolve obj if it wasn't resolved before *)resolve_objany;matchany.objwith|None->None(* obj could't be resolved *)|Someobj->Piqloc.pause();letpb=!to_pb(C.some_ofany.obj)inPiqloc.resume();any.pb<-Somepb;(* XXX: cache the result *)Somepb)letjson_of_any(any:Piqobj.any):Piqi_json_type.jsonoption=letopenAnyinmatchany.json_astwith|Some_whenany.json_string<>None->(* TODO: this is rather inefficient and redundant -- a better solution
* would be to change that JSON AST representation and parse literals
* into intermediate representation (piqobj) format in piqobj_of_json.ml
*)lets=C.some_ofany.json_stringin(* already validated, we just need to parse it in pretty-printed mode so
* that we can print it nicely while preserving the original int, float
* and string literals *)Piqloc.pause();(* no need to preserve location information here *)letjson_ast=U.with_boolPiqi_config.pp_modetrue(fun()->!json_of_strings)inPiqloc.resume();Somejson_ast|(Some_)asres->res|_->((* resolve obj if it wasn't resolved before *)resolve_objany;matchany.objwith|None->None(* obj could't be resolved *)|Someobj->Piqloc.pause();letjson=!to_jsonobjinPiqloc.resume();any.json_ast<-Somejson;(* XXX: cache the result *)Somejson)letxml_of_any(any:Piqobj.any):Piqi_xml_type.xmllistoption=letopenAnyinmatchany.xml_astwith|Some(_name,xml_list)->Somexml_list|_->((* resolve obj if it wasn't resolved before *)resolve_objany;matchany.objwith|None->None(* obj could't be resolved *)|Someobj->Piqloc.pause();letxml_list=!to_xmlobjinPiqloc.resume();any.xml_ast<-Some("undefined",xml_list);(* XXX: cache the result *)Somexml_list)(* this is used internally mostly for piq extensions and default values that are
* guaranteed to have piq representation *)letpiq_of_piqi_anypiqi_any:Piq_ast.ast=letany=any_of_piqi_anypiqi_anyinC.some_of(piq_of_anyany)(* same as the above, plus this is used only by piqic *)letpb_of_piqi_anypiqi_any:string=letany=any_of_piqi_anypiqi_anyinC.some_of(pb_of_anyany)includePiqobj