(**************************************************************************) (* *) (* OCaml *) (* *) (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 1996 Institut National de Recherche en Informatique et *) (* en Automatique. *) (* *) (* All rights reserved. This file is distributed under the terms of *) (* the GNU Lesser General Public License version 2.1, with the *) (* special exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) open Format include Topcommon include Topeval (* Read and execute commands from a file, or from stdin if [name] is "". *) let use_print_results = ref true let use_channel ppf ~wrap_in_module ic name filename = let lb = Lexing.from_channel ic in Warnings.reset_fatal (); Location.init lb filename; (* Skip initial #! line if any *) Lexer.skip_hash_bang lb; Misc.protect_refs [ R (Location.input_name, filename); R (Location.input_lexbuf, Some lb); ] (fun () -> try List.iter (fun ph -> let ph = preprocess_phrase ppf ph in if not (execute_phrase !use_print_results ppf ph) then raise Exit) (if wrap_in_module then parse_mod_use_file name lb else !parse_use_file lb); true with | Exit -> false | Sys.Break -> fprintf ppf "Interrupted.@."; false | x -> Location.report_exception ppf x; false) let use_output ppf command = let fn = Filename.temp_file "ocaml" "_toploop.ml" in Misc.try_finally ~always:(fun () -> try Sys.remove fn with Sys_error _ -> ()) (fun () -> match Printf.ksprintf Sys.command "%s > %s" command (Filename.quote fn) with | 0 -> let ic = open_in_bin fn in Misc.try_finally ~always:(fun () -> close_in ic) (fun () -> use_channel ppf ~wrap_in_module:false ic "" "(command-output)") | n -> fprintf ppf "Command exited with code %d.@." n; false) let use_file ppf ~wrap_in_module name = match name with | "" -> use_channel ppf ~wrap_in_module stdin name "(stdin)" | _ -> match Load_path.find name with | filename -> let ic = open_in_bin filename in Misc.try_finally ~always:(fun () -> close_in ic) (fun () -> use_channel ppf ~wrap_in_module ic name filename) | exception Not_found -> fprintf ppf "Cannot find file %s.@." name; false let mod_use_file ppf name = use_file ppf ~wrap_in_module:true name let use_file ppf name = use_file ppf ~wrap_in_module:false name let use_silently ppf name = Misc.protect_refs [ R (use_print_results, false) ] (fun () -> use_file ppf name) let load_file = load_file false (* Execute a script. If [name] is "", read the script from stdin. *) let run_script ppf name args = override_sys_argv args; Compmisc.init_path ~dir:(Filename.dirname name) (); (* Note: would use [Filename.abspath] here, if we had it. *) begin try toplevel_env := Compmisc.initial_env() with Env.Error _ | Typetexp.Error _ as exn -> Location.report_exception ppf exn; raise (Compenv.Exit_with_status 2) end; Sys.interactive := false; run_hooks After_setup; let explicit_name = (* Prevent use_silently from searching in the path. *) if name <> "" && Filename.is_implicit name then Filename.concat Filename.current_dir_name name else name in use_silently ppf explicit_name (* Toplevel initialization. Performed here instead of at the beginning of loop() so that user code linked in with ocamlmktop can call directives from Topdirs. *) let _ = if !Sys.interactive then (* PR#6108 *) invalid_arg "The ocamltoplevel.cma library from compiler-libs \ cannot be loaded inside the OCaml toplevel"; Sys.interactive := true; Topeval.init () let find_ocamlinit () = let ocamlinit = ".ocamlinit" in if Sys.file_exists ocamlinit then Some ocamlinit else let getenv var = match Sys.getenv var with | exception Not_found -> None | "" -> None | v -> Some v in let exists_in_dir dir file = match dir with | None -> None | Some dir -> let file = Filename.concat dir file in if Sys.file_exists file then Some file else None in let home_dir () = getenv "HOME" in let config_dir () = if Sys.win32 then None else match getenv "XDG_CONFIG_HOME" with | Some _ as v -> v | None -> match home_dir () with | None -> None | Some dir -> Some (Filename.concat dir ".config") in let init_ml = Filename.concat "ocaml" "init.ml" in match exists_in_dir (config_dir ()) init_ml with | Some _ as v -> v | None -> exists_in_dir (home_dir ()) ocamlinit let load_ocamlinit ppf = if !Clflags.noinit then () else match !Clflags.init_file with | Some f -> if Sys.file_exists f then ignore (use_silently ppf f) else fprintf ppf "Init file not found: \"%s\".@." f | None -> match find_ocamlinit () with | None -> () | Some file -> ignore (use_silently ppf file) (* The interactive loop *) exception PPerror let loop ppf = Clflags.debug := true; Location.formatter_for_warnings := ppf; if not !Clflags.noversion then fprintf ppf " OCaml version %s%s%s@.@." Config.version (if Topeval.implementation_label = "" then "" else " - ") Topeval.implementation_label; begin try initialize_toplevel_env () with Env.Error _ | Typetexp.Error _ as exn -> Location.report_exception ppf exn; raise (Compenv.Exit_with_status 2) end; let lb = Lexing.from_function refill_lexbuf in Location.init lb "//toplevel//"; Location.input_name := "//toplevel//"; Location.input_lexbuf := Some lb; Location.input_phrase_buffer := Some phrase_buffer; Sys.catch_break true; run_hooks After_setup; load_ocamlinit ppf; while true do let snap = Btype.snapshot () in try Lexing.flush_input lb; (* Reset the phrase buffer when we flush the lexing buffer. *) Buffer.reset phrase_buffer; Location.reset(); Warnings.reset_fatal (); first_line := true; let phr = try !parse_toplevel_phrase lb with Exit -> raise PPerror in let phr = preprocess_phrase ppf phr in Env.reset_cache_toplevel (); ignore(execute_phrase true ppf phr) with | End_of_file -> raise (Compenv.Exit_with_status 0) | Sys.Break -> fprintf ppf "Interrupted.@."; Btype.backtrack snap | PPerror -> () | x -> Location.report_exception ppf x; Btype.backtrack snap done