From 827183c869020e5a0b0e08c46eb4c34e8abdd3d0 Mon Sep 17 00:00:00 2001 From: Richard Huxton Date: Tue, 2 Jul 2024 19:08:01 +0000 Subject: [PATCH 1/3] cookbook: parse-command-line-arguments (2 recipes) --- .../parse-command-line-arguments/00-stdlib.ml | 74 +++++++++++++++++++ .../01-cmdliner.ml | 49 ++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 data/cookbook/parse-command-line-arguments/00-stdlib.ml create mode 100644 data/cookbook/parse-command-line-arguments/01-cmdliner.ml diff --git a/data/cookbook/parse-command-line-arguments/00-stdlib.ml b/data/cookbook/parse-command-line-arguments/00-stdlib.ml new file mode 100644 index 0000000000..aa82ab6298 --- /dev/null +++ b/data/cookbook/parse-command-line-arguments/00-stdlib.ml @@ -0,0 +1,74 @@ +--- +packages: [] +discussion: | + For a longer introduction, see the [Command-line arguments](https://ocaml.org/docs/cli-arguments) tutorial in the docs. + The standard library documents the [Arg module](https://ocaml.org/manual/latest/api/Arg.html). +--- +(* At the lowest-level we can interact with `Sys.argv` and access arguments by index. + + With this approach all our error checking will have to be entirely manual. + If we pass a non-numeric first argument then `int_of_string` raises an exception. + A leading dash on our second argument will just be treated as part of the + name - there is no option handling here. +*) +let () = + if Array.length Sys.argv <> 3 then ( + print_endline "Usage: command "; + exit 1) + else + let command_name = Sys.argv.(0) in + let num_repeats = Sys.argv.(1) |> int_of_string in + let greeting_name = Sys.argv.(2) in + for i = 1 to num_repeats do + Printf.printf "%d: Command '%s' says 'hello %s'\n" i command_name + greeting_name + done + +(* The `Arg` module from the standard library gives us a higher-level + interface than `Sys.argv`. It is a good choice for basic command-line + applications. + + We can handle options with values and repeating positional arguments too. + It also automatically provides a `--help` option to our application. + + The `Arg` module is quite imperative and updates references to a value. + Typically we initialise each option with a default value ("en", 1 etc). + + To handle multiple positional (anonymous) arguments we need a function like + `record_anon_arg` to construct a list or otherwise accumulate each item. + + With `speclist` we define the arguments we will parse. + Each argument needs the option-characters themselves, the action that will + be run when matched and a short explanatory piece of text. + + Finally, `Arg.parse` will either print an error message or succed and update + the global refs. +*) +let greet language name = + match language with + | "en" -> Printf.printf "Hello %s\n" name + | "fr" -> Printf.printf "Bonjour %s\n" name + | "sp" -> Printf.printf "Hola %s\n" name + | _ -> Printf.printf "Hi %s\n" name + +let usage_msg = "arg_module [-l en|fr|sp] [-r ] name1 [name2] ..." +let language = ref "en" +let num_repeats = ref 1 +let names = ref [] + +let record_anon_arg arg = names := arg :: !names + +let speclist = + [ + ("-l", Arg.Set_string language, "Language to use (en|fr|sp, default en)"); + ( "-r", + Arg.Set_int num_repeats, + "Number of times to repeat greeting (default 1)" ); + ] + +let () = + Arg.parse speclist record_anon_arg usage_msg; + for i = 1 to !num_repeats do + Printf.printf "Greeting %d of %d\n" i !num_repeats; + List.iter (greet !language) !names + done diff --git a/data/cookbook/parse-command-line-arguments/01-cmdliner.ml b/data/cookbook/parse-command-line-arguments/01-cmdliner.ml new file mode 100644 index 0000000000..7436f8b81e --- /dev/null +++ b/data/cookbook/parse-command-line-arguments/01-cmdliner.ml @@ -0,0 +1,49 @@ +--- +packages: +- name: cmdliner + tested_version: 1.3.0 + used_libraries: + - cmdliner +discussion: | + The main [Cmdliner docs](https://erratique.ch/software/cmdliner/doc/index.html) + offer a quick tutorial, guides to how the parsing and man-page construction + works as well as example parsers and links to API docs. +--- +(* The Cmdliner package offers a sophisticated compositional way of handling + command-line parsing. It handles options, positional arguments and + sub-commands. It will automatically generate help and a man-page. + + The core of our application in this recipe is the `greeter` function - + Cmdliner will call this for us. + + For `num_repeats` we want an optional integer (with a default of 1) + + For the `names` we want all the positional arguments in a list. + + The top-level `Term` of our parser-definition combines the function to call + along with the arguments to parse. + + The `cmd` represents the body of our application. + + Try to parse the command-line and call "greeter" if successful. If not, + prints formatted help-text and returns a non-zero exit code. +*) +open Cmdliner + +let greeter num_repeats names = + for i = 1 to num_repeats do + Printf.printf "Greeting %d of %d\n" i num_repeats; + List.iter (fun name -> Printf.printf "Hello %s\n" name) names + done + +let num_repeats = + let doc = "Repeat the greeting $(docv) times." in + Arg.(value & opt int 1 & info [ "r"; "repeat" ] ~docv:"REPEAT" ~doc) + +let names = Arg.(non_empty & pos_all string [] & info [] ~docv:"NAME") + +let recipe_t = Term.(const greeter $ num_repeats $ names) + +let cmd = Cmd.v (Cmd.info "cmdliner_module") recipe_t + +let () = exit (Cmd.eval cmd) From c7e0ddfcfdf95ec78bf5d9f1460bc02a7f015c32 Mon Sep 17 00:00:00 2001 From: Christine Rose Date: Thu, 4 Jul 2024 04:29:04 -0700 Subject: [PATCH 2/3] minor grammar --- .../cookbook/parse-command-line-arguments/00-stdlib.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/cookbook/parse-command-line-arguments/00-stdlib.ml b/data/cookbook/parse-command-line-arguments/00-stdlib.ml index aa82ab6298..05a6abc8ab 100644 --- a/data/cookbook/parse-command-line-arguments/00-stdlib.ml +++ b/data/cookbook/parse-command-line-arguments/00-stdlib.ml @@ -4,10 +4,10 @@ discussion: | For a longer introduction, see the [Command-line arguments](https://ocaml.org/docs/cli-arguments) tutorial in the docs. The standard library documents the [Arg module](https://ocaml.org/manual/latest/api/Arg.html). --- -(* At the lowest-level we can interact with `Sys.argv` and access arguments by index. +(* At the lowest level, we can interact with `Sys.argv` and access arguments by index. With this approach all our error checking will have to be entirely manual. - If we pass a non-numeric first argument then `int_of_string` raises an exception. + If we pass a non-numeric first argument, then `int_of_string` raises an exception. A leading dash on our second argument will just be treated as part of the name - there is no option handling here. *) @@ -28,16 +28,16 @@ let () = interface than `Sys.argv`. It is a good choice for basic command-line applications. - We can handle options with values and repeating positional arguments too. + We can handle options with values and repeating positional arguments, too. It also automatically provides a `--help` option to our application. The `Arg` module is quite imperative and updates references to a value. Typically we initialise each option with a default value ("en", 1 etc). - To handle multiple positional (anonymous) arguments we need a function like + To handle multiple positional (anonymous) arguments, we need a function like `record_anon_arg` to construct a list or otherwise accumulate each item. - With `speclist` we define the arguments we will parse. + With `speclist`, we define the arguments we will parse. Each argument needs the option-characters themselves, the action that will be run when matched and a short explanatory piece of text. From 130dfd98c998b84c008dbf56b0bb615d0cbfc3a5 Mon Sep 17 00:00:00 2001 From: Christine Rose Date: Thu, 4 Jul 2024 04:31:34 -0700 Subject: [PATCH 3/3] minor grammar and missing pronoun --- .../parse-command-line-arguments/01-cmdliner.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/cookbook/parse-command-line-arguments/01-cmdliner.ml b/data/cookbook/parse-command-line-arguments/01-cmdliner.ml index 7436f8b81e..c95efc4eec 100644 --- a/data/cookbook/parse-command-line-arguments/01-cmdliner.ml +++ b/data/cookbook/parse-command-line-arguments/01-cmdliner.ml @@ -10,23 +10,23 @@ discussion: | works as well as example parsers and links to API docs. --- (* The Cmdliner package offers a sophisticated compositional way of handling - command-line parsing. It handles options, positional arguments and - sub-commands. It will automatically generate help and a man-page. + command-line parsing. It handles options, positional arguments, and + subcommands. It will automatically generate help and a manpage. - The core of our application in this recipe is the `greeter` function - + The core of our application in this recipe is the `greeter` function. Cmdliner will call this for us. - For `num_repeats` we want an optional integer (with a default of 1) + For `num_repeats`, we want an optional integer (with a default of 1) - For the `names` we want all the positional arguments in a list. + For the `names`, we want all the positional arguments in a list. The top-level `Term` of our parser-definition combines the function to call along with the arguments to parse. The `cmd` represents the body of our application. - Try to parse the command-line and call "greeter" if successful. If not, - prints formatted help-text and returns a non-zero exit code. + Try to parse the command line and call "greeter" if successful. If not, + it prints formatted help-text and returns a non-zero exit code. *) open Cmdliner