123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214(*$ open Ppxlib_cinaps_helpers $*)open!ImportmoduleFormat=Stdlib.FormatmoduleFilename=Stdlib.Filename(* TODO: make the "deriving." depend on the matching attribute name. *)letend_marker_sig=Attribute.Floating.declare"deriving.end"Signature_itemAst_pattern.(pstrnil)()letend_marker_str=Attribute.Floating.declare"deriving.end"Structure_itemAst_pattern.(pstrnil)()moduletypeT1=sigtype'atendmoduleMake(M:sigtypettypecompiler_tvalget_loc:t->Location.tvalend_marker:(t,unit)Attribute.Floating.tmoduleTransform(T:T1):sigvalapply:<structure_item:structure_itemT.t;signature_item:signature_itemT.t;..>->tT.tendvalparse:Lexing.lexbuf->tlistvalto_sexp:t->Sexp.tvalto_compiler:t->compiler_tvalpp_compiler:Format.formatter->compiler_t->unitend)=structletextract_prefix~posl=letrecloopacc=function|[]->letloc={Location.loc_start=pos;loc_end=pos;loc_ghost=false}inError(Location.Error.createf~loc"ppxlib: [@@@@@@%s] attribute missing"(Attribute.Floating.nameM.end_marker),[])|x::l->(matchAttribute.Floating.convert_res[M.end_marker]xwith|OkNone->loop(x::acc)l|Ok(Some())->Ok(List.revacc,(M.get_locx).loc_start)|Errore->Errore|exceptionFailure_->loop(x::acc)l)inloop[]lletremove_loc=objectinheritAst_traverse.mapmethod!location_=Location.nonemethod!location_stack_=[]endmoduleM_map=M.Transform(structtype'at='a->'aend)letremove_locx=M_map.applyremove_locxletreclastprev=function[]->prev|x::l->lastxlletdiff_asts~generated~round_trip=letwith_temp_filef=Exn.protectx(Filename.temp_file"ppxlib""")~finally:Stdlib.Sys.remove~finwith_temp_file(funfn1->with_temp_file(funfn2->with_temp_file(funout->letdumpfnast=Out_channel.with_filefn~f:(funoc->letppf=Format.formatter_of_out_channelocinSexp.pp_humppf(M.to_sexpast);Format.pp_print_flushppf())indumpfn1generated;dumpfn2round_trip;letcmd=Printf.sprintf"patdiff -ascii -alt-old generated -alt-new \
'generated->printed->parsed' %s %s &> %s"(Filename.quotefn1)(Filename.quotefn2)(Filename.quoteout)inletok=Stdlib.Sys.commandcmd=1||letcmd=Printf.sprintf"diff --label generated --label \
'generated->printed->parsed' %s %s &> %s"(Filename.quotefn1)(Filename.quotefn2)(Filename.quoteout)inStdlib.Sys.commandcmd=1inifokthenIn_channel.read_alloutelse"<no differences produced by diff>")))letparse_strings=matchM.parse(Lexing.from_strings)with[x]->x|_->assertfalse(* To round trip our AST we convert it to the compiler's version, print it as
source using the compiler pretty-printers, parse it back using the
compiler's parser and migrate it back to our version.
Skipping the first migration can lead to errors because some subtleties may
be lost by older parsers. For instance in OCaml 5.02 [fun x y -> z] and
[fun x -> fun y -> z] have different representation but in OCaml 5.01 they
both parse to the same AST. Running the migration to the compiler AST first
anotates the AST using attributes allowing the final migration to preserve
such differences. *)letround_tripast=letcompiler_ast=M.to_compilerastinremove_loc(parse_string(Format.asprintf"%a@."M.pp_compilercompiler_ast))letrecmatch_loop~end_pos~mismatch_handler~expected~source=match(expected,source)with|[],[]->()|[],x::l->letloc={(M.get_locx)withloc_end=(M.get_loc(lastxl)).loc_end}inmismatch_handlerloc[]|_,[]->letloc={Location.loc_ghost=false;loc_start=end_pos;loc_end=end_pos}inmismatch_handlerlocexpected|x::expected,y::source->letloc=M.get_locyinletx=remove_locxinlety=remove_locyinifPoly.(<>)xythen(letround_trip=round_tripxinifPoly.(<>)xround_tripthenLocation.raise_errorf~loc"ppxlib: the corrected code doesn't round-trip.\n\
This is probably a bug in the OCaml printer:\n\
%s"(diff_asts~generated:x~round_trip);mismatch_handlerloc[x]);match_loop~end_pos~mismatch_handler~expected~sourceletdo_match~pos~expected~mismatch_handlersource=letopenResultinextract_prefix~possource>>|fun(source,end_pos)->match_loop~end_pos~mismatch_handler~expected~sourceend(*$*)moduleStr=Make(structtypet=structure_itemtypecompiler_t=Ppxlib_ast.Compiler_version.Ast.Parsetree.structure_itemletget_locx=x.pstr_locletend_marker=end_marker_strmoduleTransform(T:T1)=structletapplyo=o#structure_itemendletparse=Parse.implementationletto_sexp=Ast_traverse.sexp_of#structure_itemletto_compiler=Ppxlib_ast.Selected_ast.To_ocaml.copy_structure_itemletpp_compiler=Astlib.Compiler_pprintast.structure_itemend)(*$ str_to_sig _last_text_block *)moduleSig=Make(structtypet=signature_itemtypecompiler_t=Ppxlib_ast.Compiler_version.Ast.Parsetree.signature_itemletget_locx=x.psig_locletend_marker=end_marker_sigmoduleTransform(T:T1)=structletapplyo=o#signature_itemendletparse=Parse.interfaceletto_sexp=Ast_traverse.sexp_of#signature_itemletto_compiler=Ppxlib_ast.Selected_ast.To_ocaml.copy_signature_itemletpp_compiler=Astlib.Compiler_pprintast.signature_itemend)(*$*)letmatch_structure_res=Str.do_matchletmatch_structure~pos~expected~mismatch_handlerl=match_structure_res~pos~expected~mismatch_handlerl|>Result.handle_error~f:(fun(err,_)->Location.Error.raiseerr)letmatch_signature_res=Sig.do_matchletmatch_signature~pos~expected~mismatch_handlerl=match_signature_res~pos~expected~mismatch_handlerl|>Result.handle_error~f:(fun(err,_)->Location.Error.raiseerr)