A declarative command line parser.
Declare what the program accepts, parse once, then read values back by
name. Nothing is printed and nothing is stopped: every failure comes
back as an error_t, and even --help is reported rather than acted
on, because a library has no business deciding that a process should
end or where its text should go. See “The library never prints” below.
type(cli_t) :: cli
type(error_t) :: err
integer(int64) :: seed
call cli%set_program("fairport", "Deterministic airport simulator")
call cli%add_positional("scenario", "Scenario script to run", required=.true.)
call cli%add_option("seed", "Master RNG seed", short="s", default="0")
call cli%add_flag("hash", "Print the event-log hash and exit")
call cli%parse(err)
if (.haserror. err) call err%fatal()
if (cli%help_requested()) then
write (*, "(a)") cli%help_text()
stop 0
end if
call cli%get("seed", seed, err)
if (cli%is_set("hash")) ...
--name value and --name=value-s value for an option given a short name-- ends option parsing; everything after it is a positional, even if
it starts with a dash-h and --help are reserved: they set help_requested() and are
never passed through as optionsA flag takes no value. --flag=value is an error rather than a silently
ignored value.
Out of scope in this version: subcommands, grouped short flags (-abc),
environment variable fallbacks, and range validation.
The last occurrence wins, which is what a shell user expects when they
edit a long command line by appending to it. The earlier values are not
lost silently, though: occurrences(name) reports how many times an
option was given, so a program that wants to reject a repeat, or warn
about one, can.
help_text() returns the text. help_requested() reports the request.
Neither writes to a unit, and nothing here calls stop. A library that
prints has decided that the program has a terminal, that the text goes
to output_unit rather than to a log or a socket, and that English is
wanted. A library that stops has decided that the program has nothing
left to clean up. Those are the caller’s decisions.
parse_args is the real implementation and takes the arguments as an
array. parse only collects get_command_argument and forwards to it.
Tests therefore never need a real command line, and every case below –
including the ones that are errors – is exercised without a shell.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | private, | parameter | :: | INITIAL_CAPACITY | = | 8_default_int |
Entries reserved on the first registration; grown by doubling. |
| integer(kind=default_int), | private, | parameter | :: | KIND_FLAG | = | 2_default_int |
An entry that takes no value. |
| integer(kind=default_int), | private, | parameter | :: | KIND_OPTION | = | 1_default_int |
An entry that takes a value. |
| integer(kind=default_int), | private, | parameter | :: | KIND_POSITIONAL | = | 3_default_int |
An entry filled from the argument order rather than by name. |
A command line interface: what is accepted, and what was given.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | private | :: | count | = | 0_default_int |
Entries in use; |
|
| type(entry_t), | private, | allocatable | :: | entries(:) |
Declared entries, in registration order, so |
||
| logical, | private | :: | parsed | = | .false. |
Set once a parse has completed, successfully or not. |
|
| type(string_type), | private | :: | program_name |
Name used in the usage line. |
|||
| type(string_type), | private | :: | summary |
One-line description of the program. |
|||
| logical, | private | :: | wants_help | = | .false. |
Set when |
| procedure, public :: add_flag => cli_add_flag | |
| procedure, public :: add_option => cli_add_option | |
| procedure, public :: add_positional => cli_add_positional | |
| generic, public :: get => get_string, get_char, get_int32, get_int64, get_real_sp, get_real_dp, get_logical | |
| procedure, public :: has => cli_has | |
| procedure, public :: help_requested => cli_help_requested | |
| procedure, public :: help_text => cli_help_text | |
| procedure, public :: is_set => cli_is_set | |
| procedure, public :: occurrences => cli_occurrences | |
| procedure, public :: parse => cli_parse | |
| procedure, public :: parse_args => cli_parse_args | |
| procedure, public :: set_program => cli_set_program | |
| procedure, private :: find => cli_find | |
| procedure, private :: get_char => cli_get_char | |
| procedure, private :: get_int32 => cli_get_int32 | |
| procedure, private :: get_int64 => cli_get_int64 | |
| procedure, private :: get_logical => cli_get_logical | |
| procedure, private :: get_real_dp => cli_get_real_dp | |
| procedure, private :: get_real_sp => cli_get_real_sp | |
| procedure, private :: get_string => cli_get_string | |
| procedure, private :: reserve => cli_reserve |
One declared option, flag or positional.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| type(string_type), | private | :: | default |
The declared default, kept so that a second |
|||
| type(string_type), | private | :: | help |
One-line description, for |
|||
| integer(kind=default_int), | private | :: | kind | = | KIND_OPTION |
One of the KIND_* codes. |
|
| type(string_type), | private | :: | name |
Long name, without the leading dashes. |
|||
| logical, | private | :: | required | = | .false. |
Whether a missing positional is an error. |
|
| integer(kind=default_int), | private | :: | seen | = | 0_default_int |
How many times this entry appeared on the command line. |
|
| type(string_type), | private | :: | short |
Single-character short name, or empty if there is none. |
|||
| type(string_type), | private | :: | value |
Current value: the default until parsing overwrites it. |
Whether any declared entry has this kind.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| integer(kind=default_int), | intent(in) | :: | kind |
Index of the entry with this long name, or 0.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name to look for, without dashes. |
Index of the entry with this short name, or 0. Empty never matches.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | short |
Whether name was declared at all.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name to look for. |
Whether -h or --help was given.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this |
The help text, as a string. Nothing is printed; see the module note.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this |
Whether name appeared on the command line.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name to look for. |
How many times name appeared on the command line.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name to look for. |
The left-hand column of one help line.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(entry_t), | intent(in) | :: | entry |
Whether arg is a --name form. A bare -- is not.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | arg |
Whether arg is a -s form. A bare - is not, and neither is a
negative number, which must be able to reach a positional.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | arg |
Whether arg is a negative number rather than an option.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | arg |
Token to classify; known to start with |
Declare a flag, which takes no value.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name, given on the command line as |
||
| character(len=*), | intent(in) | :: | help |
One-line description. |
||
| character(len=*), | intent(in), | optional | :: | short |
Single-character short name. |
Declare an option that takes a value.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name, given on the command line as |
||
| character(len=*), | intent(in) | :: | help |
One-line description. |
||
| character(len=*), | intent(in), | optional | :: | short |
Single-character short name, given as |
|
| character(len=*), | intent(in), | optional | :: | default |
Value |
Declare a positional argument, filled in registration order.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Name used to read the value back, and in the usage line. |
||
| character(len=*), | intent(in) | :: | help |
One-line description. |
||
| logical, | intent(in), | optional | :: | required |
Whether a parse without it is |
|
| character(len=*), | intent(in), | optional | :: | default |
Value |
Read a value into a deferred-length character.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| character(len=:), | intent(out), | allocatable | :: | value |
The last value given, or the declared default. |
|
| type(error_t), | intent(inout), | optional | :: | err |
Set to |
Read a value as integer(int32), strictly.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| integer(kind=int32), | intent(out) | :: | value |
Parsed value; zero when the conversion failed. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set on an undeclared name or an unparsable value. |
Read a value as integer(int64), strictly.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| integer(kind=int64), | intent(out) | :: | value |
Parsed value; zero when the conversion failed. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set on an undeclared name or an unparsable value. |
Read a value as logical.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| logical, | intent(out) | :: | value |
Parsed value; false when the conversion failed. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set on an undeclared name or an unrecognised spelling. |
Read a value as real(dp), strictly.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| real(kind=dp), | intent(out) | :: | value |
Parsed value; zero when the conversion failed. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set on an undeclared name or an unparsable value. |
Read a value as real(sp), strictly.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| real(kind=sp), | intent(out) | :: | value |
Parsed value; zero when the conversion failed. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set on an undeclared name or an unparsable value. |
Read a value as a string_type.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Long name of a declared entry. |
||
| type(string_type), | intent(out) | :: | value |
The last value given, or the declared default. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set to |
Parse the real command line.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this | |||
| type(error_t), | intent(inout), | optional | :: | err |
Set on a parse or validation failure. |
Parse an explicit argument list.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this | |||
| type(string_type), | intent(in) | :: | args(:) |
Arguments, excluding the program name. |
||
| type(error_t), | intent(inout), | optional | :: | err |
Set on a parse or validation failure. |
Make room for one more entry, doubling the store when it is full.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this |
Name and one-line description, used by help_text.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(inout) | :: | this | |||
| character(len=*), | intent(in) | :: | name |
Program name as it should appear in the usage line. |
||
| character(len=*), | intent(in) | :: | summary |
One-line description of what the program does. |
Set err if it is present, and do nothing if it is not.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(error_t), | intent(inout), | optional | :: | err | ||
| integer(kind=default_int), | intent(in) | :: | code | |||
| character(len=*), | intent(in) | :: | message |
Index of the next unfilled positional, advancing used.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(cli_t), | intent(in) | :: | this | |||
| integer(kind=default_int), | intent(inout) | :: | used |
Positionals consumed so far; incremented when one is taken. |
||
| integer(kind=default_int), | intent(out) | :: | idx |
Index of the positional taken, or 0 when they are all filled. |