123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524(*
* Copyright (c) 2015 Nicolas Ojeda Bar <n.oje.bar@gmail.com>
*
* 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_parsetreeopenPrintfopenAst_404openLongidentopenAsttypesopenParsetreeopenAst_helperopenAst_mappermoduleLoc=LocationmoduleAst=Ast_convenience_404typemode=Big_endian|Little_endian|Host_endian|Bi_endiantypeprim=|Char|UInt8|UInt16|UInt32|UInt64typety=|Primofprim|Bufferofprim*inttypefield={field:string;ty:ty;off:int;}typet={name:string;fields:fieldlist;len:int;endian:mode;}letty_of_string=function|"char_t"|"char"->SomeChar|"uint8_t"|"uint8"|"int8"|"int8_t"->SomeUInt8|"uint16_t"|"uint16"|"int16"|"int16_t"->SomeUInt16|"uint32_t"|"uint32"|"int32"|"int32_t"->SomeUInt32|"uint64_t"|"uint64"|"int64"|"int64_t"->SomeUInt64|_->Noneletwidth_of_fieldf=letrecwidth=function|PrimChar->1|PrimUInt8->1|PrimUInt16->2|PrimUInt32->4|PrimUInt64->8|Buffer(prim,len)->(width(Primprim))*leninwidthf.tyletfield_to_stringf=letrecstring=function|PrimChar->"char_t"|PrimUInt8->"uint8_t"|PrimUInt16->"uint16_t"|PrimUInt32->"uint32_t"|PrimUInt64->"uint64_t"|Buffer(prim,len)->sprintf"%s[%d]"(string(Primprim))leninsprintf"%s %s"(stringf.ty)f.fieldletto_stringt=sprintf"cstruct[%d] %s { %s }"t.lent.name(String.concat"; "(List.mapfield_to_stringt.fields))letloc_errlocfmt=Location.raise_errorf~loc("ppx_cstruct error: "^^fmt)letparse_fieldlocfieldfield_typesz=matchty_of_stringfield_typewith|None->loc_errloc"Unknown type %s"field_type|Somety->beginletty=matchty,szwith|_,None->Primty|prim,Somesz->Buffer(prim,sz)inletoff=-1in{field;ty;off}endletcreate_structlocendiannamefields=letendian=matchendianwith|"little_endian"->Little_endian|"big_endian"->Big_endian|"host_endian"->Host_endian|"bi_endian"->Bi_endian|_->loc_errloc"unknown endian %s, should be little_endian, big_endian, host_endian or bi_endian"endianinletlen,fields=List.fold_left(fun(off,acc)field->letfield={fieldwithoff=off}inletoff=width_of_fieldfield+offinletacc=acc@[field]in(off,acc))(0,[])fieldsin{fields;name=name.txt;len;endian}let($.)lx=Longident.Ldot(l,x)letcstruct_id=Longident.Lident"Cstruct"letmode_mods=function|Big_endian->cstruct_id$."BE"$.s|Little_endian->cstruct_id$."LE"$.s|Host_endian->cstruct_id$."HE"$.s|Bi_endian->cstruct_id$."BL"$.sletmode_modlocxs=Exp.ident~loc{loc;txt=mode_modsx}letgetter_namesf=sprintf"get_%s_%s"s.namef.fieldletsetter_namesf=sprintf"set_%s_%s"s.namef.fieldletop_nameopsf=sprintf"%s_%s_%s"ops.namef.fieldletoutput_get_locsf=letm=mode_mod_locs.endianinletnumx=Ast.intxinmatchf.tywith|Buffer(_,_)->letlen=width_of_fieldfin[[%strilet[%pAst.pvar(op_name"get"sf)]=funsrc->Cstruct.subsrc[%enumf.off][%enumlen]];[%strilet[%pAst.pvar(op_name"copy"sf)]=funsrc->Cstruct.copysrc[%enumf.off][%enumlen]]]|Primprim->[[%strilet[%pAst.pvar(getter_namesf)]=funv->[%ematchprimwith|Char->[%exprCstruct.get_charv[%enumf.off]]|UInt8->[%exprCstruct.get_uint8v[%enumf.off]]|UInt16->[%expr[%em"get_uint16"]v[%enumf.off]]|UInt32->[%expr[%em"get_uint32"]v[%enumf.off]]|UInt64->[%expr[%em"get_uint64"]v[%enumf.off]]]]]letoutput_getlocsf=(output_getlocsf)[@metalocloc]lettype_of_int_field=function|Char->[%type:char]|UInt8->[%type:Cstruct.uint8]|UInt16->[%type:Cstruct.uint16]|UInt32->[%type:Cstruct.uint32]|UInt64->[%type:Cstruct.uint64]lettype_of_int_field_locx=type_of_int_fieldx[@metalocloc]letoutput_get_sig_locsf=matchf.tywith|Buffer(_,_)->[Sig.value(Val.mk(Loc.mknoloc(op_name"get"sf))[%type:Cstruct.t->Cstruct.t]);Sig.value(Val.mk(Loc.mknoloc(op_name"copy"sf))[%type:Cstruct.t->string])]|Primprim->letretf=type_of_int_field_locprimin[Sig.value(Val.mk(Loc.mknoloc(getter_namesf))[%type:Cstruct.t->[%tretf]])]letoutput_get_sig_locsf=output_get_sig_locsf[@metaloc_loc]letoutput_set_locsf=letm=mode_mod_locs.endianinletnumx=Ast.intxinmatchf.tywith|Buffer(_,_)->letlen=width_of_fieldfin[[%strilet[%pAst.pvar(setter_namesf)]=funsrcsrcoffdst->Cstruct.blit_from_stringsrcsrcoffdst[%enumf.off][%enumlen]];[%strilet[%pAst.pvar(op_name"blit"sf)]=funsrcsrcoffdst->Cstruct.blitsrcsrcoffdst[%enumf.off][%enumlen]]]|Primprim->[[%strilet[%pAst.pvar(setter_namesf)]=funvx->[%ematchprimwith|Char->[%exprCstruct.set_charv[%enumf.off]x]|UInt8->[%exprCstruct.set_uint8v[%enumf.off]x]|UInt16->[%expr[%em"set_uint16"]v[%enumf.off]x]|UInt32->[%expr[%em"set_uint32"]v[%enumf.off]x]|UInt64->[%expr[%em"set_uint64"]v[%enumf.off]x]]]]letoutput_set_locsf=output_set_locsf[@metaloc_loc]letoutput_set_sig_locsf=matchf.tywith|Buffer(_,_)->[Sig.value(Val.mk(Loc.mkloc(setter_namesf)_loc)[%type:string->int->Cstruct.t->unit]);Sig.value(Val.mk(Loc.mkloc(op_name"blit"sf)_loc)[%type:Cstruct.t->int->Cstruct.t->unit])][@metaloc_loc]|Primprim->letretf=type_of_int_field_locprimin[Sig.value(Val.mk(Loc.mkloc(setter_namesf)_loc)[%type:Cstruct.t->[%tretf]->unit])][@metaloc_loc]letoutput_sizeof_locs=[%strilet[%pAst.pvar("sizeof_"^s.name)]=[%eAst.ints.len]][@metaloc_loc]letoutput_sizeof_sig_locs=Sig.value(Val.mk(Loc.mknoloc("sizeof_"^s.name))[%type:int])[@metaloc_loc]letoutput_hexdump_locs=lethexdump=List.fold_left(funaf->[%expr[%ea];Buffer.add_string_buf[%eAst.str(" "^f.field^" = ")];[%ematchf.tywith|PrimChar->[%exprPrintf.bprintf_buf"%c\n"([%eAst.evar(getter_namesf)]v)]|Prim(UInt8|UInt16)->[%exprPrintf.bprintf_buf"0x%x\n"([%eAst.evar(getter_namesf)]v)]|PrimUInt32->[%exprPrintf.bprintf_buf"0x%lx\n"([%eAst.evar(getter_namesf)]v)]|PrimUInt64->[%exprPrintf.bprintf_buf"0x%Lx\n"([%eAst.evar(getter_namesf)]v)]|Buffer(_,_)->[%exprPrintf.bprintf_buf"<buffer %s>"[%eAst.str(field_to_stringf)];Cstruct.hexdump_to_buffer_buf([%eAst.evar(getter_namesf)]v)]]])(Ast.unit())s.fieldsin[[%strilet[%pAst.pvar("hexdump_"^s.name^"_to_buffer")]=fun_bufv->[%ehexdump]];[%strilet[%pAst.pvar("hexdump_"^s.name)]=funv->let_buf=Buffer.create128inBuffer.add_string_buf[%eAst.str(s.name^" = {\n")];[%eAst.evar("hexdump_"^s.name^"_to_buffer")]_bufv;print_endline(Buffer.contents_buf);print_endline"}"]][@metaloc_loc]letoutput_hexdump_sig_locs=[Sig.value(Val.mk(Loc.mkloc("hexdump_"^s.name^"_to_buffer")_loc)[%type:Buffer.t->Cstruct.t->unit]);Sig.value(Val.mk(Loc.mkloc("hexdump_"^s.name)_loc)[%type:Cstruct.t->unit])][@metaloc_loc]letoutput_struct_one_endian_locs=(* Generate functions of the form {get/set}_<struct>_<field> *)letexpr=List.fold_left(funaf->a@output_get_locsf@output_set_locsf)[output_sizeof_locs]s.fieldsinexpr@output_hexdump_locsletoutput_struct_locs=matchs.endianwith|Bi_endian->(* In case of Bi-endian, create two modules - one for BE and one for LE *)letexpr_be=Mod.structure(output_struct_one_endian_loc{swithendian=Big_endian})andexpr_le=Mod.structure(output_struct_one_endian_loc{swithendian=Little_endian})in[{pstr_desc=Pstr_module{pmb_name={txt="BE";loc=_loc};pmb_expr=expr_be;pmb_attributes=[];pmb_loc=_loc;};pstr_loc=_loc;};{pstr_desc=Pstr_module{pmb_name={txt="LE";loc=_loc};pmb_expr=expr_le;pmb_attributes=[];pmb_loc=_loc;};pstr_loc=_loc;}]|_->output_struct_one_endian_locsletoutput_struct_sig_locs=(* Generate signaturs of the form {get/set}_<struct>_<field> *)letexpr=List.fold_left(funaf->a@output_get_sig_locsf@output_set_sig_locsf)[output_sizeof_sig_locs]s.fieldsinexpr@output_hexdump_sig_locsletoutput_enum_locnamefieldswidth~sexp=letintfn,pattfn=matchty_of_stringwidthwith|None->loc_err_loc"enum: unknown width specifier %s"width|SomeChar->(funi->Exp.constant(Pconst_char(Char.chr@@Int64.to_inti))),(funi->Pat.constant(Pconst_char(Char.chr@@Int64.to_inti)))|Some(UInt8|UInt16)->(funi->Exp.constant(Pconst_integer(Int64.to_stringi,None))),(funi->Pat.constant(Pconst_integer(Int64.to_stringi,None)))|SomeUInt32->(funi->Exp.constant(Pconst_integer(Int32.to_string(Int64.to_int32i),Some'l'))),(funi->Pat.constant(Pconst_integer(Int32.to_string(Int64.to_int32i),Some'l')))|SomeUInt64->(funi->Exp.constant(Pconst_integer(Int64.to_stringi,Some'L'))),(funi->Pat.constant(Pconst_integer(Int64.to_stringi,Some'L')))inletdecls=List.map(fun(f,_)->Type.constructorf)fieldsinletgetters=(List.map(fun({txt=f;_},i)->{pc_lhs=pattfni;pc_guard=None;pc_rhs=Ast.constr"Some"[Ast.constrf[]]})fields)@[{pc_lhs=Pat.any();pc_guard=None;pc_rhs=Ast.constr"None"[]}]inletsetters=List.map(fun({txt=f;_},i)->{pc_lhs=Ast.pconstrf[];pc_guard=None;pc_rhs=intfni})fieldsinletprinters=List.map(fun({txt=f;_},_)->{pc_lhs=Ast.pconstrf[];pc_guard=None;pc_rhs=Ast.strf})fieldsinletparsers=List.map(fun({txt=f;_},_)->{pc_lhs=Ast.pstrf;pc_guard=None;pc_rhs=Ast.constr"Some"[Ast.constrf[]]})fieldsinletgetter{txt=x;_}=sprintf"int_to_%s"xinletsetter{txt=x;_}=sprintf"%s_to_int"xinletprinter{txt=x;_}=sprintf"%s_to_string"xinletparse{txt=x;_}=sprintf"string_to_%s"xinletof_sexp{txt=x;_}=sprintf"%s_of_sexp"xinletto_sexp{txt=x;_}=sprintf"sexp_of_%s"xinletoutput_sexp_struct=[[%strilet[%pAst.pvar(to_sexpname)]=funx->Sexplib.Sexp.Atom([%eAst.evar(printername)]x)];[%strilet[%pAst.pvar(of_sexpname)]=funx->matchxwith|Sexplib.Sexp.List_->raise(Sexplib.Pre_sexp.Of_sexp_error(Failure"expected Atom, got List",x))|Sexplib.Sexp.Atomv->match[%eAst.evar(parsename)]vwith|None->raise(Sexplib.Pre_sexp.Of_sexp_error(Failure"unable to parse enum string",x))|Somer->r]]inStr.type_Recursive[Type.mk~kind:(Ptype_variantdecls)name]::[%strilet[%pAst.pvar(gettername)]=funx->[%eExp.match_[%exprx]getters]]::[%strilet[%pAst.pvar(settername)]=funx->[%eExp.match_[%exprx]setters]]::[%strilet[%pAst.pvar(printername)]=funx->[%eExp.match_[%exprx]printers]]::[%strilet[%pAst.pvar(parsename)]=funx->[%eExp.match_[%exprx](parsers@[{pc_lhs=Pat.any();pc_guard=None;pc_rhs=Ast.constr"None"[]}])]]::ifsexpthenoutput_sexp_structelse[]letoutput_enum_sig_locnamefieldswidth~sexp=letoty=matchty_of_stringwidthwith|None->loc_err_loc"enum: unknown width specifier %s"width|SomeChar->[%type:char]|Some(UInt8|UInt16)->[%type:int]|SomeUInt32->[%type:int32]|SomeUInt64->[%type:int64]inletdecls=List.map(fun(f,_)->Type.constructorf)fieldsinletgetter{txt=x;_}=sprintf"int_to_%s"xinletsetter{txt=x;_}=sprintf"%s_to_int"xinletprinter{txt=x;_}=sprintf"%s_to_string"xinletparse{txt=x;_}=sprintf"string_to_%s"xinletof_sexp{txt=x;_}=sprintf"%s_of_sexp"xinletto_sexp{txt=x;_}=sprintf"sexp_of_%s"xinletctyo=[%type:[%tAst.tconstrname.txt[]]option]inletcty=Ast.tconstrname.txt[]inletoutput_sexp_sig=[Sig.value(Val.mk(Loc.mkloc(to_sexpname)_loc)[%type:[%tcty]->Sexplib.Sexp.t]);Sig.value(Val.mk(Loc.mkloc(of_sexpname)_loc)[%type:Sexplib.Sexp.t->[%tcty]])]inSig.type_Recursive[Type.mk~kind:(Ptype_variantdecls)name]::Sig.value(Val.mk(Loc.mkloc(gettername)_loc)[%type:[%toty]->[%tctyo]])::Sig.value(Val.mk(Loc.mkloc(settername)_loc)[%type:[%tcty]->[%toty]])::Sig.value(Val.mk(Loc.mkloc(printername)_loc)[%type:[%tcty]->string])::Sig.value(Val.mk(Loc.mkloc(parsename)_loc)[%type:string->[%tcty]option])::ifsexpthenoutput_sexp_sigelse[]letconstr_enum=function|{pcd_name=f;pcd_args=Pcstr_tuple[];pcd_attributes=attrs;_}->letid=matchattrswith|[{txt="id";_},PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constantcst;pexp_loc=loc;_},_);_}]]->letcst=matchcstwith|Pconst_integer(i,_)->Int64.of_stringi|_->loc_errloc"invalid id"inSomecst|_->Nonein(f,id)|{pcd_loc=loc;_}->loc_errloc"invalid cenum variant"letconstr_field{pld_name=fname;pld_type=fty;pld_loc=loc;pld_attributes=att;_}=letget=function|[{txt="len";_},PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_integer(sz,_));_},_);_}]]->Some(int_of_stringsz)|_->Noneinletsz=matchgetfty.ptyp_attributes,getattwith|Somesz,None|None,Somesz->Somesz|Some_,Some_->loc_errloc"multiple field length attribute"|None,None->Noneinletfty=matchfty.ptyp_descwith|Ptyp_constr({txt=Lidentfty;_},[])->fty|_->loc_errfty.ptyp_loc"type identifier expected"inparse_fieldlocfname.txtftyszletcstructdecl=let{ptype_name=name;ptype_kind=kind;ptype_attributes=attrs;ptype_loc=loc;_}=declinletfields=matchkindwith|Ptype_recordfields->List.mapconstr_fieldfields|_->loc_errloc"record type declaration expected"inletendian=matchattrswith|[{txt=endian;_},PStr[]]->endian|[_,_]->loc_errloc"no attribute payload expected"|_->loc_errloc"too many attributes"increate_structlocendiannamefieldsletcenumdecl=let{ptype_name=name;ptype_kind=kind;ptype_attributes=attrs;ptype_loc=loc;_}=declinletfields=matchkindwith|Ptype_variantfields->fields|_->loc_errloc"expected variant type"inletwidth,sexp=matchattrswith|({txt=width;_},PStr[])::({txt="sexp";_},PStr[])::[]->width,true|({txt=width;_},PStr[])::[]->width,false|_->loc_errloc"invalid cenum attributes"inletn=refInt64.minus_oneinletincr_n()=n:=Int64.succ!ninletfields=List.mapconstr_enumfieldsinletfields=List.map(function|(f,None)->incr_n();(f,!n)|(f,Somei)->n:=i;(f,i))fieldsinname,fields,width,sexpletsignature_item'mapper=function|{psig_desc=Psig_extension(({txt="cstruct";_},PStr[{pstr_desc=Pstr_type(_,[decl]);_}]),_);psig_loc=loc}->output_struct_sigloc(cstructdecl)|{psig_desc=Psig_extension(({txt="cenum";_},PStr[{pstr_desc=Pstr_type(_,[decl]);_}]),_);psig_loc=loc}->letname,fields,width,sexp=cenumdeclinoutput_enum_siglocnamefieldswidth~sexp|other->[default_mapper.signature_itemmapperother]letsignaturemappers=List.concat(List.map(signature_item'mapper)s)letstructure_item'mapper=function|{pstr_desc=Pstr_extension(({txt="cstruct";_},PStr[{pstr_desc=Pstr_type(_,[decl]);_}]),_);pstr_loc=loc}->output_structloc(cstructdecl)|{pstr_desc=Pstr_extension(({txt="cenum";_},PStr[{pstr_desc=Pstr_type(_,[decl]);_}]),_);pstr_loc=loc}->letname,fields,width,sexp=cenumdeclinoutput_enumlocnamefieldswidth~sexp|other->[default_mapper.structure_itemmapperother]letstructuremappers=List.concat(List.map(structure_item'mapper)s)let()=Driver.register~name:"ppx_cstruct"Versions.ocaml_404(fun_config_cookies->{default_mapperwithstructure;signature})