123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221openImportletwith_outputfn~binary~f=matchfnwith|None|Some"-"->fstdout|Somefn->Out_channel.with_filefn~binary~fmoduleKind=structtypet=Intf|Implletof_filenamefn:toption=ifCaml.Filename.check_suffixfn".ml"thenSomeImplelseifCaml.Filename.check_suffixfn".mli"thenSomeIntfelseNoneletdescribe=functionImpl->"implementation"|Intf->"interface"letequal:t->t->bool=Poly.equalendmoduleIntf_or_impl=structtypet=Intfofsignature|Implofstructureletmapt(map:Ast_traverse.map)=matchtwith|Implx->Impl(map#structurex)|Intfx->Intf(map#signaturex)letmap_with_contextt(map:_Ast_traverse.map_with_context)ctx=matchtwith|Implx->Impl(map#structurectxx)|Intfx->Intf(map#signaturectxx)letkind:_->Kind.t=functionIntf_->Intf|Impl_->ImplendmoduleAst_io=structtypeinput_version=(moduleOCaml_version)letfall_back_input_version=(moduleCompiler_version:OCaml_version)(* This should only be used when the input version can't be determined due to
loading or preprocessing errors *)typet={input_name:string;input_version:input_version;ast:Intf_or_impl.t;}typeread_error=|Not_a_binary_ast|Unknown_versionofstring*input_version|Source_parse_errorofLocation.Error.t*input_version|System_errorofLocation.Error.t*input_versiontypeinput_source=Stdin|Fileofstringtypeinput_kind=Possibly_sourceofKind.t*string|Necessarily_binaryletread_error_to_string(error:read_error)=matcherrorwith|Not_a_binary_ast->"Error: Not a binary ast"|Unknown_version(s,_)->"Error: Unknown version "^s|Source_parse_error(loc,_)->"Source parse error:"^Location.Error.messageloc|System_error(loc,_)->"System error: "^Location.Error.messagelocletparse_source_code~(kind:Kind.t)~input_name~prefix_read_from_sourceic=(* The input version is determined by the fact that the input will get parsed by
the current compiler Parse module *)letinput_version=(moduleCompiler_version:OCaml_version)intry(* 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_sourceinBytes.blit_string~src:prefix_read_from_source~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};Skip_hash_bang.skip_hash_banglexbuf;letast:Intf_or_impl.t=matchkindwith|Intf->Intf(Parse.interfacelexbuf)|Impl->Impl(Parse.implementationlexbuf)inOk{input_name;input_version;ast}withexn->(matchLocation.Error.of_exnexnwith|None->raiseexn|Someerror->Error(Source_parse_error(error,input_version)))letmagic_length=String.lengthAstlib.Config.ast_impl_magic_numberletread_magicic=letbuf=Bytes.createmagic_lengthinletlen=inputicbuf0magic_lengthinlets=Bytes.sub_stringbuf~pos:0~leniniflen=magic_lengththenOkselseErrorsletfrom_channelch~input_kind=lethandle_non_binaryprefix_read_from_source=matchinput_kindwith|Possibly_source(kind,input_name)->parse_source_code~kind~input_name~prefix_read_from_sourcech|Necessarily_binary->ErrorNot_a_binary_astinmatchread_magicchwith|Errors->handle_non_binarys|Oks->(matchFind_version.from_magicswith|Intf(moduleInput_version:OCaml_version)->letinput_name:string=input_valuechinletast=input_valuechinletmoduleInput_to_ppxlib=Convert(Input_version)(Js)inletast=Intf_or_impl.Intf(Input_to_ppxlib.copy_signatureast)inOk{input_name;input_version=(moduleInput_version:OCaml_version);ast;}|Impl(moduleInput_version:OCaml_version)->letinput_name:string=input_valuechinletast=input_valuechinletmoduleInput_to_ppxlib=Convert(Input_version)(Js)inletast=Intf_or_impl.Impl(Input_to_ppxlib.copy_structureast)inOk{input_name;input_version=(moduleInput_version:OCaml_version);ast;}|Unknown->ifString.equal(String.subs~pos:0~len:9)(String.subAstlib.Config.ast_impl_magic_number~pos:0~len:9)||String.equal(String.subs~pos:0~len:9)(String.subAstlib.Config.ast_intf_magic_number~pos:0~len:9)thenError(Unknown_version(s,fall_back_input_version))elsehandle_non_binarys)letreadinput_source~input_kind=trymatchinput_sourcewith|Stdin->from_channelstdin~input_kind|Filefn->In_channel.with_filefn~f:(from_channel~input_kind)withexn->(matchLocation.Error.of_exnexnwith|None->raiseexn|Someerror->Error(System_error(error,fall_back_input_version)))letwriteoc{input_name;input_version=(moduleInput_version);ast}~add_ppx_context=letmodulePpxlib_to_input=Convert(Js)(Input_version)inletmoduleOcaml_to_input=Convert(Compiler_version)(Input_version)inmatchastwith|Intfsg->letsg=ifadd_ppx_contextthenSelected_ast.To_ocaml.copy_signaturesg|>Astlib.Ast_metadata.add_ppx_context_sig~tool_name:"ppx_driver"|>Ocaml_to_input.copy_signatureelsePpxlib_to_input.copy_signaturesginoutput_stringocInput_version.Ast.Config.ast_intf_magic_number;output_valueocinput_name;output_valueocsg|Implst->letst=ifadd_ppx_contextthenSelected_ast.To_ocaml.copy_structurest|>Astlib.Ast_metadata.add_ppx_context_str~tool_name:"ppx_driver"|>Ocaml_to_input.copy_structureelsePpxlib_to_input.copy_structurestinoutput_stringocInput_version.Ast.Config.ast_impl_magic_number;output_valueocinput_name;output_valueocstmoduleRead_bin=structtypeast=Intfofsignature|Implofstructuretypet={ast:ast;input_name:string}letread_binaryfn=matchIn_channel.with_filefn~f:(from_channel~input_kind:Necessarily_binary)with|Ok{ast;input_name;_}->letast=matchastwith|Implstructure->Implstructure|Intfsignature->IntfsignatureinOk{ast;input_name}|Errore->Error(read_error_to_stringe)letget_astt=t.astletget_input_namet=t.input_nameendendmoduleSystem=structletrun_preprocessor~pp~input~output=letcommand=Printf.sprintf"%s %s > %s"pp(ifString.equalinput"-"then""elseCaml.Filename.quoteinput)(Caml.Filename.quoteoutput)inifCaml.Sys.commandcommand=0thenOk()elseError(command,Ast_io.fall_back_input_version)end