123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296openImportopenUtilsmoduleContext=structtype'at=|Extensionof'aExtension.Context.t|Floating_attributeof'aAttribute.Floating.Context.tletparenppppfx=Caml.Format.fprintfppf"(%a)"ppxletprinter:typea.at->Caml.Format.formatter->a->unit=letopenExtension.ContextinletopenAttribute.Floating.Contextinfunction|ExtensionClass_expr->Pprintast.class_expr|ExtensionClass_field->Pprintast.class_field|ExtensionClass_type->Pprintast.class_type|ExtensionClass_type_field->Pprintast.class_type_field|ExtensionCore_type->parenPprintast.core_type|ExtensionExpression->parenPprintast.expression|ExtensionModule_expr->Pprintast.module_expr|ExtensionModule_type->Pprintast.module_type|ExtensionPattern->parenPprintast.pattern|ExtensionSignature_item->Pprintast.signature_item|ExtensionStructure_item->Pprintast.structure_item|Floating_attributeStructure_item->Pprintast.structure_item|Floating_attributeSignature_item->Pprintast.signature_item|Floating_attributeClass_field->Pprintast.class_field|Floating_attributeClass_type_field->Pprintast.class_type_fieldendmoduleReplacement=structtypedata=|Values:'aContext.t*'aContext_free.Generated_code_hook.single_or_many->data|Textofstringtypet={start:Lexing.position;stop:Lexing.position;data:data}letmake~context~start~stop~repl()={start;stop;data=Values(context,repl)}letmake_text~start~stop~repl()={start;stop;data=Textrepl}lettextblock=matchblock.datawith|Texts->s|Values(context,generated)->lets=letprinter=Context.printercontextinmatchgeneratedwith|Singlex->Caml.Format.asprintf"%a"printerx|Manyl->Caml.Format.asprintf"%a"(funppfl->List.iterl~f:(funx->printerppfx;Caml.Format.pp_print_newlineppf()))linletis_ws=function(' '|'\t'|'\r')->true|_->falseinletstrip_wssilen=letlen=refleninwhile(!len>0&&is_wss.[i+!len-1])dolen:=!len-1done;String.subs~pos:i~len:!leninletrecloopspos=ifpos>=String.lengthsthen[]elseletidx=matchString.index_fromspos'\n'with|Somei->i|None->String.lengthsinstrip_wsspos(idx-pos)::"\n"::loops(idx+1)inString.concat~sep:""(loops0)endopenReplacementmoduleReplacements=structtypet=Replacement.tlist(* Merge locations of the generated code. Overlapping locations are merged into one. The
result is sorted from the beginning of the file to the end. *)letcheck_and_sort~input_filename~input_namerepls=List.iterrepls~f:(funrepl->ifString.(<>)repl.start.pos_fnameinput_name||String.(<>)repl.stop.pos_fnameinput_namethenLocation.raise_errorf~loc:(Location.in_fileinput_filename)"ppxlib_driver: the rewriting contains parts from another file.\n\
It is too complicated to reconcile it with the source";assert(repl.start.pos_cnum<=repl.stop.pos_cnum));letrepls=List.sortrepls~compare:(funab->letd=comparea.start.pos_cnumb.stop.pos_cnuminifd=0then(* Put the largest first, so that the following [filter] functions always picks up
the lartest first when several generated repls start at the same position *)compareb.stop.pos_cnuma.stop.pos_cnumelsed)inletrecfilterprevrepls~acc=matchreplswith|[]->List.rev(prev::acc)|repl::repls->ifprev.stop.pos_cnum>repl.start.pos_cnumthenbeginifprev.stop.pos_cnum>=repl.stop.pos_cnumthen(* [repl] is included in [prev] => skip [repl] *)filterprevrepls~accelseLocation.raise_errorf"ppxlib_driver: locations of generated code are overlapping, cannot reconcile"~loc:{loc_start=repl.start;loc_end=prev.stop;loc_ghost=false};endelsefilterreplrepls~acc:(prev::acc)inmatchreplswith|[]->[]|repl::repls->filterreplrepls~acc:[];;endletcount_newliness=letn=ref0inString.iters~f:(function|'\n'->n:=!n+1|_->());!nletgenerated_code_begin="(* -----{ GENERATED CODE BEGIN }------------------------------------- *)"letgenerated_code_end="(* -----{ GENERATED CODE END }------------------------------------- *)"typemode=|Using_line_directives|Delimiting_generated_blockstypetarget=|Outputofmode|Correctedletskip_blank_eolcontents(pos:Lexing.position)=letrecloopcnum=ifcnum=String.lengthcontentsthen{poswithpos_cnum=cnum}elsematchcontents.[cnum]with|' '|'\t'|'\r'->loop(cnum+1)|'\n'->{poswithpos_cnum=cnum+1;pos_lnum=pos.pos_lnum+1;pos_bol=cnum+1}|_->posinlooppos.pos_cnumletwith_output~styler~(kind:Kind.t)fn~f=matchstylerwith|None->with_outputfn~binary:false~f|Somecmd->lettmp_fn,oc=Caml.Filename.open_temp_file"ppxlib_driver"(matchkindwithImpl->".ml"|Intf->".mli")inletcmd=Printf.sprintf"%s %s%s"cmd(Caml.Filename.quotetmp_fn)(matchfnwith|None->""|Somefn->" > "^Caml.Filename.quotefn)inletn=Exn.protectxtmp_fn~finally:Caml.Sys.remove~f:(fun_->Exn.protectxoc~finally:Out_channel.close~f:f;Caml.Sys.commandcmd)inifn<>0thenbegineprintf"command exited with code %d: %s\n"ncmd;Caml.exit1endletreconcile?styler(repls:Replacements.t)~kind~contents~input_filename~output~input_name~target=letrepls=Replacements.check_and_sort~input_filename~input_namereplsinletoutput_name=matchoutputwith|None->"<stdout>"|Somefn->fninwith_outputoutput~styler~kind~f:(funoc->letcopy_inputpos~up_to~line~last_is_text~is_text=letpos=iflast_is_textthenposelseskip_blank_eolcontentsposinifpos.pos_cnum<up_tothenbegin(matchtargetwith|OutputUsing_line_directives->Out_channel.fprintfoc"# %d %S\n%*s"pos.pos_lnuminput_name(pos.pos_cnum-pos.pos_bol)""|OutputDelimiting_generated_blocks|Corrected->());Out_channel.output_substringoc~buf:contents~pos:pos.pos_cnum~len:(up_to-pos.pos_cnum);letline=ref(line+1)infori=pos.pos_cnumtoup_to-1doifChar.equalcontents.[i]'\n'thenline:=!line+1done;letline=!lineinifnotis_text&&Char.(<>)contents.[up_to-1]'\n'then(Out_channel.output_charoc'\n';line+1)elselineendelselineinletrecloopline(pos:Lexing.position)repls~last_is_text=matchreplswith|[]->ignore(copy_inputpos~up_to:(String.lengthcontents)~line~last_is_text~is_text:false:int)|repl::repls->letis_text=matchrepl.datawith|Text_->true|Values_->falseinletline=copy_inputpos~up_to:repl.start.pos_cnum~line~last_is_text~is_textinlets=Replacement.textreplinletline=matchtargetwith|OutputUsing_line_directives->Out_channel.fprintfoc"# %d %S\n"(line+1)output_name;line+1|OutputDelimiting_generated_blocks->Out_channel.fprintfoc"%s\n"generated_code_begin;line+1|Corrected->lineinOut_channel.output_stringocs;letline=line+count_newlinessinloop_consecutive_replslinerepl.stoprepls~last_is_text:is_textandloop_consecutive_replsline(pos:Lexing.position)repls~last_is_text=matchreplswith|[]->end_consecutive_replslineposrepls~last_is_text|repl::repls'->letpos=iflast_is_textthenposelseskip_blank_eolcontentsposinifpos.pos_cnum<repl.start.pos_cnumthenend_consecutive_replslineposrepls~last_is_textelsebeginlets=Replacement.textreplinOut_channel.output_stringocs;letline=line+count_newlinessinletlast_is_text=matchrepl.datawith|Text_->true|Values_->falseinloop_consecutive_replslinerepl.stoprepls'~last_is_textendandend_consecutive_replslineposrepls~last_is_text=(matchtargetwith|OutputUsing_line_directives|Corrected->()|OutputDelimiting_generated_blocks->Out_channel.fprintfoc"%s\n"generated_code_end);looplineposrepls~last_is_textinletpos={Lexing.pos_fname=input_name;pos_lnum=1;pos_bol=0;pos_cnum=0}inmatchreplswith|{start={pos_cnum=0;_};_}::_->(matchtargetwith|OutputUsing_line_directives|Corrected->()|OutputDelimiting_generated_blocks->Out_channel.fprintfoc"%s\n"generated_code_begin);loop_consecutive_repls1posrepls~last_is_text:false|_->loop1posrepls~last_is_text:false)