2019-05-12 19:31:22 +02:00
|
|
|
(* Parsing the command-line option for the Mini-ML compiler/interpreter *)
|
|
|
|
|
2019-05-15 15:03:15 +02:00
|
|
|
type options = {
|
|
|
|
input : string option;
|
|
|
|
eval : bool;
|
|
|
|
compile : string option;
|
|
|
|
libs : string list;
|
|
|
|
verbose : Utils.String.Set.t;
|
|
|
|
raw_edits : bool
|
|
|
|
}
|
|
|
|
|
2019-05-12 19:31:22 +02:00
|
|
|
let abort msg =
|
|
|
|
Utils.highlight (Printf.sprintf "Command-line error: %s" msg); exit 1
|
|
|
|
|
|
|
|
let printf = Printf.printf
|
|
|
|
let sprintf = Printf.sprintf
|
|
|
|
|
|
|
|
(* Help *)
|
|
|
|
|
|
|
|
let help () =
|
|
|
|
let file = Filename.basename Sys.argv.(0) in
|
|
|
|
printf "Usage: %s [<option> ...] [<input>.ml | \"-\"]\n" file;
|
|
|
|
print_endline "where <input>.mml is the Mini-ML source file (default: stdin),";
|
|
|
|
print_endline "and each <option> (if any) is one of the following:";
|
|
|
|
print_endline " -I <paths> Library paths (colon-separated)";
|
|
|
|
print_endline " -c [<file>.ml] Translate to OCaml in <file>.ml";
|
|
|
|
print_endline " (default: <input>.ml)";
|
|
|
|
print_endline " -e, --eval Interpret <input>.mml or stdin";
|
|
|
|
print_endline " --raw-edits Do not optimise translation edits";
|
2019-05-22 19:38:09 +02:00
|
|
|
print_endline " --verbose=<phases> Colon-separated phases: cmdline, lexer, parser";
|
2019-05-12 19:31:22 +02:00
|
|
|
print_endline " --version Short commit hash on stdout";
|
|
|
|
print_endline " -h, --help This help";
|
|
|
|
exit 0
|
|
|
|
|
|
|
|
(* Version *)
|
|
|
|
|
|
|
|
let version () = printf "%s\n" Version.version; exit 0
|
|
|
|
|
|
|
|
(* Specifying the command-line options a la GNU *)
|
|
|
|
|
|
|
|
let input = ref None
|
|
|
|
and eval = ref false
|
|
|
|
and compile = ref None
|
|
|
|
and verbose = ref Utils.String.Set.empty
|
|
|
|
and libs = ref []
|
|
|
|
and raw_edits = ref false
|
|
|
|
|
2019-05-15 15:03:15 +02:00
|
|
|
let verb_str = ref ""
|
|
|
|
|
2019-05-12 19:31:22 +02:00
|
|
|
let set_opt var err =
|
|
|
|
Some (fun x -> if !var = None then var := Some x else raise (Getopt.Error err))
|
|
|
|
|
|
|
|
let split_at_colon = Str.(split (regexp ":"))
|
|
|
|
|
|
|
|
let add_path p = libs := !libs @ split_at_colon p
|
|
|
|
|
|
|
|
let add_verbose d =
|
|
|
|
verbose := List.fold_left (Utils.swap Utils.String.Set.add)
|
|
|
|
!verbose
|
|
|
|
(split_at_colon d)
|
|
|
|
|
|
|
|
let specs =
|
|
|
|
let open! Getopt in [
|
|
|
|
'I', nolong, None, Some add_path;
|
|
|
|
'c', nolong, set compile (Some ""),
|
|
|
|
set_opt compile "Multiple OCaml outputs";
|
|
|
|
'e', "eval", set eval true, None;
|
|
|
|
noshort, "raw-edits", set raw_edits true, None;
|
|
|
|
noshort, "verbose", None, Some add_verbose;
|
|
|
|
'h', "help", Some help, None;
|
|
|
|
noshort, "version", Some version, None
|
|
|
|
]
|
|
|
|
;;
|
|
|
|
|
|
|
|
(* Handler of anonymous arguments *)
|
|
|
|
|
|
|
|
let anonymous arg =
|
|
|
|
match !input with
|
|
|
|
None -> input := Some arg
|
|
|
|
| Some _ -> abort (sprintf "Multiple inputs")
|
|
|
|
|
|
|
|
(* Checking options *)
|
|
|
|
|
|
|
|
let string_of convert = function
|
|
|
|
None -> "None"
|
|
|
|
| Some s -> sprintf "Some %s" (convert s)
|
|
|
|
|
|
|
|
let string_of_path p =
|
|
|
|
let apply s a = if a = "" then s else s ^ ":" ^ a
|
|
|
|
in List.fold_right apply p ""
|
|
|
|
|
|
|
|
let quote s = Printf.sprintf "\"%s\"" s
|
|
|
|
|
|
|
|
let print_opt () =
|
|
|
|
printf "COMMAND LINE\n";
|
|
|
|
printf "input = %s\n" (string_of quote !input);
|
|
|
|
printf "compile = %s\n" (string_of quote !compile);
|
|
|
|
printf "eval = %B\n" !eval;
|
|
|
|
printf "raw_edits = %b\n" !raw_edits;
|
2019-05-15 15:03:15 +02:00
|
|
|
printf "verbose = %s\n" !verb_str;
|
2019-05-12 19:31:22 +02:00
|
|
|
printf "libs = %s\n" (string_of_path !libs)
|
|
|
|
|
2019-05-15 15:03:15 +02:00
|
|
|
let check () =
|
|
|
|
let () =
|
|
|
|
if Utils.String.Set.mem "cmdline" !verbose then print_opt () in
|
|
|
|
|
|
|
|
let input =
|
|
|
|
match !input with
|
|
|
|
None | Some "-" ->
|
|
|
|
if !compile <> None then
|
|
|
|
abort "An input file is missing (for compilation)."
|
|
|
|
else !input
|
|
|
|
| Some file_path ->
|
|
|
|
if Filename.check_suffix file_path ".mml"
|
|
|
|
then if Sys.file_exists file_path
|
|
|
|
then Some file_path
|
|
|
|
else abort "Source file not found."
|
|
|
|
else abort "Source file lacks the extension .mml." in
|
|
|
|
|
|
|
|
let compile =
|
|
|
|
match !compile with
|
|
|
|
Some _ when !eval -> abort "Options -e and -c are mutually exclusive."
|
|
|
|
| None | Some "-" -> !compile
|
|
|
|
| Some "" ->
|
|
|
|
(match input with
|
|
|
|
None | Some "-" -> abort "The target OCaml filename is missing."
|
|
|
|
| Some file -> Some (Filename.remove_extension file ^ ".ml"))
|
|
|
|
| Some compile' ->
|
|
|
|
if Filename.check_suffix compile' ".ml"
|
|
|
|
then !compile
|
|
|
|
else abort "The extension of the target OCaml file is not .ml" in
|
|
|
|
|
|
|
|
(* Exporting remaining options as non-mutable values *)
|
|
|
|
|
|
|
|
let eval = !eval
|
|
|
|
and verbose = !verbose
|
|
|
|
and libs = !libs
|
|
|
|
and raw_edits = !raw_edits in
|
|
|
|
|
|
|
|
let () =
|
2019-05-12 19:31:22 +02:00
|
|
|
if Utils.String.Set.mem "cmdline" verbose then
|
|
|
|
begin
|
|
|
|
printf "\nEXPORTED COMMAND LINE\n";
|
|
|
|
printf "input = %s\n" (string_of quote input);
|
|
|
|
printf "compile = %s\n" (string_of quote compile);
|
|
|
|
printf "eval = %B\n" eval;
|
|
|
|
printf "raw_edits = %B\n" raw_edits;
|
2019-05-15 15:03:15 +02:00
|
|
|
printf "verbose = %s\n" !verb_str;
|
2019-05-12 19:31:22 +02:00
|
|
|
printf "I = %s\n" (string_of_path libs)
|
|
|
|
end
|
2019-05-15 15:03:15 +02:00
|
|
|
|
|
|
|
in {input; eval; compile; libs; verbose; raw_edits}
|
|
|
|
|
|
|
|
(* Parsing the command-line options *)
|
|
|
|
|
|
|
|
let read () =
|
|
|
|
try
|
|
|
|
Getopt.parse_cmdline specs anonymous;
|
|
|
|
(verb_str :=
|
|
|
|
let apply e a =
|
|
|
|
if a <> "" then Printf.sprintf "%s, %s" e a else e
|
|
|
|
in Utils.String.Set.fold apply !verbose "");
|
|
|
|
check ()
|
|
|
|
with Getopt.Error msg -> abort msg
|