1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330(*$ open Ppxlib_cinaps_helpers $*)openImportopenUtilsmoduleArg=Caml.Argletexe_name=Caml.Filename.basenameCaml.Sys.executable_nameletargs=ref[]letadd_argkeyspec~doc=args:=(key,spec,doc)::!argsletloc_fname=refNoneletperform_checks=refOptions.perform_checksletperform_checks_on_extensions=refOptions.perform_checks_on_extensionsletperform_locations_check=refOptions.perform_locations_checkletdebug_attribute_drop=reffalseletapply_list=refNoneletpreprocessor=refNoneletno_merge=reffalseletrequest_print_passes=reffalseletrequest_print_transformations=reffalseletuse_color=reftrueletdiff_command=refOptions.diff_commandletpretty=reffalseletstyler=refNoneletoutput_metadata_filename=refNoneletcorrected_suffix=ref".ppx-corrected"moduleLint_error=structtypet=Location.t*stringletof_stringlocs=(loc,s)endmoduleCookies=structtypet=Migrate_parsetree.Driver.cookiesletgettnamepattern=Option.map(Migrate_parsetree.Driver.get_cookietname(modulePpxlib_ast.Selected_ast))~f:(fune->Ast_pattern.parsepatterne.pexp_loceFn.id)letsettnameexpr=Migrate_parsetree.Driver.set_cookietname(modulePpxlib_ast.Selected_ast)exprlethandlers=ref[]letadd_handlerf=handlers:=!handlers@[f]letadd_simple_handlernamepattern~f=add_handler(funt->f(gettnamepattern))letacknoledge_cookiest=List.iter!handlers~f:(funf->ft)letpost_handlers=ref[]letadd_post_handlerf=post_handlers:=!post_handlers@[f]letcall_post_handlerst=List.iter!post_handlers~f:(funf->ft)endmoduleTransform=structtypet={name:string;aliases:stringlist;impl:(Parsetree.structure->Parsetree.structure)option;intf:(Parsetree.signature->Parsetree.signature)option;lint_impl:(Parsetree.structure->Lint_error.tlist)option;lint_intf:(Parsetree.signature->Lint_error.tlist)option;preprocess_impl:(Parsetree.structure->Parsetree.structure)option;preprocess_intf:(Parsetree.signature->Parsetree.signature)option;enclose_impl:(Location.toption->Parsetree.structure*Parsetree.structure)option;enclose_intf:(Location.toption->Parsetree.signature*Parsetree.signature)option;rules:Context_free.Rule.tlist;registered_at:Caller_id.t}lethas_nametname=(String.equalnamet.name)||(List.exists~f:(String.equalname)t.aliases)letall:tlistref=ref[]letprint_caller_idoc(caller_id:Caller_id.t)=matchcaller_idwith|None->Out_channel.output_stringoc"<unknown location>"|Someloc->Out_channel.fprintfoc"%s:%d"loc.filenameloc.line_number;;letregister?(extensions=[])?(rules=[])?enclose_impl?enclose_intf?impl?intf?lint_impl?lint_intf?preprocess_impl?preprocess_intf?(aliases=[])name=letrules=List.mapextensions~f:Context_free.Rule.extension@rulesinletcaller_id=Caller_id.get~skip:[Caml.__FILE__]inbeginmatchList.filter!all~f:(funct->has_namectname)with|[]->()|ct::_->eprintf"Warning: code transformation %s registered twice.\n"name;eprintf" - first time was at %a\n"print_caller_idct.registered_at;eprintf" - second time is at %a\n"print_caller_idcaller_id;end;letct={name;aliases;rules;enclose_impl;enclose_intf;impl;intf;lint_impl;preprocess_impl;preprocess_intf;lint_intf;registered_at=caller_id}inall:=ct::!all;;letreclastprevl=matchlwith|[]->prev|x::l->lastxl;;letloc_of_list~get_locl=matchlwith|[]->None|x::l->letfirst:Location.t=get_locxinletlast=get_loc(lastxl)inSome{firstwithloc_end=last.loc_end};;letmerge_into_generic_mapperst~hook~expect_mismatch_handler~tool_name=let{rules;enclose_impl;enclose_intf;impl;intf;_}=tinletmap=newContext_free.map_top_downrules~generated_code_hook:hook~expect_mismatch_handlerinletgen_header_and_footercontextwhole_locf=letheader,footer=fwhole_locin(matchwhole_locwith|Some(loc:Location.t)->letloc_header={locwithloc_end=loc.loc_start}inletloc_footer={locwithloc_start=loc.loc_end}in(matchheaderwith[]->()|_->hook.fcontextloc_header(Manyheader));(matchfooterwith[]->()|_->hook.fcontextloc_footer(Manyfooter))|None->matchheader@footerwith|[]->()|l->letpos={Lexing.pos_fname="";pos_lnum=1;pos_bol=0;pos_cnum=0}inletloc={Location.loc_start=pos;loc_end=pos;loc_ghost=false}inhook.fcontextloc(Manyl));(header,footer)inletmap_implst_with_attrs=letst=letattrs,st=List.split_whilest_with_attrs~f:(function|{pstr_desc=Pstr_attribute_;_}->true|_->false)inletheader,footer=matchenclose_implwith|None->([],[])|Somef->letwhole_loc=loc_of_listst~get_loc:(funst->st.Parsetree.pstr_loc)ingen_header_and_footerStructure_itemwhole_locfinletfile_path=File_path.get_default_path_strstinletbase_ctxt=Expansion_context.Base.top_level~tool_name~file_pathinletattrs=map#structurebase_ctxtattrsinletst=map#structurebase_ctxtstinList.concat[attrs;header;st;footer]inmatchimplwith|None->st|Somef->fstinletmap_intfsg_with_attrs=letsg=letattrs,sg=List.split_whilesg_with_attrs~f:(function|{psig_desc=Psig_attribute_;_}->true|_->false)inletheader,footer=matchenclose_intfwith|None->([],[])|Somef->letwhole_loc=loc_of_listsg~get_loc:(funsg->sg.Parsetree.psig_loc)ingen_header_and_footerSignature_itemwhole_locfinletfile_path=File_path.get_default_path_sigsginletbase_ctxt=Expansion_context.Base.top_level~tool_name~file_pathinletattrs=map#signaturebase_ctxtattrsinletsg=map#signaturebase_ctxtsginList.concat[attrs;header;sg;footer]inmatchintfwith|None->sg|Somef->fsgin{twithimpl=Somemap_impl;intf=Somemap_intf}letbuiltin_of_context_free_rewriters~hook~rules~enclose_impl~enclose_intf=merge_into_generic_mappers~hook{name="<builtin:context-free>";aliases=[];impl=None;intf=None;lint_impl=None;lint_intf=None;preprocess_impl=None;preprocess_intf=None;enclose_impl;enclose_intf;rules;registered_at=Caller_id.get~skip:[]}letpartition_transformationsts=(`Linters(List.filter_mapts~f:(funt->ifOption.is_somet.lint_impl||Option.is_somet.lint_intfthenSome{name=Printf.sprintf"<lint:%s>"t.name;aliases=[];impl=None;intf=None;lint_impl=t.lint_impl;lint_intf=t.lint_intf;enclose_impl=None;enclose_intf=None;preprocess_impl=None;preprocess_intf=None;rules=[];registered_at=t.registered_at}elseNone)),`Preprocess(List.filter_mapts~f:(funt->ifOption.is_somet.preprocess_impl||Option.is_somet.preprocess_implthenSome{name=Printf.sprintf"<preprocess:%s>"t.name;aliases=[];impl=t.preprocess_impl;intf=t.preprocess_intf;lint_impl=None;lint_intf=None;enclose_impl=None;enclose_intf=None;preprocess_impl=None;preprocess_intf=None;rules=[];registered_at=t.registered_at}elseNone)),`Rest(List.mapts~f:(funt->{twithlint_impl=None;lint_intf=None;preprocess_impl=None;preprocess_intf=None})))endletregister_transformation=Transform.registerletregister_code_transformation~name?(aliases=[])~impl~intf=register_transformationname~impl~intf~aliases;;letregister_transformation_using_ocaml_current_ast?impl?intf?aliasesname=letimpl=Option.mapimpl~f:(Ppxlib_ast.Selected_ast.of_ocaml_mapperStructure)inletintf=Option.mapintf~f:(Ppxlib_ast.Selected_ast.of_ocaml_mapperSignature)inregister_transformation?impl?intf?aliasesnameletdebug_dropped_attributename~old_dropped~new_dropped=letprint_diffwhatab=letdiff=List.filtera~f:(fun(name:_Loc.t)->not(List.existsb~f:(fun(name':_Location.loc)->phys_equalname.txtname'.txt)))inifnot(List.is_emptydiff)thenbegineprintf"The following attributes %s after applying %s:\n"whatname;List.iterdiff~f:(fun{Location.txt;loc}->Caml.Format.eprintf"- %a: %s\n"Location.printloctxt);Caml.Format.eprintf"@."endinprint_diff"disappeared"new_droppedold_dropped;print_diff"reappeared"old_droppednew_dropped;;letget_whole_ast_passes~hook~expect_mismatch_handler~tool_name=letcts=match!apply_listwith|None->List.rev!Transform.all|Somenames->List.mapnames~f:(funname->List.find_exn!Transform.all~f:(fun(ct:Transform.t)->Transform.has_namectname))inlet(`Linterslinters,`Preprocesspreprocess,`Restcts)=Transform.partition_transformationsctsin(* Allow only one preprocessor to assure deterministic order *)if(List.lengthpreprocess)>1thenbeginletpp=String.concat~sep:", "(List.mappreprocess~f:(funt->t.name))inleterr=Printf.sprintf"At most one preprocessor is allowed, while got: %s"ppinfailwitherrend;letcts=if!no_mergethenList.mapcts~f:(Transform.merge_into_generic_mappers~hook~tool_name~expect_mismatch_handler)elsebeginletget_enclosers~f=List.filter_mapcts~f:(fun(ct:Transform.t)->matchfctwith|None->None|Somex->Some(ct.name,x))(* Sort them to ensure deterministic ordering *)|>List.sort~compare:(fun(a,_)(b,_)->String.compareab)|>List.map~f:sndinletrules=List.mapcts~f:(fun(ct:Transform.t)->ct.rules)|>List.concatandimpl_enclosers=get_enclosers~f:(funct->ct.enclose_impl)andintf_enclosers=get_enclosers~f:(funct->ct.enclose_intf)inmatchrules,impl_enclosers,intf_encloserswith|[],[],[]->cts|_->letmerge_encloser=function|[]->None|enclosers->Some(funloc->letheaders,footers=List.mapenclosers~f:(funf->floc)|>List.unzipinletheaders=List.concatheadersinletfooters=List.concat(List.revfooters)in(headers,footers))inTransform.builtin_of_context_free_rewriters~rules~hook~expect_mismatch_handler~enclose_impl:(merge_encloserimpl_enclosers)~enclose_intf:(merge_encloserintf_enclosers)~tool_name::ctsendinlinters@preprocess@List.filtercts~f:(fun(ct:Transform.t)->matchct.impl,ct.intfwith|None,None->false|_->true);;letapply_transforms~tool_name~field~lint_field~dropped_so_far~hook~expect_mismatch_handlerx=letcts=get_whole_ast_passes~tool_name~hook~expect_mismatch_handlerinletx,_dropped,lint_errors=List.fold_leftcts~init:(x,[],[])~f:(fun(x,dropped,lint_errors)(ct:Transform.t)->letlint_errors=matchlint_fieldctwith|None->lint_errors|Somef->lint_errors@fxinmatchfieldctwith|None->(x,dropped,lint_errors)|Somef->letx=fxinletdropped=if!debug_attribute_dropthenbeginletnew_dropped=dropped_so_farxindebug_dropped_attributect.name~old_dropped:dropped~new_dropped;new_droppedendelse[]in(x,dropped,lint_errors))in(x,List.maplint_errors~f:(fun(loc,s)->Common.attribute_of_warninglocs));;(* +-----------------------------------------------------------------+
| Actual rewriting of structure/signatures |
+-----------------------------------------------------------------+ *)(* We want driver registered plugins to work with omp driver and vice-versa. To
simplify things we do as follow:
- we register driver as a single omp driver plugin
- driver calls the omp driver rewriting functions, which will apply everything
The registration with omp driver is at the end of the file.
*)moduleC=structtypet={hook:Context_free.Generated_code_hook.t;expect_mismatch_handler:Context_free.Expect_mismatch_handler.t}typeMigrate_parsetree.Driver.extra+=Toftletdefault={hook=Context_free.Generated_code_hook.nop;expect_mismatch_handler=Context_free.Expect_mismatch_handler.nop}letfind(config:Migrate_parsetree.Driver.config)=List.find_mapconfig.extras~f:(function|Tconfig->Someconfig|_->None)|>Option.value~defaultendletconfig~hook~expect_mismatch_handler=Migrate_parsetree.Driver.make_config()~tool_name:"ppxlib_driver"~extras:[C.T{hook;expect_mismatch_handler}]letas_ppx_config()=Migrate_parsetree.Driver.make_config()~tool_name:(Ocaml_common.Ast_mapper.tool_name())~include_dirs:!Ocaml_common.Clflags.include_dirs~load_path:(Compiler_specifics.get_load_path())~debug:!Ocaml_common.Clflags.debug?for_package:!Ocaml_common.Clflags.for_packageletprint_passes()=lettool_name="ppxlib_driver"inlethook=Context_free.Generated_code_hook.nopinletexpect_mismatch_handler=Context_free.Expect_mismatch_handler.nopinletcts=get_whole_ast_passes~hook~expect_mismatch_handler~tool_nameinif!perform_checksthenprintf"<builtin:freshen-and-collect-attributes>\n";List.itercts~f:(funct->printf"%s\n"ct.Transform.name);if!perform_checksthenbeginprintf"<builtin:check-unused-attributes>\n";if!perform_checks_on_extensionsthenprintf"<builtin:check-unused-extensions>\n"end;;(*$*)letreal_map_structureconfigcookiesst=let{C.hook;expect_mismatch_handler}=C.findconfiginCookies.acknoledge_cookiescookies;if!perform_checksthenbeginAttribute.reset_checks();Attribute.collect#structurestend;letst,lint_errors=apply_transformsst~tool_name:config.Migrate_parsetree.Driver.tool_name~field:(fun(ct:Transform.t)->ct.impl)~lint_field:(fun(ct:Transform.t)->ct.lint_impl)~dropped_so_far:Attribute.dropped_so_far_structure~hook~expect_mismatch_handlerinletst=matchlint_errorswith|[]->st|_->List.maplint_errors~f:(fun({attr_name={loc;_};_}asattr)->Ast_builder.Default.pstr_attribute~locattr)@stinCookies.call_post_handlerscookies;if!perform_checksthenbegin(* TODO: these two passes could be merged, we now have more passes for
checks than for actual rewriting. *)Attribute.check_unused#structurest;if!perform_checks_on_extensionsthenExtension.check_unused#structurest;Attribute.check_all_seen();if!perform_locations_checkthenletopenLocation_checkinignore((enforce_invariants!loc_fname)#structurestNon_intersecting_ranges.empty:Non_intersecting_ranges.t)end;st;;letmap_structure_genst~config:Migrate_parsetree.Driver.some_structure=Migrate_parsetree.Driver.rewrite_structureconfig(modulePpxlib_ast.Selected_ast)stletmap_structurest=map_structure_genst~config:(as_ppx_config())(*$ str_to_sig _last_text_block *)letreal_map_signatureconfigcookiessg=let{C.hook;expect_mismatch_handler}=C.findconfiginCookies.acknoledge_cookiescookies;if!perform_checksthenbeginAttribute.reset_checks();Attribute.collect#signaturesgend;letsg,lint_errors=apply_transformssg~tool_name:config.Migrate_parsetree.Driver.tool_name~field:(fun(ct:Transform.t)->ct.intf)~lint_field:(fun(ct:Transform.t)->ct.lint_intf)~dropped_so_far:Attribute.dropped_so_far_signature~hook~expect_mismatch_handlerinletsg=matchlint_errorswith|[]->sg|_->List.maplint_errors~f:(fun({attr_name={loc;_};_}asattr)->Ast_builder.Default.psig_attribute~locattr)@sginCookies.call_post_handlerscookies;if!perform_checksthenbegin(* TODO: these two passes could be merged, we now have more passes for
checks than for actual rewriting. *)Attribute.check_unused#signaturesg;if!perform_checks_on_extensionsthenExtension.check_unused#signaturesg;Attribute.check_all_seen();if!perform_locations_checkthenletopenLocation_checkinignore((enforce_invariants!loc_fname)#signaturesgNon_intersecting_ranges.empty:Non_intersecting_ranges.t)end;sg;;letmap_signature_gensg~config:Migrate_parsetree.Driver.some_signature=Migrate_parsetree.Driver.rewrite_signatureconfig(modulePpxlib_ast.Selected_ast)sgletmap_signaturesg=map_signature_gensg~config:(as_ppx_config())(*$*)(* +-----------------------------------------------------------------+
| Entry points |
+-----------------------------------------------------------------+ *)letmapper=letmoduleJs=Ppxlib_ast.Selected_astin(*$*)letstructure_st=Js.of_ocamlStructurest|>map_structure|>Migrate_parsetree.Driver.migrate_some_structure(moduleMigrate_parsetree.OCaml_current)in(*$ str_to_sig _last_text_block *)letsignature_sg=Js.of_ocamlSignaturesg|>map_signature|>Migrate_parsetree.Driver.migrate_some_signature(moduleMigrate_parsetree.OCaml_current)in(*$*){Ocaml_common.Ast_mapper.default_mapperwithstructure;signature};;letas_ppx_rewriter_mainargv=letargv=Caml.Sys.executable_name::argvinletusage=Printf.sprintf"%s [extra_args] <infile> <outfile>"exe_nameinmatchArg.parse_argv(Array.of_listargv)(Arg.align(List.rev!args))(fun_->raise(Arg.Bad"anonymous arguments not accepted"))usagewith|exceptionArg.Badmsg->eprintf"%s"msg;Caml.exit2|exceptionArg.Helpmsg->eprintf"%s"msg;Caml.exit0|()->mapperletrun_as_ppx_rewriter()=perform_checks:=false;Ocaml_common.Ast_mapper.run_mainas_ppx_rewriter_main;Caml.exit0letstring_contains_binary_asts=lettestmagic_number=String.is_prefixs~prefix:(String.submagic_number~pos:0~len:9)intestAst_magic.ast_intf_magic_number||testAst_magic.ast_impl_magic_numbertypepp_error={filename:string;command_line:string}exceptionPp_errorofpp_errorletreport_pp_errore=letbuff=Buffer.create128inletppf=Caml.Format.formatter_of_bufferbuffinCaml.Format.fprintfppf"Error while running external preprocessor@.\
Command line: %s@."e.command_line;Caml.Format.pp_print_flushppf();Buffer.contentsbufflet()=Location.Error.register_error_of_exn(function|Pp_errore->Some(Location.Error.make~loc:(Location.in_filee.filename)~sub:[](report_pp_errore))|_->None)letremove_no_errorfn=tryCaml.Sys.removefnwithSys_error_->()letprotectxx~f~finally=matchfxwith|v->finallyx;v|exceptione->finallyx;raisee;;letwith_preprocessed_filefn~f=match!preprocessorwith|None->ffn|Somepp->protectx(Caml.Filename.temp_file"ocamlpp""")~finally:remove_no_error~f:(funtmpfile->letcomm=Printf.sprintf"%s %s > %s"pp(ifString.equalfn"-"then""elseCaml.Filename.quotefn)(Caml.Filename.quotetmpfile)inifCaml.Sys.commandcomm<>0thenraise(Pp_error{filename=fn;command_line=comm});ftmpfile)letwith_preprocessed_inputfn~f=with_preprocessed_filefn~f:(funfn->ifString.equalfn"-"thenfstdinelseIn_channel.with_filefn~f);;letrelocate_mapper=objectinherit[string*string]Ast_traverse.map_with_contextmethod!position(old_fn,new_fn)pos=ifString.equalpos.pos_fnameold_fnthen{poswithpos_fname=new_fn}elseposend(* Set the input name globally. This is used by some ppx rewriters
such as bisect_ppx. *)letset_input_namename=Ocaml_common.Location.input_name:=nameletload_input(kind:Kind.t)fninput_name~relocateic=set_input_nameinput_name;matchMigrate_parsetree.Ast_io.from_channelicwith|Ok(ast_input_name,ast)->letast=Intf_or_impl.of_ast_ioastinifnot(Kind.equalkind(Intf_or_impl.kindast))thenLocation.raise_errorf~loc:(Location.in_filefn)"File contains a binary %s AST but an %s was expected"(Kind.describe(Intf_or_impl.kindast))(Kind.describekind);ifString.equalast_input_nameinput_name||notrelocatethenbeginset_input_nameast_input_name;(ast_input_name,ast)endelse(input_name,Intf_or_impl.map_with_contextastrelocate_mapper(ast_input_name,input_name))|Error(Unknown_version_)->Location.raise_errorf~loc:(Location.in_filefn)"File is a binary ast for an unknown version of OCaml"|Error(Not_a_binary_astprefix_read_from_file)->(* To test if a file is an AST file, we have to read the first few bytes of the
file. If it is not, we have to parse these bytes and the rest of the file as
source code.
The compiler just does [seek_on 0] in this case, however this doesn't work when
the input is a pipe.
What we do instead is create a lexing buffer from the input channel and pre-fill
it with what we read to do the test. *)letlexbuf=Lexing.from_channelicinletlen=String.lengthprefix_read_from_fileinBytes.From_string.blit~src:prefix_read_from_file~src_pos:0~dst:lexbuf.lex_buffer~dst_pos:0~len;lexbuf.lex_buffer_len<-len;lexbuf.lex_curr_p<-{pos_fname=input_name;pos_lnum=1;pos_bol=0;pos_cnum=0};Lexer.skip_hash_banglexbuf;matchkindwith|Intf->input_name,Intf(Parse.interfacelexbuf)|Impl->input_name,Impl(Parse.implementationlexbuf);;letload_source_filefn=lets=In_channel.read_allfninifstring_contains_binary_aststhenLocation.raise_errorf~loc:(Location.in_filefn)"ppxlib_driver: cannot use -reconcile with binary AST files";s;;typeoutput_mode=|Pretty_print|Dump_ast|Dparsetree|ReconcileofReconcile.mode|Null(*$*)letextract_cookies_strst=matchstwith|{pstr_desc=Pstr_attribute{attr_name={txt="ocaml.ppx.context";_};_};_}asprefix::st->letprefix=Ppxlib_ast.Selected_ast.to_ocamlStructure[prefix]inassert(List.is_empty(Ocaml_common.Ast_mapper.drop_ppx_context_str~restore:trueprefix));st|_->stletadd_cookies_strst=letprefix=Ocaml_common.Ast_mapper.add_ppx_context_str~tool_name:"ppxlib_driver"[]|>Ppxlib_ast.Selected_ast.of_ocamlStructureinprefix@st(*$ str_to_sig _last_text_block *)letextract_cookies_sigsg=matchsgwith|{psig_desc=Psig_attribute{attr_name={txt="ocaml.ppx.context";_};_};_}asprefix::sg->letprefix=Ppxlib_ast.Selected_ast.to_ocamlSignature[prefix]inassert(List.is_empty(Ocaml_common.Ast_mapper.drop_ppx_context_sig~restore:trueprefix));sg|_->sgletadd_cookies_sigsg=letprefix=Ocaml_common.Ast_mapper.add_ppx_context_sig~tool_name:"ppxlib_driver"[]|>Ppxlib_ast.Selected_ast.of_ocamlSignatureinprefix@sg(*$*)letextract_cookies(ast:Intf_or_impl.t):Intf_or_impl.t=matchastwith|Intfx->Intf(extract_cookies_sigx)|Implx->Impl(extract_cookies_strx)letadd_cookies(ast:Intf_or_impl.t):Intf_or_impl.t=matchastwith|Intfx->Intf(add_cookies_sigx)|Implx->Impl(add_cookies_strx)letcorrections=ref[]letadd_to_listrx=r:=x::!rletregister_correction~loc~repl=add_to_listcorrections(Reconcile.Replacement.make_text()~start:loc.loc_start~stop:loc.loc_end~repl)letprocess_file_hooks=ref[]letregister_process_file_hookf=add_to_listprocess_file_hooksfmoduleFile_property=structtype'at={name:string;mutabledata:'aoption;sexp_of_t:'a->Sexp.t}typepacked=T:_t->packedletall=ref[]letregistert=add_to_listall(Tt)letreset_all()=List.iter!all~f:(fun(Tt)->t.data<-None)letdump_and_reset_all()=List.filter_map(List.rev!all)~f:(fun(Tt)->matcht.datawith|None->None|Somev->t.data<-None;Some(t.name,t.sexp_of_tv))endmoduleCreate_file_property(Name:sigvalname:stringend)(T:Sexpable.S)=structlett:_File_property.t={name=Name.name;data=None;sexp_of_t=T.sexp_of_t}let()=File_property.registertletsetx=t.data<-Somexendletprocess_file(kind:Kind.t)fn~input_name~relocate~output_mode~embed_errors~output=File_property.reset_all();List.iter(List.rev!process_file_hooks)~f:(funf->f());corrections:=[];letreplacements=ref[]inlethook:Context_free.Generated_code_hook.t=matchoutput_modewith|Reconcile(Using_line_directives|Delimiting_generated_blocks)->{f=funcontext(loc:Location.t)generated->add_to_listreplacements(Reconcile.Replacement.make()~context:(Extensioncontext)~start:loc.loc_start~stop:loc.loc_end~repl:generated)}|_->Context_free.Generated_code_hook.nopinletexpect_mismatch_handler:Context_free.Expect_mismatch_handler.t={f=funcontext(loc:Location.t)generated->add_to_listcorrections(Reconcile.Replacement.make()~context:(Floating_attributecontext)~start:loc.loc_start~stop:loc.loc_end~repl:(Manygenerated))}inletinput_name,ast=tryletinput_name,ast=with_preprocessed_inputfn~f:(load_inputkindfninput_name~relocate)inletast=extract_cookiesastinletconfig=config~hook~expect_mismatch_handlerinmatchastwith|Intfx->input_name,Some_intf_or_impl.Intf(map_signature_genx~config)|Implx->input_name,Some_intf_or_impl.Impl(map_structure_genx~config)withexnwhenembed_errors->matchLocation.Error.of_exnexnwith|None->raiseexn|Someerror->letloc=Location.noneinletext=Location.Error.to_extensionerrorinletopenAst_builder.Defaultinletast=matchkindwith|Intf->Some_intf_or_impl.Intf(Sig((modulePpxlib_ast.Selected_ast),[psig_extension~locext[]]))|Impl->Some_intf_or_impl.Impl(Str((modulePpxlib_ast.Selected_ast),[pstr_extension~locext[]]))ininput_name,astinOption.iter!output_metadata_filename~f:(funfn->letmetadata=File_property.dump_and_reset_all()inOut_channel.write_allfn~data:(List.mapmetadata~f:(fun(s,sexp)->Sexp.to_string_hum(Sexp.List[Atoms;sexp])^"\n")|>String.concat~sep:""));letinput_contents=lazy(load_source_filefn)inletcorrected=fn^!corrected_suffixinletmismatches_found=match!correctionswith|[]->ifCaml.Sys.file_existscorrectedthenCaml.Sys.removecorrected;false|corrections->Reconcile.reconcilecorrections~contents:(Lazy.forceinput_contents)~output:(Somecorrected)~input_filename:fn~input_name~target:Corrected?styler:!styler~kind;truein(matchoutput_modewith|Null->()|Pretty_print->with_outputoutput~binary:false~f:(funoc->letppf=Caml.Format.formatter_of_out_channelocinletast=Intf_or_impl.of_some_intf_or_implastin(matchastwith|Intfast->Pprintast.signatureppfast|Implast->Pprintast.structureppfast);letnull_ast=matchastwith|Intf[]|Impl[]->true|_->falseinifnotnull_astthenCaml.Format.pp_print_newlineppf())|Dump_ast->with_outputoutput~binary:true~f:(funoc->letast=Some_intf_or_impl.to_ast_ioast~add_ppx_context:trueinMigrate_parsetree.Ast_io.to_channelocinput_nameast)|Dparsetree->with_outputoutput~binary:false~f:(funoc->letppf=Caml.Format.formatter_of_out_channelocinletast=Intf_or_impl.of_some_intf_or_implastinletast=add_cookiesastin(matchastwith|Intfast->Sexp.pp_humppf(Ast_traverse.sexp_of#signatureast)|Implast->Sexp.pp_humppf(Ast_traverse.sexp_of#structureast));Caml.Format.pp_print_newlineppf())|Reconcilemode->Reconcile.reconcile!replacements~contents:(Lazy.forceinput_contents)~output~input_filename:fn~input_name~target:(Outputmode)?styler:!styler~kind);ifmismatches_found&&(match!diff_commandwith|Some"-"->false|_->true)thenbeginPpxlib_print_diff.print()~file1:fn~file2:corrected~use_color:!use_color?diff_command:!diff_command;Caml.exit1end;;letoutput_mode=refPretty_printletoutput=refNoneletkind=refNoneletinput=refNoneletembed_errors=reffalseletset_inputfn=match!inputwith|None->input:=Somefn|Some_->raise(Arg.Bad"too many input files")letset_kindk=match!kindwith|Somek'whennot(Kind.equalkk')->raise(Arg.Bad"must specify at most one of -impl or -intf")|_->kind:=Somek;;letset_output_modemode=match!output_mode,modewith|Pretty_print,_->output_mode:=mode|_,Pretty_print->assertfalse|Dump_ast,Dump_ast|Dparsetree,Dparsetree->()|Reconcilea,ReconcilebwhenPoly.equalab->()|x,y->letarg_of_output_mode=function|Pretty_print->assertfalse|Dump_ast->"-dump-ast"|Dparsetree->"-dparsetree"|ReconcileUsing_line_directives->"-reconcile"|ReconcileDelimiting_generated_blocks->"-reconcile-with-comments"|Null->"-null"inraise(Arg.Bad(Printf.sprintf"%s and %s are incompatible"(arg_of_output_modex)(arg_of_output_modey)));;letprint_transformations()=List.iter!Transform.all~f:(fun(ct:Transform.t)->printf"%s\n"ct.name);;;letparse_apply_lists=letnames=ifString.equals""then[]elseString.splits~on:','inList.iternames~f:(funname->ifnot(List.exists!Transform.all~f:(fun(ct:Transform.t)->Transform.has_namectname))thenraise(Caml.Arg.Bad(Printf.sprintf"code transformation '%s' does not exist"name)));namestypemask={mutableapply:stringlistoption;mutabledont_apply:stringlistoption}letmask={apply=None;dont_apply=None}lethandle_applys=ifOption.is_somemask.applythenraise(Arg.Bad"-apply called too many times");(* This is not strictly necessary but it's more intuitive *)ifOption.is_somemask.dont_applythenraise(Arg.Bad"-apply must be called before -dont-apply");mask.apply<-Some(parse_apply_lists)lethandle_dont_applys=ifOption.is_somemask.dont_applythenraise(Arg.Bad"-apply called too many times");mask.dont_apply<-Some(parse_apply_lists)letinterpret_mask()=ifOption.is_somemask.apply||Option.is_somemask.dont_applythenbeginletselected_transform_namect=letis_candidate=matchmask.applywith|None->true|Somenames->List.existsnames~f:(Transform.has_namect)inletis_selected=matchmask.dont_applywith|None->is_candidate|Somenames->is_candidate&¬(List.existsnames~f:(Transform.has_namect))inifis_selectedthenSomect.nameelseNoneinapply_list:=Some(List.filter_map!Transform.all~f:selected_transform_name)endletshared_args=["-loc-filename",Arg.String(funs->loc_fname:=Somes),"<string> File name to use in locations";"-reserve-namespace",Arg.StringName.Reserved_namespaces.reserve,"<string> Mark the given namespace as reserved";"-no-check",Arg.Clearperform_checks," Disable checks (unsafe)";"-check",Arg.Setperform_checks," Enable checks";"-no-check-on-extensions",Arg.Clearperform_checks_on_extensions," Disable checks on extension point only";"-check-on-extensions",Arg.Setperform_checks_on_extensions," Enable checks on extension point only";"-no-locations-check",Arg.Clearperform_locations_check," Disable locations check only";"-locations-check",Arg.Setperform_locations_check," Enable locations check only";"-apply",Arg.Stringhandle_apply,"<names> Apply these transformations in order (comma-separated list)";"-dont-apply",Arg.Stringhandle_dont_apply,"<names> Exclude these transformations";"-no-merge",Arg.Setno_merge," Do not merge context free transformations (better for debugging rewriters)"]let()=List.itershared_args~f:(fun(key,spec,doc)->add_argkeyspec~doc)letset_cookies=matchString.lsplit2s~on:'='with|None->raise(Arg.Bad"invalid cookie, must be of the form \"<name>=<expr>\"")|Some(name,value)->letlexbuf=Lexing.from_stringvalueinlexbuf.Lexing.lex_curr_p<-{Lexing.pos_fname="<command-line>";pos_lnum=1;pos_bol=0;pos_cnum=0};letexpr=Parse.expressionlexbufinMigrate_parsetree.Driver.set_global_cookiename(modulePpxlib_ast.Selected_ast)exprletas_pp()=set_output_modeDump_ast;embed_errors:=trueletstandalone_args=["-as-ppx",Arg.Unit(fun()->raise(Arg.Bad"-as-ppx must be the first argument"))," Run as a -ppx rewriter (must be the first argument)";"--as-ppx",Arg.Unit(fun()->raise(Arg.Bad"--as-ppx must be the first argument"))," Same as -as-ppx";"-as-pp",Arg.Unitas_pp," Shorthand for: -dump-ast -embed-errors";"--as-pp",Arg.Unitas_pp," Same as -as-pp";"-o",Arg.String(funs->output:=Somes),"<filename> Output file (use '-' for stdout)";"-",Arg.Unit(fun()->set_input"-")," Read input from stdin";"-dump-ast",Arg.Unit(fun()->set_output_modeDump_ast)," Dump the marshaled ast to the output file instead of pretty-printing it";"--dump-ast",Arg.Unit(fun()->set_output_modeDump_ast)," Same as -dump-ast";"-dparsetree",Arg.Unit(fun()->set_output_modeDparsetree)," Print the parsetree (same as ocamlc -dparsetree)";"-embed-errors",Arg.Setembed_errors," Embed errors in the output AST (default: true when -dump-ast, false otherwise)";"-null",Arg.Unit(fun()->set_output_modeNull)," Produce no output, except for errors";"-impl",Arg.Unit(fun()->set_kindImpl),"<file> Treat the input as a .ml file";"--impl",Arg.Unit(fun()->set_kindImpl),"<file> Same as -impl";"-intf",Arg.Unit(fun()->set_kindIntf),"<file> Treat the input as a .mli file";"--intf",Arg.Unit(fun()->set_kindIntf),"<file> Same as -intf";"-debug-attribute-drop",Arg.Setdebug_attribute_drop," Debug attribute dropping";"-print-transformations",Arg.Setrequest_print_transformations," Print linked-in code transformations, in the order they are applied";"-print-passes",Arg.Setrequest_print_passes," Print the actual passes over the whole AST in the order they are applied";"-ite-check",Arg.Unit(fun()->eprintf"Warning: the -ite-check flag is deprecated \
and has no effect.\n%!";Extra_warnings.care_about_ite_branch:=true)," (no effect -- kept for compatibility)";"-pp",Arg.String(funs->preprocessor:=Somes),"<command> Pipe sources through preprocessor <command> (incompatible with -as-ppx)";"-reconcile",Arg.Unit(fun()->set_output_mode(ReconcileUsing_line_directives))," (WIP) Pretty print the output using a mix of the input source \
and the generated code";"-reconcile-with-comments",Arg.Unit(fun()->set_output_mode(ReconcileDelimiting_generated_blocks))," (WIP) same as -reconcile but uses comments to enclose the generated code";"-no-color",Arg.Clearuse_color," Don't use colors when printing errors";"-diff-cmd",Arg.String(funs->diff_command:=Somes)," Diff command when using code expectations (use - to disable diffing)";"-pretty",Arg.Setpretty," Instruct code generators to improve the prettiness of the generated code";"-styler",Arg.String(funs->styler:=Somes)," Code styler";"-cookie",Arg.Stringset_cookie,"NAME=EXPR Set the cookie NAME to EXPR";"--cookie",Arg.Stringset_cookie," Same as -cookie";"-output-metadata",Arg.String(funs->output_metadata_filename:=Somes),"FILE Where to store the output metadata";"-corrected-suffix",Arg.Set_stringcorrected_suffix,"SUFFIX Suffix to happend to corrected files"];;letget_args?(standalone_args=standalone_args)()=letargs=standalone_args@List.rev!argsinletmy_arg_names=List.rev_mapargs~f:(fun(name,_,_)->name)|>Set.of_list(moduleString)inletomp_args=(* Filter out arguments that we override *)List.filter(Migrate_parsetree.Driver.registered_args())~f:(fun(name,_,_)->not(Set.memmy_arg_namesname))inargs@omp_args;;letstandalone_main()=letusage=Printf.sprintf"%s [extra_args] [<files>]"exe_nameinletargs=get_args()inMigrate_parsetree.Driver.reset_args();Arg.parse(Arg.alignargs)set_inputusage;interpret_mask();if!request_print_transformationsthenbeginprint_transformations();Caml.exit0;end;if!request_print_passesthenbeginprint_passes();Caml.exit0;end;match!inputwith|None->eprintf"%s: no input file given\n%!"exe_name;Caml.exit2|Somefn->letkind=match!kindwith|Somek->k|None->matchKind.of_filenamefnwith|Somek->k|None->eprintf"%s: don't know what to do with '%s', use -impl or -intf.\n"exe_namefn;Caml.exit2inletinput_name,relocate=match!loc_fnamewith|None->fn,false|Somefn->fn,trueinprocess_filekindfn~input_name~relocate~output_mode:!output_mode~output:!output~embed_errors:!embed_errors;;letstandalone_run_as_ppx_rewriter()=letn=Array.lengthCaml.Sys.argvinletusage=Printf.sprintf"%s -as-ppx [extra_args] <infile> <outfile>"exe_nameinifn<4thenbegineprintf"Usage: %s\n%!"usage;Caml.exit2end;letargv=Array.create~len:(n-3)""inargv.(0)<-Caml.Sys.argv.(0);fori=1to(n-4)doargv.(i)<-Caml.Sys.argv.(i+1)done;letstandalone_args=List.mapstandalone_args~f:(fun(arg,spec,_doc)->(arg,spec," Unused with -as-ppx"))inletargs=get_args~standalone_args()inMigrate_parsetree.Driver.reset_args();matchArg.parse_argvargv(Arg.alignargs)(fun_->raise(Arg.Bad"anonymous arguments not accepted"))usagewith|exceptionArg.Badmsg->eprintf"%s"msg;Caml.exit2|exceptionArg.Helpmsg->eprintf"%s"msg;Caml.exit0|()->interpret_mask();Ocaml_common.Ast_mapper.apply~source:Caml.Sys.argv.(n-2)~target:Caml.Sys.argv.(n-1)mapper;;letstandalone()=Compiler_specifics.read_clflags_from_env();tryifArray.lengthCaml.Sys.argv>=2&&matchCaml.Sys.argv.(1)with|"-as-ppx"|"--as-ppx"->true|_->falsethenstandalone_run_as_ppx_rewriter()elsestandalone_main();Caml.exit0withexn->Location.report_exceptionCaml.Format.err_formatterexn;Caml.exit1;;letpretty()=!prettylet()=Migrate_parsetree.Driver.register~name:"ppxlib_driver"(* This doesn't take arguments registered by rewriters. It's not worth supporting
them, since [--cookie] is a much better replacement for passing parameters to
individual rewriters. *)~args:shared_args(modulePpxlib_ast.Selected_ast)(funconfigcookies->letmoduleA=Ppxlib_ast.Selected_ast.Ast.Ast_mapperinletstructure_st=real_map_structureconfigcookiesstinletsignature_sg=real_map_signatureconfigcookiessgin{A.default_mapperwithstructure;signature})letenable_checks()=(* We do not enable the locations check here, we currently require that one
to be specifically enabled. *)perform_checks:=true;perform_checks_on_extensions:=trueletenable_location_check()=perform_locations_check:=trueletdisable_location_check()=perform_locations_check:=falseletmap_structurest=map_structurest|>Migrate_parsetree.Driver.migrate_some_structure(modulePpxlib_ast.Selected_ast)