12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235(*
* 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";;letparse_const_fieldsstr=letopenQualifiersinsplit_string~on:":"str.txt|>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;;