Next: , Previous: getopt, Up: Top   [Contents]


30 getopt_io

%--------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%--------------------------------------------------%
% Copyright (C) 2005-2007, 2011 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING in the Mercury distribution.
%--------------------------------------------------%
%
% File: getopt_io.m
% Authors: fjh, zs
% Stability: medium
%
% This module exports the predicate process_options/6, which can be
% used to parse command-line options.
%
% This version allows both short (single-character) options and GNU-style long
% options. It also has the GNU extension of recognizing options anywhere in
% the command-line, not just at the start.
%
% To use this module, you must provide an `option' type which is an
% enumeration of all your different options. You must provide predicates
% `short_option(Char, Option)' and `long_option(String, Option)'
% which convert the short and/or long names for the option to this
% enumeration type. (An option can have as many names as you like,
% long or short.) You must provide a predicate
% `option_default(Option, OptionData)' which specifies both the type
% and the default value for every option. You may optionally provide
% a predicate `special_handler(Option, SpecialData, OptionTable,
% MaybeOptionTable)' for handling special option types. (See below.)
%
% We support the following "simple" option types:
%
%   - bool
%   - int
%   - maybe_int (which have a value of `no' or `yes(int)')
%   - string
%   - maybe_string (which have a value of `no' or `yes(string)')
%
% We also support one "accumulating" option type:
%
%   - accumulating (which accumulates a list of strings)
%
% And the following "special" option types:
%
%   - special
%   - bool_special
%   - int_special
%   - string_special
%   - maybe_string_special
%   - file_special
%
% For the "simple" option types, if there are multiple occurrences of the same
% option on the command-line, then the last (right-most) occurrence will take
% precedence. For "accumulating" options, multiple occurrences will be
% appended together into a list.
%
% With the exception of file_special, the "special" option types are handled
% by a special option handler (see `special_handler' below), which may perform
% arbitrary modifications to the option_table. For example, an option which
% is not yet implemented could be handled by a special handler which produces
% an error report, or an option which is a synonym for a set of more
% "primitive" options could be handled by a special handler which sets those
% "primitive" options.
%
% It is an error to use a "special" option for which there is no handler, or
% for which the handler fails.
%
% Boolean (i.e. bool or bool_special), maybe_int, maybe_string
% and accumulating options can be negated. Negating an accumulating
% option empties the accumulated list of strings.
% Single-character options can be negated by following them
% with another `-', e.g. `-x-' will negate the `-x' option.
% Long options can be negated by preceding them with `--no-',
% e.g. `--no-foo' will negate the `--foo' option.
%
% The file_special option type requires no handler, and is implemented
% entirely by this module. It always takes a single argument, a file name.
% Its handling always consists of reading the named file, converting its
% contents into a sequence of words separated by white space, and interpreting
% those words as options in the usual manner.
%
% Note that arguments following an option may be separated from the option by
% either whitespace or an equals, `=', character, e.g. `--foo 3' and `--foo=3'
% both specify the option `--foo' with the integer argument `3'.
%
% If the argument `--' is encountered on the command-line then option
% processing will immediately terminate, without processing any remaining
% options.
%
%--------------------------------------------------%
%--------------------------------------------------%

:- module getopt_io.
:- interface.

:- import_module bool.
:- import_module char.
:- import_module io.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module set.

% process_options(OptionOps, Args, NonOptionArgs, Result)
% process_options(OptionOps, Args, OptionArgs, NonOptionArgs, Result)
%
%   Scans through 'Args' looking for options, places all the option
%   arguments in `OptionArgs', places all the non-option arguments in
%   'NonOptionArgs', and records the options in the `OptionTable'.
%   `OptionTable' is a map from a user-defined option type to option_data.
%   If an invalid option is encountered, we return `error(Message)'
%   otherwise we return `ok(OptionTable)' in 'Result'.
%
%   The argument `OptionOps' is a structure holding three or four
%   predicates used to categorize a set of options. Their
%   interfaces should be like these:
%
% :- pred short_option(char::in, option::out) is semidet.
%   True if the character names a valid single-character option.
%
% :- pred long_option(string::in, option::out) is semidet.
%   True if the string names a valid long option.
%
% :- pred option_default(option::out, option_data::out) is multi.
%   Nondeterministically returns all the options with their
%   corresponding types and default values.
%
% :- pred special_handler(option::in, special_data::in,
%   option_table::in, maybe_option_table(_)::out) is semidet.
%   This predicate is invoked whenever getopt finds an option
%   (long or short) designated as special, with special_data holding
%   the argument of the option (if any). The predicate can change the
%   option table in arbitrary ways in the course of handling the option,
%   or it can return an error message.
%   The canonical examples of special options are -O options in compilers,
%   which set many other options at once.

:- pred process_options(option_ops(OptionType)::in(option_ops),
    list(string)::in, list(string)::out, maybe_option_table(OptionType)::out,
    io::di, io::uo) is det.

:- pred process_options(option_ops(OptionType)::in(option_ops),
    list(string)::in, list(string)::out, list(string)::out,
    maybe_option_table(OptionType)::out, io::di, io::uo) is det.

% process_options_track(OptionOps, Args, OptionArgs,
%       NonOptionArgs, OptionTable0, Result, OptionsSet)

:- pred process_options_track(
    option_ops_track(OptionType)::in(option_ops_track),
    list(string)::in, list(string)::out, list(string)::out,
    option_table(OptionType)::in, maybe_option_table(OptionType)::out,
    set(OptionType)::out, io::di, io::uo) is det.

% Variants of the above that return structured errors.
% These behave as the above versions except that any error values returned are
% members of the option_error/1 type rather than strings.

:- pred process_options_se(option_ops(OptionType)::in(option_ops),
    list(string)::in, list(string)::out,
    maybe_option_table_se(OptionType)::out, io::di, io::uo) is det.

:- pred process_options_se(option_ops(OptionType)::in(option_ops),
    list(string)::in, list(string)::out, list(string)::out,
    maybe_option_table_se(OptionType)::out, io::di, io::uo) is det.

:- pred process_options_track_se(
    option_ops_track(OptionType)::in(option_ops_track),
    list(string)::in, list(string)::out, list(string)::out,
    option_table(OptionType)::in, maybe_option_table_se(OptionType)::out,
    set(OptionType)::out, io::di, io::uo) is det.

:- pred init_option_table(
    pred(OptionType, option_data)::in(pred(out, out) is nondet),
    option_table(OptionType)::out) is det.

:- pred init_option_table_multi(
    pred(OptionType, option_data)::in(pred(out, out) is multi),
    option_table(OptionType)::out) is det.

:- type option_ops(OptionType)
    --->    option_ops(
                pred(char, OptionType),         % short_option
                pred(string, OptionType),       % long_option
                pred(OptionType, option_data)   % option_default
            )
    ;       option_ops(
                pred(char, OptionType),         % short_option
                pred(string, OptionType),       % long_option
                pred(OptionType, option_data),  % option_default
                pred(OptionType, special_data,  % special option handler
                    option_table(OptionType),
                    maybe_option_table(OptionType))
            )
    ;       option_ops_multi(
                pred(char, OptionType),         % short_option
                pred(string, OptionType),       % long_option
                pred(OptionType, option_data)   % option_default
            )
    ;       option_ops_multi(
                pred(char, OptionType),         % short_option
                pred(string, OptionType),       % long_option
                pred(OptionType, option_data),  % option_default
                pred(OptionType, special_data,  % special option handler
                    option_table(OptionType),
                    maybe_option_table(OptionType))
            ).

:- type option_ops_track(OptionType)
    --->    option_ops_track(
                pred(char, OptionType),         % short_option
                pred(string, OptionType),       % long_option
                pred(OptionType, special_data,  % special option handler
                    option_table(OptionType),
                    maybe_option_table(OptionType),
                    set(OptionType))
            ).

:- inst option_ops ==
    bound((
        option_ops(
            pred(in, out) is semidet,               % short_option
            pred(in, out) is semidet,               % long_option
            pred(out, out) is nondet                % option_default
        )
    ;   option_ops_multi(
            pred(in, out) is semidet,               % short_option
            pred(in, out) is semidet,               % long_option
            pred(out, out) is multi                 % option_default
        )
    ;   option_ops(
            pred(in, out) is semidet,               % short_option
            pred(in, out) is semidet,               % long_option
            pred(out, out) is nondet,               % option_default
            pred(in, in, in, out) is semidet        % special handler
        )
    ;   option_ops_multi(
            pred(in, out) is semidet,               % short_option
            pred(in, out) is semidet,               % long_option
            pred(out, out) is multi,                % option_default
            pred(in, in, in, out) is semidet        % special handler
        )
    )).

:- inst option_ops_track ==
    bound((
        option_ops_track(
            pred(in, out) is semidet,               % short_option
            pred(in, out) is semidet,               % long_option
            pred(in, in, in, out, out) is semidet   % special handler
        )
    )).

:- type option_data
    --->    bool(bool)
    ;       int(int)
    ;       string(string)
    ;       maybe_int(maybe(int))
    ;       maybe_string(maybe(string))
    ;       accumulating(list(string))
    ;       special
    ;       bool_special
    ;       int_special
    ;       string_special
    ;       maybe_string_special
    ;       file_special.

:- type special_data
    --->    none
    ;       bool(bool)
    ;       int(int)
    ;       string(string)
    ;       maybe_string(maybe(string)).

:- type option_table(OptionType) == map(OptionType, option_data).

:- type maybe_option_table(OptionType)
    --->    ok(option_table(OptionType))
    ;       error(string).

:- type maybe_option_table_se(OptionType)
    --->    ok(option_table(OptionType))
    ;       error(option_error(OptionType)).

:- type option_error(OptionType)
    --->    unrecognized_option(string)
            % An option that is not recognized appeared on the command line.
            % The argument gives the option as it appeared on the command line.

    ;       option_error(OptionType, string, option_error_reason).
            % An error occurred with a specific option. The first two
            % arguments identify the option enumeration value and the string
            % that appeared on the command line for that option respectively.
            % The third argument describes the nature of the error with that
            % option.

:- type option_error_reason
    --->    unknown_type
            % No type for this option has been specified in the
            % `option_default'/2 predicate.

    ;       requires_argument
            % The option requires an argument but it occurred on the command
            % line without one.

    ;       does_not_allow_argument(string)
            % The option does not allow an argument but it was provided with
            % one on the command line.
            % The argument gives the contents of the argument position on the
            % command line.

    ;       cannot_negate
            % The option cannot be negated but its negated form appeared on the
            % command line.

    ;       special_handler_failed
            % The special option handler predicate for the option failed.

    ;       special_handler_missing
            % A special option handler predicate was not provided
            % for the option.

    ;       special_handler_error(string)
            % The special option handler predicate for the option returned an
            % error.
            % The argument is a string describing the error.

    ;       requires_numeric_argument(string)
            % The option requires a numeric argument but it occurred on the
            % command line with a non-numeric argument.
            % The argument gives the contents of the argument position on the
            % command line.

    ;       file_special_cannot_open(string, io.error)
            % The option is a file_special option whose argument is the file
            % named by the first argument.
            % Attempting to open this file resulted in the I/O error given
            % by the second argument.

    ;       file_special_cannot_read(string, io.error)
            % The option is a file_special option whose argument is the file
            % named by the first argument.
            % Attempting to read from this file resulted in the I/O error given
            % by the second argument.

    ;       file_special_contains_non_option_args(string).
            % The option is a file_special option whose argument is the file
            % named by the argument. This file contained some non-option
            % arguments.

:- func option_error_to_string(option_error(OptionType)) = string.

    % The following three predicates search the option table for
    % an option of the specified type; if it is not found, they
    % report an error by calling error/1.

:- pred lookup_bool_option(option_table(Option)::in, Option::in,
    bool::out) is det.
:- func lookup_bool_option(option_table(Option), Option) = bool.

:- pred lookup_int_option(option_table(Option)::in, Option::in,
    int::out) is det.
:- func lookup_int_option(option_table(Option), Option) = int.

:- pred lookup_string_option(option_table(Option)::in, Option::in,
    string::out) is det.
:- func lookup_string_option(option_table(Option), Option) = string.

:- pred lookup_maybe_int_option(option_table(Option)::in, Option::in,
    maybe(int)::out) is det.
:- func lookup_maybe_int_option(option_table(Option), Option) =
    maybe(int).

:- pred lookup_maybe_string_option(option_table(Option)::in,
    Option::in, maybe(string)::out) is det.
:- func lookup_maybe_string_option(option_table(Option), Option) =
    maybe(string).

:- pred lookup_accumulating_option(option_table(Option)::in,
    Option::in, list(string)::out) is det.
:- func lookup_accumulating_option(option_table(Option), Option) =
    list(string).

%--------------------------------------------------%
%--------------------------------------------------%


Next: , Previous: getopt, Up: Top   [Contents]