123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291(*
* Copyright (c) 2018 Thomas Gazagnaire <thomas@gazagnaire.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.
*)openMdx.Migrate_astopenMdx.CompatmodulePart=structtypet={name:string;body:string;}letv~name~body={name;body}letname{name;_}=nameletbody{body;_}=bodyendmoduleLexbuf=structopenLexingtypet={contents:string;lexbuf:lexbuf;}letinitial_posname={pos_fname=name;pos_lnum=1;pos_bol=0;pos_cnum=0;}letv~fnamecontents=letlexbuf=Lexing.from_stringcontentsinlexbuf.lex_curr_p<-initial_posfname;Location.input_name:=fname;{contents;lexbuf}letof_filefname=letic=open_infnameinletlen=in_channel_lengthicinletresult=really_input_stringicleninclose_in_noerric;v~fnameresultendmodulePhrase=structopenLexingopenParsetreetypekind=Code|PartofstringexceptionCannot_parse_payloadofLocation.tletstring_of_location{Location.loc_start={pos_fname;pos_lnum;pos_bol;pos_cnum};_}=Printf.sprintf"%s, line %d, col %d"pos_fnamepos_lnum(pos_cnum-pos_bol)letpayload_constantsloc=function|PStr[{pstr_desc=Pstr_eval(expr,_);_}]->letone{pexp_loc;pexp_desc;_}=matchpexp_descwith|Pexp_apply({pexp_desc=Pexp_identident;_},[Asttypes.Nolabel,{pexp_desc=Pexp_constantconst;_}])->(pexp_loc,Someident,const)|Pexp_constantconst->(pexp_loc,None,const)|_->raise(Cannot_parse_payloadpexp_loc)inletrecconsts=function|{pexp_desc=Pexp_sequence(e,rest);_}->onee::constsrest|e->[onee]inconstsexpr|PStr[]->[]|_->raise(Cannot_parse_payloadloc)letpayload_stringsloc=function|PStr[]->[]|x->letaux=function|_,Some{Location.txt=Longident.Lident"ocaml";_},Pconst_string(str,_)->(`OCaml,str)|_,None,Pconst_string(str,_)->(`Raw,str)|loc,_,_->raise(Cannot_parse_payloadloc)inList.mapaux(payload_constantslocx)letkind_impl=function|{pstr_desc=Pstr_attribute(name,payload);pstr_loc}whenname.Asttypes.txt="part"->beginmatchpayload_stringspstr_locpayloadwith|[`Raw,part]->Partpart|_->prerr_endline(string_of_locationpstr_loc^": cannot parse [@@@part] payload");Code|exception(Cannot_parse_payloadloc)->prerr_endline(string_of_locationloc^": cannot parse [@@@part] payload");Codeend|_->Codeletkind_intf=function|{psig_desc=Psig_attribute(name,payload);psig_loc}whenname.Asttypes.txt="part"->beginmatchpayload_stringspsig_locpayloadwith|[`Raw,part]->Partpart|_->prerr_endline(string_of_locationpsig_loc^": cannot parse [@@@part] payload");Code|exception(Cannot_parse_payloadloc)->prerr_endline(string_of_locationloc^": cannot parse [@@@part] payload");Codeend|_->Code(* by default, [structure_item] locations do not contain the [;;] token,
so here we try to extend the location when this is needed. *)letshift_semi_semidocloc=letstr=doc.Lexbuf.contentsinletstop=loc.pos_cnuminletrecauxn=ifn+1>=String.lengthstrthenlocelsematchstr.[n],str.[n+1]with|'\n',_->aux(n+1)|';',';'->{locwithpos_cnum=n+2}|_,_->locinauxstopletbody_impldocs=letstart=matchswith|s::_->Somes.pstr_loc.loc_start.pos_cnum|_->Noneinletstop=matchList.revswith|s::_->Some(shift_semi_semidocs.pstr_loc.loc_end).pos_cnum|_->Noneinmatchstart,stopwith|Somestart,Somestop->String.subdoc.Lexbuf.contentsstart(stop-start)|_->""letbody_intfdocs=letstart=matchswith|s::_->Somes.psig_loc.loc_start.pos_cnum|_->Noneinletstop=matchList.revswith|s::_->Some(shift_semi_semidocs.psig_loc.loc_end).pos_cnum|_->Noneinmatchstart,stopwith|Somestart,Somestop->String.subdoc.Lexbuf.contentsstart(stop-start)|_->""letparts~bodydocphrases=letrecauxpartspartstrs=function|(s,Code)::rest->auxpartspart(s::strs)rest|(_,Partname)::rest->letbody=bodydoc(List.revstrs)inletparts=Part.v~name:part~body::partsinauxpartsname[]rest|[]->letparts=ifpart<>""||strs<>[]thenletbody=bodydoc(List.revstrs)inPart.v~name:part~body::partselsepartsinList.revpartsinaux[]""[]phraseslethandle_syntax_errore=#ifOCAML_MAJOR>=4&&OCAML_MINOR>=8(* The function is now Parse.prepare_error, but it is not
exposed; luckily enough, it is register to print the
exception. *)Fmt.failwith"Cannot parse: %s"(Printexc.to_string(Syntaxerr.Errore))#elseFmt.failwith"Cannot parse: %a"Syntaxerr.report_errore#endifletread_impldoc=tryletstrs=Parse.implementationdoc.Lexbuf.lexbufinList.map(funx->x,kind_implx)strswithSyntaxerr.Errore->handle_syntax_erroreletread_intfdoc=tryletstrs=Parse.interfacedoc.Lexbuf.lexbufinList.map(funx->x,kind_intfx)strswithSyntaxerr.Errore->handle_syntax_erroreendtypefile=|PartsofPart.tlist|Bodyof(exn*string)letread_impllexbuf=Phrase.(parts~body:body_impllexbuf(read_impllexbuf))letread_intflexbuf=Phrase.(parts~body:body_intflexbuf(read_intflexbuf))letreadfile=letlexbuf=Lexbuf.of_filefileinletread=matchFilename.extensionfilewith|".ml"->read_impl|".mli"->read_intf|s->Fmt.failwith"unknown extension: %s"sintrylexbuf|>read|>funx->Partsxwithe->Body(e,lexbuf.Lexbuf.contents)leterr_parse_error(e,_)=Fmt.failwith"Parse error: %a"Fmt.exneletfindfile~part=matchfile,partwith|Body(_,s),None->Some[s]|Bodyb,_->err_parse_errorb|Partsparts,Somepart->(matchList.find_opt(funp->String.equal(Part.namep)part)partswith|Somep->Some[Part.bodyp]|None->None)|Partsparts,None->List.fold_left(funaccp->Part.bodyp::[""]@acc)[]parts|>List.rev|>funx->Somexletreplacefile~part~lines=matchfile,partwith|Body(e,_),None->Body(e,String.concat"\n"lines)|Bodyb,_->err_parse_errorb|Partsparts,_->letpart=matchpartwithNone->""|Somep->pinList.map(funp->letname=Part.namepinifString.equalnamepartthen{pwithbody=String.concat"\n"lines}elsep)parts|>funx->Partsxletcontents=function|Body(_,s)->String.trims^"\n"|Partsparts->letlines=List.fold_left(funaccp->letbody=Part.bodypinmatchPart.namepwith|""->body::acc|n->body::("\n[@@@part \""^n^"\"] ;;\n")::acc)[]partsinletlines=List.revlinesinletlines=String.concat"\n"linesinString.trimlines^"\n"