1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249(*
* Copyright (c) 2016 Xavier R. Guérin <copyright@applepine.org>
*
* 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.
*)openMigrate_parsetreeopenAst_405openAst_convenience_405openAst_mapperopenAsttypesopenParsetreeopenLexingopenPrintf(*
* Version management
*)letocaml_version=Versions.ocaml_405(* Type definition *)moduleEntity=structtypet={txt:string;exp:Parsetree.expression;pat:Parsetree.pattern}letmksym=leti=ref1000infunname->incri;leti=!iinsprintf"__ppxbitstring_%s_%d"namei;;letmake~locv=lettxt=mksymvin{txt;exp=evar~loctxt;pat=pvar~loctxt}endmoduleContext=structtypet={dat:Entity.t;off:Entity.t;len:Entity.t}letmake~loc=letdat=Entity.make~loc"dat"andoff=Entity.make~loc"off"andlen=Entity.make~loc"len"in{dat;off;len}letnext~loct=letoff=Entity.make~loc"off"andlen=Entity.make~loc"len"in{twithoff;len}endmoduleType=structtypet=|Int|String|BitstringendmoduleSign=structtypet=|Signed|Unsignedletto_string=function|Signed->"signed"|Unsigned->"unsigned"endmoduleEndian=structtypet=|Little|Big|Native|ReferredofParsetree.expressionletto_string=function|Little->"le"|Big->"be"|Native->"ne"|Referred_->"ee"endmoduleQualifiers=structtypet={value_type:Type.toption;sign:Sign.toption;endian:Endian.toption;check:Parsetree.expressionoption;bind:Parsetree.expressionoption;map:Parsetree.expressionoption;save_offset_to:Parsetree.expressionoption;offset:Parsetree.expressionoption;}letempty={value_type=None;sign=None;endian=None;check=None;bind=None;map=None;save_offset_to=None;offset=None;}letdefault={value_type=SomeType.Int;sign=SomeSign.Unsigned;endian=SomeEndian.Big;check=None;bind=None;map=None;save_offset_to=None;offset=None;}letset_value_type_defaultq=matchq.value_typewith|None->{qwithvalue_type=SomeType.Int}|_->q;;letset_sign_defaultq=matchq.signwith|None->{qwithsign=SomeSign.Unsigned}|_->q;;letset_endian_defaultq=matchq.endianwith|None->{qwithendian=SomeEndian.Big}|_->q;;letset_defaultsv=v|>set_value_type_default|>set_sign_default|>set_endian_default;;endmoduleMatchField=structtypebitlen=(Parsetree.expression*intoption);;typetuple={pat:Parsetree.pattern;len:bitlen;qls:Qualifiers.t;opt:bool}typet=|AnyofParsetree.pattern|Tupleoftuple;;end(* Exception *)letlocation_exn~locmsg=Location.Error(Location.error~locmsg)|>raise;;(* Helper functions *)letsplit_string~ons=Str.split(Str.regexpon)s;;letoption_bindoptf=matchoptwith|None->None|Somev->fv;;letrecprocess_expr_loc~locexpr=matchexprwith|{pexp_desc=Pexp_ident(ident)}->letlident=Location.mklocident.txtlocin{exprwithpexp_desc=Pexp_ident(lident);pexp_loc=loc}|{pexp_desc=Pexp_tuple(ops)}->letfld=List.fold_left(funaccexp->acc@[process_expr_loc~locexp])[]opsin{exprwithpexp_desc=Pexp_tuple(fld);pexp_loc=loc}|{pexp_desc=Pexp_construct(ident,ops)}->letlident=Location.mklocident.txtlocinletlops=beginmatchopswith|Someo->Some(process_expr_loc~loco)|None->Noneendin{exprwithpexp_desc=Pexp_construct(lident,lops);pexp_loc=loc}|{pexp_desc=Pexp_apply(ident,ops)}->letlident=process_expr_loc~locidentinletfld=List.fold_left(funacc(lbl,exp)->acc@[(lbl,(process_expr_loc~locexp))])[]opsin{exprwithpexp_desc=Pexp_apply(lident,fld);pexp_loc=loc}|{pexp_desc=Pexp_fun(ident,ops,{ppat_desc=Ppat_var(pid);ppat_loc;ppat_attributes},exp)}->letlpid=Location.mklocpid.txtlocinletlpat={ppat_desc=Ppat_varlpid;ppat_loc=loc;ppat_attributes}inletlops=beginmatchopswith|Someo->Some(process_expr_loc~loco)|None->Noneendinletlexp=process_expr_loc~locexpin{exprwithpexp_desc=Pexp_fun(ident,lops,lpat,lexp);pexp_loc=loc}|_->{exprwithpexp_loc=loc};;letparse_exprexpr=tryParse.expressionVersions.ocaml_405(Lexing.from_stringexpr.txt)|>process_expr_loc~loc:expr.locwith_->location_exn~loc:expr.loc("Parse expression error: '"^expr.txt^"'");;letrecprocess_pat_loc~locpat=matchpatwith|{ppat_desc=Ppat_var(ident);ppat_loc;ppat_attributes}->letlident=Location.mklocident.txtlocin{ppat_desc=Ppat_var(lident);ppat_loc=loc;ppat_attributes}|_->{patwithppat_loc=loc};;letparse_patternpat=tryParse.patternVersions.ocaml_405(Lexing.from_stringpat.txt)|>process_pat_loc~loc:pat.locwith_->location_exn~loc:pat.loc("Parse pattern error: '"^pat.txt^"'");;(* Location parser and splitter *)letfind_loc_boundaries~loclastrem=letopenLocationinlet{loc_start;loc_end;loc_ghost}=locinletxtr_lines=List.lengthreminletxtr_char=List.fold_left(+)xtr_linesreminletne={loc_startwithpos_lnum=loc_start.pos_lnum+xtr_lines;pos_bol=loc_start.pos_bol+xtr_char;pos_cnum=loc_start.pos_cnum+xtr_char+last}andns=ifxtr_lines=0then{loc_startwithpos_cnum=loc_start.pos_cnum+xtr_char+last+1}else{loc_startwithpos_lnum=loc_start.pos_lnum+xtr_lines;pos_bol=loc_start.pos_bol+xtr_char;pos_cnum=loc_start.pos_cnum+xtr_char}inlettloc={loc_start;loc_end=ne;loc_ghost}inletnloc={loc_start=ns;loc_end;loc_ghost}in(tloc,nloc);;letrecsplit_loc_rec~loc=function|[]->[]|hd::tl->letline_list=split_string~on:"\n"hd|>List.rev|>List.mapString.lengthinbeginmatchline_listwith|[]->[]|last::rem->let(tloc,nloc)=find_loc_boundaries~loclastremin[tloc]@(split_loc_rec~loc:nloctl)end;;letsplit_loc~loclst=split_loc_rec~loclst|>List.map2(funeloc->Location.mkloc(String.trime)loc)lst;;(* Processing qualifiers *)letcheck_map_functorsub=matchsubwith|[%expr(fun[%p?_]->[%e?_])]->Some(sub)|_->None;;letprocess_qualstatequal=letopenQualifiersinletloc=qual.pexp_locinmatchqualwith|[%exprint]->beginmatchstate.value_typewith|Somev->location_exn~loc"Value type redefined"|None->{statewithvalue_type=SomeType.Int}end|[%exprstring]->beginmatchstate.value_typewith|Somev->location_exn~loc"Value type redefined"|None->{statewithvalue_type=SomeType.String}end|[%exprbitstring]->beginmatchstate.value_typewith|Somev->location_exn~loc"Value type redefined"|None->{statewithvalue_type=SomeType.Bitstring}end|[%exprsigned]->beginmatchstate.signwith|Somev->location_exn~loc"Signedness redefined"|None->{statewithsign=SomeSign.Signed}end|[%exprunsigned]->beginmatchstate.signwith|Somev->location_exn~loc"Signedness redefined"|None->{statewithsign=SomeSign.Unsigned}end|[%exprlittleendian]->beginmatchstate.endianwith|Somev->location_exn~loc"Endianness redefined"|None->{statewithendian=SomeEndian.Little}end|[%exprbigendian]->beginmatchstate.endianwith|Somev->location_exn~loc"Endianness redefined"|None->{statewithendian=SomeEndian.Big}end|[%exprnativeendian]->beginmatchstate.endianwith|Somev->location_exn~loc"Endianness redefined"|None->{statewithendian=SomeEndian.Native}end|[%exprendian[%e?sub]]->beginmatchstate.endianwith|Somev->location_exn~loc"Endianness redefined"|None->{statewithendian=Some(Endian.Referredsub)}end|[%exprbind[%e?sub]]->beginmatchstate.bind,state.mapwith|Someb,None->location_exn~loc"Bind expression redefined"|None,Somem->location_exn~loc"Map expression already defined"|Someb,Somem->location_exn~loc"Inconsistent internal state"|None,None->{statewithbind=Somesub}end|[%exprmap[%e?sub]]->beginmatchstate.bind,state.mapwith|Someb,None->location_exn~loc"Bind expression already defined"|None,Somem->location_exn~loc"Map expression redefined"|Someb,Somem->location_exn~loc"Inconsistent internal state"|None,None->beginmatchcheck_map_functorsubwith|Somesub->{statewithmap=Somesub}|None->location_exn~loc"Invalid map functor"endend|[%exprcheck[%e?sub]]->beginmatchstate.checkwith|Somev->location_exn~loc"Check expression redefined"|None->{statewithcheck=Somesub}end|[%exprsave_offset_to[%e?sub]]->beginmatchstate.save_offset_towith|Somev->location_exn~loc"Save offset expression redefined"|None->{statewithsave_offset_to=Somesub}end|[%exproffset[%e?sub]]->beginmatchstate.offsetwith|Somev->location_exn~loc"Offset expression redefined"|None->{statewithoffset=Somesub}end|_->location_exn~loc"Invalid qualifier";;letparse_qualsquals=letexpr=parse_exprqualsinletrecprocess_qualsstate=function|[]->state|hd::tl->process_quals(process_qualstatehd)tlinmatchexprwith(* single named qualifiers *)|{pexp_desc=Pexp_ident(_)}->process_qualQualifiers.emptyexpr(* single functional qualifiers *)|{pexp_desc=Pexp_apply(_,_)}->process_qualQualifiers.emptyexpr(* multiple qualifiers *)|{pexp_desc=Pexp_tuple(e)}->process_qualsQualifiers.emptye(* Unrecognized expression *)|expr->location_exn~loc:expr.pexp_loc"Invalid qualifiers list";;(* Processing expression *)letrecevaluate_expr=function|[%expr[%e?lhs]+[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(l+r)|_->Noneend|[%expr[%e?lhs]-[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(l-r)|_->Noneend|[%expr[%e?lhs]*[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(l*r)|_->Noneend|[%expr[%e?lhs]/[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(l/r)|_->Noneend|[%expr[%e?lhs]land[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(llandr)|_->Noneend|[%expr[%e?lhs]lor[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(llorr)|_->Noneend|[%expr[%e?lhs]lxor[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(llxorr)|_->Noneend|[%expr[%e?lhs]lsr[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(llsrr)|_->Noneend|[%expr[%e?lhs]asr[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(lasrr)|_->Noneend|[%expr[%e?lhs]mod[%e?rhs]]->beginmatchevaluate_exprlhs,evaluate_exprrhswith|Somel,Somer->Some(lmodr)|_->Noneend|{pexp_desc=Pexp_constant(const)}->beginmatchconstwith|Pconst_integer(i,_)->Some(int_of_stringi)|_->Noneend|_->None;;(* Parsing fields *)letparse_match_fieldsstr=letopenMatchFieldinsplit_string~on:":"str.txt|>split_loc~loc:str.loc|>function|[{txt="_";loc}aspat]->MatchField.Any(parse_patternpat)|[spat;slen]->letqls=Qualifiers.defaultandeln=parse_exprslenandpat=parse_patternspatandopt=falseinletlen=(eln,evaluate_expreln)inMatchField.Tuple{pat;len;qls;opt}|[spat;slen;sqls]->letqls=Qualifiers.set_defaults(parse_qualssqls)andeln=parse_exprslenandpat=parse_patternspatandopt=falseinletlen=(eln,evaluate_expreln)inMatchField.Tuple{pat;len;qls;opt}|[stmt]->letpat_str=stmt.txtinlocation_exn~loc:stmt.loc("Invalid statement: '"^pat_str^"'")|_->location_exn~loc:str.loc"Invalid number of fields in statement";;(*
* Some operators like the subtype cast operator (:>) can throw off the parser.
* The function below resolve these ambiguities on a case-by-case basis.
*)letstitch_ambiguous_operatorslst=letfne=function|[]->[e]|hd::tlwhenhd=""||e==""->e::hd::tl|hd::tlwhenStr.first_charshd1=">"->(e^":"^hd)::tl|l->e::linList.fold_rightfnlst[]letparse_const_fieldsstr=letopenQualifiersinsplit_string~on:":"str.txt|>stitch_ambiguous_operators|>split_loc~loc:str.loc|>function|[vl;len]->(parse_exprvl,Some(parse_exprlen),SomeQualifiers.default)|[vl;len;quals]->letq=Qualifiers.set_defaults(parse_qualsquals)inbeginmatchq.bind,q.map,q.check,q.save_offset_towith|Some_,_,_,_->location_exn~loc:str.loc"Bind meaningless in constructor"|_,Some_,_,_->location_exn~loc:str.loc"Map meaningless in constructor"|_,_,Some_,_->location_exn~loc:str.loc"Check meaningless in constructor"|_,_,_,Some_->location_exn~loc:str.loc"Saving offset meaningless in constructor"|None,None,None,None->(parse_exprvl,Some(parse_exprlen),Some(q))end|[stmt]->letpat_str=stmt.txtinlocation_exn~loc:stmt.loc("Invalid statement: '"^pat_str^"'")|_->location_exn~loc:str.loc"Invalid number of fields in statement";;(* Match generators *)letcheck_field_len~locfld=let(l,v)=fld.MatchField.leninmatchv,fld.MatchField.qls.Qualifiers.value_typewith|Some(n),Some(Type.String)->ifn<-1||(n>0&&(nmod8)<>0)thenlocation_exn~loc"Length of string must be > 0 and multiple of 8, or the special value -1"elseSomen|Some(n),Some(Type.Bitstring)->ifn<-1thenlocation_exn~loc"Length of bitstring must be >= 0 or the special value -1"elseSomen|Some(n),Some(Type.Int)->ifn<1||n>64thenlocation_exn~loc"Length of int field must be [1..64]"elseSomen|None,Some(_)->None|_,None->location_exn~loc"No type to check";;letget_inttype~loc~fastpath=function|vwhenv>8&&v<=16->iffastpaththen"int16"else"int"|vwhenv>16&&v<=31->iffastpaththen"int32"else"int"|vwhenv=32->"int32"|vwhenv>32&&v<=64->"int64"|_->location_exn~loc"Invalid integer size"letgen_int_extractor_static~locnxtsizesignendian=letedat=nxt.Context.dat.Entity.expandeoff=nxt.Context.off.Entity.expinletsn=Sign.to_stringsignandft=get_inttype~loc~fastpath:truesizeanden=Endian.to_stringendianinletfp=sprintf"Bitstring.extract_fastpath_%s_%s_%s"ftensnin[%expr[%eevar~locfp][%eedat]([%eeoff]lsr3)][@metalocloc];;letgen_int_extractor_dynamic~locnxtsizesignendian=letedat=nxt.Context.dat.Entity.expandeoff=nxt.Context.off.Entity.expandelen=nxt.Context.len.Entity.expinletsn=Sign.to_stringsignandit=get_inttype~loc~fastpath:falsesizeanden=Endian.to_stringendianinletex=sprintf"Bitstring.extract_%s_%s_%s"itensnin[%expr[%eevar~locex][%eedat][%eeoff][%eelen][%eint~locsize]][@metalocloc];;letgen_int_extractor~locnxtfld=letopenQualifiersinlet(l,v)=fld.MatchField.leninletedat=nxt.Context.dat.Entity.expandeoff=nxt.Context.off.Entity.expandelen=nxt.Context.len.Entity.expinmatchv,fld.MatchField.qls.sign,fld.MatchField.qls.endianwith(* 1-bit type *)|Some(size),Some(_),Some(_)whensize=1->[%exprBitstring.extract_bit[%eedat][%eeoff][%eelen][%el]][@metalocloc](* 8-bit type *)|Some(size),Some(sign),Some(_)whensize>=2&&size<=8->letex=sprintf"Bitstring.extract_char_%s"(Sign.to_stringsign)in[%expr[%eevar~locex][%eedat][%eeoff][%eelen][%eint~locsize]][@metalocloc](* 16|32|64-bit type with referred endianness *)|Some(size),Some(sign),Some(Endian.Referredr)->letss=Sign.to_stringsignandit=get_inttype~loc~fastpath:falsesizeinletex=sprintf"Bitstring.extract_%s_ee_%s"itssin[%expr[%eevar~locex]([%er])[%eedat][%eeoff][%eelen][%eint~locsize]][@metalocloc](* 16|32|64-bit type with immediate endianness *)|Some(size),Some(sign),Some(endian)->iffld.MatchField.optthengen_int_extractor_static~locnxtsizesignendianelsegen_int_extractor_dynamic~locnxtsizesignendian(* Variable size *)|None,Some(sign),Some(Endian.Referredr)->letss=Sign.to_stringsigninletex=sprintf"Bitstring.extract_int64_ee_%s"ssin[%expr[%eevar~locex]([%er])[%eedat][%eeoff][%eelen]([%el])][@metalocloc]|None,Some(sign),Some(endian)->letes=Endian.to_stringendianandss=Sign.to_stringsigninletex=sprintf"Bitstring.extract_int64_%s_%s"esssin[%expr[%eevar~locex][%eedat][%eeoff][%eelen]([%el])][@metalocloc](* Invalid type *)|_,_,_->location_exn~loc"Invalid type";;letgen_extractor~locnxtfld=letopenQualifiersinlet(l,v)=fld.MatchField.leninletedat=nxt.Context.dat.Entity.expandeoff=nxt.Context.off.Entity.expandelen=nxt.Context.len.Entity.expinmatchfld.MatchField.qls.value_typewith|Some(Type.Bitstring)->beginmatchvwith|Some(-1)->[%expr([%eedat],[%eeoff],[%eelen])][@metalocloc]|Some(_)|None->[%expr([%eedat],[%eeoff],[%el])][@metalocloc]end|Some(Type.String)->[%expr(Bitstring.string_of_bitstring([%eedat],[%eeoff],[%el]))][@metalocloc]|Some(Type.Int)->gen_int_extractor~locnxtfld|_->location_exn~loc"Invalid type";;letgen_value~locfldresbeh=letopenQualifiersinmatchfld.MatchField.qls.bind,fld.MatchField.qls.mapwith|Someb,None->[%exprlet[%pfld.pat]=[%eb]in[%ebeh]][@metalocloc]|None,Somem->[%exprlet[%pfld.pat]=[%em][%eres]in[%ebeh]][@metalocloc]|_,_->beh;;letrecgen_next~loccurnxtfldbehfields=letopenEntityinletopenContextinlet(l,v)=fld.MatchField.leninmatchvwith|Some(-1)->[%exprlet[%pnxt.off.pat]=[%enxt.off.exp]+[%enxt.len.exp]and[%pnxt.len.pat]=0in[%e(gen_fields~loccurnxtbehfields)]][@metalocloc]|Some(_)|None->[%exprlet[%pnxt.off.pat]=[%enxt.off.exp]+[%el]and[%pnxt.len.pat]=[%enxt.len.exp]-[%el]in[%e(gen_fields~loccurnxtbehfields)]][@metalocloc]andgen_next_all~loccurnxtbehfields=letopenEntityinletopenContextin[%exprlet[%pnxt.off.pat]=[%enxt.off.exp]+[%enxt.len.exp]and[%pnxt.len.pat]=0in[%e(gen_fields~loccurnxtbehfields)]][@metalocloc]andgen_match_check~loc=function|Somechk->chk|None->constr~loc"true"[]andgen_match~loccurnxtfldbehfields=letopenEntityinletopenContextinletopenQualifiersinletvalue=Entity.make~loc"val"and(l,_)=fld.MatchField.leninletmcheck=gen_match_check~locfld.MatchField.qls.checkandmfields=gen_fields~loccurnxtbehfieldsandmres=gen_extractor~locnxtfldinletmwrap=gen_value~locfldvalue.expmfieldsinletmcase=[%exprbeginmatch[%evalue.exp]with|[%pfld.MatchField.pat]when[%emcheck]->[%emwrap]|_->()end][@metalocloc]in[%exprlet[%pvalue.pat]=[%emres]and[%pnxt.off.pat]=[%enxt.off.exp]+[%el]and[%pnxt.len.pat]=[%enxt.len.exp]-[%el]in[%emcase]][@metalocloc]andgen_offset~loccurnxtfldbeh=letopenContextinletopenEntityinletopenQualifiersinmatchfld.MatchField.qls.offsetwith|Some({pexp_loc}asoff)->[%exprlet[%pnxt.off.pat]=[%ecur.off.exp]+[%eoff]in[%ebeh]][@metalocpexp_loc]|None->behandgen_offset_saver~loccurnxtfldbeh=letopenContextinletopenEntityinletopenQualifiersinmatchfld.MatchField.qls.save_offset_towith|Some{pexp_desc=Pexp_ident({txt;loc=eloc})}->letptxt=pvar~loc:eloc(Longident.lasttxt)in[%exprlet[%pptxt]=[%enxt.off.exp]-[%ecur.off.exp]in[%ebeh]][@metaloceloc]|Some_|None->behandgen_unbound_string~loccurnxtfldbehfields=letopenEntityinletopenContextinletp=fld.MatchField.patinmatchpwith|{ppat_desc=Ppat_var(_)}->[%exprlet[%pp]=[%e(gen_extractor~locnxtfld)]in[%e(gen_next_all~loccurnxtbehfields)]][@metalocloc]|[%pat?_]->[%expr[%e(gen_next_all~loccurnxtbehfields)]][@metalocloc]|_->location_exn~loc"Unbound string or bitstring can only be assigned to a variable or skipped"andgen_bound_bitstring~loccurnxtfldbehfields=letopenEntityinletopenContextinletp=fld.MatchField.patand(l,_)=fld.MatchField.leninmatchpwith|{ppat_desc=Ppat_var(_)}->[%exprifPervasives.(>=)[%enxt.len.exp][%el]thenlet[%pp]=[%e(gen_extractor~locnxtfld)]in[%e(gen_next~loccurnxtfldbehfields)]else()][@metalocloc]|[%pat?_]->[%exprifPervasives.(>=)[%enxt.len.exp][%el]then[%e(gen_next~loccurnxtfldbehfields)]else()][@metalocloc]|_->location_exn~loc"Bound bitstring can only be assigned to variables or skipped"andgen_bound_string~loccurnxtfldbehfields=letopenEntityinletopenContextinlet(l,_)=fld.MatchField.lenin[%exprifPervasives.(>=)[%enxt.len.exp][%el]then[%e(gen_match~loccurnxtfldbehfields)]else()][@metalocloc]andgen_bound_int_with_size~loccurnxtfldbehfields=letopenEntityinletopenContextinlet(l,_)=fld.MatchField.lenin[%exprifPervasives.(>=)[%enxt.len.exp][%el]then[%e(gen_match~loccurnxtfldbehfields)]else()][@metalocloc]andgen_bound_int~loccurnxtfldbehfields=letopenEntityinletopenContextinlet(l,_)=fld.MatchField.lenin[%exprifPervasives.(>=)[%el]1&&Pervasives.(<=)[%el]64&&Pervasives.(>=)[%enxt.len.exp][%el]then[%e(gen_match~loccurnxtfldbehfields)]else()][@metalocloc]andgen_fields_with_quals_by_type~loccurnxtfldbehfields=letopenQualifiersinmatchcheck_field_len~locfld,fld.MatchField.qls.value_typewith|Some(-1),Some(Type.Bitstring|Type.String)->gen_unbound_string~loccurnxtfldbehfields|(Some(_)|None),Some(Type.Bitstring)->gen_bound_bitstring~loccurnxtfldbehfields|(Some(_)|None),Some(Type.String)->gen_bound_string~loccurnxtfldbehfields|Some(s),Some(Type.Int)->ifs>=1&&s<=64thengen_bound_int_with_size~loccurnxtfldbehfieldselselocation_exn~loc"Invalid bit length for type Integer"|None,Some(Type.Int)->gen_bound_int~loccurnxtfldbehfields|_,_->location_exn~loc"No type to generate"andgen_fields_with_quals~loccurnxtfldbehfields=gen_fields_with_quals_by_type~loccurnxtfldbehfields|>gen_offset_saver~loccurnxtfld|>gen_offset~loccurnxtfldandgen_fields~loccurnxtbehfields=letopenQualifiersinlet(exp,alias)=behinmatchfieldswith|[]->beginmatchaliaswith|None->exp|Somea->[%exprlet[%ppvar~loca]=([%ecur.dat.exp],[%ecur.off.exp],([%ecur.len.exp]-[%enxt.len.exp]))in[%eexp]][@metalocloc]end|MatchField.Any(_)::tl->beginmatchaliaswith|None->exp|Somea->[%exprlet[%ppvar~loca]=([%ecur.dat.exp],[%ecur.off.exp],[%ecur.len.exp])in[%eexp]][@metalocloc]end|MatchField.Tuple(fld)::tl->gen_fields_with_quals~loccurnxtfldbehtl;;letis_field_size_open_ended=function|(_,Some(-1))->true|_->falseletcheck_for_open_endednessfields=letcheckinitfld=letp=fld.MatchField.patandl=fld.MatchField.leninletoe=is_field_size_open_endedlinifinit||(oe&&init)thenlocation_exn~loc:p.ppat_loc"Pattern is already open-ended"elseoeinletinspectinit=function|MatchField.Any(_)->init&&false|MatchField.Tuplefld->checkinitfldinletrecscaninit=function|[]->()|hd::tl->scan(inspectinithd)tlinscanfalsefields;fields;;letmark_optimized_fastpathfields=letopenQualifiersinletopenMatchFieldinletcheck_fieldofftuple=matchtuplewith|{pat;len=(l,Some(v));qls={value_type=Some(Type.Int)};_}->if(offland7)=0&&(v=16||v=32||v=64)then(Some(off+v),MatchField.Tuple{tuplewithopt=true})else(None,MatchField.Tupletuple)|_->(None,MatchField.Tupletuple)inletcheck_offset_and_fieldoffsetfld=matchoffset,fldwith|Some(off),MatchField.Tuple(tuple)->check_fieldofftuple|_,_->(None,fld)inletrecscanoffsetresult=function|[]->result|hd::tl->let(noff,nfld)=check_offset_and_fieldoffsethdinscannoff(result@[nfld])tlinscan(Some0)[]fields;;letgen_case_constant~loccurnxtrescasevaluealias=letopenEntityinletbeh=[%expr[%eres.exp]:=Some([%ecase.pc_rhs]);raiseExit][@metalocloc]inletbeh=matchcase.pc_guardwith|None->beh|Somecond->[%exprif[%econd]then[%ebeh]else()][@metalocloc]insplit_string~on:";"value|>split_loc~loc|>List.mapparse_match_fields|>check_for_open_endedness|>mark_optimized_fastpath|>gen_fields~loccurnxt(beh,alias)letgen_casecurnxtrescase=letopenEntityinletloc=case.pc_lhs.ppat_locinmatchcase.pc_lhs.ppat_descwith|Ppat_constant(Pconst_string(value,_))->gen_case_constant~loccurnxtrescasevalueNone|Ppat_alias({ppat_desc=Ppat_constant(Pconst_string(value,_))},{txt=a})->gen_case_constant~loccurnxtrescasevalue(Somea)|_->location_exn~loc"Wrong pattern type";;letrecgen_cases_sequence~loc=function|[]->location_exn~loc"Empty case list"|[hd]->hd|hd::tl->[%expr[%ehd];[%egen_cases_sequence~loctl]][@metalocloc];;letgen_cases~locidentcases=letopenEntityinletopenContextinletcur=Context.make~locandres=Entity.make~loc"res"inletnxt=Context.next~loccurandtupl=[%pat?([%pcur.dat.pat],[%pcur.off.pat],[%pcur.len.pat])][@metalocloc]andfnam=str~locloc.Location.loc_start.pos_fnameandlpos=int~locloc.Location.loc_start.pos_lnumandcpos=int~loc(loc.Location.loc_start.pos_cnum-loc.Location.loc_start.pos_bol)inList.fold_left(funacccase->acc@[gen_casecurnxtrescase])[]cases|>gen_cases_sequence~loc|>funseq->[%exprlet[%ptupl]=[%eident]inlet[%pnxt.off.pat]=[%ecur.off.exp]and[%pnxt.len.pat]=[%ecur.len.exp]and[%pres.pat]=refNonein(try[%eseq];with|Exit->());match![%eres.exp]with|Somex->x|None->raise(Match_failure([%efnam],[%elpos],[%ecpos]))][@metalocloc];;letgen_function~loccases=letopenEntityinletcas=Entity.make~loc"case"in[%expr(fun[%pcas.pat]->[%e(gen_cases~loccas.expcases)])][@metalocloc](* Constructor generators *)letgen_constructor_exn~loc=letopenLocationin[%exprBitstring.Construct_failure([%estr~loc"Bad field value"],[%estr~locloc.loc_start.pos_fname],[%eint~locloc.loc_start.pos_lnum],[%eint~locloc.loc_start.pos_cnum])][@metalocloc];;letgen_constructor_bitstring~locsym(l,_,_)=[%exprBitstring.construct_bitstring[%esym.Entity.exp][%el]][@metalocloc];;letgen_constructor_string~locsym(l,_,_)=[%exprBitstring.construct_string[%esym.Entity.exp][%el]][@metalocloc];;letget_1_bit_constr_value~loc(l,_,_)=match(evaluate_exprl)with|Some(1)->[%exprtrue][@metalocloc]|Some(0)->[%exprfalse][@metalocloc]|Some(_)|None->l;;letgen_constructor_int~locsymfld=letopenQualifiersinlet(l,s,q)=fldinleteexc=gen_constructor_exn~locandesym=sym.Entity.expinlet(fnc,vl,sz)=match(evaluate_exprs),q.sign,q.endianwith(* 1-bit type *)|Some(size),Some(_),Some(_)whensize=1->(evar~loc"Bitstring.construct_bit",get_1_bit_constr_value~locfld,[%expr1])(* 8-bit type *)|Some(size),Some(sign),Some(_)whensize>=2&&size<=8->letsn=Sign.to_stringsigninletex=sprintf"Bitstring.construct_char_%s"snin(evar~locex,l,int~locsize)(* 16|32|64-bit type *)|Some(size),Some(sign),Some(Endian.Referredr)->letss=Sign.to_stringsignandit=get_inttype~loc~fastpath:falsesizeinletex=sprintf"Bitstring.construct_%s_ee_%s"itssin([%expr[%eevar~locex][%er]],l,s)|Some(size),Some(sign),Some(endian)->lettp=get_inttype~loc~fastpath:falsesizeanden=Endian.to_stringendianandsn=Sign.to_stringsigninletex=sprintf"Bitstring.construct_%s_%s_%s"tpensnin(evar~locex,l,int~locsize)(* Variable size types *)|None,Some(sign),Some(Endian.Referredr)->letss=Sign.to_stringsigninletex=sprintf"Bitstring.construct_int64_ee_%s"ssin([%expr[%eevar~locex][%er]],l,s)|None,Some(sign),Some(endian)->leten=Endian.to_stringendianandsn=Sign.to_stringsigninletex=sprintf"Bitstring.construct_int64_%s_%s"ensnin(evar~locex,l,s)(* Invalid type *)|_,_,_->location_exn~loc"Invalid type"in[%expr[%efnc][%eesym][%evl][%esz][%eeexc]][@metalocloc];;letgen_constructor_complete~locsymfld=let(_,_,q)=fldinmatchq.Qualifiers.value_typewith|Some(Type.Bitstring)->gen_constructor_bitstring~locsymfld|Some(Type.String)->gen_constructor_string~locsymfld|Some(Type.Int)->gen_constructor_int~locsymfld|_->location_exn~loc"Invalid type";;letgen_constructor~locsym=function|(f,Some(s),Some(q))->gen_constructor_complete~locsym(f,s,q)|_->location_exn~loc"Invalid field format";;letgen_assignment_size_of_sized_field~loc(f,s,q)=match(evaluate_exprs),option_bindq(funq->q.Qualifiers.value_type)with(* Deal with String type *)|Some(-1),Some(Type.String)->[%expr(String.length[%ef]*8)]|Some(v),Some(Type.String)whenv>0&&(vmod8)=0->s|Some(_),Some(Type.String)->location_exn~loc"Length of string must be > 0 and multiple of 8, or the special value -1"(* Deal with Bitstring type *)|Some(-1),Some(Type.Bitstring)->[%expr(Bitstring.bitstring_length[%ef])]|Some(v),Some(Type.Bitstring)whenv>0->s|Some(_),Some(Type.Bitstring)->location_exn~loc"Length of bitstring must be >= 0 or the special value -1"(* Deal with other types *)|Some(v),_whenv>0->s|Some(v),_->location_exn~loc"Negative or null field size in constructor"(* Unknown field size, arbitrary expression *)|None,_->s;;letgen_assignment_size_of_field~loc=function|(_,None,_)->[%expr0]|(f,Some(s),q)->gen_assignment_size_of_sized_field~loc(f,s,q);;letrecgen_assignment_size~loc=function|[]->[%expr0]|field::tl->letthis=gen_assignment_size_of_field~locfieldinletnext=gen_assignment_size~loctlin[%expr[%ethis]+([%enext])][@metalocloc];;letgen_assignment_behavior~locsymfields=letsize=gen_assignment_size~locfieldsinletres=sym.Entity.expinletrep=[%exprBitstring.Buffer.contents[%eres]][@metalocloc]inletlen=match(evaluate_exprsize)with|Some(v)->intv|None->sizeinletpost=[%exprlet_res=[%erep]inifPervasives.(=)(Bitstring.bitstring_length_res)[%elen]then_reselseraiseExit][@metalocloc]inletseq=List.fold_right(funfldacc->[%expr[%e(gen_constructor~locsymfld)];[%eacc]])fieldspostin[%exprlet[%psym.Entity.pat]=Bitstring.Buffer.create()in[%eseq]][@metalocloc];;letparse_assignment_behavior~locsymvalue=split_string~on:";"value|>split_loc~loc|>List.map(funflds->parse_const_fieldsflds)|>gen_assignment_behavior~locsym;;letgen_constructor_expr~locvalue=letopenEntityinletsym=Entity.make~loc"constructor"inletbeh=parse_assignment_behavior~locsymvaluein[%exprlet[%psym.pat]=fun()->[%ebeh]in[%esym.exp]()];;lettransform_single_let~locastexpr=matchast.pvb_pat.ppat_desc,ast.pvb_expr.pexp_descwith|Parsetree.Ppat_var(s),Pexp_constant(Pconst_string(value,_))->letpat=pvar~locs.txtinletconstructor_expr=gen_constructor_exprlocvaluein[%exprlet[%ppat]=[%econstructor_expr]in[%eexpr]]|_->location_exn~loc"Invalid pattern type";;(*
* Rewriter. See:
* https://github.com/let-def/ocaml-migrate-parsetree/blob/master/MANUAL.md#new-registration-interface
*)letextensionexpr=letloc=expr.pexp_locinmatchexpr.pexp_descwith|Pexp_constant(Pconst_string(value,(_:stringoption)))->gen_constructor_exprlocvalue|Pexp_let(Nonrecursive,bindings,expr)->List.fold_right(funbindingexpr->transform_single_let~locbindingexpr)bindingsexpr|Pexp_match(ident,cases)->gen_cases~locidentcases|Pexp_function(cases)->gen_function~loccases|_->location_exn~loc"'bitstring' can only be used with 'let', 'match', and as '[%bitstring]'"letexpressionmapper=function|[%expr[%bitstring[%e?e0]]]->mapper.exprmapper(extensione0)|expr->Ast_mapper.default_mapper.exprmapperexprletstructure_item_mappermapper=function|[%stri[%%bitstringlet[%p?var]=[%e?e0]]]->[%strilet[%pmapper.patmappervar]=[%emapper.exprmapper(extensione0)]]|stri->Ast_mapper.default_mapper.structure_itemmapperstriletrewriterconfigcookies={Ast_mapper.default_mapperwithexpr=expression;structure_item=structure_item_mapper;}let()=Driver.register~name:"ppx_bitstring"~args:[]Versions.ocaml_405rewriter;;