123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662(*
* 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.
*)openPrintfopenPpxlibopenAst_helpermoduleAst=structincludeAst_builder.Defaultleteconstr~loctagxs=pexp_construct~loc(Loc.make~loc(lidenttag))(matchxswith|[]->None|_->Some(pexp_tuple~locxs))letpconstr~loctagxs=ppat_construct~loc(Loc.make~loc(lidenttag))(matchxswith|[]->None|_->Some(ppat_tuple~locxs))lettconstr~loctagxs=ptyp_constr~loc(Loc.make~loc(lidenttag))xsendtypemode=Big_endian|Little_endian|Host_endian|Bi_endiantypeprim=|Char|UInt8|UInt16|UInt32|UInt64typety=|Primofprim|Bufferofprim*inttyperaw_field={name:string;ty:ty;definition_loc:Location.t;}typenamed_field={name:string;ty:ty;definition_loc:Location.t;off:int;}typefield=|Named_fieldofnamed_field|Ignored_fieldletfield_is_ignoredname=String.getname0='_'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_prim=function|Char->1|UInt8->1|UInt16->2|UInt32->4|UInt64->8letwidth_of_ty=function|Primp->width_of_primp|Buffer(p,len)->width_of_primp*lenletwidth_of_fieldf=width_of_tyf.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.nameletloc_errlocfmt=Location.raise_errorf~loc("ppx_cstruct: "^^fmt)letparse_fieldlocnamefield_typesz=matchty_of_stringfield_typewith|None->loc_errloc"Unknown type %s"field_type|Somety->beginletty=matchty,szwith|_,None->Primty|prim,Somesz->Buffer(prim,sz)in{name;ty;definition_loc=loc}endletcheck_for_duplicatesfields=letmoduleStringSet=Set.Make(String)inlet_:StringSet.t=List.fold_left(funseenf->matchfwith|Ignored_field->seen|Named_field{name;definition_loc;_}->ifStringSet.memnameseenthenloc_errdefinition_loc"field %s is present several times in this type"nameelseStringSet.addnameseen)StringSet.emptyfieldsin()letcreate_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)({name;ty;definition_loc}:raw_field)->letfield=iffield_is_ignorednamethenIgnored_fieldelseNamed_field{name;ty;off;definition_loc}inletoff=width_of_tyty+offinletacc=acc@[field]in(off,acc))(0,[])fieldsincheck_for_duplicatesfields;{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}typeop=|Op_getofnamed_field|Op_setofnamed_field|Op_copyofnamed_field|Op_blitofnamed_field|Op_sizeof|Op_hexdump|Op_hexdump_to_bufferletop_namesop=letparts=matchopwith|Op_getf->["get";s.name;f.name]|Op_setf->["set";s.name;f.name]|Op_copyf->["copy";s.name;f.name]|Op_blitf->["blit";s.name;f.name]|Op_sizeof->["sizeof";s.name]|Op_hexdump->["hexdump";s.name]|Op_hexdump_to_buffer->["hexdump";s.name;"to_buffer"]inString.concat"_"partsletop_pvar~locsop=Ast.pvar~loc(op_namesop)letop_evar~locsop=Ast.evar~loc(op_namesop)letget_exprlocsf=letm=mode_modlocs.endianinletnumx=Ast.eint~locxinmatchf.tywith|Buffer(_,_)->letlen=width_of_fieldfin[%exprfunsrc->Cstruct.subsrc[%enumf.off][%enumlen]]|Primprim->[%exprfunv->[%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]]]]lettype_of_int_field~loc=function|Char->[%type:char]|UInt8->[%type:Cstruct.uint8]|UInt16->[%type:Cstruct.uint16]|UInt32->[%type:Cstruct.uint32]|UInt64->[%type:Cstruct.uint64]letset_exprlocsf=letm=mode_modlocs.endianinletnumx=Ast.eint~locxinmatchf.tywith|Buffer(_,_)->letlen=width_of_fieldfin[%exprfunsrcsrcoffdst->Cstruct.blit_from_stringsrcsrcoffdst[%enumf.off][%enumlen]]|Primprim->[%exprfunvx->[%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]]]lettype_of_set~locf=matchf.tywith|Buffer(_,_)->[%type:string->int->Cstruct.t->unit]|Primprim->letretf=type_of_int_field~locprimin[%type:Cstruct.t->[%tretf]->unit]lethexdump_expr~locs=[%exprfunv->letbuf=Buffer.create128inBuffer.add_stringbuf[%eAst.estring~loc(s.name^" = {\n")];[%eop_evar~locsOp_hexdump_to_buffer]bufv;print_endline(Buffer.contentsbuf);print_endline"}"]lethexdump_to_buffer_expr~locs=letprim_format_string=function|Char->[%expr"%c\n"]|UInt8|UInt16->[%expr"0x%x\n"]|UInt32->[%expr"0x%lx\n"]|UInt64->[%expr"0x%Lx\n"]inlethexdump_field=function|Ignored_field->[%expr()]|Named_fieldf->letget_f=op_evar~locs(Op_getf)inletexpr=matchf.tywith|Primp->[%exprPrintf.bprintfbuf[%eprim_format_stringp]([%eget_f]v)]|Buffer(_,_)->[%exprPrintf.bprintfbuf"<buffer %s>"[%eAst.estring~loc(field_to_stringf)];Cstruct.hexdump_to_bufferbuf([%eget_f]v)]in[%exprPrintf.bprintfbuf" %s = "[%eAst.estring~locf.name];[%eexpr]]in[%exprfunbufv->[%eAst.esequence~loc(List.maphexdump_fields.fields)]]letop_exprlocs=function|Op_sizeof->Ast.eint~locs.len|Op_hexdump->hexdump_expr~locs|Op_hexdump_to_buffer->hexdump_to_buffer_expr~locs|Op_getf->get_exprlocsf|Op_setf->set_exprlocsf|Op_copyf->letlen=width_of_fieldfin[%exprfunsrc->Cstruct.to_stringsrc~off:[%eAst.eint~locf.off]~len:[%eAst.eint~loclen]]|Op_blitf->letlen=width_of_fieldfin[%exprfunsrcsrcoffdst->Cstruct.blitsrcsrcoffdst[%eAst.eint~locf.off][%eAst.eint~loclen]]letfield_ops_for=function|Ignored_field->[]|Named_fieldf->letif_bufferx=matchf.tywith|Buffer(_,_)->[x]|Prim_->[]inList.concat[[Op_getf];if_buffer(Op_copyf);[Op_setf];if_buffer(Op_blitf)]letops_fors=([Op_sizeof]@List.concat(List.mapfield_ops_fors.fields)@[Op_hexdump_to_buffer;Op_hexdump;])(** Generate functions of the form {get/set}_<struct>_<field> *)letoutput_struct_one_endianlocs=List.map(funop->[%strilet[@ocaml.warning"-32"][%pop_pvar~locsop]=[%eop_exprlocsop]])(ops_fors)letoutput_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=Some"BE";loc=_loc};pmb_expr=expr_be;pmb_attributes=[];pmb_loc=_loc;};pstr_loc=_loc;};{pstr_desc=Pstr_module{pmb_name={txt=Some"LE";loc=_loc};pmb_expr=expr_le;pmb_attributes=[];pmb_loc=_loc;};pstr_loc=_loc;}]|_->output_struct_one_endian_locslettype_of_get~locf=matchf.tywith|Buffer(_,_)->[%type:Cstruct.t->Cstruct.t]|Primprim->letretf=type_of_int_field~locprimin[%type:Cstruct.t->[%tretf]]letop_typ~loc=function|Op_sizeof->[%type:int]|Op_hexdump_to_buffer->[%type:Buffer.t->Cstruct.t->unit]|Op_hexdump->[%type:Cstruct.t->unit]|Op_getf->type_of_get~locf|Op_setf->type_of_set~locf|Op_copy_->[%type:Cstruct.t->string]|Op_blit_->[%type:Cstruct.t->int->Cstruct.t->unit](** Generate signatures of the form {get/set}_<struct>_<field> *)letoutput_struct_siglocs=List.map(funop->Sig.value(Val.mk(Loc.make(op_namesop)~loc)(op_typ~locop)))(ops_fors)typeenum_op=|Enum_to_sexp|Enum_of_sexp|Enum_get|Enum_set|Enum_print|Enum_parse|Enum_comparetypecenum={name:stringLoc.t;fields:(stringLoc.t*int64)list;prim:prim;sexp:bool;}letenum_op_namecenum=lets=cenum.name.txtinfunction|Enum_to_sexp->sprintf"sexp_of_%s"s|Enum_of_sexp->sprintf"%s_of_sexp"s|Enum_get->sprintf"int_to_%s"s|Enum_set->sprintf"%s_to_int"s|Enum_print->sprintf"%s_to_string"s|Enum_parse->sprintf"string_to_%s"s|Enum_compare->sprintf"compare_%s"sletenum_pattern~loc{prim;_}=letpat_integerfsuffixi=Pat.constant(Pconst_integer(fi,suffix))inmatchprimwith|Char->(funi->Ast.pchar~loc(Char.chr(Int64.to_inti)))|(UInt8|UInt16)->pat_integerInt64.to_stringNone|UInt32->pat_integer(funi->Int32.to_string(Int64.to_int32i))(Some'l')|UInt64->pat_integerInt64.to_string(Some'L')letenum_integer~loc{prim;_}=letexpr_integerfsuffixi=Exp.constant(Pconst_integer(fi,suffix))inmatchprimwith|Char->(funi->Ast.echar~loc(Char.chr(Int64.to_inti)))|(UInt8|UInt16)->expr_integerInt64.to_stringNone|UInt32->expr_integer(funi->Int32.to_string(Int64.to_int32i))(Some'l')|UInt64->expr_integerInt64.to_string(Some'L')letdeclare_enum_expr~loc({fields;_}ascenum)=function|Enum_to_sexp->[%exprfunx->Sexplib.Sexp.Atom([%eAst.evar~loc(enum_op_namecenumEnum_print)]x)]|Enum_of_sexp->[%exprfunx->matchxwith|Sexplib.Sexp.List_->raise(Sexplib.Pre_sexp.Of_sexp_error(Failure"expected Atom, got List",x))|Sexplib.Sexp.Atomv->match[%eAst.evar~loc(enum_op_namecenumEnum_parse)]vwith|None->raise(Sexplib.Pre_sexp.Of_sexp_error(Failure"unable to parse enum string",x))|Somer->r]|Enum_get->letgetters=(List.map(fun({txt=f;_},i)->Exp.case(enum_pattern~loccenumi)[%exprSome[%eAst.econstr~locf[]]])fields)@[Exp.case[%pat?_][%exprNone]]inExp.function_getters|Enum_set->letsetters=List.map(fun({txt=f;_},i)->Exp.case(Ast.pconstr~locf[])(enum_integer~loccenumi))fieldsinExp.function_setters|Enum_print->letprinters=List.map(fun({txt=f;_},_)->Exp.case(Ast.pconstr~locf[])(Ast.estring~locf))fieldsinExp.function_printers|Enum_parse->letparsers=List.map(fun({txt=f;_},_)->Exp.case(Ast.pstring~locf)[%exprSome[%eAst.econstr~locf[]]])fieldsinExp.function_(parsers@[Exp.case[%pat?_][%exprNone]])|Enum_compare->[%exprfunxy->letto_int=[%eAst.evar~loc(enum_op_namecenumEnum_set)]inStdlib.compare(to_intx)(to_inty)]letenum_ops_for{sexp;_}=Enum_get::Enum_set::Enum_compare::Enum_print::Enum_parse::ifsexpthen[Enum_to_sexp;Enum_of_sexp]else[]letenum_type_decl{name;fields;_}=letdecls=List.map(fun(f,_)->Type.constructorf)fieldsinType.mk~kind:(Ptype_variantdecls)nameletoutput_enum~loccenum=Str.type_Recursive[enum_type_declcenum]::List.map(funop->[%strilet[@ocaml.warning"-32"][%pAst.pvar~loc(enum_op_namecenumop)]=[%edeclare_enum_expr~loccenumop]])(enum_ops_forcenum)letenum_op_type~loc{name;prim;_}=letcty=Ast.tconstr~locname.txt[]inletoty=matchprimwith|Char->[%type:char]|(UInt8|UInt16)->[%type:int]|UInt32->[%type:int32]|UInt64->[%type:int64]infunction|Enum_get->[%type:[%toty]->[%tcty]option]|Enum_set->[%type:[%tcty]->[%toty]]|Enum_print->[%type:[%tcty]->string]|Enum_parse->[%type:string->[%tcty]option]|Enum_to_sexp->[%type:[%tcty]->Sexplib.Sexp.t]|Enum_of_sexp->[%type:Sexplib.Sexp.t->[%tcty]]|Enum_compare->[%type:[%tcty]->[%tcty]->int]letoutput_enum_sigloc(cenum:cenum)=Sig.type_Recursive[enum_type_declcenum]::List.map(funop->letname=enum_op_namecenumopinlettyp=enum_op_type~loccenumopinSig.value(Val.mk(Loc.makename~loc)typ))(enum_ops_forcenum)letconstr_enum=function|{pcd_name=f;pcd_args=Pcstr_tuple[];pcd_attributes=attrs;_}->letid=matchattrswith|[{attr_name={txt="id";_};attr_payload=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"letget_len=function|[{attr_name={txt="len";loc};attr_payload=PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_integer(sz,None));_},_);_}];_}]->letn=int_of_stringszinifn>0thenSomenelseloc_errloc"[@len] argument should be > 0"|[{attr_name={txt="len";loc};_}]->loc_errloc"[@len] argument should be an integer"|_->Noneletconstr_field{pld_name=fname;pld_type=fty;pld_loc=loc;pld_attributes=att;_}=letsz=matchget_lenfty.ptyp_attributes,get_lenattwith|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|[{attr_name={txt=endian;_};attr_payload=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|({attr_name={txt=width;_};attr_payload=PStr[];_})::({attr_name={txt="sexp";_};attr_payload=PStr[];_})::[]->width,true|({attr_name={txt=width;_};attr_payload=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))fieldsinletprim=matchty_of_stringwidthwith|None->loc_errloc"enum: unknown width specifier %s"width|Somep->pin{name;fields;prim;sexp;}letsignature_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}->output_enum_sigloc(cenumdecl)|other->[mapperother]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;_}->output_enum~loc(cenumdecl)|other->[mapperother]classmapper=objectinheritAst_traverse.mapassupermethod!signatures=List.concat(List.map(signature_item'super#signature_item)s)method!structures=List.concat(List.map(structure_item'super#structure_item)s)endlet()=letmapper=newmapperinDriver.register_transformation"ppx_cstruct"~impl:mapper#structure~intf:mapper#signature