123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282(*
Copyright 2009, 2010, 2011, 2012, 2013, 2014, 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.
*)(* handling of Piq-specific Piqi properties *)moduleC=Piqi_commonopenC(* check correspondent between primitive Piqi type and Piq representation format
*)letcheck_piq_formatobjpiq_formatpiqtype=letpiqtype=C.unaliaspiqtypeinmatchpiq_format,piqtypewith|`word,`string->()|`text,`string->()|_whenC.is_typedefpiqtype->errorobj("piq-format can not be defined for non-primitive type "^U.quote(C.piqi_typenamepiqtype))|_->errorobj("invalid piq-format for type "^U.quote(C.piqi_typenamepiqtype))letrecresolve_piq_format(piqtype:T.piqtype)=(* upper-level setting overrides lower-level setting *)matchpiqtypewith|`aliasx->letpiq_format=x.A.piq_formatinifpiq_format<>Nonethenpiq_formatelse(* try looking in lower-level aliases *)resolve_piq_format(some_ofx.A.piqtype)|_->None(* piq format can not be defined for non-primitive types *)letcheck_resolve_piq_formatobjpiq_formatpiqtype=matchpiq_format,piqtypewith|Somef,Somet->(* already defined, just check *)check_piq_formatobjft;piq_format|None,Somet->resolve_piq_formatt|Somet,None->errorobj"piq-format can not be defined when there is no type"|None,None->Noneletresolve_field_piq_formatx=letopenFinx.piq_format<-check_resolve_piq_formatxx.piq_formatx.piqtypeletresolve_option_piq_formatx=letopenOinx.piq_format<-check_resolve_piq_formatxx.piq_formatx.piqtypeletresolve_typedef_piq_format=function|`recordr->List.iterresolve_field_piq_formatr.R.field|`variantv->List.iterresolve_option_piq_formatv.V.option|`aliasa->a.A.piq_format<-check_resolve_piq_formataa.A.piq_formata.A.piqtype|`listl->l.L.piq_format<-check_resolve_piq_formatll.L.piq_formatl.L.piqtype|`enum_->()letprocess_field_piq_positionalrecord_piq_positionalx=letopenFinbegin(matchx.name,x.typenamewith|Some_,None->(* flag *)ifx.piq_positional=Sometruethenerrorx"flags can not be positional"|_->());(* inherit the record-level setting when the local per-field setting is
* missing *)ifx.piq_positional=Nonethenx.piq_positional<-record_piq_positionalendletprocess_typedef_piq_positional=function|`recordx->List.iter(process_field_piq_positionalx.R.piq_positional)x.R.field|_->()letcheck_namex=ifnot(Piqi_name.is_valid_namex)thenerrorx("invalid piq alias name: "^U.quotex)else()letcheck_opt_name=function|None->()|Somex->check_namexletcheck_field_piq_aliasx=check_opt_namex.F.piq_aliasletcheck_option_piq_aliasx=check_opt_namex.O.piq_aliasletcheck_typedef_piq_alias=function|`recordx->List.itercheck_field_piq_aliasx.R.field|`variantx->List.itercheck_option_piq_aliasx.V.option|_->()letprocess_typedefstypedefs=(* resolve Piq representation format settings *)List.iterresolve_typedef_piq_formattypedefs;(* stuff related to .piq-positional property *)List.iterprocess_typedef_piq_positionaltypedefs;(* validate .piq-alias names
* TODO, XXX: check for all sorts of duplicates; warn if .name masks
* .piq-alias *)List.itercheck_typedef_piq_aliastypedefs;()(* for internal use only: string -> piq_ast *)letpiq_of_strings:piq_ast=letpiq_parser=Piq_parser.init_from_string"embedded"sinletres=tryPiq_parser.read_allpiq_parserwithC.Error((_,lnum',cnum'),error)->(* string location can be missing when we parse from Piq embedded some
* other representation, e.g. Protobuf or XML *)let(fname,lnum,cnum)=tryPiqloc.findswithNot_found->("embedded",1,-1)in(* XXX, TODO: adjust location -- this does't work well if there are
* escaped characters in a Json string; in particular, newlines (which are
* always escaped in json strings) will throw things off significantly *)letloc=(fname,lnum+lnum'-1,cnum+cnum')inC.error_atloc("error parsing embedded Piq: "^error)inmatchreswith|[x]->x|_::o::_->C.erroro"string includes more than one Piq value"|[]->C.errors"string doesn't have Piq data"let_=Piqobj.piq_of_string:=(funx->piq_of_stringx);Piqobj.string_of_piq:=(funx->Piq_gen.to_stringx~nl:false)letrecto_portable_ast(ast:piq_ast):Piq_piqi.piq_node=letloc=trylet(file,line,column)=Piqloc.findastinSomePiq_piqi.Loc.({file=file;line=line;column=column})withNot_found->Noneinletpiq=matchastwith|`int(x,_)->`intx|`uint(x,_)->`uintx|`float(x,_)->`floatx|`boolx->`boolx|`string(x,_)->`stringx|`raw_stringx->`raw_stringx|`wordx->`wordx|`textx->`textx|`binary(x,_)->`binaryx|`namex->`namex|`typenamex->`typenamex|`named{Piq_ast.Named.name=name;Piq_ast.Named.value=value}->`namedPiq_piqi.Named.({name=name;value=to_portable_astvalue})|`typed{Piq_ast.Typed.typename=typename;Piq_ast.Typed.value=value}->`typedPiq_piqi.Typed.({typename=typename;value=to_portable_astvalue})|`listl->`list(List.mapto_portable_astl)|`form(`namename,args)->`splice{Piq_piqi.Splice.name=name;Piq_piqi.Splice.item=List.mapto_portable_astargs}(* XXX, TODO: any shouldn't be used in plain Piq ASTs? *)|`form_->assertfalse|`any_->assertfalseinPiq_piqi.Piq_node.({piq=piq;loc=loc})letaddloclocast=(* TODO: this is not enough -- need to add location to the option values
* themselves *)Piqloc.setlocloc;(matchastwith|`int(x,_)->Piqloc.addx|`uint(x,_)->Piqloc.addx|`float(x,_)->Piqloc.addx|`boolx->()|`string(x,_)->Piqloc.addx|`raw_stringx->Piqloc.addx|`wordx->Piqloc.addx|`textx->Piqloc.addx|`binary(x,_)->Piqloc.addx|`namex->Piqloc.addx|`typenamex->Piqloc.addx|`named{Piq_ast.Named.name=name;Piq_ast.Named.value=value}->Piqloc.addname|`typed{Piq_ast.Typed.typename=typename;Piq_ast.Typed.value=value}->Piqloc.addtypename|`listl->Piqloc.addl|`form((`names)asname,args)->Piqloc.adds;Piqloc.addname(* XXX, TODO: any shouldn't be used in plain Piq ASTs? *)|`form(_,_)->assertfalse|`any_->assertfalse);Piqloc.addastletrecof_portable_ast(piq_node:Piq_piqi.piq_node):piq_ast=letrecauxpiq_node=letast=matchpiq_node.Piq_piqi.Piq_node.piqwith|`intx->`int(x,"")|`uintx->`uint(x,"")|`floatx->`float(x,"")|`boolx->`boolx|`stringx->`string(x,"")|`raw_stringx->`raw_stringx|`wordx->`wordx|`textx->`textx|`binaryx->`binary(x,"")|`namex->`namex|`typenamex->`typenamex|`named{Piq_piqi.Named.name=name;Piq_piqi.Named.value=value}->`namedPiq_ast.Named.({name=name;value=auxvalue})|`typed{Piq_piqi.Typed.typename=typename;Piq_piqi.Typed.value=value}->`typedPiq_ast.Typed.({typename=typename;value=auxvalue})|`listl->`list(List.mapauxl)|`splice{Piq_piqi.Splice.name=name;Piq_piqi.Splice.item=items}->`form(`namename,List.mapauxitems)inletopenPiq_piqi.Locin(matchpiq_node.Piq_piqi.Piq_node.locwith|None->()|Some{file=file;line=line;column=column}->letloc=(file,line,column)inaddloclocast);astinauxpiq_node