123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2017, 2018 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.
*)modulePiqirun=Piqi_piqirunmoduleT=Piqi_impl_piqimoduleRecord=T.RecordmoduleField=T.FieldmoduleVariant=T.VariantmoduleOption=T.OptionmoduleEnum=T.EnummoduleAlias=T.AliasmoduleImport=T.ImportmoduleInclud=T.IncludmoduleExtend=T.ExtendmoduleAny=T.AnymoduleR=RecordmoduleF=FieldmoduleV=VariantmoduleO=OptionmoduleE=EnummoduleA=AliasmoduleL=T.Piqi_listmoduleP=T.PiqimoduleConfig=Piqi_configmoduleIolist=Piqi_iolistmoduleU=Piqi_util(* NOTE: Std can be opened explicitly as U.Std or C.Std or included implicitly
* by opening Piqi_common *)moduleStd=U.StdincludeStdtypepiq_ast=Piq_ast.ast(*
* common global variables
*)(* provide default values automatically when loading data from various intput
* formats: Piq, Wire, JSON *)letresolve_defaults=reffalse(* whether we are parsing Piqi right now *)letis_inside_parse_piqi=reffalse(* a subset of "Piqi.piqi_spec.P.resolved_typedef" (i.e. Piqi self-spec) that
* corresponds to built-in types (built-in types are aliases that have
* .piqi-type property defined; this value is set from Piqi.boot () function *)letbuiltin_typedefs:T.typedeflistref=ref[](*
* Common functions
*)letsome_of=function|Somex->x|None->assertfalse(* Run a function with a specific resolve_defauls setting. The global
* "resolve_defaults" variable is preserved *)letwith_resolve_defaultsnew_resolve_defaultsf=U.with_boolresolve_defaultsnew_resolve_defaultsfletget_parent(typedef:T.typedef):T.namespace=letparent=matchtypedefwith|`recordt->t.R.parent|`variantt->t.V.parent|`enumt->t.E.parent|`aliast->t.A.parent|`listt->t.L.parentinsome_ofparentletset_parent(def:T.typedef)(parent:T.namespace)=letparent=Someparentinmatchdefwith|`recordx->x.R.parent<-parent|`variantx->x.V.parent<-parent|`enumx->x.E.parent<-parent|`aliasx->x.A.parent<-parent|`listx->x.L.parent<-parentletget_parent_piqi(def:T.typedef):T.piqi=letparent=get_parentdefinmatchparentwith|`importx->some_ofx.Import.piqi|`piqix->xlettypedef_name(typedef:T.typedef)=letres=matchtypedefwith|`recordx->x.R.name|`variantx->x.V.name|`enumx->x.E.name|`aliasx->x.A.name|`listx->x.L.nameinsome_ofres(* whether typedef represents a built-in type *)letis_builtin_def(typedef:T.typedef)=matchtypedefwith|`aliasx->x.A.piqi_type<>None|_->falseletpiqi_typename(t:T.piqtype)=letopenTin(* XXX: built-in types should't be used at that stage *)matchtwith|`int->"int"|`float->"float"|`bool->"bool"|`string->"string"|`binary->"binary"|`any->"piqi-any"|#T.typedefasx->typedef_namexletfull_piqi_typenamex=letname=piqi_typenamexinmatchxwith|#T.typedefasdef->ifis_builtin_defdefthennameelseletpiqi=get_parent_piqidefinletparent_name=some_ofpiqi.P.modnameinifname="piq-node"&&parent_name="embedded/piq"then"piq"elseparent_name^"/"^name|_->(* built-in type *)(* XXX: normally built-in types should be used at the time when this
* function is used *)nameletname_of_fieldf=letopenT.Fieldinmatchf.name,f.piqtypewith|Somen,_->n|None,Somet->piqi_typenamet|_whenf.typename<>None->(* field type hasn't been resolved yet *)Piqi_name.get_local_name(some_off.typename)|_->assertfalseletname_of_optiono=letopenT.Optioninmatcho.name,o.piqtypewith|Somen,_->n|None,Somet->piqi_typenamet|_wheno.typename<>None->(* option type hasn't been resolved yet *)Piqi_name.get_local_name(some_ofo.typename)|_->assertfalseletrecunalias=function|`aliast->lett=some_oft.T.Alias.piqtypeinunaliast|t->tletis_typedeft=matchunaliastwith|#T.typedef->true|_->false(* is record or list or alias of the two *)letis_container_typet=matchunaliastwith|(#T.typedefast)->(matchtwith|`record_|`list_->true|_->false)|_->false(* check if the module is a Piqi self-specification, i.e. the module's name is
* "piqi" or it includes another module named "piqi" *)letis_self_spec(piqi:T.piqi)=(* XXX: cache this information to avoid computing it over and over again *)List.exists(funx->matchx.P.modnamewith|None->false|Somemodname->(* check if the last segment equals "piqi" which is a reserved name
* for a self-spec *)Piqi_name.get_local_namemodname="piqi")piqi.P.included_piqi(* check if any of the module's definitions depends on "piqi-any" type *)letdepends_on_piqi_any(piqi:T.piqi)=letauxx=letis_anyx=(unaliasx)=`anyinletis_any_opt=function|Somex->is_anyx|None->falseinmatchxwith|`recordx->List.exists(funx->is_any_optx.F.piqtype)x.R.field|`variantx->List.exists(funx->is_any_optx.O.piqtype)x.V.option|`listx->is_any(some_ofx.L.piqtype)|`enum_->false|`alias_->false(* don't check aliases, we do unalias instead *)inList.existsauxpiqi.P.resolved_typedef(*
* error reporting, printing and handling
* TODO: move these functions to piqi_util.ml
*)letstring_of_loc(file,line,col)=file^":"^string_of_intline^":"^string_of_intcolletstrerrlocs=string_of_locloc^": "^sletstring_of_exnexn=Printexc.to_stringexn(* this is not supported in all runtime modes, and it is not compatible with
* OCaml 3.10 *)(*
Printexc.to_string exn ^ " ; backtrace: " ^ Printexc.get_backtrace ()
*)(* piq/piqi language error *)exceptionErrorofPiqloc.loc*string(* piqi utility error *)exceptionPiqi_errorofstringletpiqi_errors=raise(Piqi_errors)letpiqi_warnings=prerr_endline("Warning: "^s)leterror_atlocs=(*
failwith (strerr loc s)
*)raise(Error(loc,s))letreferencefx=Piqloc.referencefxletlocationobj=tryPiqloc.findobjwithNot_found->("unknown",0,0)leterrorobjs=letloc=locationobjinerror_atlocsleterror_stringobjs=letloc=locationobjinstrerrlocsletwarningobjs=letloc=locationobjinifnot!Config.flag_no_warningsthenprerr_endline("Warning: "^strerrlocs)lettrace_indent=ref0letprint_trace_indent()=fori=1to!trace_indentdoprerr_string" "doneleteprintf_ifcondfmt=ifcondthenbeginprint_trace_indent();(* XXX: it turns out OCaml 4.02 doesn't do line buffering for stderr
* automatically; flushing stderr ourselves
*
Printf.fprintf stderr fmt
*)Printf.kfprintf(funstderr->flushstderr)stderrfmtendelse(*
Printf.ifprintf stderr fmt
*)Printf.ikfprintf(fun_->())stderrfmtletdebugfmt=eprintf_if(!Config.debug_level>1)fmtlettracefmt=eprintf_if(!Config.flag_trace||!Config.debug_level>0)fmtlettrace_enter()=incrtrace_indentlettrace_leavex=decrtrace_indent;x