123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220openImportletwith_outputfn~binary~f=matchfnwith|None|Some"-"->fstdout|Somefn->Out_channel.with_filefn~binary~f;;moduleKind=structtypet=Intf|Implletof_filenamefn:toption=ifCaml.Filename.check_suffixfn".ml"thenSomeImplelseifCaml.Filename.check_suffixfn".mli"thenSomeIntfelseNone;;letdescribe=function|Impl->"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=function|Intf_->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_binaryletparse_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};Lexer.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.lengthOcaml_common.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_magicchwithErrors->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.subOcaml_common.Config.ast_impl_magic_number~pos:0~len:9)||String.equal(String.subs~pos:0~len:9)(String.subOcaml_common.Config.ast_intf_magic_number~pos:0~len:9)thenError(Unknown_version(s,fall_back_input_version))else(handle_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|>Ocaml_common.Ast_mapper.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|>Ocaml_common.Ast_mapper.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_valueocstendmoduleSystem=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